diff --git a/src/common/Constants.h b/src/common/Constants.h index 722244dd17..fee8a4f68d 100644 --- a/src/common/Constants.h +++ b/src/common/Constants.h @@ -35,6 +35,13 @@ static constexpr uint32_t kMaxColorAttachments = 4u; static constexpr uint32_t kTextureRowPitchAlignment = 256u; // Dynamic buffer offsets require offset to be divisible by 256 static constexpr uint64_t kMinDynamicBufferOffsetAlignment = 256u; +// Max numbers of dynamic uniform buffers +static constexpr uint32_t kMaxDynamicUniformBufferCount = 8u; +// Max numbers of dynamic storage buffers +static constexpr uint32_t kMaxDynamicStorageBufferCount = 4u; +// Max numbers of dynamic buffers +static constexpr uint32_t kMaxDynamicBufferCount = + kMaxDynamicUniformBufferCount + kMaxDynamicStorageBufferCount; // Indirect command sizes static constexpr uint64_t kDispatchIndirectSize = 3 * sizeof(uint32_t); static constexpr uint64_t kDrawIndirectSize = 4 * sizeof(uint32_t); diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp index 4abff6e098..ff9acfa4a7 100644 --- a/src/dawn_native/BindGroupLayout.cpp +++ b/src/dawn_native/BindGroupLayout.cpp @@ -30,6 +30,8 @@ namespace dawn_native { } std::bitset bindingsSet; + uint32_t dynamicUniformBufferCount = 0; + uint32_t dynamicStorageBufferCount = 0; for (uint32_t i = 0; i < descriptor->bindingCount; ++i) { const BindGroupLayoutBinding& binding = descriptor->bindings[i]; DAWN_TRY(ValidateShaderStageBit(binding.visibility)); @@ -48,7 +50,14 @@ namespace dawn_native { switch (binding.type) { case dawn::BindingType::UniformBuffer: + if (binding.dynamic) { + ++dynamicUniformBufferCount; + } + break; case dawn::BindingType::StorageBuffer: + if (binding.dynamic) { + ++dynamicStorageBufferCount; + } break; case dawn::BindingType::SampledTexture: case dawn::BindingType::Sampler: @@ -69,6 +78,17 @@ namespace dawn_native { bindingsSet.set(binding.binding); } + + if (dynamicUniformBufferCount > kMaxDynamicUniformBufferCount) { + return DAWN_VALIDATION_ERROR( + "The number of dynamic uniform buffer exceeds the maximum value"); + } + + if (dynamicStorageBufferCount > kMaxDynamicStorageBufferCount) { + return DAWN_VALIDATION_ERROR( + "The number of dynamic storage buffer exceeds the maximum value"); + } + return {}; } @@ -116,7 +136,20 @@ namespace dawn_native { if (binding.dynamic) { mBindingInfo.dynamic.set(index); - mDynamicBufferCount++; + switch (binding.type) { + case dawn::BindingType::UniformBuffer: + ++mDynamicUniformBufferCount; + break; + case dawn::BindingType::StorageBuffer: + ++mDynamicStorageBufferCount; + break; + case dawn::BindingType::SampledTexture: + case dawn::BindingType::Sampler: + case dawn::BindingType::ReadonlyStorageBuffer: + case dawn::BindingType::StorageTexture: + UNREACHABLE(); + break; + } } mBindingInfo.multisampled.set(index, binding.multisampled); @@ -157,7 +190,15 @@ namespace dawn_native { } uint32_t BindGroupLayoutBase::GetDynamicBufferCount() const { - return mDynamicBufferCount; + return mDynamicStorageBufferCount + mDynamicUniformBufferCount; + } + + uint32_t BindGroupLayoutBase::GetDynamicUniformBufferCount() const { + return mDynamicUniformBufferCount; + } + + uint32_t BindGroupLayoutBase::GetDynamicStorageBufferCount() const { + return mDynamicStorageBufferCount; } } // namespace dawn_native diff --git a/src/dawn_native/BindGroupLayout.h b/src/dawn_native/BindGroupLayout.h index 4607c3718f..98e912b2cc 100644 --- a/src/dawn_native/BindGroupLayout.h +++ b/src/dawn_native/BindGroupLayout.h @@ -57,13 +57,16 @@ namespace dawn_native { }; uint32_t GetDynamicBufferCount() const; + uint32_t GetDynamicUniformBufferCount() const; + uint32_t GetDynamicStorageBufferCount() const; private: BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag); LayoutBindingInfo mBindingInfo; bool mIsBlueprint = false; - uint32_t mDynamicBufferCount = 0; + uint32_t mDynamicUniformBufferCount = 0; + uint32_t mDynamicStorageBufferCount = 0; }; } // namespace dawn_native diff --git a/src/dawn_native/PipelineLayout.cpp b/src/dawn_native/PipelineLayout.cpp index ee3b89ae26..8c2a4296a2 100644 --- a/src/dawn_native/PipelineLayout.cpp +++ b/src/dawn_native/PipelineLayout.cpp @@ -32,9 +32,24 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("too many bind group layouts"); } + uint32_t totalDynamicUniformBufferCount = 0; + uint32_t totalDynamicStorageBufferCount = 0; for (uint32_t i = 0; i < descriptor->bindGroupLayoutCount; ++i) { DAWN_TRY(device->ValidateObject(descriptor->bindGroupLayouts[i])); + totalDynamicUniformBufferCount += + descriptor->bindGroupLayouts[i]->GetDynamicUniformBufferCount(); + totalDynamicStorageBufferCount += + descriptor->bindGroupLayouts[i]->GetDynamicStorageBufferCount(); } + + if (totalDynamicUniformBufferCount > kMaxDynamicUniformBufferCount) { + return DAWN_VALIDATION_ERROR("too many dynamic uniform buffers in pipeline layout"); + } + + if (totalDynamicStorageBufferCount > kMaxDynamicStorageBufferCount) { + return DAWN_VALIDATION_ERROR("too many dynamic storage buffers in pipeline layout"); + } + return {}; } diff --git a/src/dawn_native/d3d12/BindGroupD3D12.cpp b/src/dawn_native/d3d12/BindGroupD3D12.cpp index bfd0cd0aed..275f1ce3fa 100644 --- a/src/dawn_native/d3d12/BindGroupD3D12.cpp +++ b/src/dawn_native/d3d12/BindGroupD3D12.cpp @@ -42,6 +42,12 @@ namespace dawn_native { namespace d3d12 { auto d3d12Device = ToBackend(GetDevice())->GetD3D12Device(); for (uint32_t bindingIndex : IterateBitSet(layout.mask)) { + // It's not necessary to create descriptors in descriptor heap for dynamic resources. + // So skip allocating descriptors in descriptor heaps for dynamic buffers. + if (layout.dynamic[bindingIndex]) { + continue; + } + switch (layout.types[bindingIndex]) { case dawn::BindingType::UniformBuffer: { BufferBinding binding = GetBindingAsBufferBinding(bindingIndex); diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp index 2634464439..ec81ad6d0c 100644 --- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp +++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp @@ -24,6 +24,13 @@ namespace dawn_native { namespace d3d12 { const auto& groupInfo = GetBindingInfo(); for (uint32_t binding : IterateBitSet(groupInfo.mask)) { + // For dynamic resources, Dawn uses root descriptor in D3D12 backend. + // So there is no need to allocate the descriptor from descriptor heap. Skip counting + // dynamic resources for calculating size of descriptor heap. + if (groupInfo.dynamic[binding]) { + continue; + } + switch (groupInfo.types[binding]) { case dawn::BindingType::UniformBuffer: mBindingOffsets[binding] = mDescriptorCounts[CBV]++; @@ -42,8 +49,6 @@ namespace dawn_native { namespace d3d12 { case dawn::BindingType::ReadonlyStorageBuffer: UNREACHABLE(); break; - - // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. } } @@ -89,6 +94,25 @@ namespace dawn_native { namespace d3d12 { descriptorOffsets[Sampler] = 0; for (uint32_t binding : IterateBitSet(groupInfo.mask)) { + if (groupInfo.dynamic[binding]) { + // Dawn is using values in mBindingOffsets to decide register number in HLSL. + // Root descriptor needs to set this value to set correct register number in + // generated HLSL shader. + switch (groupInfo.types[binding]) { + case dawn::BindingType::UniformBuffer: + case dawn::BindingType::StorageBuffer: + mBindingOffsets[binding] = baseRegister++; + break; + case dawn::BindingType::SampledTexture: + case dawn::BindingType::Sampler: + case dawn::BindingType::StorageTexture: + case dawn::BindingType::ReadonlyStorageBuffer: + UNREACHABLE(); + break; + } + continue; + } + switch (groupInfo.types[binding]) { case dawn::BindingType::UniformBuffer: mBindingOffsets[binding] += descriptorOffsets[CBV]; diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index a70338cf28..3c89500fc5 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -151,10 +151,54 @@ namespace dawn_native { namespace d3d12 { PipelineLayout* pipelineLayout, BindGroup* group, uint32_t index, + uint32_t dynamicOffsetCount, + uint64_t* dynamicOffsets, bool force = false) { if (mBindGroups[index] != group || force) { mBindGroups[index] = group; + uint32_t currentDynamicBufferIndex = 0; + const BindGroupLayout::LayoutBindingInfo& layout = + group->GetLayout()->GetBindingInfo(); + for (uint32_t bindingIndex : IterateBitSet(layout.dynamic)) { + ASSERT(dynamicOffsetCount > 0); + uint32_t parameterIndex = + pipelineLayout->GetDynamicRootParameterIndex(index, bindingIndex); + BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); + + // Calculate buffer locations that root descriptors links to. The location is + // (base buffer location + initial offset + dynamic offset) + uint64_t offset = dynamicOffsets[currentDynamicBufferIndex++] + binding.offset; + D3D12_GPU_VIRTUAL_ADDRESS bufferLocation = + ToBackend(binding.buffer)->GetVA() + offset; + + switch (layout.types[bindingIndex]) { + case dawn::BindingType::UniformBuffer: + if (mInCompute) { + commandList->SetComputeRootConstantBufferView(parameterIndex, + bufferLocation); + } else { + commandList->SetGraphicsRootConstantBufferView(parameterIndex, + bufferLocation); + } + break; + case dawn::BindingType::StorageBuffer: + if (mInCompute) { + commandList->SetComputeRootUnorderedAccessView(parameterIndex, + bufferLocation); + } else { + commandList->SetGraphicsRootUnorderedAccessView(parameterIndex, + bufferLocation); + } + break; + case dawn::BindingType::SampledTexture: + case dawn::BindingType::Sampler: + case dawn::BindingType::StorageTexture: + case dawn::BindingType::ReadonlyStorageBuffer: + UNREACHABLE(); + break; + } + } uint32_t cbvUavSrvCount = ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount(); uint32_t samplerCount = ToBackend(group->GetLayout())->GetSamplerDescriptorCount(); @@ -198,7 +242,17 @@ namespace dawn_native { namespace d3d12 { uint32_t inheritUntil = oldLayout->GroupsInheritUpTo(newLayout); for (uint32_t i = 0; i < inheritUntil; ++i) { - SetBindGroup(commandList, newLayout, mBindGroups[i], i, true); + const BindGroupLayout* layout = ToBackend(mBindGroups[i]->GetLayout()); + const uint32_t dynamicBufferCount = layout->GetDynamicBufferCount(); + // TODO(shaobo.yan@intel.com) : Need to handle dynamic resources inherited with last + // dynamic offsets. + if (dynamicBufferCount > 0) { + std::vector zeroOffsets(dynamicBufferCount, 0); + SetBindGroup(commandList, newLayout, mBindGroups[i], i, dynamicBufferCount, + zeroOffsets.data(), true); + } else { + SetBindGroup(commandList, newLayout, mBindGroups[i], i, 0, nullptr, true); + } } } @@ -353,6 +407,9 @@ namespace dawn_native { namespace d3d12 { case Command::SetBindGroup: { SetBindGroupCmd* cmd = commands->NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); + if (cmd->dynamicOffsetCount) { + commands->NextData(cmd->dynamicOffsetCount); + } bindingTracker->TrackSetBindGroup(group, cmd->index, indexInSubmit); } break; case Command::BeginRenderPass: { @@ -747,7 +804,12 @@ namespace dawn_native { namespace d3d12 { case Command::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); - bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index); + uint64_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + } + bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index, + cmd->dynamicOffsetCount, dynamicOffsets); } break; default: { UNREACHABLE(); } break; @@ -1005,7 +1067,13 @@ namespace dawn_native { namespace d3d12 { case Command::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); - bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index); + uint64_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + } + + bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index, + cmd->dynamicOffsetCount, dynamicOffsets); } break; case Command::SetIndexBuffer: { diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp index e63d9eaeac..f5ae7c72ef 100644 --- a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp +++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp @@ -23,10 +23,40 @@ using Microsoft::WRL::ComPtr; namespace dawn_native { namespace d3d12 { + namespace { + D3D12_SHADER_VISIBILITY ShaderVisibilityType(dawn::ShaderStageBit visibility) { + ASSERT(visibility != dawn::ShaderStageBit::None); + + if (visibility == dawn::ShaderStageBit::Vertex) { + return D3D12_SHADER_VISIBILITY_VERTEX; + } + + if (visibility == dawn::ShaderStageBit::Fragment) { + return D3D12_SHADER_VISIBILITY_PIXEL; + } + + // For compute or any two combination of stages, visibility must be ALL + return D3D12_SHADER_VISIBILITY_ALL; + } + + D3D12_ROOT_PARAMETER_TYPE RootParameterType(dawn::BindingType type) { + switch (type) { + case dawn::BindingType::UniformBuffer: + return D3D12_ROOT_PARAMETER_TYPE_CBV; + case dawn::BindingType::StorageBuffer: + return D3D12_ROOT_PARAMETER_TYPE_UAV; + case dawn::BindingType::SampledTexture: + case dawn::BindingType::Sampler: + case dawn::BindingType::StorageTexture: + case dawn::BindingType::ReadonlyStorageBuffer: + UNREACHABLE(); + } + } + } // anonymous namespace PipelineLayout::PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor) : PipelineLayoutBase(device, descriptor) { - D3D12_ROOT_PARAMETER rootParameters[kMaxBindGroups * 2]; + D3D12_ROOT_PARAMETER rootParameters[kMaxBindGroups * 2 + kMaxDynamicBufferCount]; // A root parameter is one of these types union { @@ -46,6 +76,7 @@ namespace dawn_native { namespace d3d12 { for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) { const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group)); + const BindGroupLayout::LayoutBindingInfo& groupInfo = bindGroupLayout->GetBindingInfo(); // Set the root descriptor table parameter and copy ranges. Ranges are offset by the // bind group index Returns whether or not the parameter was set. A root parameter is @@ -81,6 +112,30 @@ namespace dawn_native { namespace d3d12 { bindGroupLayout->GetSamplerDescriptorRanges())) { mSamplerRootParameterInfo[group] = parameterIndex++; } + + // Get calculated shader register for root descriptors + const auto& shaderRegisters = bindGroupLayout->GetBindingOffsets(); + + // Init root descriptors in root signatures. + for (uint32_t dynamicBinding : IterateBitSet(groupInfo.dynamic)) { + D3D12_ROOT_PARAMETER* rootParameter = &rootParameters[parameterIndex]; + + // Setup root descriptor. + D3D12_ROOT_DESCRIPTOR rootDescriptor; + rootDescriptor.ShaderRegister = shaderRegisters[dynamicBinding]; + rootDescriptor.RegisterSpace = group; + + // Set root descriptors in root signatures. + rootParameter->Descriptor = rootDescriptor; + mDynamicRootParameterIndices[group][dynamicBinding] = parameterIndex++; + + // Set parameter types according to bind group layout descriptor. + rootParameter->ParameterType = RootParameterType(groupInfo.types[dynamicBinding]); + + // Set visibilities according to bind group layout descriptor. + rootParameter->ShaderVisibility = + ShaderVisibilityType(groupInfo.visibilities[dynamicBinding]); + } } D3D12_ROOT_SIGNATURE_DESC rootSignatureDescriptor; @@ -113,4 +168,11 @@ namespace dawn_native { namespace d3d12 { ComPtr PipelineLayout::GetRootSignature() { return mRootSignature; } + + uint32_t PipelineLayout::GetDynamicRootParameterIndex(uint32_t group, uint32_t binding) const { + ASSERT(group < kMaxBindGroups); + ASSERT(binding < kMaxBindingsPerGroup); + ASSERT(GetBindGroupLayout(group)->GetBindingInfo().dynamic[binding]); + return mDynamicRootParameterIndices[group][binding]; + } }} // namespace dawn_native::d3d12 diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.h b/src/dawn_native/d3d12/PipelineLayoutD3D12.h index 90c1802a4c..2b8358caee 100644 --- a/src/dawn_native/d3d12/PipelineLayoutD3D12.h +++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.h @@ -30,12 +30,16 @@ namespace dawn_native { namespace d3d12 { uint32_t GetCbvUavSrvRootParameterIndex(uint32_t group) const; uint32_t GetSamplerRootParameterIndex(uint32_t group) const; + // Returns the index of the root parameter reserved for a dynamic buffer binding + uint32_t GetDynamicRootParameterIndex(uint32_t group, uint32_t binding) const; + ComPtr GetRootSignature(); private: std::array mCbvUavSrvRootParameterInfo; std::array mSamplerRootParameterInfo; - + std::array, kMaxBindGroups> + mDynamicRootParameterIndices; ComPtr mRootSignature; }; diff --git a/src/tests/end2end/DynamicBufferOffsetTests.cpp b/src/tests/end2end/DynamicBufferOffsetTests.cpp index 1af54ae0fb..f55b9e5b94 100644 --- a/src/tests/end2end/DynamicBufferOffsetTests.cpp +++ b/src/tests/end2end/DynamicBufferOffsetTests.cpp @@ -27,16 +27,21 @@ class DynamicBufferOffsetTests : public DawnTest { void SetUp() override { DawnTest::SetUp(); + // Mix up dynamic and non dynamic resources in one bind group and using not continuous + // binding number to cover more cases. std::array uniformData = {0}; - uniformData[0] = 1; uniformData[1] = 2; - uniformData[uniformData.size() - 2] = 5; - uniformData[uniformData.size() - 1] = 6; mUniformBuffer = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, dawn::BufferUsageBit::Uniform); + uniformData[uniformData.size() - 2] = 5; + uniformData[uniformData.size() - 1] = 6; + + mDynamicUniformBuffer = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, + dawn::BufferUsageBit::Uniform); + dawn::BufferDescriptor storageBufferDescriptor; storageBufferDescriptor.size = kBufferSize; storageBufferDescriptor.usage = dawn::BufferUsageBit::Storage | @@ -45,15 +50,23 @@ class DynamicBufferOffsetTests : public DawnTest { mStorageBuffer = device.CreateBuffer(&storageBufferDescriptor); + mDynamicStorageBuffer = device.CreateBuffer(&storageBufferDescriptor); + mBindGroupLayout = utils::MakeBindGroupLayout( device, {{0, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, - dawn::BindingType::UniformBuffer, true}, + dawn::BindingType::UniformBuffer}, {1, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, + dawn::BindingType::StorageBuffer}, + {3, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, + dawn::BindingType::UniformBuffer, true}, + {4, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, dawn::BindingType::StorageBuffer, true}}); - mBindGroup = utils::MakeBindGroup( - device, mBindGroupLayout, - {{0, mUniformBuffer, 0, kBindingSize}, {1, mStorageBuffer, 0, kBindingSize}}); + mBindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, mUniformBuffer, 0, kBindingSize}, + {1, mStorageBuffer, 0, kBindingSize}, + {3, mDynamicUniformBuffer, 0, kBindingSize}, + {4, mDynamicStorageBuffer, 0, kBindingSize}}); } // Create objects to use as resources inside test bind groups. @@ -61,6 +74,8 @@ class DynamicBufferOffsetTests : public DawnTest { dawn::BindGroupLayout mBindGroupLayout; dawn::Buffer mUniformBuffer; dawn::Buffer mStorageBuffer; + dawn::Buffer mDynamicUniformBuffer; + dawn::Buffer mDynamicStorageBuffer; dawn::Texture mColorAttachment; dawn::RenderPipeline CreateRenderPipeline() { @@ -75,15 +90,22 @@ class DynamicBufferOffsetTests : public DawnTest { dawn::ShaderModule fsModule = utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"( #version 450 - layout(std140, set = 0, binding = 0) uniform uBuffer { + layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { + uvec2 notDynamicValue; + }; + layout(std140, set = 0, binding = 1) buffer sBufferNotDynamic { + uvec2 notDynamicResult; + } mid; + layout(std140, set = 0, binding = 3) uniform uBuffer { uvec2 value; }; - layout(std140, set = 0, binding = 1) buffer SBuffer { + layout(std140, set = 0, binding = 4) buffer SBuffer { uvec2 result; } sBuffer; layout(location = 0) out vec4 fragColor; void main() { - sBuffer.result.xy = value.xy; + mid.notDynamicResult.xy = notDynamicValue.xy; + sBuffer.result.xy = value.xy + mid.notDynamicResult.xy; fragColor = vec4(value.x / 255.0f, value.y / 255.0f, 1.0f, 1.0f); })"); @@ -103,15 +125,22 @@ class DynamicBufferOffsetTests : public DawnTest { utils::CreateShaderModule(device, utils::ShaderStage::Compute, R"( #version 450 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; - layout(std140, set = 0, binding = 0) uniform UniformBuffer { - uvec2 value; + layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { + uvec2 notDynamicValue; }; - layout(std140, set = 0, binding = 1) buffer SBuffer { - uvec2 result; + layout(std140, set = 0, binding = 1) buffer sBufferNotDynamic { + uvec2 notDynamicResult; + } mid; + layout(std140, set = 0, binding = 3) uniform uBuffer { + uvec2 value; + }; + layout(std140, set = 0, binding = 4) buffer SBuffer { + uvec2 result; } sBuffer; void main() { - sBuffer.result.xy = value.xy; + mid.notDynamicResult.xy = notDynamicValue.xy; + sBuffer.result.xy = value.xy + mid.notDynamicResult.xy; })"); dawn::ComputePipelineDescriptor csDesc; @@ -144,9 +173,9 @@ TEST_P(DynamicBufferOffsetTests, BasicRenderPipeline) { dawn::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); - std::vector expectedData = {1, 2}; + std::vector expectedData = {2, 4}; EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 255, 255), renderPass.color, 0, 0); - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, 0, expectedData.size()); + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mDynamicStorageBuffer, 0, expectedData.size()); } // Have non-zero dynamic offsets. @@ -166,9 +195,9 @@ TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsRenderPipeline) { dawn::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); - std::vector expectedData = {5, 6}; + std::vector expectedData = {6, 8}; EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0); - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mDynamicStorageBuffer, kMinDynamicBufferOffsetAlignment, expectedData.size()); } @@ -187,8 +216,8 @@ TEST_P(DynamicBufferOffsetTests, BasicComputePipeline) { dawn::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); - std::vector expectedData = {1, 2}; - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, 0, expectedData.size()); + std::vector expectedData = {2, 4}; + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mDynamicStorageBuffer, 0, expectedData.size()); } // Have non-zero dynamic offsets. @@ -207,9 +236,13 @@ TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsComputePipeline) { dawn::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); - std::vector expectedData = {5, 6}; - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, + std::vector expectedData = {6, 8}; + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mDynamicStorageBuffer, kMinDynamicBufferOffsetAlignment, expectedData.size()); } -DAWN_INSTANTIATE_TEST(DynamicBufferOffsetTests, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(DynamicBufferOffsetTests, + D3D12Backend, + MetalBackend, + OpenGLBackend, + VulkanBackend); diff --git a/src/tests/unittests/validation/BindGroupValidationTests.cpp b/src/tests/unittests/validation/BindGroupValidationTests.cpp index 3f7162983a..24d6c8f002 100644 --- a/src/tests/unittests/validation/BindGroupValidationTests.cpp +++ b/src/tests/unittests/validation/BindGroupValidationTests.cpp @@ -406,6 +406,34 @@ TEST_F(BindGroupValidationTest, ErrorLayout) { } class BindGroupLayoutValidationTest : public ValidationTest { + public: + void TestCreateBindGroupLayout(dawn::BindGroupLayoutBinding* binding, + uint32_t count, + bool expected) { + dawn::BindGroupLayoutDescriptor descriptor; + + descriptor.bindingCount = count; + descriptor.bindings = binding; + + if (!expected) { + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor)); + } else { + device.CreateBindGroupLayout(&descriptor); + } + } + + void TestCreatePipelineLayout(dawn::BindGroupLayout* bgl, uint32_t count, bool expected) { + dawn::PipelineLayoutDescriptor descriptor; + + descriptor.bindGroupLayoutCount = count; + descriptor.bindGroupLayouts = bgl; + + if (!expected) { + ASSERT_DEVICE_ERROR(device.CreatePipelineLayout(&descriptor)); + } else { + device.CreatePipelineLayout(&descriptor); + } + } }; // Tests setting OOB checks for kMaxBindingsPerGroup in bind group layouts. @@ -487,6 +515,74 @@ TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutVisibilityNone) { ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor)); } +// Check that dynamic buffer numbers exceed maximum value in one bind group layout. +TEST_F(BindGroupLayoutValidationTest, DynamicBufferNumberLimit) { + dawn::BindGroupLayout bgl[2]; + std::vector maxUniformDB; + std::vector maxStorageDB; + + for (uint32_t i = 0; i < kMaxDynamicUniformBufferCount; ++i) { + maxUniformDB.push_back( + {i, dawn::ShaderStageBit::Compute, dawn::BindingType::UniformBuffer, true}); + } + + for (uint32_t i = 0; i < kMaxDynamicStorageBufferCount; ++i) { + maxStorageDB.push_back( + {i, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer, true}); + } + + auto MakeBindGroupLayout = [&](dawn::BindGroupLayoutBinding* binding, + uint32_t count) -> dawn::BindGroupLayout { + dawn::BindGroupLayoutDescriptor descriptor; + descriptor.bindingCount = count; + descriptor.bindings = binding; + return device.CreateBindGroupLayout(&descriptor); + }; + + { + bgl[0] = MakeBindGroupLayout(maxUniformDB.data(), maxUniformDB.size()); + bgl[1] = MakeBindGroupLayout(maxStorageDB.data(), maxStorageDB.size()); + + TestCreatePipelineLayout(bgl, 2, true); + } + + // Check dynamic uniform buffers excedd maximum in pipeline layout. + { + bgl[0] = MakeBindGroupLayout(maxUniformDB.data(), maxUniformDB.size()); + bgl[1] = utils::MakeBindGroupLayout( + device, { + {0, dawn::ShaderStageBit::Compute, dawn::BindingType::UniformBuffer, true}, + }); + + TestCreatePipelineLayout(bgl, 2, false); + } + + // Check dynamic storage buffers exceed maximum in pipeline layout + { + bgl[0] = MakeBindGroupLayout(maxStorageDB.data(), maxStorageDB.size()); + bgl[1] = utils::MakeBindGroupLayout( + device, { + {0, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer, true}, + }); + + TestCreatePipelineLayout(bgl, 2, false); + } + + // Check dynamic uniform buffers exceed maximum in bind group layout. + { + maxUniformDB.push_back({kMaxDynamicUniformBufferCount, dawn::ShaderStageBit::Compute, + dawn::BindingType::UniformBuffer, true}); + TestCreateBindGroupLayout(maxUniformDB.data(), maxUniformDB.size(), false); + } + + // Check dynamic storage buffers exceed maximum in bind group layout. + { + maxStorageDB.push_back({kMaxDynamicStorageBufferCount, dawn::ShaderStageBit::Compute, + dawn::BindingType::StorageBuffer, true}); + TestCreateBindGroupLayout(maxStorageDB.data(), maxStorageDB.size(), false); + } +} + constexpr uint64_t kBufferSize = 2 * kMinDynamicBufferOffsetAlignment + 8; constexpr uint32_t kBindingSize = 9;