Dynamic Buffer Offset : D3D Backend
In a typical graphics application it is a common usage to update some uniforms once per draw, and such uniforms include the word positions, orientations, and so on. In the current state of WebGPU, this means that for each draw call we have to create a new bind group to set the right uniform values. Bind group creation is expected to be more expensive than recording draws because a memory allocation is required. The functionality of dynamic buffer offset is to reduce the number of bind groups that need to be created. The patch implements dynamic buffer offset on D3D backend using root descriptor. Bug=dawn:55 Change-Id: Ia713a4edb3c0ab8f3bba048d7813f343e9dee166 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9040 Commit-Queue: Shaobo Yan <shaobo.yan@intel.com> Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
d08611b7a8
commit
351ea23830
|
@ -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);
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
std::bitset<kMaxBindingsPerGroup> 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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<uint64_t> 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<SetBindGroupCmd>();
|
||||
BindGroup* group = ToBackend(cmd->group.Get());
|
||||
if (cmd->dynamicOffsetCount) {
|
||||
commands->NextData<uint64_t>(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<SetBindGroupCmd>();
|
||||
BindGroup* group = ToBackend(cmd->group.Get());
|
||||
bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index);
|
||||
uint64_t* dynamicOffsets = nullptr;
|
||||
if (cmd->dynamicOffsetCount > 0) {
|
||||
dynamicOffsets = mCommands.NextData<uint64_t>(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<SetBindGroupCmd>();
|
||||
BindGroup* group = ToBackend(cmd->group.Get());
|
||||
bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index);
|
||||
uint64_t* dynamicOffsets = nullptr;
|
||||
if (cmd->dynamicOffsetCount > 0) {
|
||||
dynamicOffsets = mCommands.NextData<uint64_t>(cmd->dynamicOffsetCount);
|
||||
}
|
||||
|
||||
bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index,
|
||||
cmd->dynamicOffsetCount, dynamicOffsets);
|
||||
} break;
|
||||
|
||||
case Command::SetIndexBuffer: {
|
||||
|
|
|
@ -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<ID3D12RootSignature> 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
|
||||
|
|
|
@ -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<ID3D12RootSignature> GetRootSignature();
|
||||
|
||||
private:
|
||||
std::array<uint32_t, kMaxBindGroups> mCbvUavSrvRootParameterInfo;
|
||||
std::array<uint32_t, kMaxBindGroups> mSamplerRootParameterInfo;
|
||||
|
||||
std::array<std::array<uint32_t, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
mDynamicRootParameterIndices;
|
||||
ComPtr<ID3D12RootSignature> mRootSignature;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<uint32_t, kBufferElementsCount> 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<uint32_t> expectedData = {1, 2};
|
||||
std::vector<uint32_t> 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<uint32_t> expectedData = {5, 6};
|
||||
std::vector<uint32_t> 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<uint32_t> expectedData = {1, 2};
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, 0, expectedData.size());
|
||||
std::vector<uint32_t> 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<uint32_t> expectedData = {5, 6};
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer,
|
||||
std::vector<uint32_t> 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);
|
||||
|
|
|
@ -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<dawn::BindGroupLayoutBinding> maxUniformDB;
|
||||
std::vector<dawn::BindGroupLayoutBinding> 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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue