d3d12: Handle memory barriers
authorJesse Natalie <jenatali@microsoft.com>
Wed, 29 Dec 2021 23:55:27 +0000 (15:55 -0800)
committerMarge Bot <emma+marge@anholt.net>
Fri, 7 Jan 2022 03:31:16 +0000 (03:31 +0000)
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>

src/gallium/drivers/d3d12/d3d12_batch.cpp
src/gallium/drivers/d3d12/d3d12_batch.h
src/gallium/drivers/d3d12/d3d12_context.cpp
src/gallium/drivers/d3d12/d3d12_draw.cpp
src/microsoft/resource_state_manager/D3D12ResourceState.cpp

index 9cb571a..406e5b0 100644 (file)
@@ -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;
 }
 
index f49fe61..7eb29ad 100644 (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
index ff649a0..b1e6065 100644 (file)
@@ -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);
index b523ef8..53194c0 100644 (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;
index 528e672..5acb948 100644 (file)
@@ -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,