Skip to content

Commit

Permalink
[D3D11] Fixed undefined behavior of UpdateSubresource() on deferred c…
Browse files Browse the repository at this point in the history
…ommand 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
  • Loading branch information
LukasBanana committed Feb 6, 2025
1 parent 89617e5 commit 2ae011f
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 5 deletions.
25 changes: 23 additions & 2 deletions sources/Renderer/Direct3D11/Buffer/D3D11Buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ BufferDescriptor D3D11Buffer::GetDesc() const
return bufferDesc;
}

void D3D11Buffer::WriteSubresource(ID3D11DeviceContext* context, const void* data, UINT dataSize, UINT offset)
void D3D11Buffer::WriteSubresource(ID3D11DeviceContext* context, const void* data, UINT dataSize, UINT offset, bool needsCommandListEmulation)
{
/* Validate parameters */
LLGL_ASSERT_RANGE(dataSize + offset, GetSize());
Expand Down Expand Up @@ -127,7 +127,28 @@ void D3D11Buffer::WriteSubresource(ID3D11DeviceContext* context, const void* dat
{
/* Update subresource region of buffer */
const D3D11_BOX dstBox{ offset, 0, 0, offset + dataSize, 1, 1 };
context->UpdateSubresource(GetNative(), 0, &dstBox, data, 0, 0);
if (offset != 0 && needsCommandListEmulation && context->GetType() == D3D11_DEVICE_CONTEXT_DEFERRED)
{
#if LLGL_D3D11_ENABLE_FEATURELEVEL >= 1
ComPtr<ID3D11DeviceContext1> context1;
if (SUCCEEDED(context->QueryInterface(IID_PPV_ARGS(&context1))))
{
/* Update buffer with UpdateSubresource1() which fixes the issue described below */
context1->UpdateSubresource1(GetNative(), 0, &dstBox, data, 0, 0, 0);
}
else
#endif
{
/*
Update subresource region of buffer with adjusted source pointer to workaround limitation of emulated command lists.
See https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-updatesubresource#calling-updatesubresource-on-a-deferred-context
*/
const char* dataWithOffset = static_cast<const char*>(data) - offset;
context->UpdateSubresource(GetNative(), 0, &dstBox, dataWithOffset, 0, 0);
}
}
else
context->UpdateSubresource(GetNative(), 0, &dstBox, data, 0, 0);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion sources/Renderer/Direct3D11/Buffer/D3D11Buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class D3D11Buffer : public Buffer

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

void WriteSubresource(ID3D11DeviceContext* context, const void* data, UINT dataSize, UINT offset);
void WriteSubresource(ID3D11DeviceContext* context, const void* data, UINT dataSize, UINT offset, bool needsCommandListEmulation);
void ReadSubresource(ID3D11DeviceContext* context, void* data, UINT dataSize, UINT offset);

void* Map(ID3D11DeviceContext* context, const CPUAccess access, UINT offset, UINT length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ void D3D11PrimaryCommandBuffer::UpdateBuffer(
std::uint16_t dataSize)
{
auto& dstBufferD3D = LLGL_CAST(D3D11Buffer&, dstBuffer);
dstBufferD3D.WriteSubresource(GetNative(), data, static_cast<UINT>(dataSize), static_cast<UINT>(dstOffset));
const bool needsCommandListEmulation = context_.GetStateManager().NeedsCommandListEmulation();
dstBufferD3D.WriteSubresource(GetNative(), data, static_cast<UINT>(dataSize), static_cast<UINT>(dstOffset), needsCommandListEmulation);
}

void D3D11PrimaryCommandBuffer::CopyBuffer(
Expand Down
2 changes: 1 addition & 1 deletion sources/Renderer/Direct3D11/D3D11RenderSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ void D3D11RenderSystem::Release(BufferArray& bufferArray)
void D3D11RenderSystem::WriteBuffer(Buffer& buffer, std::uint64_t offset, const void* data, std::uint64_t dataSize)
{
auto& bufferD3D = LLGL_CAST(D3D11Buffer&, buffer);
bufferD3D.WriteSubresource(context_.Get(), data, static_cast<UINT>(dataSize), static_cast<UINT>(offset));
bufferD3D.WriteSubresource(context_.Get(), data, static_cast<UINT>(dataSize), static_cast<UINT>(offset), stateMngr_->NeedsCommandListEmulation());
}

void D3D11RenderSystem::ReadBuffer(Buffer& buffer, std::uint64_t offset, void* data, std::uint64_t dataSize)
Expand Down
11 changes: 11 additions & 0 deletions sources/Renderer/Direct3D11/RenderState/D3D11StateManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ class D3D11StateManager
return bindingTable_;
}

// Returns whether the device context this state manager operates on needs command list emulation.
// This is true if the D3D device does not natively support command lists.
inline bool NeedsCommandListEmulation() const
{
#if LLGL_D3D11_ENABLE_FEATURELEVEL >= 1
return needsCommandListEmulation_;
#else
return true;
#endif
}

private:

struct D3DInputAssemblyState
Expand Down

0 comments on commit 2ae011f

Please sign in to comment.