diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp index 1bd2efd36b..f71cad864a 100644 --- a/src/dawn_native/CommandEncoder.cpp +++ b/src/dawn_native/CommandEncoder.cpp @@ -522,7 +522,7 @@ namespace dawn_native { RenderPassEncoder* CommandEncoder::APIBeginRenderPass(const RenderPassDescriptor* descriptor) { DeviceBase* device = GetDevice(); - PassResourceUsageTracker usageTracker(PassType::Render); + RenderPassResourceUsageTracker usageTracker; uint32_t width = 0; uint32_t height = 0; @@ -946,7 +946,7 @@ namespace dawn_native { TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "CommandEncoder::ValidateFinish"); DAWN_TRY(GetDevice()->ValidateObject(this)); - for (const PassResourceUsage& passUsage : mEncodingContext.GetRenderPassUsages()) { + for (const RenderPassResourceUsage& passUsage : mEncodingContext.GetRenderPassUsages()) { DAWN_TRY(ValidateSyncScopeResourceUsage(passUsage)); } // TODO(dawn:632): The synchronization scopes of compute passes should be validated here diff --git a/src/dawn_native/ComputePassEncoder.cpp b/src/dawn_native/ComputePassEncoder.cpp index 666235e7ef..15f13112c2 100644 --- a/src/dawn_native/ComputePassEncoder.cpp +++ b/src/dawn_native/ComputePassEncoder.cpp @@ -27,15 +27,14 @@ namespace dawn_native { ComputePassEncoder::ComputePassEncoder(DeviceBase* device, CommandEncoder* commandEncoder, EncodingContext* encodingContext) - : ProgrammablePassEncoder(device, encodingContext, PassType::Compute), - mCommandEncoder(commandEncoder) { + : ProgrammablePassEncoder(device, encodingContext), mCommandEncoder(commandEncoder) { } ComputePassEncoder::ComputePassEncoder(DeviceBase* device, CommandEncoder* commandEncoder, EncodingContext* encodingContext, ErrorTag errorTag) - : ProgrammablePassEncoder(device, encodingContext, errorTag, PassType::Compute), + : ProgrammablePassEncoder(device, encodingContext, errorTag), mCommandEncoder(commandEncoder) { } @@ -55,8 +54,7 @@ namespace dawn_native { return {}; })) { - mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage(), - PassType::Compute); + mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage()); } } @@ -134,6 +132,29 @@ namespace dawn_native { }); } + void ComputePassEncoder::APISetBindGroup(uint32_t groupIndexIn, + BindGroupBase* group, + uint32_t dynamicOffsetCount, + const uint32_t* dynamicOffsets) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + BindGroupIndex groupIndex(groupIndexIn); + + if (IsValidationEnabled()) { + DAWN_TRY( + ValidateSetBindGroup(groupIndex, group, dynamicOffsetCount, dynamicOffsets)); + } + + RecordSetBindGroup(allocator, groupIndex, group, dynamicOffsetCount, dynamicOffsets); + mCommandBufferState.SetBindGroup(groupIndex, group); + + // TODO(dawn:632): This doesn't match the WebGPU specification. Instead the + // synchronization scopes should be created on Dispatch(). + mUsageTracker.AddBindGroup(group); + + return {}; + }); + } + void ComputePassEncoder::APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { if (IsValidationEnabled()) { diff --git a/src/dawn_native/ComputePassEncoder.h b/src/dawn_native/ComputePassEncoder.h index fcff7a9f96..18305d8422 100644 --- a/src/dawn_native/ComputePassEncoder.h +++ b/src/dawn_native/ComputePassEncoder.h @@ -15,7 +15,9 @@ #ifndef DAWNNATIVE_COMPUTEPASSENCODER_H_ #define DAWNNATIVE_COMPUTEPASSENCODER_H_ +#include "dawn_native/CommandBufferStateTracker.h" #include "dawn_native/Error.h" +#include "dawn_native/PassResourceUsageTracker.h" #include "dawn_native/ProgrammablePassEncoder.h" namespace dawn_native { @@ -36,6 +38,11 @@ namespace dawn_native { void APIDispatchIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset); void APISetPipeline(ComputePipelineBase* pipeline); + void APISetBindGroup(uint32_t groupIndex, + BindGroupBase* group, + uint32_t dynamicOffsetCount = 0, + const uint32_t* dynamicOffsets = nullptr); + void APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex); protected: @@ -45,6 +52,9 @@ namespace dawn_native { ErrorTag errorTag); private: + CommandBufferStateTracker mCommandBufferState; + ComputePassResourceUsageTracker mUsageTracker; + // For render and compute passes, the encoding context is borrowed from the command encoder. // Keep a reference to the encoder to make sure the context isn't freed. Ref mCommandEncoder; diff --git a/src/dawn_native/EncodingContext.cpp b/src/dawn_native/EncodingContext.cpp index 1752f6c422..fe347db9e5 100644 --- a/src/dawn_native/EncodingContext.cpp +++ b/src/dawn_native/EncodingContext.cpp @@ -75,21 +75,20 @@ namespace dawn_native { mCurrentEncoder = passEncoder; } - void EncodingContext::ExitPass(const ObjectBase* passEncoder, - PassResourceUsage passUsage, - PassType type) { - // Assert we're not at the top level. + void EncodingContext::ExitPass(const ObjectBase* passEncoder, RenderPassResourceUsage usages) { ASSERT(mCurrentEncoder != mTopLevelEncoder); - // Assert the pass encoder is current. ASSERT(mCurrentEncoder == passEncoder); mCurrentEncoder = mTopLevelEncoder; + mRenderPassUsages.push_back(std::move(usages)); + } - if (type == PassType::Render) { - mRenderPassUsages.push_back(std::move(passUsage)); - } else { - mComputePassUsages.push_back(std::move(passUsage)); - } + void EncodingContext::ExitPass(const ObjectBase* passEncoder, ComputePassResourceUsage usages) { + ASSERT(mCurrentEncoder != mTopLevelEncoder); + ASSERT(mCurrentEncoder == passEncoder); + + mCurrentEncoder = mTopLevelEncoder; + mComputePassUsages.push_back(std::move(usages)); } const RenderPassUsages& EncodingContext::GetRenderPassUsages() const { diff --git a/src/dawn_native/EncodingContext.h b/src/dawn_native/EncodingContext.h index 7d0f88d22e..b97e317abb 100644 --- a/src/dawn_native/EncodingContext.h +++ b/src/dawn_native/EncodingContext.h @@ -74,7 +74,8 @@ namespace dawn_native { // Functions to set current encoder state void EnterPass(const ObjectBase* passEncoder); - void ExitPass(const ObjectBase* passEncoder, PassResourceUsage passUsages, PassType type); + void ExitPass(const ObjectBase* passEncoder, RenderPassResourceUsage usages); + void ExitPass(const ObjectBase* passEncoder, ComputePassResourceUsage usages); MaybeError Finish(); const RenderPassUsages& GetRenderPassUsages() const; diff --git a/src/dawn_native/PassResourceUsage.h b/src/dawn_native/PassResourceUsage.h index 0f643468e4..701319f2ba 100644 --- a/src/dawn_native/PassResourceUsage.h +++ b/src/dawn_native/PassResourceUsage.h @@ -23,12 +23,15 @@ namespace dawn_native { + // This file declares various "ResourceUsage" structures. They are produced by the frontend + // while recording commands to be used for later validation and also some operations in the + // backends. The are produced by the "Encoder" objects that finalize them on "EndPass" or + // "Finish". Internally the "Encoder" may use the "StateTracker" to create them. + class BufferBase; class QuerySetBase; class TextureBase; - enum class PassType { Render, Compute }; - // The texture usage inside passes must be tracked per-subresource. using TextureSubresourceUsage = SubresourceStorage; @@ -43,18 +46,36 @@ namespace dawn_native { std::vector textureUsages; }; - // Additional data tracked per-pass. - struct PassResourceUsage : public SyncScopeResourceUsage { + // Contains all the resource usage data for a compute pass. + // + // TODO(dawn:632) Not now, but in the future, compute passes will contain a list of + // SyncScopeResourceUsage, one per Dispatch as required by the WebGPU specification. They will + // also store inline the set of all buffers and textures used, because some unused BindGroups + // may not be used at all in synchronization scope but their resources still need to be + // validated on Queue::Submit. + struct ComputePassResourceUsage : public SyncScopeResourceUsage {}; + + // Contains all the resource usage data for a render pass. + // + // In the WebGPU specification render passes are synchronization scopes but we also need to + // track additional data. It is stored for render passes used by a CommandBuffer, but also in + // RenderBundle so they can be merged into the render passes' usage on ExecuteBundles(). + struct RenderPassResourceUsage : public SyncScopeResourceUsage { + // Storage to track the occlusion queries used during the pass. std::vector querySets; std::vector> queryAvailabilities; }; - using RenderPassUsages = std::vector; - using ComputePassUsages = std::vector; + using RenderPassUsages = std::vector; + using ComputePassUsages = std::vector; + // Contains a hierarchy of "ResourceUsage" that mirrors the hierarchy of the CommandBuffer and + // is used for validation and to produce barriers and lazy clears in the backends. struct CommandBufferResourceUsage { RenderPassUsages renderPasses; ComputePassUsages computePasses; + + // Resources used in commands that aren't in a pass. std::set topLevelBuffers; std::set topLevelTextures; std::set usedQuerySets; diff --git a/src/dawn_native/PassResourceUsageTracker.cpp b/src/dawn_native/PassResourceUsageTracker.cpp index 806d40be95..25ebe67b1e 100644 --- a/src/dawn_native/PassResourceUsageTracker.cpp +++ b/src/dawn_native/PassResourceUsageTracker.cpp @@ -14,6 +14,7 @@ #include "dawn_native/PassResourceUsageTracker.h" +#include "dawn_native/BindGroup.h" #include "dawn_native/Buffer.h" #include "dawn_native/EnumMaskIterator.h" #include "dawn_native/Format.h" @@ -23,17 +24,14 @@ #include namespace dawn_native { - PassResourceUsageTracker::PassResourceUsageTracker(PassType passType) : mPassType(passType) { - } - void PassResourceUsageTracker::BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage) { + void SyncScopeUsageTracker::BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage) { // std::map's operator[] will create the key and return 0 if the key didn't exist // before. mBufferUsages[buffer] |= usage; } - void PassResourceUsageTracker::TextureViewUsedAs(TextureViewBase* view, - wgpu::TextureUsage usage) { + void SyncScopeUsageTracker::TextureViewUsedAs(TextureViewBase* view, wgpu::TextureUsage usage) { TextureBase* texture = view->GetTexture(); const SubresourceRange& range = view->GetSubresourceRange(); @@ -51,8 +49,8 @@ namespace dawn_native { }); } - void PassResourceUsageTracker::AddTextureUsage(TextureBase* texture, - const TextureSubresourceUsage& textureUsage) { + void SyncScopeUsageTracker::AddTextureUsage(TextureBase* texture, + const TextureSubresourceUsage& textureUsage) { // Get or create a new TextureSubresourceUsage for that texture (initially filled with // wgpu::TextureUsage::None) auto it = mTextureUsages.emplace( @@ -66,32 +64,63 @@ namespace dawn_native { const wgpu::TextureUsage& addedUsage) { *storedUsage |= addedUsage; }); } - void PassResourceUsageTracker::TrackQueryAvailability(QuerySetBase* querySet, - uint32_t queryIndex) { - // The query availability only need to be tracked again on render pass for checking query - // overwrite on render pass and resetting query set on Vulkan backend. - DAWN_ASSERT(mPassType == PassType::Render); - DAWN_ASSERT(querySet != nullptr); + void SyncScopeUsageTracker::AddBindGroup(BindGroupBase* group) { + for (BindingIndex bindingIndex{0}; bindingIndex < group->GetLayout()->GetBindingCount(); + ++bindingIndex) { + const BindingInfo& bindingInfo = group->GetLayout()->GetBindingInfo(bindingIndex); - // Gets the iterator for that querySet or create a new vector of bool set to false - // if the querySet wasn't registered. - auto it = mQueryAvailabilities.emplace(querySet, querySet->GetQueryCount()).first; - it->second[queryIndex] = true; + switch (bindingInfo.bindingType) { + case BindingInfoType::Buffer: { + BufferBase* buffer = group->GetBindingAsBufferBinding(bindingIndex).buffer; + switch (bindingInfo.buffer.type) { + case wgpu::BufferBindingType::Uniform: + BufferUsedAs(buffer, wgpu::BufferUsage::Uniform); + break; + case wgpu::BufferBindingType::Storage: + BufferUsedAs(buffer, wgpu::BufferUsage::Storage); + break; + case wgpu::BufferBindingType::ReadOnlyStorage: + BufferUsedAs(buffer, kReadOnlyStorageBuffer); + break; + case wgpu::BufferBindingType::Undefined: + UNREACHABLE(); + } + break; + } + + case BindingInfoType::Texture: { + TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex); + TextureViewUsedAs(view, wgpu::TextureUsage::Sampled); + break; + } + + case BindingInfoType::StorageTexture: { + TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex); + switch (bindingInfo.storageTexture.access) { + case wgpu::StorageTextureAccess::ReadOnly: + TextureViewUsedAs(view, kReadOnlyStorageTexture); + break; + case wgpu::StorageTextureAccess::WriteOnly: + TextureViewUsedAs(view, wgpu::TextureUsage::Storage); + break; + case wgpu::StorageTextureAccess::Undefined: + UNREACHABLE(); + } + break; + } + + case BindingInfoType::Sampler: + break; + } + } } - const QueryAvailabilityMap& PassResourceUsageTracker::GetQueryAvailabilityMap() const { - return mQueryAvailabilities; - } - - // Returns the per-pass usage for use by backends for APIs with explicit barriers. - PassResourceUsage PassResourceUsageTracker::AcquireResourceUsage() { - PassResourceUsage result; + SyncScopeResourceUsage SyncScopeUsageTracker::AcquireSyncScopeUsage() { + SyncScopeResourceUsage result; result.buffers.reserve(mBufferUsages.size()); result.bufferUsages.reserve(mBufferUsages.size()); result.textures.reserve(mTextureUsages.size()); result.textureUsages.reserve(mTextureUsages.size()); - result.querySets.reserve(mQueryAvailabilities.size()); - result.queryAvailabilities.reserve(mQueryAvailabilities.size()); for (auto& it : mBufferUsages) { result.buffers.push_back(it.first); @@ -103,16 +132,49 @@ namespace dawn_native { result.textureUsages.push_back(std::move(it.second)); } + mBufferUsages.clear(); + mTextureUsages.clear(); + + return result; + } + + ComputePassResourceUsage ComputePassResourceUsageTracker::AcquireResourceUsage() { + ComputePassResourceUsage result; + *static_cast(&result) = AcquireSyncScopeUsage(); + return result; + } + + RenderPassResourceUsage RenderPassResourceUsageTracker::AcquireResourceUsage() { + RenderPassResourceUsage result; + *static_cast(&result) = AcquireSyncScopeUsage(); + + result.querySets.reserve(mQueryAvailabilities.size()); + result.queryAvailabilities.reserve(mQueryAvailabilities.size()); + for (auto& it : mQueryAvailabilities) { result.querySets.push_back(it.first); result.queryAvailabilities.push_back(std::move(it.second)); } - mBufferUsages.clear(); - mTextureUsages.clear(); mQueryAvailabilities.clear(); return result; } + void RenderPassResourceUsageTracker::TrackQueryAvailability(QuerySetBase* querySet, + uint32_t queryIndex) { + // The query availability only needs to be tracked again on render passes for checking + // query overwrite on render pass and resetting query sets on the Vulkan backend. + DAWN_ASSERT(querySet != nullptr); + + // Gets the iterator for that querySet or create a new vector of bool set to false + // if the querySet wasn't registered. + auto it = mQueryAvailabilities.emplace(querySet, querySet->GetQueryCount()).first; + it->second[queryIndex] = true; + } + + const QueryAvailabilityMap& RenderPassResourceUsageTracker::GetQueryAvailabilityMap() const { + return mQueryAvailabilities; + } + } // namespace dawn_native diff --git a/src/dawn_native/PassResourceUsageTracker.h b/src/dawn_native/PassResourceUsageTracker.h index d56d724bf5..691d302f87 100644 --- a/src/dawn_native/PassResourceUsageTracker.h +++ b/src/dawn_native/PassResourceUsageTracker.h @@ -23,35 +23,56 @@ namespace dawn_native { + class BindGroupBase; class BufferBase; class QuerySetBase; class TextureBase; using QueryAvailabilityMap = std::map>; - // Helper class to encapsulate the logic of tracking per-resource usage during the - // validation of command buffer passes. It is used both to know if there are validation - // errors, and to get a list of resources used per pass for backends that need the - // information. - class PassResourceUsageTracker { + // Helper class to build SyncScopeResourceUsages + class SyncScopeUsageTracker { public: - PassResourceUsageTracker(PassType passType); void BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage); void TextureViewUsedAs(TextureViewBase* texture, wgpu::TextureUsage usage); void AddTextureUsage(TextureBase* texture, const TextureSubresourceUsage& textureUsage); + + // Walks the bind groups and tracks all its resources. + void AddBindGroup(BindGroupBase* group); + + // Returns the per-pass usage for use by backends for APIs with explicit barriers. + SyncScopeResourceUsage AcquireSyncScopeUsage(); + + private: + std::map mBufferUsages; + std::map mTextureUsages; + }; + + // Helper class to build ComputePassResourceUsages + class ComputePassResourceUsageTracker : public SyncScopeUsageTracker { + public: + ComputePassResourceUsage AcquireResourceUsage(); + + private: + // Hide AcquireSyncScopeUsage since users of this class should use AcquireResourceUsage + // instead. + using SyncScopeUsageTracker::AcquireSyncScopeUsage; + }; + + // Helper class to build RenderPassResourceUsages + class RenderPassResourceUsageTracker : public SyncScopeUsageTracker { + public: void TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex); const QueryAvailabilityMap& GetQueryAvailabilityMap() const; - // Returns the per-pass usage for use by backends for APIs with explicit barriers. - PassResourceUsage AcquireResourceUsage(); + RenderPassResourceUsage AcquireResourceUsage(); private: - PassType mPassType; - std::map mBufferUsages; - std::map mTextureUsages; - // Dedicated to track the availability of the queries used on render pass. The same query - // cannot be written twice in same render pass, so each render pass also need to have its - // own query availability map for validation. + // Hide AcquireSyncScopeUsage since users of this class should use AcquireResourceUsage + // instead. + using SyncScopeUsageTracker::AcquireSyncScopeUsage; + + // Tracks queries used in the render pass to validate that they aren't written twice. QueryAvailabilityMap mQueryAvailabilities; }; diff --git a/src/dawn_native/ProgrammablePassEncoder.cpp b/src/dawn_native/ProgrammablePassEncoder.cpp index c905de1b27..c4be109a5c 100644 --- a/src/dawn_native/ProgrammablePassEncoder.cpp +++ b/src/dawn_native/ProgrammablePassEncoder.cpp @@ -27,76 +27,18 @@ namespace dawn_native { - namespace { - void TrackBindGroupResourceUsage(PassResourceUsageTracker* usageTracker, - BindGroupBase* group) { - for (BindingIndex bindingIndex{0}; bindingIndex < group->GetLayout()->GetBindingCount(); - ++bindingIndex) { - const BindingInfo& bindingInfo = group->GetLayout()->GetBindingInfo(bindingIndex); - - switch (bindingInfo.bindingType) { - case BindingInfoType::Buffer: { - BufferBase* buffer = group->GetBindingAsBufferBinding(bindingIndex).buffer; - switch (bindingInfo.buffer.type) { - case wgpu::BufferBindingType::Uniform: - usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Uniform); - break; - case wgpu::BufferBindingType::Storage: - usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Storage); - break; - case wgpu::BufferBindingType::ReadOnlyStorage: - usageTracker->BufferUsedAs(buffer, kReadOnlyStorageBuffer); - break; - case wgpu::BufferBindingType::Undefined: - UNREACHABLE(); - } - break; - } - - case BindingInfoType::Texture: { - TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex); - usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Sampled); - break; - } - - case BindingInfoType::StorageTexture: { - TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex); - switch (bindingInfo.storageTexture.access) { - case wgpu::StorageTextureAccess::ReadOnly: - usageTracker->TextureViewUsedAs(view, kReadOnlyStorageTexture); - break; - case wgpu::StorageTextureAccess::WriteOnly: - usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Storage); - break; - case wgpu::StorageTextureAccess::Undefined: - UNREACHABLE(); - } - break; - } - - case BindingInfoType::Sampler: - break; - } - } - } - } // namespace - ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device, - EncodingContext* encodingContext, - PassType passType) + EncodingContext* encodingContext) : ObjectBase(device), mEncodingContext(encodingContext), - mUsageTracker(passType), mValidationEnabled(device->IsValidationEnabled()) { } ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device, EncodingContext* encodingContext, - ErrorTag errorTag, - PassType passType) + ErrorTag errorTag) : ObjectBase(device, errorTag), mEncodingContext(encodingContext), - mUsageTracker(passType), mValidationEnabled(device->IsValidationEnabled()) { } @@ -153,79 +95,75 @@ namespace dawn_native { }); } - void ProgrammablePassEncoder::APISetBindGroup(uint32_t groupIndexIn, - BindGroupBase* group, - uint32_t dynamicOffsetCountIn, - const uint32_t* dynamicOffsetsIn) { - mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { - BindGroupIndex groupIndex(groupIndexIn); + MaybeError ProgrammablePassEncoder::ValidateSetBindGroup( + BindGroupIndex index, + BindGroupBase* group, + uint32_t dynamicOffsetCountIn, + const uint32_t* dynamicOffsetsIn) const { + DAWN_TRY(GetDevice()->ValidateObject(group)); - if (IsValidationEnabled()) { - DAWN_TRY(GetDevice()->ValidateObject(group)); + if (index >= kMaxBindGroupsTyped) { + return DAWN_VALIDATION_ERROR("Setting bind group over the max"); + } - if (groupIndex >= kMaxBindGroupsTyped) { - return DAWN_VALIDATION_ERROR("Setting bind group over the max"); - } + ityp::span dynamicOffsets(dynamicOffsetsIn, + BindingIndex(dynamicOffsetCountIn)); - ityp::span dynamicOffsets( - dynamicOffsetsIn, BindingIndex(dynamicOffsetCountIn)); + // Dynamic offsets count must match the number required by the layout perfectly. + const BindGroupLayoutBase* layout = group->GetLayout(); + if (layout->GetDynamicBufferCount() != dynamicOffsets.size()) { + return DAWN_VALIDATION_ERROR("dynamicOffset count mismatch"); + } - // Dynamic offsets count must match the number required by the layout perfectly. - const BindGroupLayoutBase* layout = group->GetLayout(); - if (layout->GetDynamicBufferCount() != dynamicOffsets.size()) { - return DAWN_VALIDATION_ERROR("dynamicOffset count mismatch"); - } + for (BindingIndex i{0}; i < dynamicOffsets.size(); ++i) { + const BindingInfo& bindingInfo = layout->GetBindingInfo(i); - for (BindingIndex i{0}; i < dynamicOffsets.size(); ++i) { - const BindingInfo& bindingInfo = layout->GetBindingInfo(i); + // BGL creation sorts bindings such that the dynamic buffer bindings are first. + // ASSERT that this true. + ASSERT(bindingInfo.bindingType == BindingInfoType::Buffer); + ASSERT(bindingInfo.buffer.hasDynamicOffset); - // BGL creation sorts bindings such that the dynamic buffer bindings are first. - // ASSERT that this true. - ASSERT(bindingInfo.bindingType == BindingInfoType::Buffer); - ASSERT(bindingInfo.buffer.hasDynamicOffset); - - if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) { - return DAWN_VALIDATION_ERROR("Dynamic Buffer Offset need to be aligned"); - } - - BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i); - - // During BindGroup creation, validation ensures binding offset + binding size - // <= buffer size. - ASSERT(bufferBinding.buffer->GetSize() >= bufferBinding.size); - ASSERT(bufferBinding.buffer->GetSize() - bufferBinding.size >= - bufferBinding.offset); - - if ((dynamicOffsets[i] > bufferBinding.buffer->GetSize() - - bufferBinding.offset - bufferBinding.size)) { - if ((bufferBinding.buffer->GetSize() - bufferBinding.offset) == - bufferBinding.size) { - return DAWN_VALIDATION_ERROR( - "Dynamic offset out of bounds. The binding goes to the end of the " - "buffer even with a dynamic offset of 0. Did you forget to specify " - "the binding's size?"); - } else { - return DAWN_VALIDATION_ERROR("Dynamic offset out of bounds"); - } - } - } + if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) { + return DAWN_VALIDATION_ERROR("Dynamic Buffer Offset need to be aligned"); } - mCommandBufferState.SetBindGroup(groupIndex, group); + BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i); - SetBindGroupCmd* cmd = allocator->Allocate(Command::SetBindGroup); - cmd->index = groupIndex; - cmd->group = group; - cmd->dynamicOffsetCount = dynamicOffsetCountIn; - if (dynamicOffsetCountIn > 0) { - uint32_t* offsets = allocator->AllocateData(cmd->dynamicOffsetCount); - memcpy(offsets, dynamicOffsetsIn, dynamicOffsetCountIn * sizeof(uint32_t)); + // During BindGroup creation, validation ensures binding offset + binding size + // <= buffer size. + ASSERT(bufferBinding.buffer->GetSize() >= bufferBinding.size); + ASSERT(bufferBinding.buffer->GetSize() - bufferBinding.size >= bufferBinding.offset); + + if ((dynamicOffsets[i] > + bufferBinding.buffer->GetSize() - bufferBinding.offset - bufferBinding.size)) { + if ((bufferBinding.buffer->GetSize() - bufferBinding.offset) == + bufferBinding.size) { + return DAWN_VALIDATION_ERROR( + "Dynamic offset out of bounds. The binding goes to the end of the " + "buffer even with a dynamic offset of 0. Did you forget to specify " + "the binding's size?"); + } else { + return DAWN_VALIDATION_ERROR("Dynamic offset out of bounds"); + } } + } - TrackBindGroupResourceUsage(&mUsageTracker, group); + return {}; + } - return {}; - }); + void ProgrammablePassEncoder::RecordSetBindGroup(CommandAllocator* allocator, + BindGroupIndex index, + BindGroupBase* group, + uint32_t dynamicOffsetCount, + const uint32_t* dynamicOffsets) const { + SetBindGroupCmd* cmd = allocator->Allocate(Command::SetBindGroup); + cmd->index = index; + cmd->group = group; + cmd->dynamicOffsetCount = dynamicOffsetCount; + if (dynamicOffsetCount > 0) { + uint32_t* offsets = allocator->AllocateData(cmd->dynamicOffsetCount); + memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint32_t)); + } } } // namespace dawn_native diff --git a/src/dawn_native/ProgrammablePassEncoder.h b/src/dawn_native/ProgrammablePassEncoder.h index 8816914cd7..4cea69628b 100644 --- a/src/dawn_native/ProgrammablePassEncoder.h +++ b/src/dawn_native/ProgrammablePassEncoder.h @@ -15,11 +15,10 @@ #ifndef DAWNNATIVE_PROGRAMMABLEPASSENCODER_H_ #define DAWNNATIVE_PROGRAMMABLEPASSENCODER_H_ -#include "dawn_native/CommandBufferStateTracker.h" #include "dawn_native/CommandEncoder.h" #include "dawn_native/Error.h" +#include "dawn_native/IntegerTypes.h" #include "dawn_native/ObjectBase.h" -#include "dawn_native/PassResourceUsageTracker.h" #include "dawn_native/dawn_platform.h" @@ -30,34 +29,36 @@ namespace dawn_native { // Base class for shared functionality between ComputePassEncoder and RenderPassEncoder. class ProgrammablePassEncoder : public ObjectBase { public: - ProgrammablePassEncoder(DeviceBase* device, - EncodingContext* encodingContext, - PassType passType); + ProgrammablePassEncoder(DeviceBase* device, EncodingContext* encodingContext); void APIInsertDebugMarker(const char* groupLabel); void APIPopDebugGroup(); void APIPushDebugGroup(const char* groupLabel); - void APISetBindGroup(uint32_t groupIndex, - BindGroupBase* group, - uint32_t dynamicOffsetCount = 0, - const uint32_t* dynamicOffsets = nullptr); - protected: bool IsValidationEnabled() const; MaybeError ValidateProgrammableEncoderEnd() const; + // Compute and render passes do different things on SetBindGroup. These are helper functions + // for the logic they have in common. + MaybeError ValidateSetBindGroup(BindGroupIndex index, + BindGroupBase* group, + uint32_t dynamicOffsetCountIn, + const uint32_t* dynamicOffsetsIn) const; + void RecordSetBindGroup(CommandAllocator* allocator, + BindGroupIndex index, + BindGroupBase* group, + uint32_t dynamicOffsetCount, + const uint32_t* dynamicOffsets) const; + // Construct an "error" programmable pass encoder. ProgrammablePassEncoder(DeviceBase* device, EncodingContext* encodingContext, - ErrorTag errorTag, - PassType passType); + ErrorTag errorTag); EncodingContext* mEncodingContext = nullptr; - PassResourceUsageTracker mUsageTracker; uint64_t mDebugGroupStackSize = 0; - CommandBufferStateTracker mCommandBufferState; private: const bool mValidationEnabled; diff --git a/src/dawn_native/RenderBundle.cpp b/src/dawn_native/RenderBundle.cpp index 930eb6e53f..f4e0a8c0e0 100644 --- a/src/dawn_native/RenderBundle.cpp +++ b/src/dawn_native/RenderBundle.cpp @@ -24,7 +24,7 @@ namespace dawn_native { RenderBundleBase::RenderBundleBase(RenderBundleEncoder* encoder, const RenderBundleDescriptor* descriptor, Ref attachmentState, - PassResourceUsage resourceUsage) + RenderPassResourceUsage resourceUsage) : ObjectBase(encoder->GetDevice()), mCommands(encoder->AcquireCommands()), mAttachmentState(std::move(attachmentState)), @@ -53,7 +53,7 @@ namespace dawn_native { return mAttachmentState.Get(); } - const PassResourceUsage& RenderBundleBase::GetResourceUsage() const { + const RenderPassResourceUsage& RenderBundleBase::GetResourceUsage() const { ASSERT(!IsError()); return mResourceUsage; } diff --git a/src/dawn_native/RenderBundle.h b/src/dawn_native/RenderBundle.h index 41d686a86b..f971ed6a36 100644 --- a/src/dawn_native/RenderBundle.h +++ b/src/dawn_native/RenderBundle.h @@ -36,14 +36,14 @@ namespace dawn_native { RenderBundleBase(RenderBundleEncoder* encoder, const RenderBundleDescriptor* descriptor, Ref attachmentState, - PassResourceUsage resourceUsage); + RenderPassResourceUsage resourceUsage); static RenderBundleBase* MakeError(DeviceBase* device); CommandIterator* GetCommands(); const AttachmentState* GetAttachmentState() const; - const PassResourceUsage& GetResourceUsage() const; + const RenderPassResourceUsage& GetResourceUsage() const; protected: ~RenderBundleBase() override; @@ -53,7 +53,7 @@ namespace dawn_native { CommandIterator mCommands; Ref mAttachmentState; - PassResourceUsage mResourceUsage; + RenderPassResourceUsage mResourceUsage; }; } // namespace dawn_native diff --git a/src/dawn_native/RenderBundleEncoder.cpp b/src/dawn_native/RenderBundleEncoder.cpp index 8afcd32424..daff3eb33b 100644 --- a/src/dawn_native/RenderBundleEncoder.cpp +++ b/src/dawn_native/RenderBundleEncoder.cpp @@ -123,18 +123,17 @@ namespace dawn_native { // errors. DAWN_TRY(mBundleEncodingContext.Finish()); - PassResourceUsage usages = mUsageTracker.AcquireResourceUsage(); + RenderPassResourceUsage usages = mUsageTracker.AcquireResourceUsage(); if (IsValidationEnabled()) { DAWN_TRY(GetDevice()->ValidateObject(this)); DAWN_TRY(ValidateProgrammableEncoderEnd()); - DAWN_TRY(ValidateFinish(mBundleEncodingContext.GetIterator(), usages)); + DAWN_TRY(ValidateFinish(usages)); } return new RenderBundleBase(this, descriptor, AcquireAttachmentState(), std::move(usages)); } - MaybeError RenderBundleEncoder::ValidateFinish(CommandIterator* commands, - const PassResourceUsage& usages) const { + MaybeError RenderBundleEncoder::ValidateFinish(const RenderPassResourceUsage& usages) const { TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "RenderBundleEncoder::ValidateFinish"); DAWN_TRY(GetDevice()->ValidateObject(this)); DAWN_TRY(ValidateSyncScopeResourceUsage(usages)); diff --git a/src/dawn_native/RenderBundleEncoder.h b/src/dawn_native/RenderBundleEncoder.h index 27d5de26d2..13439b7ef7 100644 --- a/src/dawn_native/RenderBundleEncoder.h +++ b/src/dawn_native/RenderBundleEncoder.h @@ -41,7 +41,7 @@ namespace dawn_native { RenderBundleEncoder(DeviceBase* device, ErrorTag errorTag); ResultOrError FinishImpl(const RenderBundleDescriptor* descriptor); - MaybeError ValidateFinish(CommandIterator* commands, const PassResourceUsage& usages) const; + MaybeError ValidateFinish(const RenderPassResourceUsage& usages) const; EncodingContext mBundleEncodingContext; }; diff --git a/src/dawn_native/RenderEncoderBase.cpp b/src/dawn_native/RenderEncoderBase.cpp index f87bb3b54e..5faf5c33e9 100644 --- a/src/dawn_native/RenderEncoderBase.cpp +++ b/src/dawn_native/RenderEncoderBase.cpp @@ -32,7 +32,7 @@ namespace dawn_native { RenderEncoderBase::RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext, Ref attachmentState) - : ProgrammablePassEncoder(device, encodingContext, PassType::Render), + : ProgrammablePassEncoder(device, encodingContext), mAttachmentState(std::move(attachmentState)), mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)), mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) { @@ -41,7 +41,7 @@ namespace dawn_native { RenderEncoderBase::RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext, ErrorTag errorTag) - : ProgrammablePassEncoder(device, encodingContext, errorTag, PassType::Render), + : ProgrammablePassEncoder(device, encodingContext, errorTag), mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)), mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) { } @@ -308,4 +308,24 @@ namespace dawn_native { }); } + void RenderEncoderBase::APISetBindGroup(uint32_t groupIndexIn, + BindGroupBase* group, + uint32_t dynamicOffsetCount, + const uint32_t* dynamicOffsets) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + BindGroupIndex groupIndex(groupIndexIn); + + if (IsValidationEnabled()) { + DAWN_TRY( + ValidateSetBindGroup(groupIndex, group, dynamicOffsetCount, dynamicOffsets)); + } + + RecordSetBindGroup(allocator, groupIndex, group, dynamicOffsetCount, dynamicOffsets); + mCommandBufferState.SetBindGroup(groupIndex, group); + mUsageTracker.AddBindGroup(group); + + return {}; + }); + } + } // namespace dawn_native diff --git a/src/dawn_native/RenderEncoderBase.h b/src/dawn_native/RenderEncoderBase.h index 4f312707ee..29dadd2324 100644 --- a/src/dawn_native/RenderEncoderBase.h +++ b/src/dawn_native/RenderEncoderBase.h @@ -16,7 +16,9 @@ #define DAWNNATIVE_RENDERENCODERBASE_H_ #include "dawn_native/AttachmentState.h" +#include "dawn_native/CommandBufferStateTracker.h" #include "dawn_native/Error.h" +#include "dawn_native/PassResourceUsageTracker.h" #include "dawn_native/ProgrammablePassEncoder.h" namespace dawn_native { @@ -52,6 +54,11 @@ namespace dawn_native { uint64_t offset, uint64_t size); + void APISetBindGroup(uint32_t groupIndex, + BindGroupBase* group, + uint32_t dynamicOffsetCount = 0, + const uint32_t* dynamicOffsets = nullptr); + const AttachmentState* GetAttachmentState() const; Ref AcquireAttachmentState(); @@ -59,6 +66,9 @@ namespace dawn_native { // Construct an "error" render encoder base. RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext, ErrorTag errorTag); + CommandBufferStateTracker mCommandBufferState; + RenderPassResourceUsageTracker mUsageTracker; + private: Ref mAttachmentState; const bool mDisableBaseVertex; diff --git a/src/dawn_native/RenderPassEncoder.cpp b/src/dawn_native/RenderPassEncoder.cpp index c5c52e4bb2..a7b6abc68a 100644 --- a/src/dawn_native/RenderPassEncoder.cpp +++ b/src/dawn_native/RenderPassEncoder.cpp @@ -51,7 +51,7 @@ namespace dawn_native { RenderPassEncoder::RenderPassEncoder(DeviceBase* device, CommandEncoder* commandEncoder, EncodingContext* encodingContext, - PassResourceUsageTracker usageTracker, + RenderPassResourceUsageTracker usageTracker, Ref attachmentState, QuerySetBase* occlusionQuerySet, uint32_t renderTargetWidth, @@ -101,8 +101,7 @@ namespace dawn_native { allocator->Allocate(Command::EndRenderPass); return {}; })) { - mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage(), - PassType::Render); + mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage()); } } @@ -222,7 +221,7 @@ namespace dawn_native { for (uint32_t i = 0; i < count; ++i) { bundles[i] = renderBundles[i]; - const PassResourceUsage& usages = bundles[i]->GetResourceUsage(); + const RenderPassResourceUsage& usages = bundles[i]->GetResourceUsage(); for (uint32_t i = 0; i < usages.buffers.size(); ++i) { mUsageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]); } diff --git a/src/dawn_native/RenderPassEncoder.h b/src/dawn_native/RenderPassEncoder.h index dc22bf1e6e..19fcc80365 100644 --- a/src/dawn_native/RenderPassEncoder.h +++ b/src/dawn_native/RenderPassEncoder.h @@ -27,7 +27,7 @@ namespace dawn_native { RenderPassEncoder(DeviceBase* device, CommandEncoder* commandEncoder, EncodingContext* encodingContext, - PassResourceUsageTracker usageTracker, + RenderPassResourceUsageTracker usageTracker, Ref attachmentState, QuerySetBase* occlusionQuerySet, uint32_t renderTargetWidth, diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index 9d7f96d58c..367fbb0870 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -612,7 +612,7 @@ namespace dawn_native { namespace d3d12 { // Records the necessary barriers for the resource usage pre-computed by the frontend auto PrepareResourcesForRenderPass = [](CommandRecordingContext* commandContext, - const PassResourceUsage& usages) -> bool { + const RenderPassResourceUsage& usages) -> bool { std::vector barriers; ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); @@ -666,7 +666,7 @@ namespace dawn_native { namespace d3d12 { // TODO(jiawei.shao@intel.com): move the resource lazy clearing inside the barrier tracking // for compute passes. auto PrepareResourcesForComputePass = [](CommandRecordingContext* commandContext, - const PassResourceUsage& usages) { + const ComputePassResourceUsage& usages) { for (size_t i = 0; i < usages.buffers.size(); ++i) { Buffer* buffer = ToBackend(usages.buffers[i]); diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp index c014c85b17..f926029673 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -531,7 +531,7 @@ namespace dawn_native { namespace vulkan { // And resets the used query sets which are rewritten on the render pass. auto PrepareResourcesForRenderPass = [](Device* device, CommandRecordingContext* recordingContext, - const PassResourceUsage& usages) { + const RenderPassResourceUsage& usages) { std::vector bufferBarriers; std::vector imageBarriers; VkPipelineStageFlags srcStages = 0; @@ -583,7 +583,7 @@ namespace dawn_native { namespace vulkan { // for compute passes. auto PrepareResourcesForComputePass = [](Device* device, CommandRecordingContext* recordingContext, - const PassResourceUsage& usages) { + const ComputePassResourceUsage& usages) { for (size_t i = 0; i < usages.buffers.size(); ++i) { Buffer* buffer = ToBackend(usages.buffers[i]); buffer->EnsureDataInitialized(recordingContext);