Skip to content

Commit

Permalink
[d3d11] Reset dirty bindings on command submission
Browse files Browse the repository at this point in the history
  • Loading branch information
doitsujin committed Feb 19, 2025
1 parent 970ea75 commit 7031a44
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/d3d11/d3d11_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3186,6 +3186,7 @@ namespace dxvk {
if (!bindMask)
return;

// Need to clear dirty bits before binding
const auto& state = m_state.cbv[Stage];
DirtyMask.cbvMask -= bindMask;

Expand All @@ -3208,6 +3209,7 @@ namespace dxvk {
if (!bindMask)
return;

// Need to clear dirty bits before binding
const auto& state = m_state.samplers[Stage];
DirtyMask.samplerMask -= bindMask;

Expand All @@ -3230,6 +3232,7 @@ namespace dxvk {
if (!bindMask)
continue;

// Need to clear dirty bits before binding
DirtyMask.srvMask[maskIndex] -= bindMask;

for (uint32_t slot : bit::BitMask(bindMask))
Expand Down Expand Up @@ -4889,6 +4892,8 @@ namespace dxvk {
for (uint32_t i = 0; i < m_state.so.targets.size(); i++)
BindXfbBuffer(i, m_state.so.targets[i].buffer.ptr(), ~0u);

// Reset dirty binding and shader masks before applying
// bindings to avoid implicit null binding overrids.
ResetDirtyTracking();

for (uint32_t i = 0; i < uint32_t(DxbcProgramType::Count); i++) {
Expand Down
77 changes: 76 additions & 1 deletion src/d3d11/d3d11_context_imm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,12 @@ namespace dxvk {
D3D10DeviceLock lock = LockContext();

auto commandList = static_cast<D3D11CommandList*>(pCommandList);


// Reset dirty binding tracking before submitting any CS chunks.
// This is needed so that any submission that might occur during
// this call does not disrupt bindings set by the deferred context.
ResetDirtyTracking();

// Clear state so that the command list can't observe any
// current context state. The command list itself will clean
// up after execution to ensure that no state changes done
Expand Down Expand Up @@ -979,6 +984,73 @@ namespace dxvk {
}


void D3D11ImmediateContext::ApplyDirtyNullBindings() {
// At the end of a submission, set all bindings that have not been applied yet
// to null on the DXVK context. This way, we avoid keeping resources alive that
// are bound to the DXVK context but not to the immediate context.
//
// Note: This requires that all methods that may modify dirty bindings on the
// DXVK context also reset the corresponding dirty bits *before* performing the
// bind operation, or otherwise an implicit flush can potentially override them.
auto& dirtyState = m_state.lazy.bindingsDirty;

EmitCs<false>([
cDirtyState = dirtyState
] (DxvkContext* ctx) {
for (uint32_t i = 0; i < uint32_t(DxbcProgramType::Count); i++) {
auto dxStage = DxbcProgramType(i);
auto vkStage = GetShaderStage(dxStage);

// Unbind all dirty constant buffers
auto cbvSlot = computeConstantBufferBinding(dxStage, 0);

for (uint32_t index : bit::BitMask(cDirtyState[dxStage].cbvMask))
ctx->bindUniformBuffer(vkStage, cbvSlot + index, DxvkBufferSlice());

// Unbind all dirty samplers
auto samplerSlot = computeSamplerBinding(dxStage, 0);

for (uint32_t index : bit::BitMask(cDirtyState[dxStage].samplerMask))
ctx->bindResourceSampler(vkStage, samplerSlot + index, nullptr);

// Unbind all dirty shader resource views
auto srvSlot = computeSrvBinding(dxStage, 0);

for (uint32_t m = 0; m < cDirtyState[dxStage].srvMask.size(); m++) {
for (uint32_t index : bit::BitMask(cDirtyState[dxStage].srvMask[m]))
ctx->bindResourceImageView(vkStage, srvSlot + index, nullptr);
}
}
});

// Since we set the DXVK context bindings to null, any bindings that are null
// on the D3D context are no longer dirty, so we can clear the respective bits.
for (uint32_t i = 0; i < uint32_t(DxbcProgramType::Count); i++) {
auto stage = DxbcProgramType(i);

for (uint32_t index : bit::BitMask(dirtyState[stage].cbvMask)) {
if (!m_state.cbv[stage].buffers[index].buffer.ptr())
dirtyState[stage].cbvMask &= ~(1u << index);
}

for (uint32_t index : bit::BitMask(dirtyState[stage].samplerMask)) {
if (!m_state.samplers[stage].samplers[index])
dirtyState[stage].samplerMask &= ~(1u << index);
}

for (uint32_t m = 0; m < dirtyState[stage].srvMask.size(); m++) {
for (uint32_t index : bit::BitMask(dirtyState[stage].srvMask[m])) {
if (!m_state.srv[stage].views[index + m * 64u].ptr())
dirtyState[stage].srvMask[m] &= ~(uint64_t(1u) << index);
}
}

if (dirtyState[stage].empty())
m_state.lazy.shadersDirty.clr(stage);
}
}


void D3D11ImmediateContext::ConsiderFlush(
GpuFlushType FlushType) {
uint64_t chunkId = GetCurrentSequenceNumber();
Expand All @@ -1002,6 +1074,9 @@ namespace dxvk {
if (!GetPendingCsChunks() && !hEvent)
return;

// Unbind unused resources
ApplyDirtyNullBindings();

// Signal the submission fence and flush the command list
uint64_t submissionId = ++m_submissionId;

Expand Down
2 changes: 2 additions & 0 deletions src/d3d11/d3d11_context_imm.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ namespace dxvk {

uint64_t GetPendingCsChunks();

void ApplyDirtyNullBindings();

void ConsiderFlush(
GpuFlushType FlushType);

Expand Down
3 changes: 3 additions & 0 deletions src/d3d11/d3d11_video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,9 @@ namespace dxvk {
continue;

if (!hasStreamsEnabled) {
m_ctx->ResetDirtyTracking();
m_ctx->ResetCommandListState();

BindOutputView(pOutputView);
hasStreamsEnabled = true;
}
Expand All @@ -1047,6 +1049,7 @@ namespace dxvk {

if (hasStreamsEnabled) {
UnbindResources();

m_ctx->RestoreCommandListState();
}

Expand Down

0 comments on commit 7031a44

Please sign in to comment.