这篇文章我自己早就尝试着翻译过,但是翻译质量太差,又使用谷歌翻译来进行翻译,觉得不错,但是我发现翻译软件并不能执行的很好,

全部让自己翻译,也没有这个时间精力了,但是如果再停留在quake3和doom3的时代,有很多的东西已经过时,而且速度很慢,

所以有必要要研究一下。再加上自己的理解,我看翻译的质量不错,我觉得可以了,当然以后还是要修改的。

原文名称DOOM-3-BFG-Technical-Note

1
DOOM 3 BFG技术说明
作者  J.M.P.范·波伦
201 3月2日,3日
抽象
本技术说明描述了一些性能问题
在DOOM 3 BFG开发过程中遇到。
介绍
DOOM 3 BFG版是DOOM 3的重新编写的版本,第三批定义了特许经营类型。
这个修改版本扩展并增强了原始DOOM 3的完整体验,并使之成为了
所有当前主流平台(PC,Xbox 360,PlayStation 3)的游戏均具有丝滑的平滑帧速率
高度响应的控制。
虽然看起来DOOM 3 BFG是原始游戏的一个相当直接的港口,但还是做了很大的努力
重新优化当前硬件的游戏。在发布时(2004年),原DOOM 3显示每秒约20帧,分辨率为640x480,在最小规格PC上具有中等到低质量的设置。

DOOM 3 BFG的目标是以每秒60帧的速度运行,分辨率为至少1280x720,所有当前(2012)硬件具有最高质量的设置。这是数量的3倍更新频率更新3倍的渲染像素。当然1280x720甚至不算高分辨率了。每个人都希望以更高的分辨率运行某些形式的MSAA,这样很容易添加另外几个方面的因素。

换句话说,DOOM 3 BFG的目标至少要运行10次,当前硬件速度更快。显然电脑硬件自发布以来显着改善
原来的DOOM 3.然而,10倍或更多的性能提升的结果证明是不免费的
在计算机硬件方面有8年的改进(PC上的8年,而Xbox 360和PlayStation 3
受益于仅仅几年的硬件改进)。

中央处理单元(CPU)在处理单个指令时已经变得更加高效执行线程。更具技术性的是,每个指令(CPI)的时钟周期数量已经下降。
但是,CPU的运行频率并没有太大的提高。换句话说,虽然数量每个时钟周期可以执行的指令已经上升,每秒的时钟周期数也没有
增加。相反,大多数CPU现在都可以实现多个可以并行执行代码的内核每个核心的处理器可以同时提前执行多个线程。这转化为一个

很大的潜在的性能提升,但只有当软件被写入以利用多个核心时实现可以并行运行的多个执行线程。

不幸的是,原来的DOOM 3 [10]是主要是单核心,所有趟数的提升是在一个执行线程上完成的。图形处理单元(GPU)的性能大幅提升。

GPU一直以来设计为高度并行的机器,随着时间的推移,数量显着增加执行线程。 GPU是具有各种硬连线高性能组件的复杂机器。
有趣的是,其中的一些组件刚刚在DOOM 3原版发布时刚刚上线。

2粗Z-Cull / Hierarchical-Z是一种具有显着改进潜力的组件
渲染性能。尽管DOOM 3 BFG从该组件中获益匪浅,充分发挥其优势,粗糙的Z-Cull / Hierarchical-Z在渲染模板阴影时是一个有趣的挑战
卷,

DOOM 3的重要渲染特征之一[1,2]。尽管多年来CPU和GPU的潜力显着提升,但性能却有所提升
随机存取存储器(RAM)没有以相同的速率进行改进。特别是内存带宽和内存延迟也没有提高。因此可以执行的指令数量
内存访问增加。这一变化提出了另外一系列的挑战,并强迫了许多,原始DOOM 3 [3,4,5,6,7,8,9]的优化将重新实现新的平衡
计算和内存访问。

内存限制
在一些最糟糕的情况下,DOOM 3 BFG在PC系统上缓存颠簸缓存量。特别是,渲染器后台/驱动程序之间可能存在不良的交互
动态阴影卷构建代码(全部运行在不同的线程中)。这真的不是什么新鲜事
因为缓存抖动和内存带宽在原始DOOM 3中也是一个问题。即使DOOM 3 BFG显着改进,游戏现在也执行了更多的CPU
工作时仍然触摸并产生大量数据,部分是卸载GPU并避免Z-Cull / Hi-Z问题
在一些图形硬件上。与原始的DOOM 3不同,没有任何事情可以懒洋洋地完成(除了生成动画框架,没有更多
标志和测试如:“如果尚未计算,现在就计算”)。一个

高性能CPU工作现在遵循流式编程模型。对于动态阴影卷,引擎现在对光量进行剔除,以减少阴影卷三角形的数量,并减少每个光通道重绘的三角形数量。

引擎现在也执行精确的测试以确定视图与阴影卷相交或者在阴影卷内。这种精确测试对于显着减少需要使用Z-fail [1]渲染影子卷的情况很重要,因为Z-fail渲染在各种图形硬件(特别是高分辨率)上显着变慢。对于这个精确的insidetest,对于线条从视图原点到光源的每个单独的阴影体进行线对比扩展三角形交叉测试。

阴影体积也被变​​换为夹层空间,并且多边形在均匀空间中被剪裁到视图以计算非常紧密的深度。特别是在必须使用Z-fail渲染影子卷的情况下,深度界限用于获得更多的Z-Cull / Hi-Z优势(至少在支持深度边界测试的硬件上)。现在在GPU上完成了可见的网格和阴影卷。与原始DOOM 3不同,所有动画顶点现在都处于静态GPU缓冲区中,顶点永远不会在运行时复制到GPUmemory。然而,许多源数据被使用,并且在两个阴影卷上都在CPU上生成了大量的三角形索引,并且能够仅渲染那些在轻量级内的三角形。 Allindices使用_mm_stream_si128()内在函数写入单个映射的索引缓冲区。为了生成这些指示并执行精确的内部测试,代码将引入大量的源数据(CPU内存中的全三角形网格)。换句话说,游戏会在CPU上运行并生成大量数据。

例如,在马尔斯堡1号(doom3地图之一)介绍序列中,游戏可能会升高每帧使用以下内存量,只需要阴影体积构建/设置和浅三角形淘汰:6.0 MB的阴影体积构建/设置和光线输入数据三角形剔除.2.5 MB的生成阴影体积和剔除的光三角指数。请注意,游戏代码和图形驱动程序并行运行,与阴影体积结构/设置和光三角形淘汰重叠。游戏代码和图形驱动程序通常非常喜欢在没有外部帮助的情况下自行颠覆CPU缓存。以下代码用于验证图形驱动程序返回的索引缓冲区是否存在于写入内存中,

至少在用例中

bool IsWriteCombined( void * base ) {
MEMORY_BASIC_INFORMATION info;
SIZE_T size = VirtualQueryEx( GetCurrentProcess(), base, &info, sizeof( info ) );
if ( size == 0 ) {
DWORD error = GetLastError();
error = error;
return false;
}

bool isWriteCombined = ( ( info.AllocationProtect & PAGE_WRITECOMBINE ) != 0 );
return isWriteCombined;
}
void * buffer = glMapBufferRange( GL_ARRAY_BUFFER_ARB, 0, GetAllocedSize(),
GL_MAP_WRITE_BIT |
GL_MAP_INVALIDATE_RANGE_BIT |
GL_MAP_UNSYNCHRONIZED_BIT );
assert( IsWriteCombined( buffer ) );

换句话说,游戏不会在写出阴影体时颠覆缓存,剔除光三角形指示。然而,阴影体构建/设置和光三角形淘汰的源数据生活在CPU可缓存内存上。< /P > < P > 通过小窗口(通常为4kB左右)的重叠流式用于所有源数据,用于阴影卷构建/设置和光三角形淘汰。一切都被优化为执行连续流,尽可能少地触摸内存,并且可以尽可能少地读取存储器。

在源数据被使用一次,并且在游戏帧期间仅使用一次的情况下,可以通过以下方式避免大规模高速缓存清除在处理完缓存后,使用_mm_clflush()内在函数立即从缓存中清除sourcedata的小窗口。

不幸的是,当在不同线程的游戏帧中使用samesource数据多次时(例如,当并行构建具有多个光和多个阴影卷的网格交互)时,这是一个坏主意。从一个线程刷新缓存中的源数据是一个坏主意,而另一个线程可能几乎同时使用samesource数据。

在这种情况下,线程/内核之间的共享缓存实际上不能帮助性能是有趣的.4这一切都归结为即使在高端x86CPU上使用流编程模型,也难以实现与Cell相似的吞吐量处理器。当然,单元格SPU是硬的,因为所有的代码必须从一开始就被优化了很多。代码和数据都必须适应256 kB的内存,并且对于每个程序(或作业)加上也是理想的

结合数据,以适应128KB的内存,允许重叠的输入和输出流。 Cell SPU也是SIMD,所以从一开始就需要编写SIMD代码来减少二进制代码的大小,因为将SIMI的编译器标量代码引入了合理的开销。换句话说,如果程序员花费了,那么可以实现一些令人印象深刻的吞吐量(对于Cell处理器来说,它不是一个GPU)。

在x86中有SSE4.1内在的_mm_stream_load_si128()。这个内在的加载数据通过“readcombine”缓冲区而不是缓存。不幸的是,这种内在的只适用于写入组合的内存。一旦存储器被写入组合,所有代码就更好地使用这个内在的,否则所有的性能下注都是关闭的。通过_mm_stream_load_si128()的流量负载是预测性的,所以有可能实现非常接近的最大理论带宽。换句话说,_mm_stream_load_si128()内在函数提供了大量的DMA控制器可以做的事情,只要代码不需要收集/分散操作,这就使得单元SPU的DMA控制器实际上可以做得相当好。

除了收集/散布操作之外,对于这个实际的DMA控制器可能是需要的,如果__________________s_128()的内置函数也可以在可缓存的内存中工作(就像_mm_stream_si128())一样。这将允许通过缓存和“读取组合”缓冲区访问可缓存内存。显然,缓存的读/写和“读组合”读取之间没有一致性,但对于有效只读的数据而言并不适用在运行时。只读运行时数据在许多游戏中很常见。考虑碰撞检测数据,声音遮挡数据,渲染几何结构和选择源数据等等。

当然,SSE4.1指令在DOOM 3BFG所针对的所有CPU上都不可用,因此这仍然只是实际支持的较新CPU的解决方案SSE4.1.幸运的是,在DOOM 3中,BFG将大部分写入内存的数据流传输到写入组合内存。然而,值得注意的是,将大量数据流出可缓存内存需要x86 / x64双倍带宽。如果要将整个高速缓存行的数据写入可缓存的存储器,那么首先从存储器中将高速缓存行从高速缓存中取出是浪费的,只能完全覆盖高速缓存行。因此,Cell处理器实现'dcbz'指令。该指令分配与给定内存地址相关联的acache行。而不是使用内存从内存初始化高速缓存行,高速缓存行被设置为全零。有一个cache-line-clear指令onx86 / x64.53将是有用的。计算与内存原始DOOM 3和DOOM 3之间的另一个有趣的(但不是太令人惊讶)的区别是从内存使用到计算的转换。而在原始DOOM 3中,游戏主角的动画网格每帧只有一次皮肤,在DOOM 3 BFG中,一个游戏角色的动画网格每帧可以皮肤多次,只是因为它更快。

在原始的DOOM 3中游戏角色的单个实例的动画网格仅被皮肤剥离,并且网格的唯一肤色版本被存储到内存中。然后,使用皮肤拷贝内存来构建阴影卷,并将其发送到GPU以实际渲染themesh(或阴影卷)。

在8年前,当计算和内存带宽之间的平衡有些不同时,这一切都是有意义的。在DOOM 3 BFG中,游戏角色的单个实例的动画网格可能会在每个帧上多次蒙皮。简单的观察是,剥离网格的成本可能大部分隐藏在将“未剥皮”的源网格从内存中传播/流出的成本之后。换句话说,从内存中读取/流式传输“不合格”的源网格并将其贴合到现有的成本并不比仅仅从内存中读取/流式传输已经皮肤的网格花费更多,而不执行任何额外的计算。大赢家来自于不必写皮肤的网状物回到内存中,从不将其拷贝/复制到GPU。此外,读取的内存要少得多,因为在游戏角色的许多实例中可以使用相同的“未剥皮”的源网格,完全肤色的网格对于游戏角色的单一属性是唯一的。如果数据可以保留在CPU缓存中,读取较少的内存会降低内存带宽。它也可能导致更少的缓存颠簸,特别是当并行执行大量工作时,并行线程(或任务)使用相同的源数据(与唯一源数据相反)。

要构建阴影体,请先读取源网格,就地成皮,然后立即计算面对的三角形和轮廓边缘。

在DOOM 3中,BFG的一个阴影体不过是一组指纹,它指的是一个静态的“un-skinned”源网格GPU内存中的皮肤

在GPU上进行右前锋化。结果,在DOOM 3 BFG中,每个帧的游戏角色的动画网格可以被皮肤多次。例如,如果动画网格与2个阴影投射灯(非常常见)相互作用,则可以在DOOM 3 BFG.2x CPU中对每个框架进行7次皮肤剥皮,以构建2个阴影卷1x图形用户界面,以将深度pass2x GPU皮肤渲染为渲染2个阴影卷2x图形用于渲染2个光面的GPU换句话说,对于典型的动画网格,DOOM 3 BFG可以使用与原始DOOM 3相比的FLOPS数量的7倍。

2x CPU skinned to construct 2 shadow volumes
1x GPU skinned to render the depth pass
2x GPU skinned to render 2 shadow volumes
2x GPU skinned to render 2 light surfaces

但是,DOOM 3 BFG在当今的硬件上运行明显更快。作为一个额外的好处,DOOM 3 BFG现在维持较少的状态(不再存储游戏角色的网格的皮肤拷贝)。维护的状态越少,了解和理解代码越容易,代码越有可能出现错误。无状态也允许许多代码并行运行,而不用资源争用。现在可以并行构建所有卷卷,而不需要异步原语,如互斥体或关键部分。链接数据结构DOOM 3 BFG源代码[11]包括链接列表和分层数据结构的几个可疑模板。

如“idLinkList”和“idQueue”。 这两个类的实现特别差,但是一般来说,链表和分层数据结构的泛型(模板化)类存在问题。这些通用类往往会冗余,并且它们是侵入性的,或者要求每个“链接的对象”都需要独立的内存分配,并使用指向“链接对象”的指针

/*
A public member variable of this type is added to the class of the "linked object".
Not only is this class intrusive, the member variable also needs to be public.
To fetch the "linked object" the class wastes space for a pointer to the "linked object".
This class is also confusing because it is used both for a "link" and the "head" of the list.

这种类型的公共成员变量被添加到“链接对象”这个类不仅是侵入性的,所以成员变量也需要公开。

为了获取“链接对象”,该类浪费了指向“链接对象”的指针的空间。这个类也是混淆的,因为它用于列表的“链接”和“头”。
*/
template< class type >
class idLinkList {
idLinkList * head;
idLinkList * next;
idLinkList * prev;
type * owner;
};

如果泛型类型是插入式的,则它会被冗余化(像上面的idLinkList是32位系统上惊人的16字节),或者需要知道“链接对象”(如下面的idQueue)。在这两种情况下,通用类类型的whoember变量需要在“链接对象”上公开,这是显而易见的./*

* / template <typename type> class idQueueNode {type * next;}; / **
A public member variable of this type is added to the class of the "linked object".
Not only is this class intrusive, the member variable also needs to be public.
这种类型的公共成员变量被添加到“链接对象”的类中。*/

template< typename type >
class idQueueNode {
type * next;
};
/*
The idQueue class then needs to know the offset to the idQueueNode member
variable stored on the "linked object".

idQueue类然后需要知道存储在idQueueNode成员变量上的偏移量/
*/
template< typename type, idQueueNode<type> type::*nodePtr >
class idQueue {
type * first;
type * last;
};

侵入性链表和侵入式分层数据结构通常是有问题的,因为它们不能将对象轻松链接到多个列表或层次结构中。需要为对象需要链接的每个列表或层次结构添加一个或多个新成员变量。这些成员变量通常需要公开以允许第三方遍历对象,并且它们通常也必须是可变的,以保持所有代码const正确,因为从列表或层次结构中添加/删除对象不一定会改变对象的真实状态如果通用类是非侵入性的,那么通用类的对象是单独分配的,并且存在冗余,因为通用类需要存储指向“链接对象”的指针。

通常,“链接对象”还存储指向泛型类的指针,以便能够轻松地将对象从列表或层次结构中取消链接(再次引入入侵)。 'idLinkList'类可以以这种方式使用,其中“idLinkList”对象被单独分配,并且可选地“链接对象”存储指向该“idLinkList”对象的指针。从差的代码质量来看,通常最好远离指针因为性能不佳,因此基于运行时引擎代码的链表和层次结构。使用“idList”指针和/或使用索引而不是指针通常更有效。指数通常可能小于指针,它们导致高速缓存位置,并且当使用索引偏移单个指针时,编译器通常会生成更有效的代码,而不是增加指针或步态链接指针。在旁注中,名称'idList'是非常不幸的,考虑到这个类实现了一个可调整大小的堆数组(尽管名称可能是很差的'vector')。 < /P> < P > 为了评估潜在的性能优势,比较下列代码:

class idMyClass {
bool valid;
byte otherMembers[64]; // over a cache line worth of other members通过其他成员的高速缓存行
idMyClass * next;
};
idMyClass * myObjects;
for ( idMyClass * c = myObjects; c != NULL; c = c->next ) {
if ( c->valid ) {
}
}
< /SPAN >

在第一种情况下,每次迭代存在两个潜在的高速缓存未命中,因为“有效”标志和“下一个”指针比高速缓存行分开。每个缓存未命中花费数百个时钟周期,而不是用于执行实际工作。< /SPAN >

在具有指针的数组的情况下,当读取“有效”标签时,仍然可能存在高速缓存未命中,但是每16个指针(假设32位指针和64-8字节高速缓存行)将至多存在至多一个额外的高速缓存未命中,并且现代CPU将预测性地预取内存和缓存指针,因为它们在内存中连续存储。另外,如果CPU没有预测预取,那么'idList'与指针允许手动预取,而链表不能。直接从链表中删除一个对象通常被用作使用链表的一个原因。来自使用非侵入式链表结构的链表的对象显然是昂贵的,因为链表首先必须被移动以找到对象被链接的位置。另一方面,从一个入侵的链表中删除一个对象被认为是便宜的,但实际上并不是这样。从双链表中删除对象的典型代码如下所示:

// potential cache miss touching the ‘prev’ pointer
if ( object->prev != NULL ) {
// potential cache miss touching the ‘next’ pointer on the ‘prev’ object
object->prev->next = object->next;
} else {
head = object->next;
}
// touching the ‘next’ pointer probably does not result in a cache miss
// because it is typically on the same cache line as the ‘prev’ pointer

if ( object->next != NULL ) {
// potential cache miss touching the ‘prev’ pointer on the ‘next’ object
object->next->prev = object->prev;
} else {
tail = object->prev;
}

因为它通常在与“prev”指针相同的缓存行(object-> next!= NULL){//潜在的缓存未命中触摸'next'对象的'prev'指针 - > next-> prev= object-> prev; } else {tail= object-> prev;}<

如果我们假设'head'和'tail'指针在缓存中(或者甚至可能没有'tail'指针), 那么从链接列表中取消链接的对象就是成本的三个潜在的高速缓存未命中,每个都是数百个时钟周期。 < P> 换句话说,当查看执行的操作次数时,将对象从链表中取消链接可能看起来很便宜,当降低到高速缓存未命中的数量以及总时钟周期数量时,可能会非常昂贵。从“idList”中删除指针指针听起来很贵,因为在不知道存储指针的索引的情况下,列表必须被迭代才能找到指针的位置。显然,列表中的每个对象都可以将索引存储在存储指向对象的指针的列表中,但会使列表有效插入是不合适的。但是,带有指针的“idList”可以存储每个高速缓存行的16个指针(假设32位指针和64-bytecache行)。< /P>

比较指针同时迭代列表不需要指针被去引用,并且不超过几个时钟周期,因为分支是高度可预测的(所有指针比较不等于除最后一个)。换句话说,16个指针可以以单缓存错误的成本的一小部分进行比较,并且以三个缓存未命中的成本(从双向链接表中取消链接的典型成本)进行比较。现代CPU将预测内存访问模式,并自动从列表中预取指针,因为它们在内存中是连续的。因此,实际上可以将数百个指针与成本相当的三个缓存未命中进行比较。

因此,在中等大小的列表中找到一个指针远远不如人们想象的那么昂贵。显然,在具有数千个指针的列表中找到一个指针仍然是昂贵的,或者可能导致高速缓存颠簸.9如果列表需要保持排序并且列表没有被填充以具有任何NULL指针,那么从“idList”中去除指针可能是昂贵的。在这种情况下,存储要删除指针的索引之外的所有指针都需要向下移动。由于在现代CPU上的预测预取,这再一次不会像看起来那么昂贵,但它也不是免费的。

但是,如果指针的顺序不是aconcern,那么'idList'实现两个方便的方法“RemoveFast”和“RemoveIndexFast”。这些方法使用列表中的最后一个元素来替换要删除的元素。这显然要少得多。因此,一般来说,代替使用链表,最好使用带有指针的“idList”,因为它是非侵入性的,不需要大量内存或许多内存分配,而且迭代更多高效。 'idList'也被绑定检查,代码通常会更干净(没有侵入式的公共变量,容易添加到多个列表的对象,更多的const正确代码等)。

在许多情况下,从“idList”中移除一个对象将会比删除一个对象更快

从链表。查看DOOM 3 BFG代码,列表比对象被删除的频率更高,或者偶然更昂贵地从“idList”中删除,由于在迭代链表时没有高速缓存未命中的额外成本而被抵消。如果(并且只有)从“idList”删除对象的性能确实成为问题,那么使用链表仍然可以被考虑(可能是手工推出的,不会受到前文提到的一般性模板类的困扰)。

然而,在许多情况下,可能最好使用'idHashIndex'来快速找到'idList'中的指针索引(例如处理非常长的列表时)。这些是当代码的一部分被从性能角度确定为有问题时要考虑的事情。我们想要的最后一件事是以侵入式数据结构为代价的过早优化,这样可以减少干净的代码。

当涉及哈希表时,“idHashTable”模板类不同于各种原因。这个类不是插入式的,但是对于添加的每个元素都需要一个内存分配。对于每个元素,阳极被分配以将元素链接到散列表中。这样的节点存储散列密钥,元素的“值”和指向散列链中的下一个元素的指针。存储散列键和“下一个”指针为每个添加的元素添加至少8个字节的开销,并且一般内存分配的开销将会浪费大量内存。由于所有单独的记忆分配,内存访问模式也将很差。

'idHashTable'也被设计为唯一访问一组元素,因为每个元素的'value'存储在一个节点上。每个节点都可以存储指向元素的指针(保存在单独的列表中),但是由于额外的指针追踪和高速缓存未命中,这将进一步降低性能。重要的是要意识到散列表实际上不仅仅是加速数据结构。再次,使用'idList'来存储元素是比较重要的,只有当查找“idList”中的元素被识别为性能问题时,那么值得考虑的替代方案。如果性能很重要,那么通常使用'idHashIndex'而不是使用'idHashTable'。起初,“idHashIndex”类可能不太直观,但是它的性能非常好,开销很小。相反,“idHashTable”完全失败了,因为它是一个加速度数据结构,并没有特别好的性能。

不像“idHashTable”,“idHashIndex”并不代替“idList”。

相反,'idHashIndex'添加在存储元素的'idList'旁边。如果需要基于多个键的快速访问,则可以使用单个“idList”旁边添加多个“idHashIndex”对象。接下来存储一个哈希表,一个'idHashIndex'addsonly每个元素的4个字节的开销。 'idHashIndex'也仅使用两个内存分配,并且数据紧密封装,以避免任何额外的开销和改进的内存访问模式。结论对于今天的硬件的优化DOOM 3不仅揭示了高性能软件的设计和实现方式的变化,还暴露了一些有趣的硬件限制。

今天的软件已经实现了可以并行运行的多个执行线程,以利用当前CPU上的多个内核。然而,如果这些线程接触到大量的内存,那么高速缓存可能会发生冲突,而许多CPU设备不足以避免大规模的缓存污染。没有额外的管理或绕过缓存的指令,所有CPU内核之间的共享缓存可能导致性能下降。今天的GPU实现了各种硬连线的高性能组件,可以显着提高性能。然而,充分利用这些组件,如粗Z-Cull / Hierarchical-Z,可能是具有挑战性的,特别是当渲染算法需要使用不同的深度测试时。

DOOM 3使用模板阴影卷来定义位于遮挡物阴影的空间中的区域。如果视图与阴影卷相交或位于阴影卷内,那么定义体积的三角形的碎片在更新模板缓冲区时,如果深度不合适测试(Z-fail rendering)。在各种硬件上,由于粗体Z-Cull / Hierarchical-Z在硬件中的实现,导致性能不佳。

分析DOOM 3和DOOM 3 BFG代码的性能也揭示了各种数据结构,可以显着降低当今硬件的性能。这些数据结构往往导致内存访问模式差和缓存超时错误。更糟糕的是,其中一些数据结构也导致了代码质量的下降。

References1。

约翰Carmack id软件,2000年5月。硬件加速渲染的实用和坚固的模具阴影卷

ingCass Everitt,Mark J. KilgardNVIDIA开发人员网站,

20023年3月。使用英特尔Streaming SIMD ExtensionsJ.M.P优化动画模型的渲染流水线。 van WaverenIntel软件网络,

2006年6月。Slerping Clock CyclesJ.M.P。 van WaverenIntel软件网络,

2006年6月。从四元数到矩阵和BackJ.M.P。 van WaverenIntel软件网络,2006年6月。骷髅装配线J.M.P。

van WaverenIntel软件网络,

2006年6月。快速SkinningJ.M.P。 van WaverenIntel软件网络,2006年6月。

导出三角平面公式J.M.P。 van WaverenIntel软件网络,

2006年6月。阴影体积建设J.M.P。 van WaverenIntel软件网络,

2006年6月。DOOM 3源代码软件,2011https://github.com/id-Software/DOOM-311。

DOOM 3 BFG源代码软件,2012https://github.com/id-Software/DOOM-3-BFG

Doom3bfg 技术说明相关推荐

  1. 基于SLAM融合构图的自主轮式仓储货运机器人技术说明

    基于SLAM融合构图的自主轮式仓储货运机器人技术说明 本文为基于SLAM融合构图的自主轮式仓储货运机器人技术说明文档,旨在说明基于SLAM融合构图的自主轮式仓储货运机器人环境依赖与操作配置.操作演示请 ...

  2. 艾美捷SequENZ测序级改造型胰蛋白酶用途和技术说明

    艾美捷SequENZ测序级改造型胰蛋白酶背景: 胰蛋白酶是一种胰丝氨酸蛋白酶,基于带正电荷的赖氨酸和精氨酸侧链,具有底物特异性.它来源于34kDa的无活性前体酶原胰蛋白酶原,在酶促去除N-末端6-氨基 ...

  3. 计算机网络唤醒技术说明与实现

    网络唤醒简介 网络唤醒是一种远程唤醒计算机的技术,也称为Wake-on-LAN (WOL).它可以通过局域网内的其他设备向计算机发送唤醒信号,使得计算机从睡眠状态或者关机状态中被唤醒. 网络唤醒通常需 ...

  4. 【HCIA-cloud】【1】云计算的定义、什么是云计算、云计算的架构与技术说明、华为云计算产品、华为内存DDR配置工具说明

    文章目录 云计算简介 什么是云计算 IT发展趋势 我们身边的it 云计算的定义 云计算的五大基本特质 云计算的4类部署模式 私有云(Private Cloud) 社区云/行业云(Community c ...

  5. 全自动加药装置自动加药系统的技术说明

    全自动加药装置自动加药系统的技术说明: 1.全自动加药装置LJJY-200C电磁隔膜加药泵: 当智能控制系统给出加药指令时,加药泵打开加药,加药量达到设定值时,加药泵关闭停止加药. 进口产品,性能稳定 ...

  6. 网络舆情监测服务系统技术说明,日常网络舆情监测记录?

    随着互联网快速发展,网络舆情监测服务系统技术可以实现对互联网信息的全面监控,具备敏感词监控.图片识别.预警推送.舆情浏览.综合分析.事件分析.小视频监测等功能.接下来TOOM舆情监测带您简单了解网络舆 ...

  7. 语音相似度打分技术说明【音频质量专题】

                     语音相似度打分技术说明[音频质量专题]                                                                 ...

  8. 6ES7532-5HF00-0AB0的技术说明

    6ES7532-5HF00-0AB0的技术说明 规格型号 6ES7516-3AN02-0AB0 6ES7532-5HF00-0AB0 6GK5116-0BA00-2AB2 6ES7135-6HB00- ...

  9. 【第三组】用例+功能说明+技术说明

    场景: 一个富有创造力的玩家想要绘制自己喜欢的图形. 背景: (1)典型用户:Mondrian (2)用户的需求/迫切需要解决的问题: 用户可以得到自己想要画的图形: 用户需要应用为其提供绘画工具: ...

  10. NFV业务技术说明—Vecloud微云

    网络功能虚拟化(NFV)从根本上改变了电信基础设施的部署方式,而这反过来又将极大改变通信服务提供商(CSP)交付服务的方式,因而会对运营支撑系统(OSS)产生重大影响.然而,正如运营团队常说的,NFV ...

最新文章

  1. Java CountDownLatch的两种常用场景
  2. boost::to_string用法的测试程序
  3. 公共子串 字符串哈希
  4. java发送http post请求报文_Java 用HTTP的方式发送JSON报文请求
  5. 基于selenium的爬虫
  6. python 列表 移除_python:列表中多元素的删除(移除)
  7. EditText控件常用属性
  8. Linux-Shell编程之数组操作
  9. 参数修饰符 params、 out、ref
  10. nginx中配置虚拟主机
  11. 咸鱼笔记:《实用软件工程》第一、二章课后简答题及参考答案
  12. 南京大学生租房补贴申领必看
  13. DIV网页排版入门指南
  14. STM32F103开发环境的搭建
  15. 如何强化淘宝店铺标签 店铺标签优化方法
  16. 视频弹幕技术 php,HTML5实现视频弹幕功能
  17. android pixel 2,谷歌 Pixel 2 评测:目前最好的安卓手机
  18. 软件自动化测试可行性分析,基于 AI 的软件自动化测试思考与实践—kylinTOP 测试与监控平台...
  19. 广西民族大学相思湖学院计算机考试,广西民族大学相思湖学院教务处,教务管理系统...
  20. 难解的AIoT焦虑 华为在准备特效药?

热门文章

  1. [转载]基于Servlet的Google Earth之旅
  2. Android判断手机是否在口袋中,距离传感器
  3. IBM X3650 M4服务器数据恢复成功案例
  4. golang对比python
  5. raspberry pi系统配置
  6. 虾皮入驻后怎么经营才能:快速出单?
  7. python高频词汇表大全_利用python统计word文档高频词汇
  8. aid learning安装应用_极致安卓—Termux/Aid Learning安装宇宙最强VS Code
  9. 小武匠师PPT 基础篇(一)-武文杰-专题视频课程
  10. java reflection singleton factorypattern