游戏引擎学习第89天

news2025/2/7 6:55:14

回顾

由于一直没有渲染器,终于决定开始动手做一个渲染器,虽然开始时并不确定该如何进行,但一旦开始做,发现这其实是正确的决定。因此,接下来可能会花一到两周的时间来编写渲染器,甚至可能更长时间,因为写完整个渲染器需要时间。即使不写整个渲染器,至少会写够多的部分来解决当前的问题。因为之前有很多冗余的代码在没有渲染器的情况下产生了不少麻烦,这时候不得不决定开始着手渲染器的开发。

另一个考虑因素是,地面渲染代码也已经开始有了雏形,想要支持多层地面,并希望实现这一目标。面临的一个问题是,目前没有缩放功能,无法测试地面物件的渲染效果,尤其是想实现低层次的物体看起来更远,需要一种能够模拟这种效果的方式。而为了测试这一点,即使渲染器不完美,至少需要一个简单的渲染器来验证它是否可行,确保渲染效果能够满足需求。因此,决定继续推进渲染器开发,直到遇到需要调整方向的情况。

黑板:渲染

为了让渲染器能够正常工作,游戏代码需要发出一系列指令,告诉渲染器应该做什么,但这些指令不会立刻执行。换句话说,游戏代码会发出指令,当需要渲染某些内容时,它会把这些指令放入一个缓冲区。这些指令可能包括渲染某个物体的头部,或者渲染一些粒子效果等。这个缓冲区被称为“推送缓冲区”,原因是因为可以不断向缓冲区中推送新的指令,缓冲区不会缩小,只会持续增加,直到渲染器最终将所有指令渲染并丢弃缓冲区中的内容。

使用这种方法有几个原因。通过将指令放入缓冲区,渲染器可以延迟执行这些指令,直到渲染时才统一处理,从而优化渲染流程。

黑板:使用 PushBuffer 的原因

推送缓冲区的第一个,也是最重要的原因,是希望能够进行排序。为了做到这一点,需要能够在渲染之前先看到所有需要渲染的内容,并决定它们的渲染顺序。虽然可以尝试设计游戏代码,让它按照渲染的顺序逐步处理每个任务,但这样会给游戏带来很多约束,导致游戏需要进行大量不必要的计算,而这并不是理想的设计。游戏的结构应该以提高灵活性和速度为目标,并且优化离屏计算的效率,而不是仅仅围绕如何渲染来构建整个游戏架构。

黑板:围绕渲染器构建的游戏架构历史

过去,游戏架构完全围绕渲染器来构建并不罕见,尤其是在资源极其有限的时代,比如 Atari 2600、ColecoVision 和 Commodore 64。当时,若试图将渲染命令排队后再执行,游戏根本无法运行,因为没有足够的系统资源来高效处理这些操作,游戏会非常缓慢,内存也可能不足。而今天的硬件资源充足,允许在不影响性能的情况下使用推送缓冲区来排队渲染命令,并在需要时执行。

黑板:现代的奢侈品和权衡

在现代硬件资源充足的情况下,可以做出不同的权衡,以优化游戏代码的整体结构。然而,强调避免使用大量低效的代码来浪费 CPU 资源,尤其是在如今机器性能强大的情况下,更应该避免这种做法。建议尽量找到合理的权衡点,使得游戏代码整体更加高效和灵活。在渲染命令的排序上,通过将排序逻辑与游戏代码的结构分离,能使游戏代码更加高效和灵活,同时在需要时可以根据具体情况调整排序方式。这种方式的优势在于,它能够根据不同的场景灵活地进行排序,从而提升整体的效率和性能。

黑板:输出目标

通过使用推送缓冲区,可以实现将渲染命令输出到不同的目标平台上,如 OpenGL、Direct3D、Mantle 等,甚至可以为不同的硬件平台和软件渲染器提供优化。例如,可以为 ARM 架构的处理器提供专门的 NEON 版本,或者为不同的硬件平台设计专用的渲染版本。通过这种方式,推送缓冲区不仅仅是排序,它还允许将渲染命令转化为最适合目标平台的形式,从而实现不同渲染器的优化,而不需要重写整个游戏代码。这为游戏提供了一层可移植性,使得可以在多种渲染目标之间进行优化,而不影响游戏的整体架构。

黑板:平衡优化方法

对于优化效率的追求,存在一个平衡点。极端的优化哲学,追求每个细节的完美,可能会导致项目无法完成,因为总有更多的优化可以做。同样,忽视性能,完全不关心它,也是不理智的,因为这会导致低效和不可靠的结果。理想的做法是在项目的时间范围内,找到一个合理的效率平衡,使得产品既能提供良好的性能,又能确保高质量,使得运行时感觉流畅、可靠且美观。

黑板:PushBuffer 的好处

Push缓冲区可以在渲染过程中提供良好的平衡。它使得游戏代码能够迅速、简洁地描述渲染需求,并且避免过度消耗资源。这个缓冲区充当了一种中间表示,类似于编译器中的中间步骤,使得游戏能够在不影响性能的前提下优化渲染操作。通过这种方式,渲染目标(如OpenGL、Direct3D或软件渲染器)能够高效地处理渲染命令,而不需要每帧都做过多处理。此外,Push缓冲区还支持在不同平台间的移植,使得代码在不同的渲染API上能够灵活转换并实现优化。

黑板:支持多个目标的考虑

在设计渲染系统时,虽然目前对一些渲染目标(如OpenGL、软件渲染器)已有经验,但仍保持一种谨慎的态度,假设对于某些输出目标并不完全了解。这是因为游戏可能会面临新的渲染目标(例如Vulkan或Mantle),而开发者无法预见所有的技术挑战。目标是确保系统可以灵活适应新平台,无需重写整个游戏代码,以此实现高效的渲染优化。这种方式能够帮助快速适应不同的渲染API,同时保持较好的性能,而不会陷入繁琐的重构工作。

黑板:我们的第一个目标:软件 “GPU 类渲染”

在开发软件渲染器时,目标是使其模拟GPU的工作方式,尽管它不会完全与GPU相同,但通过这种方式可以获得宝贵的对现代游戏渲染流程的洞察。此类渲染器将稍微慢一些,因为不会做出专门针对CPU优化的捷径,而是力求最大程度地还原GPU的工作机制,从而为开发者提供更深刻的理解,尤其是关于着色器的结构和性能特点。

这种方法有助于理解GPU如何处理图形,并通过该方式开发渲染器时,可以从中获得对现代游戏图形渲染的宝贵见解。虽然软件渲染器的效率不如GPU,但它的设计有助于开发者深入了解图形渲染的本质。

黑板:两种渲染工作方式的视角:1) 显式表面光栅化器

一种常见的渲染方法是通过扫描线来绘制图形。例如,绘制三角形时,首先会确定三角形所触及的扫描线,然后遍历每条扫描线,计算出三角形在每条扫描线上的跨度,最终确定哪些像素属于三角形。这种方法是传统的软件光栅化方式,在过去的Pentium时代较为常见。

然而,虽然这种方法在某些情况下可能是更高效的,当前的开发策略并不打算使用这种传统的扫描线渲染方式。即使这种方式能够提供较好的性能,也不会采用它,而是选择一种不同的渲染策略。

黑板:2) 隐式表面光栅化器

当前的渲染策略采用了一种隐式的表面光栅化方法,而不是传统的显式光栅化。传统的光栅化方法会逐像素遍历并填充需要的像素,而隐式光栅化则通过粗略估算哪些区域会被图形触及来进行处理。例如,可以使用一个4x4像素的方格(16个像素)来估算三角形与屏幕上的区域的交点,而不需要明确计算每个像素是否被三角形触及。

具体操作是,先估算哪些4x4的像素区块会被图形触及,然后对这些区块中的所有像素进行计算,而不管三角形是否真的经过这些像素。最后,会通过掩蔽操作去除不被触及的像素,仅填充真正被图形覆盖的区域。

黑板:使用隐式方法的理由

采用这种方式有很多原因,虽然现在不打算详细展开,但随着实现的推进,这些原因会变得更加清晰。现代GPU通常采用类似的方案进行渲染。

黑板:我们的渲染器大概会是什么样

渲染器的工作流程将包括以下几个阶段:首先是推送缓冲区(push buffer),接着进入一个定位阶段,确定屏幕上各个三角形的位置。然后,渲染器将选择需要填充的4x4区域,针对每个三角形或其他几何图形进行处理。之后,将有一个计算阶段,负责计算这些4x4区域的像素值。这个阶段会使用类似光栅化的算法来处理这些区域。

黑板:SIMD 指令集

渲染器将在不同的处理器架构上进行优化,具体根据不同的SIMD(单指令多数据)宽度进行调整。对于SSE2(最小目标),每次计算四个像素,AVX则是八个像素,AVX-512则是16个像素。渲染器将根据平台的不同,分别进行四倍、双倍或单倍的处理。最终,通过这种方式,渲染器可以在不同平台上有效运行,同时保证每个像素的渲染按预定的方式进行,处理过程会像流水线一样进行,确保高效执行。

黑板:我们正在构建的概述

目前的工作重点是处理矩形渲染,因为游戏引擎是基于精灵的,矩形是主要的处理对象。虽然有时可能会考虑三角形,但在实际情况中,处理矩形的成本和处理三角形相似,因此选择专门处理矩形可以节省许多计算工作。接下来,渲染器将扩展功能,包括支持缩放和旋转等操作,虽然目前游戏还没有请求这些功能的接口,但为了能够在后续阶段处理这些需求,需要在渲染管线中预留空间。当前的目标是清理和优化推送缓冲区,并确保渲染器能够表达这些新的功能。

开始提取绘制函数

开始整理和提取一些功能,包括绘制矩形、绘制矩形轮廓、绘制位图等操作。所有这些功能将集中在一个地方,方便查看和检查,确保没有出现错误。虽然在操作过程中遇到了一些小问题,但这些可以逐步解决。
在这里插入图片描述

在这里插入图片描述

看看我们目前的进展

检查了当前状态后,确认一切正常,缓存填充工作进行顺利,没有需要担心的地方,项目目前处于可工作状态,可以继续推进。

开始玩代码

在当前工作中,发现了 PieceCount 仍然存在,但不再使用,进行了对齐操作,确保一切显示正常。
在这里插入图片描述

Vaporise PieceCount

决定删除 PieceCount,因为它不再被使用,进行清理工作。

研究在这里添加多种命令类型,首先将 entity_visible_piece 重命名为 render_group_entry

为了便于扩展命令类型,决定将 entity_visible_piece 更名为 render_group_entry。这样可以更清晰地表示其实际功能,并且更好地适应未来可能的复杂命令操作。
在这里插入图片描述

把 RenderGroupToOutput 提取到 game_render_group.cpp 中

在这一过程中,计划将一些功能提取到game_render_group中,主要是为了更好的组织结构,便于集中管理相关内容。此举与代码本身无关,完全是为了个人方便。将这一部分称为“RenderGroupToOutput”,并将原本在推送缓冲区内进行的操作移到渲染组中。接下来,需要传递一些必要的参数,包括渲染组本身和输出缓冲区等。

对于ScreenCenter坐标(X和Y)等信息,可以从缓冲区中计算得出。像将米转换为像素这类操作,则可以通过RenderGroup直接进行,尽管这可能会在稍后的过程中稍作不同的处理。最终的目标是确保所有需要的值都可以通过RenderGroup或其输出目标来获得。

重点是,所需的“绘制缓冲区”实际上是一个加载的位图,它代表了绘制的目标。因此,将该位图作为输出目标传递,其他相关的计算(如屏幕中心)也可以通过这个目标来完成。

总的来说,这一过程并不涉及任何设计变动,仅仅是一个简单的编程步骤,确保代码结构更加清晰。
在这里插入图片描述

在这里插入图片描述

面向压缩编程

**压缩导向编程(Compression Oriented Programming,COP)**是一种编程范式,重点在于将压缩技术作为系统和算法设计的核心主题。其目标是通过在系统架构中广泛应用压缩技术来优化数据的存储、传输和处理。该方法不仅能够减少存储和传输成本,还可以通过减少内存使用和提高数据处理速度,特别是在处理大型数据集时,来改善性能。

压缩导向编程的主要原则包括:

  1. 数据表示:使用压缩格式作为数据存储和处理的主要表示方式,确保最小化空间使用。

  2. 算法设计:开发能够高效处理压缩数据的算法,使其在不完全解压的情况下也能快速访问和操作数据。这可能涉及专门的数据结构或编码方案,以便实现压缩感知计算。

  3. 权衡:管理压缩效率与压缩和解压操作开销之间的权衡。虽然压缩可以节省空间,但压缩和解压所花费的时间也需要平衡,以确保性能不受影响。

  4. 流处理与实时处理:实现允许高效处理数据流的压缩技术,确保在实时处理时不会显著影响性能,通常使用如流式压缩算法等技术。

  5. 可扩展性:设计能够随着数据规模的增大而扩展的系统,确保即使在大规模分布式系统或大数据环境中,压缩的优势仍然能够显现。

这种方法在数据传输(例如网络传输)、存储管理以及处理大型数据文件或数据库等领域尤为有用,因为数据的庞大规模可能会成为瓶颈。

在这段讨论中,强调了设计过程应当是自然、有机地进行的,而不是过度设计或过度思考。整个过程应该是逐步推进的,通过不断提取和简化现有的结构,按照已有的代码要求来决定各部分内容的组织和功能,而不是在早期就过度规划。提到在编程中,重要的是写出可行的代码,然后逐步整理、优化,而不是一开始就进行复杂的设计,例如绘制类图等,这些通常没有实际意义。

这种方法避免了“过度设计”的坏习惯,鼓励更关注实际代码的实现,并逐步将其分解为更简洁、清晰的模块。这是一种非常有效的方式,经过多次实践证明其有效性,强调了本能的编程方式比许多书籍所教的“标准方法”更为高效,尤其是在解决实际问题时。

考虑对一组实体进行操作

在这段内容中,主要讨论了如何优化渲染指令的处理方式。首先,讨论了推送缓冲区(push buffer)中的命令是如何被交错执行的,虽然这种做法可能效率较低,因为每个命令都需要检查其类型并执行相应操作,但目标是尽量精简命令,以避免频繁处理不必要的渲染指令。例如,如果绘制大量粒子效果时,每个粒子都是一个矩形且使用相同的纹理,不应该单独把每个粒子作为一个渲染命令推送到缓冲区。这是因为每个粒子的处理都会引入额外的判断和开销,极大影响性能。

为了优化这一过程,推荐将具有相同纹理或相似渲染需求的物体合并为一个命令。例如,对于具有相同纹理的多个位图图形,可以将它们合并成一个渲染指令,从而减少每个命令的切换和判断开销。虽然推送缓冲区中的命令数量可能很庞大,但为了避免因为粒子数量庞大(可能达到十万甚至更多)而导致性能瓶颈,必须谨慎选择在推送缓冲区中添加的命令。

最终的目标是减少切换和判断的开销,以保证渲染效率。虽然目前开始的实现较为基础,但在未来的优化中,必须考虑如何减少这些开销,确保推送缓冲区的效率不成为瓶颈。

设置处理不同类型条目的情况

在这段内容中,讨论了如何改进处理推送缓冲区条目的方式。首先,将Piece一词更改为Entry,以便更清晰地表示这些条目是什么。在处理这些条目时,首先需要判断条目的类型,然后根据类型执行不同的操作。例如,某个条目可能是一个“clear”操作,另一个可能是一个矩形渲染命令,后者通常用于精灵图像的渲染。
在这里插入图片描述

在这里插入图片描述

当前并没有明确所有条目的类型,但目标是确保能够处理所有可能的情况。为此,需要引入一个机制,能够根据不同类型的条目执行相应的渲染操作。通过这种方式,可以确保在推送缓冲区中的每个条目都能被正确处理,无论它是何种类型。
在这里插入图片描述

引入 InvalidDefaultCase

在这段内容中,讨论了使用一个宏“InvalidDefaultCase”来处理switch语句中的默认情况。这个宏的作用是确保在switch语句中,如果进入默认分支,程序会触发断言(assert),以确保不允许默认情况发生。原因在于,推送到缓冲区的条目应该是可以处理的,如果遇到无法处理的情况,就需要为其添加一个专门的case,但在处理这个case时什么也不做,意味着不处理这种情况。例如,某些特定于OpenGL的情况可能就不需要处理。总之,目标是确保所有被推送到缓冲区的条目都是可以处理的,而不允许进入默认情况。
在这里插入图片描述

根据 Entry->Type 增加 BaseAddress

在这段内容中,讨论了如何调整基地址的变化。在处理条目时,基地址的增量将根据当前条目的内容进行不同的调整,这意味着每个条目的偏移量将依据其具体类型而有所不同。虽然这种方法可能不是最优化的方式,但目的是确保系统能够支持这种方式。最终是否选择这种方式,还需要在后续根据实际效果做进一步评估。

此外,提到了一种可能的替代方案,即通过指针追踪的方式来调整基地址的增量,但目前不确定哪种方式更适合。计划保持灵活性,待到实际实现时根据具体情况决定最终的选择。
在这里插入图片描述

编写这些情况

在这段内容中,讨论了如何处理渲染条目的类型转换。首先,通过将渲染条目(如清除操作或矩形操作)进行类型转换,确保每个条目都被正确识别和处理。这种方式类似于使用带有区分的联合类型(discriminated union),其中根据条目的类型,执行相应的操作。比如,清除操作会被转换为对应的类型,矩形操作则会转换为矩形类型。

此外,还提到了一种可能的改进,即将条目统一命名为“entry”,并将其类型设为“TypelessEntry”或类似名称。这样一来,能够更方便地识别和处理不同类型的条目。处理完每个条目后,将根据条目的大小来推进基地址,确保内存布局和条目顺序正确。
在这里插入图片描述

创建对应的 render_entries

在这段内容中,讨论了如何组织渲染条目,特别是如何将渲染条目分为不同的类型,如“清除(clear)”和“矩形(rectangle)”。为了清晰区分这两种类型,决定为每个类型创建不同的结构体,例如render_entry_clearrender_entry_rectangle。每种类型将拥有不同的字段,例如“清除”条目可能包含一个RGBA值,用于指定清除颜色。

此外,还提到需要为每个条目添加一个头部(header),这个头部将包含条目的类型(如清除或矩形)。考虑是否需要在头部中添加其他字段,但最终决定目前只需要类型字段,因为其他信息暂时没有想到需要添加。

为了进一步简化,考虑将头部命名为“render_group_entry_header”,或者使用更通用的名称“typeless_render_group_entry”,但目前没有决定。最后,提到会用枚举(enum)来表示不同的渲染条目类型,具体包括“clear”和“rectangle”两种类型。
在这里插入图片描述

在这里插入图片描述

描述 “紧凑判别联合体”

在这段内容中,讨论了使用“判别联合体”(discriminated union)来组织不同类型的渲染条目。判别联合体的特点是通过一个类型字段来区分不同的可能类型,这使得每个条目在内存中可以根据其类型分配适当的空间。相比于传统的联合体(union),传统联合体会使用最大尺寸来确保所有类型都能容纳,而判别联合体则使用一个类型字段来判断每个条目的具体类型,从而根据实际类型调整内存的分配大小。

举个例子,假设系统中有两种渲染条目类型:一种是清除操作(clear),另一种是矩形(rectangle)。如果使用传统的联合体,内存分配将为这两种类型中占用空间最大的类型提供足够的空间。例如,clear可能只需要一个RGBA值,而rectangle可能需要更多的数据,如位置、尺寸等。传统联合体会为rectangle分配足够大的空间,以容纳所有可能的数据,但这会浪费内存,因为clear类型只需要小部分空间。

然而,使用判别联合体时,可以通过类型字段来区分这两种类型。当处理清除操作时,只分配必要的内存(如RGBA值的大小),而处理矩形时,则分配矩形需要的内存大小(例如包含位置和尺寸的结构体)。这样,每次处理时,只会根据实际类型的需求来分配内存,不会浪费空间。

为了进一步提高效率,系统决定避免使用固定的最大内存块,而是根据每个条目的类型动态调整内存地址的偏移。例如,在处理矩形时,如果矩形需要更大的内存块,可以根据其实际大小进行内存推进;如果是清除操作,则只推进小量的内存。这种方式可以有效减少内存的浪费,并使得推送缓冲区能够处理任意大小的条目。

至于头部字段的问题,原本计划为每个条目添加一个头部字段,可能包含类型和其他信息,但经过讨论后,认为仅保留类型字段就足够了,这样可以简化结构体的设计。

进一步地,为了提高内存效率,计划将这些类型压缩,而不是使用最大的内存块。这意味着,每次处理条目时,不再按照最大类型大小推进内存,而是根据实际类型的大小来调整内存位置。这种方法的目的是支持推送任意大小的条目到推送缓冲区,这在后续的实现中将证明非常有用。

此外,讨论了是否需要添加一个头部字段(header)。虽然目前仍然保留了头部字段,但认为可能仅需要类型字段。总体来说,目标是通过这种方式有效地管理内存,并确保系统能够灵活地处理不同大小的渲染条目。

回顾我们新的能力

在这段内容中,讨论了如何通过判别联合体处理不同类型的渲染操作,例如清除操作和矩形渲染操作。通过判别联合体的使用,可以根据不同类型的渲染条目采取不同的操作。例如,在遇到清除操作时,可以执行清除操作,而遇到矩形时则执行相应的矩形渲染操作。系统的设计允许在渲染过程中灵活处理不同类型的渲染条目,使得可以扩展支持更多类型的渲染操作。

接下来,计划将矩形操作进一步细化,添加不同类型的矩形,例如矩形轮廓(outline)。这一点体现了通过判别联合体的使用,能够更加灵活地扩展渲染系统,支持更多复杂的渲染类型。此时,可以通过不同的渲染操作类型组合,形成更加丰富的渲染系统。

关于推送缓冲区(push buffer)的操作,强调了在处理过程中所采取的架构决策。通过经验积累,能够较为迅速地做出正确的架构选择,而不需要经历每次都从零开始的尝试。这种经验优势能够帮助在设计系统时做出更加高效的决策。对于没有这种经验的人来说,可能需要更多的迭代和尝试,但最终编程的方式大体上是相似的。

总的来说,判别联合体的使用提高了渲染操作的灵活性,并且经验的积累在系统架构设计中起到了重要作用。
在这里插入图片描述

荒谬技巧:将类型名称与 RenderGroupEntryType 连接以构成标识符,并使用 #define PushRenderElement 宏以类型安全的方式在一步中正确设置类型字段

在这段内容中,讨论了如何在推送渲染元素时,不仅推送渲染元素的大小,还推送渲染元素的类型。为了实现这一点,提出了一个技巧,通过宏来动态生成类型标识符,并将其应用于推送操作。

具体来说,宏 PushRenderElement 被设计成接受渲染组和渲染元素类型作为参数。宏的作用是调用实际的推送函数,传递渲染组,并推送该类型的大小。同时,宏会将类型名称的前缀附加到类型上,从而创建一个带前缀的类型标识符。这个前缀是通过枚举类型中的结构类型名称生成的。

这种做法避免了复杂的模板或不必要的复杂性,同时保持类型安全。在调用 PushRenderElement 时,只需要指定希望推送的类型(例如矩形类型),其他的处理会自动完成。通过这种方式,系统能够正确地设置类型字段,确保类型的一致性,并简化了渲染元素推送的过程。

整体而言,这种方法使得渲染元素的推送更加高效、安全且简洁,避免了冗余的模板操作,并且确保了类型字段的正确性和一致性。
在这里插入图片描述

将类型传递给 PushRenderElement

在这段内容中,讨论了如何在推送渲染元素时处理内存分配和类型信息。当从推送缓冲区获取结果时,计划将结果作为一个“头部”来处理,这意味着不管分配的内存如何,它都会包含某种形式的头部信息。即使某些元素可能不需要该头部,系统也能进行处理,允许没有头部信息的元素插入。如果决定允许这种情况,还可以在更深一层添加推送大小的处理。

此外,提到如果内存空间不足,可以通过在头部进行检查来轻松支持空间不足的情况。当空间不足时,元素会被丢弃,简单地忘记它们即可,不需要更复杂的处理。

总结来说,目标是通过头部信息和类型字段来管理推送的渲染元素,并通过简单的检查确保空间充足,避免复杂的错误处理机制。
在这里插入图片描述

编译并清理

在这段内容中,讨论了如何正确获取并处理渲染元素的头部。首先,获取头部信息后,计划将其转换为相应的类型。这涉及到通过名称的修改来适应不同类型的转换。通过这些步骤,可以确保在内存中正确地管理头部信息,并根据需要将其转换为相应的渲染元素类型。

此外,还提到了一些细节,例如在进行类型转换时需要确保位置正确,以避免错误发生。总体来说,目标是通过这些操作确保渲染元素的类型和头部信息能够正确管理,便于后续操作的顺利进行。
在这里插入图片描述

检查游戏中一切是否正常

讨论了为了简化操作和提升效率,进一步优化了代码结构。通过进行一些调整,目标是让系统能够更方便地处理不同的任务,使得后续的操作更加高效。这种优化为系统提供了更好的区域化管理,使得处理过程更加便捷和直观
在这里插入图片描述

在这里插入图片描述

创建位图类型

在这段内容中,讨论了将原本的“位图”和“矩形”操作分离成两个独立的处理流程。通过这样做,系统能够分别处理位图和矩形,确保每个操作有针对性的实现。在处理时,对于位图,系统会执行相应的位图绘制操作;而对于矩形,则会按矩形的方式绘制。最终,两个不同的操作被分别调用,分别通过对应的推送方法来实现。这种方式提高了代码的清晰度和可维护性,使得不同的渲染操作可以被更独立地处理。
在这里插入图片描述

在这里插入图片描述

为位图调用 PushPiece 函数

在这段内容中,讨论了对位图操作进行逐步处理。首先,继续调用“PushPiece”函数来处理位图类型。在执行时,确保传入正确的类型,以便系统能够正确地处理位图。通过逐步调试和确认每个步骤,保证系统能够正确地处理位图数据。
在这里插入图片描述

由于 Entry->Bitmap 未填充,触发了断言

在这段内容中,讨论了在绘制固体矩形时,可能会遇到断言失败的情况,特别是当位图条目没有填充时。为了避免这种情况,接下来需要进行调整,确保在绘制固体矩形时相关的位图数据已经正确填充。
在这里插入图片描述

确保在调用位图类型时推送一个矩形

在这段内容中,讨论了如何调整代码,以确保在调用绘制矩形的函数时,实际推送的是一个矩形,而不是其他类型的数据。为了确保这一点,暂时通过复制代码的方式来处理,随后再优化代码,避免重复。
在这里插入图片描述

检查游戏中一切是否正常

在这段内容中,提到矩形的绘制虽然按预期工作,但代码中存在重复部分。接下来需要考虑将这些重复的代码提取出来,优化代码结构,以减少冗余和提高可维护性。

将代码压缩成更可用的形式

在这段内容中,计划通过压缩和简化现有的代码来提高可用性。主要目标是将与位置相关的计算(如偏移量和坐标)与形状的绘制分离,创建一个“实体基础”概念,这样可以更清晰地管理实体的位置信息。进一步地,打算通过引入一个函数来处理这些计算,以简化代码,并避免冗余。此外,还提到在处理过程中需要注意位图的尺寸问题,确保这些计算正确处理。
在这里插入图片描述

将偏移量嵌入 PushRect 中

在这段内容中,计划对矩形的绘制进行优化,主要目的是简化坐标和偏移量的计算。通过调整“中心”与“半尺寸”的处理,建议将计算过程更清晰地集中到一个统一的协调点,从而消除原本由于坐标变换带来的复杂性。此外,进一步调整了尺寸计算方式,通过引入“MetersToPixels”的转换来确保矩形绘制更加合理。还讨论了坐标系中的Y轴翻转问题,确保矩形在绘制时的偏移量计算正确。

提取 EntityBasis 计算

在这段内容中,计划对计算过程进行进一步优化,目的是简化并统一实体基础的计算。通过提取出一个用于计算“render_entity_basis”的函数,来避免重复代码。该函数将接受渲染实体基础并计算偏移量,最终返回一个包含这些计算结果的值。进一步的调整包括将entry改为EntityBasis,并清理冗余代码,确保整体代码更简洁和可维护。
在这里插入图片描述

在这里插入图片描述

清理

在这一段中,计划调整函数的输入参数,将ScreenCenterRenderGroup传递给相关的函数,并确保在计算过程中正确处理它们。ScreenCenter可能已经在管道中全程可用,因此可以进一步优化这一点。另外,dim被移除,因为它仅在矩形处理时使用,之后通过调整代码将其放置到矩形部分。接下来需要填充基础结构,调整命名保持一致,并修复可能存在的命名不一致问题。由于在处理时存在一定的注意力分散,可能会有一些错误,但目标是改进代码结构和简化操作。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

看看我们现在的进展

目前代码调整看起来已经顺利进行,基本上已经达到了预期的状态,所有的更改看起来是正确的。到此为止,工作可以暂时停下来,尽管还有一些小的调整,但整体上进展顺利。

当你在游戏中遇到 bug 并能重现它时,你会在编程中看到 bug 行吗,还是它是如何工作的?

当遇到游戏中的错误并能够重现时,通常有两种类型的错误。一种是比较容易追踪的,能快速定位到问题所在并进行修复;另一种则比较复杂,需要花费时间创建特定的情境来推断问题的根本原因,然后再进行修复。至于“bug line”这个术语不太清楚是什么意思,抱歉没有更明确的解释。

关于是否值得对渲染条目进行对齐,这个问题也涉及到优化和代码结构,可能要根据具体的需求来决定是否进行对齐。

是否值得对 render_entries 进行对齐?

目前来说,render_entries不需要特别对齐,因为所有的render_entries都会已经是8字节对齐的。这是因为render_entries中会包含指针,而指针通常是8字节对齐的,所以这种情况下已经足够。但是,当开始处理更为可变大小的内容时,可能需要对齐,以避免把数据放在不对齐的内存地址上,这样做有助于避免性能问题。至于进一步的对齐,可能不会做太多处理,但需要根据实际情况测试。对齐有时能带来性能提升,但有时也可能带来空间浪费,进而影响内存带宽或缓存效率,可能会抵消对齐带来的好处。

至于Mantle,它是一个由AMD推出的低级别图形API,旨在提供比OpenGL和DirectX更直接的硬件访问,从而提高性能。

Mantle 是什么?

Mantle 是AMD推出的一种图形API,旨在提供更直接的硬件访问,主要用于访问其GPU。它比传统的图形API(如OpenGL和DirectX)提供了更为直接的控制,使得开发者能够更高效地与硬件进行交互。

有关 GPU、渲染、光栅化的阅读推荐吗,除了谷歌搜索?

对于关于GPU渲染和光栅化的阅读推荐,推荐访问Fabien Gazin的博客,他是图形编程和优化领域的顶尖程序员之一。在他的博客中,尤其有一篇关于图形管线的文章,非常值得阅读。这篇文章深入讲解了图形工作原理,并且提供了一个良好的起点来理解图形渲染过程。虽然这篇文章发布于2011年,内容可能有些过时,但它依然能提供关于2015年及以前的图形管线的扎实理解,并帮助读者更好地理解后来的更新内容。

如果能花时间深入研究这篇文章,将能大大提高对图形管线的理解,并能轻松阅读其他与图形渲染相关的资料。总的来说,这篇博客被认为是了解图形渲染的一个极为重要的资源。
google 搜索The ryg blog
https://fgiesen.wordpress.com/
博客 不过这个博客好像外网才能访问
在这里插入图片描述

为什么使用指针而不是引用?

在讨论为什么使用指针而不是引用时,指出指针和引用之间实际上没有太大区别,尤其是在不使用C++特性时,二者在CPU执行上没有本质差异。指针和引用都可以实现相同的功能。指针是引用的超集,因为指针可以改变指向的对象,而引用则不能更改指向的对象(尽管有些C++的最新规范似乎允许引用指向的对象变化)。

之所以不使用引用,是因为它们并不带来额外的好处。在实际编码中,指针在做指针运算时更加灵活,而引用只能用于直接的引用,功能相对较弱。因此,倾向于使用指针,因为指针可以做更多事情,而引用则没有这些能力。

关于 Mantle,你是否知道 AMD 已经停止开发它,并将大量人力投入帮助推动 Vulkan?

关于Mantle,虽然已经知道AMD曾试图通过Mantle为Vulkan奠定基础,并在推动Vulkan的过程中做出贡献,但并不清楚AMD已经停止了对Mantle的开发。此前并没有关注到AMD停止支持Mantle的消息。实际上,可能仍然假设他们会在未来某个时刻继续进行Mantle的工作。总的来说,未对AMD的更新有深入关注,因此并不清楚最新的动态。

是一个依赖内存进行通信的抽象层,而不是一堆函数,如果我理解正确的话。这是你通常喜欢的 API 设计方式吗?

对于API设计,倾向于使用基于内存的设计,而不是基于函数的设计。基于内存的API更灵活,具有更高的可扩展性,并且能够支持如跟踪、捕捉等重要功能。相比之下,基于函数的API通常更复杂、更难以文档化,容易变得杂乱无章。对于渲染器这类问题明确、输入输出清晰的场景,基于内存的API设计尤其合适。然而,在一些更为复杂的场景中,比如整个游戏系统的API设计,基于内存的设计可能就不适用了,因为它会过于复杂。总体而言,在遇到明确问题时,倾向于采用基于内存的API设计。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2294164.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux学习笔记16---高精度延时实验

延时函数是很常用的 API 函数,在前面的实验中我们使用循环来实现延时函数,但是使用循环来实现的延时函数不准确,误差会很大。虽然使用到延时函数的地方精度要求都不会很严格( 要求严格的话就使用硬件定时器了 ) ,但是延时函数肯定…

杨氏数组中查找某一数值是否存在

判断数据是否存在于杨氏矩阵中 (小米真题) 题目:有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。 要求:时间复杂度小于O(N) …

51单片机 02 独立按键

一、独立按键控制LED亮灭 轻触按键&#xff1a;相当于是一种电子开关&#xff0c;按下时开关接通&#xff0c;松开时开关断开&#xff0c;实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通和断开。 #include <STC89C5xRC.H> void main() { // P20xFE;while(1){…

AI + 编程时代,飞算JavaAI如何引领行业趋势变革

在当今科技飞速发展的浪潮下&#xff0c;AI 与编程的深度融合已成为不可阻挡的时代趋势&#xff0c;正重塑着各个行业的格局。在这场变革中&#xff0c;飞算JavaAI脱颖而出&#xff0c;凭借其卓越的特性和创新的理念&#xff0c;在 AI 编程领域展现出强大的引领力量&#xff0…

Deepseek本地部署指南:在linux服务器部署,在mac远程web-ui访问

1. 在Linux服务器上部署DeepSeek模型 要在 Linux 上通过 Ollama 安装和使用模型&#xff0c;您可以按照以下步骤进行操作&#xff1a; 步骤 1&#xff1a;安装 Ollama 安装 Ollama&#xff1a; 使用以下命令安装 Ollama&#xff1a; curl -sSfL https://ollama.com/download.…

1-R语言概述

1.认识R语言 1.1 选择R语言的依据 免费的软件编程方便&#xff0c;语言灵活&#xff0c;图形功能强大优秀的内在帮助系统高质量、广泛的统计分析、数据挖掘平台国际上R语言已然是专业数据分析领域的标准 1.2 R的来源 ​ R是S语言的一种实现。S语言是由 AT&T贝尔实验室…

【BQ3568HM开发板】智能家居中控屏连接华为云IoTDA物联网平台

目录 引言 安装OpenHarmony的MQTT库 华为云平台的操作 建立设备 建立物模型 连接华为云平台 发布LED灯状态 代码重构 测试结果 接收平台发送的属性修改命令 设备侧API Topic 下行请求参数说明 上行响应参数说明 程序修改 应用侧API 测试设备属性设置功能 结语…

java 8 在 idea 无法创建 java spring boot 项目的 变通解决办法

java 8 在 idea 无法创建 java spring boot 项目的 变通解决办法 spring boot 3 官方强制 要用 java 17 &#xff0c;但是 不想安装java 17的 &#xff0c;但是又想 使用 spring boot &#xff0c;可以这样 &#xff1a; 在这个网站 https://start.aliyun.com/ 选择 你相对…

web-文件上传-CTFHub

前言 在众多的CTF平台当中&#xff0c;作者认为CTFHub对于初学者来说&#xff0c;是入门平台的不二之选。CTFHub通过自己独特的技能树模块&#xff0c;可以帮助初学者来快速入门。具体请看官方介绍&#xff1a;CTFHub。 作者更新了CTFHub系列&#xff0c;希望小伙伴们多多支持…

SQLAlchemy-2.0中模型定义和alembic的数据库迁移工具

SQLAlchemy-2.0中模型定义和alembic的数据库迁移工具 一、SQLAIchemy的介绍二、数据库引擎1、支持的数据库1.1、sqlite数据库1.2、MySQL数据库1.3、数据库引擎的参数 三、定义模型类1、定义模型2、engine负责数据库迁移 四、alembic数据库迁移⼯具1、安装alembic2、初始化alemb…

C# OpenCV机器视觉:图像风格迁移

在一个充满奇思妙想的创意工作室里&#xff0c;小李正像只热锅上的蚂蚁&#xff0c;为客户的项目挠破了脑袋&#xff0c;急需寻找灵感的火花。他望着眼前那幅平淡无奇的风景图像&#xff0c;心想&#xff1a;“这玩意儿也太普通啦&#xff0c;就像一杯白开水&#xff0c;怎么能…

语言月赛 202311【基因】题解(AC)

》》》点我查看「视频」详解》》》 [语言月赛 202311] 基因 题目描述 有一个长度为 n n n 的字符串 S S S。其只包含有大写字母。 小 A 将 S S S 进行翻转后&#xff0c;得到另一个字符串 S ′ S S′。两个字符串 S S S 与 S ′ S S′ 对应配对。例如说&#xff0c;对…

Spring @PropertySource:让你的应用配置更加模块化和可维护

PropertySource注解在Spring中的作用&#xff0c;就像是给Spring应用配了一个“外部配置箱”。 想象一下&#xff0c;你在开发一个Spring应用时&#xff0c;有很多配置信息需要设置&#xff0c;比如数据库的连接信息、应用的某些功能开关等。如果这些信息都硬编码在代码中&…

Deep Sleep 96小时:一场没有硝烟的科技保卫战

2025年1月28日凌晨3点&#xff0c;当大多数人还沉浸在梦乡时&#xff0c;一场没有硝烟的战争悄然打响。代号“Deep Sleep”的服务器突遭海量数据洪流冲击&#xff0c;警报声响彻机房&#xff0c;一场针对中国关键信息基础设施的网络攻击来势汹汹&#xff01; 面对美国发起的这场…

快速搭建GPU环境 | docker、k8s中使用gpu

目录 一、裸机部署安装 GPU Driver安装 CUDA Toolkit测试 二、Docker 环境安装 nvidia-container-toolkit配置使用该 runtime 三、 k8s 环境安装 device-plugin安装 GPU 监控 一、裸机部署 裸机中要使用上 GPU 需要安装以下组件&#xff1a; GPU DriverCUDA Toolkit 二者的关…

npm中央仓库

1、官网地址 npm | Home 2、搜索依赖包

2025年软考考试时间及考试科目如何安排?附考试注意事项!

一、考试时间 2025年软考举行两次考试&#xff0c;分别安排在上半年和下半年。根据最新公布的信息&#xff0c;2025年软考考试的具体时间安排如下&#xff1a; 上半年考试时间&#xff1a;5月24日至5月27日 下半年考试时间&#xff1a;11月8日至11月11日 考生需要在规定的时间内…

4.PPT:日月潭景点介绍【18】

目录 NO1、2、3、4​ NO5、6、7、8 ​ ​NO9、10、11、12 ​ 表居中或者水平/垂直居中单元格内容居中或者水平/垂直居中 NO1、2、3、4 新建一个空白演示文稿&#xff0c;命名为“PPT.pptx”&#xff08;“.pptx”为扩展名&#xff09;新建幻灯片 开始→版式“PPT_素材.doc…

HTML排版标签、语义化标签、块级和行内元素详解

目录 前言 一、HTML中的排版标签 1. 文本相关标签 1.1 标题标签 ~ 1.2 段落标签 1.3 强调和加粗 1.4 换行标签 1.5 水平线标签 二、HTML中的语义化标签 2.1 语义化标签概述 2.2 常见的语义化标签 示例&#xff08;核心代码部分&#xff09;&#xff1a; 三、HTM…

机器学习中的关键概念:通过SKlearn的MNIST实验深入理解

欢迎来到我的主页&#xff1a;【Echo-Nie】 本篇文章收录于专栏【机器学习】 1 sklearn相关介绍 Scikit-learn 是一个广泛使用的开源机器学习库&#xff0c;提供了简单而高效的数据挖掘和数据分析工具。它建立在 NumPy、SciPy 和 matplotlib 等科学计算库之上&#xff0c;支持…