dawn-cmake/src/dawn/native/d3d12/PipelineLayoutD3D12.cpp
Jiawei Shao 47f051984b D3D12: Only set root parameters for dynamic storage buffers when needed
This patch skips setting the items for dynamic storage buffer lengths
in root signatures when there is no dynamic storage buffer in the
pipeline layout so that we can avoid adding a root constant with
Num32BitValues == 0 in the root signature.

BUG=dawn:1262

Change-Id: I7d698425c94092299aefaf8cb6ef465745c8d194
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/79742
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
2022-02-10 00:05:01 +00:00

378 lines
18 KiB
C++

// Copyright 2017 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn/native/d3d12/PipelineLayoutD3D12.h"
#include <sstream>
#include "dawn/common/Assert.h"
#include "dawn/common/BitSetIterator.h"
#include "dawn/native/d3d12/BindGroupLayoutD3D12.h"
#include "dawn/native/d3d12/D3D12Error.h"
#include "dawn/native/d3d12/DeviceD3D12.h"
#include "dawn/native/d3d12/PlatformFunctions.h"
using Microsoft::WRL::ComPtr;
namespace dawn::native::d3d12 {
namespace {
// Reserve register names for internal use. This registers map to bindings in the shader,
// but are not directly related to allocation of the root signature.
// In the root signature, it the index of the root parameter where these registers are
// used that determines the layout of the root signature.
static constexpr uint32_t kRenderOrComputeInternalRegisterSpace = kMaxBindGroups + 1;
static constexpr uint32_t kRenderOrComputeInternalBaseRegister = 0;
static constexpr uint32_t kDynamicStorageBufferLengthsRegisterSpace = kMaxBindGroups + 2;
static constexpr uint32_t kDynamicStorageBufferLengthsBaseRegister = 0;
static constexpr uint32_t kInvalidDynamicStorageBufferLengthsParameterIndex =
std::numeric_limits<uint32_t>::max();
D3D12_SHADER_VISIBILITY ShaderVisibilityType(wgpu::ShaderStage visibility) {
ASSERT(visibility != wgpu::ShaderStage::None);
if (visibility == wgpu::ShaderStage::Vertex) {
return D3D12_SHADER_VISIBILITY_VERTEX;
}
if (visibility == wgpu::ShaderStage::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(wgpu::BufferBindingType type) {
switch (type) {
case wgpu::BufferBindingType::Uniform:
return D3D12_ROOT_PARAMETER_TYPE_CBV;
case wgpu::BufferBindingType::Storage:
case kInternalStorageBufferBinding:
return D3D12_ROOT_PARAMETER_TYPE_UAV;
case wgpu::BufferBindingType::ReadOnlyStorage:
return D3D12_ROOT_PARAMETER_TYPE_SRV;
case wgpu::BufferBindingType::Undefined:
UNREACHABLE();
}
}
} // anonymous namespace
ResultOrError<Ref<PipelineLayout>> PipelineLayout::Create(
Device* device,
const PipelineLayoutDescriptor* descriptor) {
Ref<PipelineLayout> layout = AcquireRef(new PipelineLayout(device, descriptor));
DAWN_TRY(layout->Initialize());
return layout;
}
MaybeError PipelineLayout::Initialize() {
Device* device = ToBackend(GetDevice());
// Parameters are D3D12_ROOT_PARAMETER_TYPE which is either a root table, constant, or
// descriptor.
std::vector<D3D12_ROOT_PARAMETER> rootParameters;
size_t rangesCount = 0;
for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) {
const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group));
rangesCount += bindGroupLayout->GetCbvUavSrvDescriptorRanges().size() +
bindGroupLayout->GetSamplerDescriptorRanges().size();
}
// We are taking pointers to `ranges`, so we cannot let it resize while we're pushing to it.
std::vector<D3D12_DESCRIPTOR_RANGE> ranges(rangesCount);
uint32_t rangeIndex = 0;
for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) {
const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group));
// 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
// not set if the number of ranges is 0
auto SetRootDescriptorTable =
[&](const std::vector<D3D12_DESCRIPTOR_RANGE>& descriptorRanges) -> bool {
auto rangeCount = descriptorRanges.size();
if (rangeCount == 0) {
return false;
}
D3D12_ROOT_PARAMETER rootParameter = {};
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParameter.DescriptorTable.NumDescriptorRanges = rangeCount;
rootParameter.DescriptorTable.pDescriptorRanges = &ranges[rangeIndex];
for (auto& range : descriptorRanges) {
ASSERT(range.RegisterSpace == kRegisterSpacePlaceholder);
ranges[rangeIndex] = range;
ranges[rangeIndex].RegisterSpace = static_cast<uint32_t>(group);
rangeIndex++;
}
rootParameters.emplace_back(rootParameter);
return true;
};
if (SetRootDescriptorTable(bindGroupLayout->GetCbvUavSrvDescriptorRanges())) {
mCbvUavSrvRootParameterInfo[group] = rootParameters.size() - 1;
}
if (SetRootDescriptorTable(bindGroupLayout->GetSamplerDescriptorRanges())) {
mSamplerRootParameterInfo[group] = rootParameters.size() - 1;
}
// Init root descriptors in root signatures for dynamic buffer bindings.
// These are packed at the beginning of the layout binding info.
for (BindingIndex dynamicBindingIndex{0};
dynamicBindingIndex < bindGroupLayout->GetDynamicBufferCount();
++dynamicBindingIndex) {
const BindingInfo& bindingInfo =
bindGroupLayout->GetBindingInfo(dynamicBindingIndex);
if (bindingInfo.visibility == wgpu::ShaderStage::None) {
// Skip dynamic buffers that are not visible. D3D12 does not have None
// visibility.
continue;
}
D3D12_ROOT_PARAMETER rootParameter = {};
// Setup root descriptor.
D3D12_ROOT_DESCRIPTOR rootDescriptor;
rootDescriptor.ShaderRegister =
bindGroupLayout->GetShaderRegister(dynamicBindingIndex);
rootDescriptor.RegisterSpace = static_cast<uint32_t>(group);
// Set root descriptors in root signatures.
rootParameter.Descriptor = rootDescriptor;
mDynamicRootParameterIndices[group][dynamicBindingIndex] = rootParameters.size();
// Set parameter types according to bind group layout descriptor.
rootParameter.ParameterType = RootParameterType(bindingInfo.buffer.type);
// Set visibilities according to bind group layout descriptor.
rootParameter.ShaderVisibility = ShaderVisibilityType(bindingInfo.visibility);
rootParameters.emplace_back(rootParameter);
}
}
// Make sure that we added exactly the number of elements we expected. If we added more,
// |ranges| will have resized and the pointers in the |rootParameter|s will be invalid.
ASSERT(rangeIndex == rangesCount);
D3D12_ROOT_PARAMETER renderOrComputeInternalConstants{};
renderOrComputeInternalConstants.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
renderOrComputeInternalConstants.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
// Always allocate 3 constants for either:
// - vertex_index and instance_index
// - num_workgroups_x, num_workgroups_y and num_workgroups_z
// NOTE: We should consider delaying root signature creation until we know how many values
// we need
renderOrComputeInternalConstants.Constants.Num32BitValues = 3;
renderOrComputeInternalConstants.Constants.RegisterSpace =
kRenderOrComputeInternalRegisterSpace;
renderOrComputeInternalConstants.Constants.ShaderRegister =
kRenderOrComputeInternalBaseRegister;
mFirstIndexOffsetParameterIndex = rootParameters.size();
mNumWorkgroupsParameterIndex = rootParameters.size();
// NOTE: We should consider moving this entry to earlier in the root signature since offsets
// would need to be updated often
rootParameters.emplace_back(renderOrComputeInternalConstants);
// Loops over all of the dynamic storage buffer bindings in the layout and build
// a mapping from the binding to the next offset into the root constant array where
// that dynamic storage buffer's binding size will be stored. The next register offset
// to use is tracked with |dynamicStorageBufferLengthsShaderRegisterOffset|.
// This data will be used by shader translation to emit a load from the root constant
// array to use as the binding's size in runtime array calculations.
// Each bind group's length data is stored contiguously in the root constant array,
// so the loop also computes the first register offset for each group where the
// data should start.
uint32_t dynamicStorageBufferLengthsShaderRegisterOffset = 0;
for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) {
const BindGroupLayoutBase* bgl = GetBindGroupLayout(group);
mDynamicStorageBufferLengthInfo[group].firstRegisterOffset =
dynamicStorageBufferLengthsShaderRegisterOffset;
mDynamicStorageBufferLengthInfo[group].bindingAndRegisterOffsets.reserve(
bgl->GetBindingCountInfo().dynamicStorageBufferCount);
for (BindingIndex bindingIndex(0); bindingIndex < bgl->GetDynamicBufferCount();
++bindingIndex) {
if (bgl->IsStorageBufferBinding(bindingIndex)) {
mDynamicStorageBufferLengthInfo[group].bindingAndRegisterOffsets.push_back(
{bgl->GetBindingInfo(bindingIndex).binding,
dynamicStorageBufferLengthsShaderRegisterOffset++});
}
}
ASSERT(mDynamicStorageBufferLengthInfo[group].bindingAndRegisterOffsets.size() ==
bgl->GetBindingCountInfo().dynamicStorageBufferCount);
}
ASSERT(dynamicStorageBufferLengthsShaderRegisterOffset <=
kMaxDynamicStorageBuffersPerPipelineLayout);
if (dynamicStorageBufferLengthsShaderRegisterOffset > 0) {
D3D12_ROOT_PARAMETER dynamicStorageBufferLengthConstants{};
dynamicStorageBufferLengthConstants.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
dynamicStorageBufferLengthConstants.ParameterType =
D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
dynamicStorageBufferLengthConstants.Constants.Num32BitValues =
dynamicStorageBufferLengthsShaderRegisterOffset;
dynamicStorageBufferLengthConstants.Constants.RegisterSpace =
kDynamicStorageBufferLengthsRegisterSpace;
dynamicStorageBufferLengthConstants.Constants.ShaderRegister =
kDynamicStorageBufferLengthsBaseRegister;
mDynamicStorageBufferLengthsParameterIndex = rootParameters.size();
rootParameters.emplace_back(dynamicStorageBufferLengthConstants);
} else {
mDynamicStorageBufferLengthsParameterIndex =
kInvalidDynamicStorageBufferLengthsParameterIndex;
}
D3D12_ROOT_SIGNATURE_DESC rootSignatureDescriptor;
rootSignatureDescriptor.NumParameters = rootParameters.size();
rootSignatureDescriptor.pParameters = rootParameters.data();
rootSignatureDescriptor.NumStaticSamplers = 0;
rootSignatureDescriptor.pStaticSamplers = nullptr;
rootSignatureDescriptor.Flags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
HRESULT hr = device->GetFunctions()->d3d12SerializeRootSignature(
&rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
if (DAWN_UNLIKELY(FAILED(hr))) {
std::ostringstream messageStream;
if (error) {
messageStream << static_cast<const char*>(error->GetBufferPointer());
// |error| is observed to always end with a \n, but is not
// specified to do so, so we add an extra newline just in case.
messageStream << std::endl;
}
messageStream << "D3D12 serialize root signature";
DAWN_TRY(CheckHRESULT(hr, messageStream.str().c_str()));
}
DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateRootSignature(
0, signature->GetBufferPointer(), signature->GetBufferSize(),
IID_PPV_ARGS(&mRootSignature)),
"D3D12 create root signature"));
return {};
}
uint32_t PipelineLayout::GetCbvUavSrvRootParameterIndex(BindGroupIndex group) const {
ASSERT(group < kMaxBindGroupsTyped);
return mCbvUavSrvRootParameterInfo[group];
}
uint32_t PipelineLayout::GetSamplerRootParameterIndex(BindGroupIndex group) const {
ASSERT(group < kMaxBindGroupsTyped);
return mSamplerRootParameterInfo[group];
}
ID3D12RootSignature* PipelineLayout::GetRootSignature() const {
return mRootSignature.Get();
}
const PipelineLayout::DynamicStorageBufferLengthInfo&
PipelineLayout::GetDynamicStorageBufferLengthInfo() const {
return mDynamicStorageBufferLengthInfo;
}
uint32_t PipelineLayout::GetDynamicRootParameterIndex(BindGroupIndex group,
BindingIndex bindingIndex) const {
ASSERT(group < kMaxBindGroupsTyped);
ASSERT(bindingIndex < kMaxDynamicBuffersPerPipelineLayoutTyped);
ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).buffer.hasDynamicOffset);
ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).visibility !=
wgpu::ShaderStage::None);
return mDynamicRootParameterIndices[group][bindingIndex];
}
uint32_t PipelineLayout::GetFirstIndexOffsetRegisterSpace() const {
return kRenderOrComputeInternalRegisterSpace;
}
uint32_t PipelineLayout::GetFirstIndexOffsetShaderRegister() const {
return kRenderOrComputeInternalBaseRegister;
}
uint32_t PipelineLayout::GetFirstIndexOffsetParameterIndex() const {
return mFirstIndexOffsetParameterIndex;
}
uint32_t PipelineLayout::GetNumWorkgroupsRegisterSpace() const {
return kRenderOrComputeInternalRegisterSpace;
}
uint32_t PipelineLayout::GetNumWorkgroupsShaderRegister() const {
return kRenderOrComputeInternalBaseRegister;
}
uint32_t PipelineLayout::GetNumWorkgroupsParameterIndex() const {
return mNumWorkgroupsParameterIndex;
}
uint32_t PipelineLayout::GetDynamicStorageBufferLengthsRegisterSpace() const {
return kDynamicStorageBufferLengthsRegisterSpace;
}
uint32_t PipelineLayout::GetDynamicStorageBufferLengthsShaderRegister() const {
return kDynamicStorageBufferLengthsBaseRegister;
}
uint32_t PipelineLayout::GetDynamicStorageBufferLengthsParameterIndex() const {
ASSERT(mDynamicStorageBufferLengthsParameterIndex !=
kInvalidDynamicStorageBufferLengthsParameterIndex);
return mDynamicStorageBufferLengthsParameterIndex;
}
ID3D12CommandSignature* PipelineLayout::GetDispatchIndirectCommandSignatureWithNumWorkgroups() {
// mDispatchIndirectCommandSignatureWithNumWorkgroups won't be created until it is needed.
if (mDispatchIndirectCommandSignatureWithNumWorkgroups.Get() != nullptr) {
return mDispatchIndirectCommandSignatureWithNumWorkgroups.Get();
}
D3D12_INDIRECT_ARGUMENT_DESC argumentDescs[2] = {};
argumentDescs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT;
argumentDescs[0].Constant.RootParameterIndex = GetNumWorkgroupsParameterIndex();
argumentDescs[0].Constant.Num32BitValuesToSet = 3;
argumentDescs[0].Constant.DestOffsetIn32BitValues = 0;
// A command signature must contain exactly 1 Draw / Dispatch / DispatchMesh / DispatchRays
// command. That command must come last.
argumentDescs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
D3D12_COMMAND_SIGNATURE_DESC programDesc = {};
programDesc.ByteStride = 6 * sizeof(uint32_t);
programDesc.NumArgumentDescs = 2;
programDesc.pArgumentDescs = argumentDescs;
// The root signature must be specified if and only if the command signature changes one of
// the root arguments.
ToBackend(GetDevice())
->GetD3D12Device()
->CreateCommandSignature(
&programDesc, GetRootSignature(),
IID_PPV_ARGS(&mDispatchIndirectCommandSignatureWithNumWorkgroups));
return mDispatchIndirectCommandSignatureWithNumWorkgroups.Get();
}
} // namespace dawn::native::d3d12