勤奋引擎中GPU资源更新策略的比较(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/comparing-gpu-resource-update-strategies-in-dilige-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 16 分钟阅读 - 7887 个词 阅读量 0勤奋引擎中GPU资源更新策略的比较(译文)
原文地址:https://www.codeproject.com/Articles/1259678/Comparing-GPU-Resource-Update-Strategies-in-Dilige
原文作者:EgorYusov
译文由本站 robot-v1.0 翻译
前言
This article describes several strategies to update GPU resources in Diligent Engine (a modern low-level graphic library) as well as important internal details and performance implications related to each method.
本文介绍了几种在Diligent Engine(现代的低级图形库)中更新GPU资源的策略,以及与每种方法相关的重要内部细节和性能含义. 免责声明(Disclaimer):本文使用的资料发布于(: This article uses material published on) 勤奋引擎网站(Diligent Engine web site) .(.)
介绍(Introduction)
对于3D渲染器或利用现代GPU的功能的任何其他应用程序而言,有效地将数据提供给图形处理单元(GPU)至关重要.由于CPU和GPU通常具有独立的内存系统并在不同的时间轴上执行操作,因此它并不总是简单地在给定地址写入字节那样简单.实际上,最佳方式取决于预期的使用方案.本文介绍了在Diligent Engine中更新资源的不同方法,以及与每种方法有关的重要内部细节和性能含义.(Efficiently supplying data to the graphics processing unit (GPU) is essential for a 3D renderer or any other application that utilizes the power of modern GPUs. As CPU and GPU usually have separate memory systems and perform operations in different timelines, it is not always as straightforward as simply writing bytes at a given address. In fact, the optimal way depends on the expected usage scenario. This article describes different ways a resource can be updated in Diligent Engine as well as important internal details and performance implications related to each method.)
背景(Background)
Diligent Engine是现代的跨平台低级图形库,具有Direct3D11,Direct3D12,OpenGL/GLES和Vulkan后端,并支持Windows,Linux,MacOS,iOS和Android平台.(Diligent Engine is a modern cross-platform low-level graphic library that has Direct3D11, Direct3D12, OpenGL/GLES and Vulkan backends and supports Windows, Linux, MacOS, iOS and Android platforms.) 本文(This article) 介绍了引擎.(gives an introduction to the engine.)
缓冲液(Buffers)
缓冲区代表线性内存,是最基本的资源类型.使用本段中描述的方法之一,可以在初始化期间以及在运行时将数据写入缓冲区.(Buffers represent linear memory and are the most basic resource type. The data can be written to a buffer during initialization, as well as at run time using one of the methods described in this paragraph.)
缓冲区初始化(Buffer Initialization)
将数据提供给缓冲区的最基本方法是在缓冲区初始化期间提供数据:(The most basic way to supply data into a buffer is to provide it during the buffer initialization:)
// Create index buffer
BufferDesc IndBuffDesc;
IndBuffDesc.Name = "Cube index buffer";
IndBuffDesc.Usage = USAGE_STATIC;
IndBuffDesc.BindFlags = BIND_INDEX_BUFFER;
IndBuffDesc.uiSizeInBytes = sizeof(Indices);
BufferData IBData;
IBData.pData = Indices;
IBData.DataSize = sizeof(Indices);
pDevice->CreateBuffer(IndBuffDesc, IBData, &m_CubeIndexBuffer);
缓冲区使用率定义了预期其内容更改的频率.(The buffer usage defines how often its content is expected to change.) USAGE_STATIC
缓冲区在创建后无法更新,必须始终提供初始数据.(buffers cannot be updated after being created and initial data must always be provided.) USAGE_DEFAULT
缓冲区偶尔会被更新,而(buffers are expected to be updated occasionally, while) USAGE_DYNAMIC
缓冲区已针对频繁更新进行了优化.(buffers are optimized for very frequent updates.)
要初始化缓冲区,Digligent Engine将执行以下步骤.(To initialize the buffer, Diligent Engine performs the steps described below.)
OpenGL/GLES后端(OpenGL/GLES backend)
该操作直接转换为(The operation directly translates to) glBufferData(glBufferData) .(.)
Direct3D11后端(Direct3D11 backend)
在Direct3D11后端中,初始数据将传递到(In Direct3D11 backend, the initial data is passed over to*) ID3D11Device :: CreateBuffer(ID3D11Device::CreateBuffer) 方法(method*) .).(
Direct3D12/Vulkan后端(Direct3D12/Vulkan backend)
在下一代后端中(In next-gen backends,) USAGE_STATIC
和(and) USAGE_DEFAULT
缓冲区分配在CPU无法直接访问的纯GPU内存中.为了初始化缓冲区,Digligent Engine将创建一个临时登台缓冲区,该缓冲区将在CPU可见的内存中分配,将数据复制到该临时缓冲区,然后发出GPU端复制命令.命令完成后,将释放临时缓冲区,并将登台内存返回给系统.(buffers are allocated in GPU-only memory that is not directly accessible by CPU. To initialize the buffer, Diligent Engine creates a temporary staging buffer that is allocated in a memory visible to CPU, copies the data to this temporary buffer and then issues a GPU-side copy command. As soon as the command completes, temporary buffer is released and the staging memory is returned to the system.) USAGE_DYNAMIC
无法以这种方式初始化,将在本段后面进行介绍.(cannot be initialized this way and are described later in this paragraph.)
使用IBuffer :: UpdateData()更新缓冲区(Updating Buffers with IBuffer::UpdateData())
在运行时更新缓冲区内容的第一种方法是使用(The first way to update a buffer contents at run time is to use) IBuffer::UpdateData()
方法.仅使用创建的缓冲区(method. Only buffers created with) USAGE_DEFAULT
标志可以使用这种方式.此方法将新数据写入给定的缓冲区子区域,如下例所示:(flag can use this way. This method writes new data to a given buffer subregion, as in the example below:)
m_CubeVertexBuffer[BufferIndex]->UpdateData(
m_pImmediateContext, // Device context to use for the operation
FirstVertToUpdate * sizeof(Vertex), // Start offset in bytes
NumVertsToUpdate * sizeof(Vertex), // Data size in bytes
Vertices // Data pointer
);
在后台,勤奋引擎将此调用转换为以下操作:(Under the hood, Diligent Engine translates this call into the following operations:)
OpenGL/GLES后端(OpenGL/GLES Backend)
该操作直接转换为(The operation directly translates to) glBufferSubData(glBufferSubData) .(.)
Direct3D11后端(Direct3D11 Backend)
该操作直接转换为(The operation directly translates to) ID3D11DeviceContext :: UpdateSubresource.(ID3D11DeviceContext::UpdateSubresource.)
Direct3D12/Vulkan后端(Direct3D12/Vulkan Backend)
默认缓冲区分配在仅GPU的可访问内存中,因此无法直接写入数据.为了执行该操作,引擎首先在CPU可见的内存中分配临时存储,将数据复制到该临时存储,然后发出GPU命令将数据从存储复制到最终目的地.它还执行必要的资源状态转换(例如着色器资源->复制目标).(Default buffers are allocated in GPU-only accessible memory, so the data cannot be written directly. To perform the operation, the engine first allocates temporary storage in a CPU-visible memory, copies the data to this temporary storage and then issues GPU command to copy the data from the storage to the final destination. It also performs necessary resource state transitions (such as shader resource -> copy destination).)
性能(Performance)
IBuffer::UpdateData()
当前是更新默认(仅GPU)缓冲区中数据的唯一方法.该操作涉及两个复制操作.但是,此方法的主要但不是很明显的性能问题是状态转换.每次在复制操作中使用缓冲区时,都需要将其转换为复制目标状态.每次在着色器中使用它时,都需要将其转换为着色器资源状态.来回转换会使GPU管线停滞不前,并大大降低性能.(is currently the only way to update data in a default (GPU-only) buffer. The operation involves two copy operations. However, the main and not so obvious performance issue with this method is state transitions. Every time when a buffer is used in a copy operation, it needs to be transitioned to copy destination state. Every time it is used in a shader, it needs to be transitioned to shader resource state. Transitioning back and forth stalls the GPU pipeline and degrades performance dramatically.)
当缓冲区内容在大多数时间保持恒定并且仅需要偶尔更新时,应使用此方法,通常在一个帧中更新的频率不超过一次,例如,在重用现有缓冲区以写入新的网格数据(顶点/索引)时.此方法不应用于高频更新,例如动画或常量缓冲区更新.(This method should be used when a buffer content stays constant most of the time and only needs to be updated occasionally, usually no more often than once in a frame, for example, when reusing existing buffer to write new mesh data (vertices/indices). This method should not be used for high frequency updates such as animation or constant buffer updates.)
局限性(Limitations)
低效率的频繁更新.(Inefficient for frequent updates.)
通过映射更新动态缓冲区(Updating Dynamic Buffers via Mapping)
当需要频繁更新缓冲区内容(每帧一次或多次)时,应使用以下命令创建缓冲区(When buffer contents need to be updated frequently (once or more times per frame), the buffer should be created with) USAGE_DYNAMIC
旗.动态缓冲区不能用更新(flag. Dynamic buffers cannot be updated with) IBuffer::UpdateData()
.相反,它们需要映射以获得一个指针,该指针可用于直接将数据写入缓冲区,如以下示例所示:(. Instead, they need to be mapped to obtain a pointer that can be used to write data directly to the buffer, as in the example below:)
Vertex* Vertices = nullptr;
VertexBuffer->Map(m_pImmediateContext, MAP_WRITE, MAP_FLAG_DISCARD,
reinterpret_cast<PVoid&>(Vertices));
for(Uint32 v=0; v < _countof(CubeVerts); ++v)
{
const auto& SrcVert = CubeVerts[v];
Vertices[v].uv = SrcVert.uv;
Vertices[v].pos = SrcVert.pos;
}
VertexBuffer->Unmap(m_pImmediateContext, MAP_WRITE, MAP_FLAG_DISCARD);
OpenGL/GLES后端(OpenGL/GLES Backend)
该操作转换为(The operation translates to) glMapBufferRange(glMapBufferRange) 与(with) GL_MAP_WRITE_BIT
和(and) GL_MAP_INVALIDATE_BUFFER_BIT
标志设置.(flags set.)
Direct3D11后端(Direct3D11 Backend)
在Direct3D11后端中,此调用直接转换为(In Direct3D11 backend, this call directly translates to) ID3D11DeviceContext :: Map(ID3D11DeviceContext::Map) 与(with) D3D11_MAP_WRITE_DISCARD
旗.(flag.)
Direct3D12/Vulkan后端(Direct3D12/Vulkan Backend)
在Direct3D12或Vulkan后端中创建动态缓冲区时,不会分配任何内存.相反,两个后端都具有特殊的动态存储,这是在可持久访问的CPU可访问内存中创建的缓冲区.映射动态缓冲区时,将在此缓冲区中保留一个区域.该操作归结为(When dynamic buffer is created in Direct3D12 or Vulkan backend, no memory is allocated. Instead, both backends have special dynamic storage which is a buffer created in CPU-accessible memory that is persistently mapped. When dynamic buffer is mapped, a region is reserved in this buffer. This operation boils down to) 只需移动电流偏移(simply moving current offset) 而且很便宜.然后,返回一个引用该内存的指针,应用程序可以直接写入数据,从而避免了所有副本.使用动态缓冲区进行渲染时,将绑定内部动态缓冲区,并应用适当的偏移量.内部动态缓冲区已预先转换为只读状态,并且在运行时从未执行任何转换.引擎负责同步,以确保缓冲区中的区域在被GPU使用时不会分配给应用程序.(and is very cheap. A pointer is then returned that references this memory and the application can write data directly, avoiding all copies. When a dynamic buffer is used for rendering, internal dynamic buffer is bound instead and the proper offset is applied. Internal dynamic buffer is pre-transitioned to read-only state and no transitions are ever performed at run time. The engine takes care of synchronization making sure that a region in the buffer is never given to the application while being used by the GPU.)
性能(Performance)
在Direct3D12/Vulkan后端中,将动态缓冲区映射为(In Direct3D12/Vulkan backends, mapping dynamic buffers with) MAP_FLAG_DISCARD
该标志非常便宜,因为它仅涉及更新当前偏移量.很难说Direct3D11和OpenGL到底做了什么,但是很可能类似.但是,有一个重大区别:Direct3D11和OpenGL保留帧之间动态缓冲区的内容,而Direct3D12和Vulkan后端则不保留.结果,下一代后端的映射效率提高了很多倍.(flag is very cheap as it only involves updating current offset. It is hard to say what exactly Direct3D11 and OpenGL do under the hood, but most likely something similar. There is one significant difference however: Direct3D11 and OpenGL preserve contents of dynamic buffers between frames while Direct3D12 and Vulkan backends do not. As a result, mapping is many times more efficient in next-gen backends.)
动态缓冲区应用于经常更改的内容,通常每帧多次.最常见的示例是一个常量缓冲区,该缓冲区在每次绘制调用之前都会使用不同的转换矩阵进行更新.动态缓冲区不应用于永不更改的常量数据.(Dynamic buffers should be used for content that changes often, typically multiple time per frame. The most common example is a constant buffer that is updated with different transformation matrices before every draw call. Dynamic buffers should not be used for constant data that never changes.)
局限性(Limitations)
当前只有整个缓冲区可以使用(Only the entire buffer can currently be mapped with) MAP_FLAG_DISCARD
旗.(flag.)
在Direct3D12和Vulkan后端中,所有动态资源的内容在每一帧结束时都会丢失.动态缓冲区在首次使用之前必须在每个帧中进行映射.(In Direct3D12 and Vulkan backends, the contents of all dynamic resources are lost at the end of every frame. A dynamic buffer must be mapped in every frame before its first use.)
可以限制CPU可访问的内存总量.此外,与GPU专用内存相比,来自GPU的访问速度可能会更慢,因此动态缓冲区不应用于存储恒定或不经常更改的资源.(The total amount of CPU-accessible memory can be limited. Besides, access from the GPU may be slower compared to GPU-only memory, so dynamic buffers should not be used to store resources that are constant or change infrequently.)
流缓冲区(Streaming Buffer)
流缓冲区不是API对象,而是一种策略,该策略允许以有效的方式将可变数量的数据上传到GPU.流缓冲区的思想可以概括如下:(Streaming buffer is not an API object, but rather a strategy that allows uploading variable amounts of data to the GPU in an efficient manner. The idea of streaming buffer can be summarized as follows:)
-
创建足够大的动态缓冲区,以容纳可以上传到GPU的最大数据量.(Create dynamic buffer large enough to encompass the maximum amount of data that can be uploaded to GPU.)
-
第一次,将缓冲区映射为(First time, map the buffer with)
MAP_FLAG_DISCARD
旗.(flag.)- 这将丢弃先前的缓冲区内容并分配新的内存.(This will discard previous buffer contents and allocate new memory.)
-
将当前缓冲区偏移量设置为零,将数据写入缓冲区并相应地更新偏移量.(Set the current buffer offset to zero, write data to the buffer and update offset accordingly.)
-
取消映射缓冲区并发出绘制命令.(Unmap the buffer and issue draw command.)
- 请注意,在Direct3D12和Vulkan后端中,不需要取消映射缓冲区,可以安全地跳过缓冲区以提高性能.(Note that in Direct3D12 and Vulkan backends, unmapping the buffer is not required and can be safely skipped to improve performance.)
-
下次映射缓冲区时,请检查剩余空间是否足以容纳新的多边形数据.(When mapping the buffer next time, check if the remaining space is enough to encompass the new polygon data.)
-
如果有足够的空间,则将缓冲区映射为(If there is enough space, map the buffer with)
MAP_FLAG_DO_NOT_SYNCHRONIZE
旗.(flag.)- 这将告诉系统返回先前分配的内存.应用程序负责不覆盖GPU正在使用的内存.(This will tell the system to return previously allocated memory. It is the responsibility of the application to not overwrite the memory that is in use by the GPU.)
- 以当前偏移量写入新数据(这保证了先前写入的字节和当前由GPU使用的字节不会受到影响)并更新偏移量.(Write new data at current offset (which guarantees that bytes previously written and currently used by the GPU will not be affected) and update the offset.)
-
如果没有足够的空间,请将偏移量重置为零,然后使用(If there is not enough space, reset the offset to zero and map the buffer with)
MAP_FLAG_DISCARD
标志以请求新的内存块.(flag to request new chunk of memory.)
贴图(Textures)
尽管缓冲区只是内存的线性区域,但纹理已针对有效的采样操作进行了优化,并使用通常不向应用程序公开的不透明布局.结果,只有驱动程序知道如何将数据写入纹理. Direct3D12和Vulkan中允许使用线性纹理布局,但是效率较低.(While buffers are simply linear regions of memory, textures are optimized for efficient sampling operations and use opaque layouts that are typically not exposed to the application. As a result, only the driver knows how to write data to the texture. Linear texture layouts are allowed in Direct3D12 and Vulkan, but they are less efficient.)
纹理初始化(Texture Initialization)
与缓冲区类似,可以在创建时将初始数据提供给纹理.对于(Similar to buffers, initial data can be supplied to textures at creation time. For) USAGE_STATIC
纹理,这是唯一的方法.(textures, this is the only way.)
TexDesc TexDesc;
TexDesc.Type = RESOURCE_DIM_TEX_2D;
TexDesc.Format = TEX_FORMAT_RGBA8_UNORM_SRGB;
TexDesc.Width = 1024;
TexDesc.Height = 1024;
TexDesc.MipLevels = 1;
TexDesc.BindFlags = BIND_SHADER_RESOURCE;
TexDesc.Usage = USAGE_STATIC;
TextureData InitData;
// Pointer to subresouce data, one for every mip level
InitData.pSubResources = subresources;
InitData.NumSubresources = _countof(subresources);
RefCntAutoPtr<ITexture> Texture;
Device->CreateTexture(TexDesc, InitData, &Texture);
纹理初始化的执行类似于缓冲区初始化.在Direct3D11和OpenGL/GLES后端中,有相应的本机API调用.在Direct3D12/Vulkan后端中,引擎在可写CPU的内存中创建临时的暂存纹理,然后将数据复制到此内存,然后发出GPU复制命令.(Texture initialization is performed similar to buffer initialization. In Direct3D11 and OpenGL/GLES backends, there are corresponding native API calls. In Direct3D12/Vulkan backends, the engine creates temporary staging texture in a CPU-writable memory, copies the data to this memory and then issues a GPU copy command.)
使用ITexture :: UpdateData()更新纹理(Updating Textures with ITexture::UpdateData())
在运行时更新纹理的第一种方法是使用(The first way to update a texture at run time is to use) ITexture::UpdateData()
方法.该方法的工作原理类似于(method. The method works similar to) IBuffer::UpdateData()
并将新数据写入给定的纹理区域:(and writes new data to a given texture region:)
Box UpdateBox;
Uint32 Width = 128;
Uint32 Height = 64;
UpdateBox.MinX = 16;
UpdateBox.MinY = 32;
UpdateBox.MaxX = UpdateBox.MinX + Width;
UpdateBox.MaxY = UpdateBox.MinY + Height;
TextureSubResData SubresData;
SubresData.Stride = Width * 4;
SubresData.pData = Data.data();
Uint32 MipLevel = 0;
Uint32 ArraySlice = 0;
Texture->UpdateData(m_pImmediateContext, MipLevel,
ArraySlice, UpdateBox, SubresData);
在后台,这映射到以下本机API命令:(Under the hood, this maps to the following native API commands:)
OpenGL/GLES后端(OpenGL/GLES Backend)
该操作直接转换为(The operation directly translates to) glTexSubImage (glTexSubImage) 功能家族.(family of functions.)
Direct3D11后端(Direct3D11 Backend)
与缓冲区更新一样,在Direct3D11后端中,此调用直接映射到(As with buffer updates, in Direct3D11 backend, this call directly maps to) ID3D11DeviceContext::UpdateSubresource
.(.)
Direct3D12/Vulkan后端(Direct3D12/Vulkan Backend)
与缓冲区一样,要更新纹理,下一代后端首先在CPU可访问的内存中分配区域,然后将客户端数据复制到该区域.然后,它们执行必要的状态转换,并发出GPU复制命令,该命令使用GPU特定的布局将像素写入纹理.(As with buffers, to update a texture the next-gen backends first allocate region in a CPU-accessible memory and copy client data to this region. They then perform necessary state transitions and issue GPU copy command that writes pixels to the texture using GPU-specific layout.)
性能(Performance)
使用方案与缓冲区更新相似:该操作应用于内容保持基本恒定且仅偶尔需要更新的纹理.(Usage scenarios are similar to buffer updates: the operation should be used for textures whose contents stay mostly constant and only occasionally requires updates.)
局限性(Limitations)
由于该操作涉及两个副本和状态转换,因此对于频繁的纹理更新而言效率不高.(As the operation involves two copies and state transitions, it is not efficient for frequent texture updates.)
贴图纹理(Mapping Textures)
映射纹理是更新其内容的第二种方法.从API方面来看,映射纹理看起来类似于映射缓冲区:(Mapping a texture is a second way to update its contents. From the API side, mapping textures looks similar to mapping buffers:)
Uint32 MipLevel = 0;
Uint32 ArraySlice = 0;
MappedTextureSubresource MappedSubres;
Box MapRegion;
Uint32 Width = 128;
Uint32 Height = 256;
MapRegion.MinX = 32;
MapRegion.MinY = 64;
MapRegion.MaxX = MapRegion.MinX + Width;
MapRegion.MaxY = MapRegion.MinY + Height;
Texture->Map(m_pImmediateContext, MipLevel, ArraySlice,
MAP_WRITE, MAP_FLAG_DISCARD,
&MapRegion, MappedSubres);
WriteTextureData((Uint8*)MappedSubres.pData, Width,
Height, MappedSubres.Stride);
Texture.Unmap(m_pImmediateContext, 0, 0);
与缓冲区相比,引擎盖下发生的事情非常不同.(What happens under the hood is very different compared to buffers.)
OpenGL/GLES后端(OpenGL/GLES Backend)
OpenGL/GLES后端当前不支持映射纹理.(Mapping textures is currently not supported in OpenGL/GLES backends.)
Direct3D11后端(Direct3D11 Backend)
在Direct3D11后端中,此调用直接映射到(In Direct3D11 backend, this call directly maps to) ID3D11DeviceContext :: Map(ID3D11DeviceContext::Map) 与(with) D3D11_MAP_WRITE_DISCARD
旗.(flag.)
Direct3D12/Vulkan后端(Direct3D12/Vulkan Backend)
下一代后端没有类似于动态缓冲区的动态纹理.通过绑定父缓冲区并应用偏移量可以轻松地从另一个缓冲区中对缓冲区进行子分配,但纹理没有类似的方法.因此,即使所需的内存是从动态缓冲区中再分配的,也无法将其视为纹理.也不允许将内存绑定到现有纹理.因此,在Direct3D12/Vulkan后端中映射纹理与使用(There are no dynamic textures in next-gen backends in a way similar to dynamic buffers. While buffers can easily be suballocated from another buffer by binding parent buffer and applying an offset, there is no similar way for textures. So even if the required memory was suballocated from the dynamic buffer, there would be no way to treat this memory as a texture. Binding the memory to an existing texture is also not allowed. As a result, mapping textures in Direct3D12/Vulkan backend does not differ significantly from updating textures with) ITexture::UpdateData()
.映射纹理时,引擎将指针直接返回到CPU可访问的内存,从而避免了一次复制.但是,仍会执行GPU端复制以及最重要的状态转换.(. When mapping a texture, the engine returns the pointer to the CPU-accessible memory directly that avoids one copy. However, GPU-side copy and most importantly state transitions are still performed.)
性能(Performance)
目前尚不清楚Direct3D11的功能.两种最可能的选择是创建线性布局纹理,并在每次调用Map时从CPU可访问的内存中对其进行子分配,或者执行与Diligent的下一代后端相同的操作.(It is not exactly clear what Direct3D11 does under the hood. The two most likely options are either creating linear-layout texture and suballocating it from CPU-accessible memory every time Map is called, or performing the same operations as Diligent’s next-gen backends.)
映射动态纹理的效率不如映射动态缓冲区,并且典型的使用场景类似于(Mapping dynamic textures is not as efficient as mapping dynamic buffers, and typical usage scenarios are similar to) ITexture::UpdateData()
.(.)
没有简单的方法可以在所有API上实现高频纹理更新,因此Diligent希望这将由应用程序使用低级API互操作性来实现.对于Direct3D12和Vulkan后端,一种可能的方法是在可写CPU的内存中创建许多线性布局纹理,并以循环方式使用它们.由于此方法是非常特定于应用程序的,因此Diligent Engine不会通过通用API公开它.(There is no simple way to implement high-frequency texture updates across all APIs, so Diligent expects that this will be implemented by the application using low-level API interoperability. For Direct3D12 and Vulkan backends, one possible way is to create a number of linear-layout textures in CPU-writable memory and use them in a round-robin fashion. As this method is very application-specific, Diligent Engine does not expose it through common API.)
局限性(Limitations)
OpenGL/GLES后端当前未实现纹理映射.(Texture mapping is not currently implemented in OpenGL/GLES backend.)
在Direct3D11中,只能使用(In Direct3D11, only the entire texture level can be mapped with) D3D11_MAP_WRITE_DISCARD
旗.(flag.)
在Direct3D12/Vulkan后端中,映射动态纹理不如映射动态缓冲区有效.实际上,这与使用以下命令更新纹理非常相似(In Direct3D12/Vulkan backends, mapping dynamic textures is not as efficient as mapping dynamic buffers. In fact, it is very similar to updating textures with) ITexture::UpdateData()
并且仅避免一次CPU端复制.(and only avoids one CPU-side copy.)
概要(Summary)
下表总结了缓冲区的更新方法:(The following table summarizes update methods for buffers:)
更新方案(Update scenario) | 用法(Usage) | 更新方式(Update Method) | 评论(Comment) |
---|---|---|---|
恒定数据(Constant data) | USAGE_STATIC |
n/a | 只能在缓冲区初始化期间写入数据(Data can only be written during buffer initialization) |
<每帧一次(< Once per frame) | USAGE_DEFAULT |
IBuffer::UpdateData() |
|
> =每帧一次(>= Once per frame) | USAGE_DYNAMIC |
IBuffer::Map() |
每帧末尾使动态缓冲区的内容无效(The content of dynamic buffers is invalidated at the end of every frame) |
下表总结了纹理的更新方法:(The following table summarizes update methods for textures:)
更新方案(Update scenario) | 使用/更新方法(Usage/Update Method) | 评论(Comment) |
---|---|---|
恒定数据(Constant data) | USAGE_STATIC /不适用(/ n/a) |
只能在纹理初始化期间写入数据(Data can only be written during texture initialization) |
<每帧一次(< Once per frame) | USAGE_DEFAULT + ITexture::UpdateData() or USAGE_DYNAMIC + ITexture::Map() |
|
> =每帧一次(>= Once per frame) | 由应用程序实现(Implemented by the application) | 动态纹理的实现方式与动态缓冲区的实现方式不同(Dynamic textures cannot be implemented the same way as dynamic buffers) |
源代码(Source Code)
完整的引擎源代码可从以下网址下载:(Full engine source code is available for download at) 的GitHub(GitHub) .(.) 以下教程说明了本文中描述的思想:(The following tutorials illustrate the ideas described in this article:) 教程10-数据流(Tutorial 10 - Data Streaming)
教程11-资源更新(Tutorial 11 - Resource Updates)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C++ Windows DirectX OpenGL Vulkan 3D 新闻 翻译