Skip to content

Commit 2ae011f

Browse files
committed
[D3D11] Fixed undefined behavior of UpdateSubresource() on deferred command context (#156).
Use UpdateSubresource1() when the destination offset is non-zero or emulate it as described in the MSDN docs: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-updatesubresource#calling-updatesubresource-on-a-deferred-context
1 parent 89617e5 commit 2ae011f

File tree

5 files changed

+38
-5
lines changed

5 files changed

+38
-5
lines changed

sources/Renderer/Direct3D11/Buffer/D3D11Buffer.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ BufferDescriptor D3D11Buffer::GetDesc() const
8484
return bufferDesc;
8585
}
8686

87-
void D3D11Buffer::WriteSubresource(ID3D11DeviceContext* context, const void* data, UINT dataSize, UINT offset)
87+
void D3D11Buffer::WriteSubresource(ID3D11DeviceContext* context, const void* data, UINT dataSize, UINT offset, bool needsCommandListEmulation)
8888
{
8989
/* Validate parameters */
9090
LLGL_ASSERT_RANGE(dataSize + offset, GetSize());
@@ -127,7 +127,28 @@ void D3D11Buffer::WriteSubresource(ID3D11DeviceContext* context, const void* dat
127127
{
128128
/* Update subresource region of buffer */
129129
const D3D11_BOX dstBox{ offset, 0, 0, offset + dataSize, 1, 1 };
130-
context->UpdateSubresource(GetNative(), 0, &dstBox, data, 0, 0);
130+
if (offset != 0 && needsCommandListEmulation && context->GetType() == D3D11_DEVICE_CONTEXT_DEFERRED)
131+
{
132+
#if LLGL_D3D11_ENABLE_FEATURELEVEL >= 1
133+
ComPtr<ID3D11DeviceContext1> context1;
134+
if (SUCCEEDED(context->QueryInterface(IID_PPV_ARGS(&context1))))
135+
{
136+
/* Update buffer with UpdateSubresource1() which fixes the issue described below */
137+
context1->UpdateSubresource1(GetNative(), 0, &dstBox, data, 0, 0, 0);
138+
}
139+
else
140+
#endif
141+
{
142+
/*
143+
Update subresource region of buffer with adjusted source pointer to workaround limitation of emulated command lists.
144+
See https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-updatesubresource#calling-updatesubresource-on-a-deferred-context
145+
*/
146+
const char* dataWithOffset = static_cast<const char*>(data) - offset;
147+
context->UpdateSubresource(GetNative(), 0, &dstBox, dataWithOffset, 0, 0);
148+
}
149+
}
150+
else
151+
context->UpdateSubresource(GetNative(), 0, &dstBox, data, 0, 0);
131152
}
132153
}
133154
}

sources/Renderer/Direct3D11/Buffer/D3D11Buffer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class D3D11Buffer : public Buffer
3434

3535
D3D11Buffer(ID3D11Device* device, const BufferDescriptor& desc, const void* initialData = nullptr);
3636

37-
void WriteSubresource(ID3D11DeviceContext* context, const void* data, UINT dataSize, UINT offset);
37+
void WriteSubresource(ID3D11DeviceContext* context, const void* data, UINT dataSize, UINT offset, bool needsCommandListEmulation);
3838
void ReadSubresource(ID3D11DeviceContext* context, void* data, UINT dataSize, UINT offset);
3939

4040
void* Map(ID3D11DeviceContext* context, const CPUAccess access, UINT offset, UINT length);

sources/Renderer/Direct3D11/Command/D3D11PrimaryCommandBuffer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ void D3D11PrimaryCommandBuffer::UpdateBuffer(
9696
std::uint16_t dataSize)
9797
{
9898
auto& dstBufferD3D = LLGL_CAST(D3D11Buffer&, dstBuffer);
99-
dstBufferD3D.WriteSubresource(GetNative(), data, static_cast<UINT>(dataSize), static_cast<UINT>(dstOffset));
99+
const bool needsCommandListEmulation = context_.GetStateManager().NeedsCommandListEmulation();
100+
dstBufferD3D.WriteSubresource(GetNative(), data, static_cast<UINT>(dataSize), static_cast<UINT>(dstOffset), needsCommandListEmulation);
100101
}
101102

102103
void D3D11PrimaryCommandBuffer::CopyBuffer(

sources/Renderer/Direct3D11/D3D11RenderSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ void D3D11RenderSystem::Release(BufferArray& bufferArray)
195195
void D3D11RenderSystem::WriteBuffer(Buffer& buffer, std::uint64_t offset, const void* data, std::uint64_t dataSize)
196196
{
197197
auto& bufferD3D = LLGL_CAST(D3D11Buffer&, buffer);
198-
bufferD3D.WriteSubresource(context_.Get(), data, static_cast<UINT>(dataSize), static_cast<UINT>(offset));
198+
bufferD3D.WriteSubresource(context_.Get(), data, static_cast<UINT>(dataSize), static_cast<UINT>(offset), stateMngr_->NeedsCommandListEmulation());
199199
}
200200

201201
void D3D11RenderSystem::ReadBuffer(Buffer& buffer, std::uint64_t offset, void* data, std::uint64_t dataSize)

sources/Renderer/Direct3D11/RenderState/D3D11StateManager.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ class D3D11StateManager
110110
return bindingTable_;
111111
}
112112

113+
// Returns whether the device context this state manager operates on needs command list emulation.
114+
// This is true if the D3D device does not natively support command lists.
115+
inline bool NeedsCommandListEmulation() const
116+
{
117+
#if LLGL_D3D11_ENABLE_FEATURELEVEL >= 1
118+
return needsCommandListEmulation_;
119+
#else
120+
return true;
121+
#endif
122+
}
123+
113124
private:
114125

115126
struct D3DInputAssemblyState

0 commit comments

Comments
 (0)