d3d12: Handle memory barriers

This is a bit fragile. The algorithm is essentially:
- Let the driver track state for non-dual-bound resources.
- For resources that are dual-bound as SSBO/image and a second
  bind point, assume they're being used as UAVs.
- When a MemoryBarrier is issued, dirty all destination bind points
  so they re-assert their state, and if the destination is not UAV,
  temporarily suppress UAV state re-assertion.

Reviewed-by: Sil Vilerino <sivileri@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/14342>
This commit is contained in:
Jesse Natalie
2021-12-29 15:55:27 -08:00
committed by Marge Bot
parent 63fe4888ea
commit 156ef05ec7
5 changed files with 81 additions and 14 deletions
@@ -136,6 +136,7 @@ d3d12_reset_batch(struct d3d12_context *ctx, struct d3d12_batch *batch, uint64_t
return false;
}
batch->has_errors = false;
batch->pending_memory_barrier = false;
return true;
}
+1
View File
@@ -53,6 +53,7 @@ struct d3d12_batch {
struct d3d12_descriptor_heap *sampler_heap;
struct d3d12_descriptor_heap *view_heap;
bool has_errors;
bool pending_memory_barrier;
};
bool
@@ -2022,6 +2022,54 @@ d3d12_replace_buffer_storage(struct pipe_context *pctx,
d3d12_bo_unreference(old_bo);
}
static void
d3d12_memory_barrier(struct pipe_context *pctx, unsigned flags)
{
struct d3d12_context *ctx = d3d12_context(pctx);
if (flags & PIPE_BARRIER_VERTEX_BUFFER)
ctx->state_dirty |= D3D12_DIRTY_VERTEX_BUFFERS;
if (flags & PIPE_BARRIER_INDEX_BUFFER)
ctx->state_dirty |= D3D12_DIRTY_INDEX_BUFFER;
if (flags & PIPE_BARRIER_FRAMEBUFFER)
ctx->state_dirty |= D3D12_DIRTY_FRAMEBUFFER;
if (flags & PIPE_BARRIER_STREAMOUT_BUFFER)
ctx->state_dirty |= D3D12_DIRTY_STREAM_OUTPUT;
/* TODO:
* PIPE_BARRIER_INDIRECT_BUFFER
*/
for (unsigned i = 0; i < D3D12_GFX_SHADER_STAGES; ++i) {
if (flags & PIPE_BARRIER_CONSTANT_BUFFER)
ctx->shader_dirty[i] |= D3D12_SHADER_DIRTY_CONSTBUF;
if (flags & PIPE_BARRIER_TEXTURE)
ctx->shader_dirty[i] |= D3D12_SHADER_DIRTY_SAMPLER_VIEWS;
if (flags & PIPE_BARRIER_SHADER_BUFFER)
ctx->shader_dirty[i] |= D3D12_SHADER_DIRTY_SSBO;
if (flags & PIPE_BARRIER_IMAGE)
ctx->shader_dirty[i] |= D3D12_SHADER_DIRTY_IMAGE;
}
/* Indicate that UAVs shouldn't override transitions. Ignore barriers that are only
* for UAVs or other fixed-function state that doesn't need a draw to resolve.
*/
const unsigned ignored_barrier_flags =
PIPE_BARRIER_IMAGE |
PIPE_BARRIER_SHADER_BUFFER |
PIPE_BARRIER_UPDATE |
PIPE_BARRIER_MAPPED_BUFFER |
PIPE_BARRIER_QUERY_BUFFER;
d3d12_current_batch(ctx)->pending_memory_barrier = (flags & ~ignored_barrier_flags) != 0;
if (flags & (PIPE_BARRIER_IMAGE | PIPE_BARRIER_SHADER_BUFFER)) {
D3D12_RESOURCE_BARRIER uavBarrier;
uavBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
uavBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
uavBarrier.UAV.pResource = nullptr;
ctx->cmdlist->ResourceBarrier(1, &uavBarrier);
}
}
struct pipe_context *
d3d12_context_create(struct pipe_screen *pscreen, void *priv, unsigned flags)
{
@@ -2099,6 +2147,8 @@ d3d12_context_create(struct pipe_screen *pscreen, void *priv, unsigned flags)
ctx->base.flush = d3d12_flush;
ctx->base.flush_resource = d3d12_flush_resource;
ctx->base.memory_barrier = d3d12_memory_barrier;
ctx->gfx_pipeline_state.sample_mask = ~0;
d3d12_context_surface_init(&ctx->base);
+17 -14
View File
@@ -303,21 +303,23 @@ fill_image_descriptors(struct d3d12_context *ctx,
unreachable("Unexpected image view dimension");
}
if (res->base.b.target == PIPE_BUFFER) {
d3d12_transition_resource_state(ctx, res, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_BIND_INVALIDATE_NONE);
} else {
unsigned transition_first_layer = view->u.tex.first_layer;
unsigned transition_array_size = array_size;
if (res->base.b.target == PIPE_TEXTURE_3D) {
transition_first_layer = 0;
transition_array_size = 0;
if (!batch->pending_memory_barrier) {
if (res->base.b.target == PIPE_BUFFER) {
d3d12_transition_resource_state(ctx, res, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_BIND_INVALIDATE_NONE);
} else {
unsigned transition_first_layer = view->u.tex.first_layer;
unsigned transition_array_size = array_size;
if (res->base.b.target == PIPE_TEXTURE_3D) {
transition_first_layer = 0;
transition_array_size = 0;
}
d3d12_transition_subresources_state(ctx, res,
view->u.tex.level, 1,
transition_first_layer, transition_array_size,
0, 1,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
D3D12_BIND_INVALIDATE_NONE);
}
d3d12_transition_subresources_state(ctx, res,
view->u.tex.level, 1,
transition_first_layer, transition_array_size,
0, 1,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
D3D12_BIND_INVALIDATE_NONE);
}
d3d12_batch_reference_resource(batch, res, true);
@@ -930,6 +932,7 @@ d3d12_draw_vbo(struct pipe_context *pctx,
draws[0].start, dinfo->start_instance);
ctx->state_dirty = 0;
batch->pending_memory_barrier = false;
if (index_buffer)
ctx->cmdlist_dirty = 0;
@@ -274,6 +274,18 @@ void ResourceStateManager::ProcessTransitioningResource(ID3D12Resource* pTransit
continue;
}
// This is a transition into a state that is both write and non-write.
// This is invalid according to D3D12. We're venturing into undefined behavior
// land, but let's just pick the write state.
if (IsD3D12WriteState(after) &&
(after & ~RESOURCE_STATE_ALL_WRITE_BITS) != 0)
{
after &= RESOURCE_STATE_ALL_WRITE_BITS;
// For now, this is the only way I've seen where this can happen.
assert(after == D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
}
ProcessTransitioningSubresourceExplicit(
CurrentState,
i,