| // Copyright 2017 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "src/dawn/native/d3d12/PipelineLayoutD3D12.h" |
| |
| #include <limits> |
| #include <sstream> |
| #include <utility> |
| |
| #include "src/dawn/native/d3d/D3DError.h" |
| #include "src/dawn/native/d3d12/BindGroupLayoutD3D12.h" |
| #include "src/dawn/native/d3d12/DeviceD3D12.h" |
| #include "src/dawn/native/d3d12/PlatformFunctionsD3D12.h" |
| #include "src/dawn/native/d3d12/ResourceTableD3D12.h" |
| #include "src/dawn/native/d3d12/UtilsD3D12.h" |
| #include "src/utils/assert.h" |
| #include "src/utils/compiler.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. |
| // TODO(crbug.com/366291600): Use Immediates to support internal constants. |
| 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 kDynamicStorageBufferOffsetsRegisterSpace = kMaxBindGroups + 3; |
| static constexpr uint32_t kDynamicStorageBufferOffsetsBaseRegister = 0; |
| |
| static constexpr uint32_t kImmediatesRegisterSpace = kMaxBindGroups + 4; |
| static constexpr uint32_t kImmediatesBaseRegister = 0; |
| |
| // Keep this last as we need a large range of register spaces for resource table descriptors, |
| // one per resource type. For example, for all texture types, we need 26 spaces. |
| static constexpr uint32_t kBaseResourceTableRegisterSpace = kMaxBindGroups + 5; |
| |
| static constexpr uint32_t kInvalidResourceTableRootParameterIndex = |
| std::numeric_limits<uint32_t>::max(); |
| static constexpr uint32_t kInvalidDynamicStorageBufferLengthsParameterIndex = |
| std::numeric_limits<uint32_t>::max(); |
| static constexpr uint32_t kInvalidDynamicStorageBufferOffsetsParameterIndex = |
| std::numeric_limits<uint32_t>::max(); |
| static constexpr uint32_t kInvalidDynamicUniformBufferParameterIndex = |
| std::numeric_limits<uint32_t>::max(); |
| static constexpr uint32_t kInvalidImmediatesParameterIndex = std::numeric_limits<uint32_t>::max(); |
| |
| 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: |
| case kInternalReadOnlyStorageBufferBinding: |
| return D3D12_ROOT_PARAMETER_TYPE_SRV; |
| case wgpu::BufferBindingType::BindingNotUsed: |
| case wgpu::BufferBindingType::Undefined: |
| default: |
| DAWN_UNREACHABLE(); |
| } |
| } |
| |
| HRESULT SerializeRootParameter1_0(Device* device, |
| const D3D12_VERSIONED_ROOT_SIGNATURE_DESC& rootSignature1_1, |
| ID3DBlob** ppBlob, |
| ID3DBlob** ppErrorBlob) { |
| std::vector<std::vector<D3D12_DESCRIPTOR_RANGE>> allDescriptorRanges1_0; |
| std::vector<D3D12_ROOT_PARAMETER> rootParameters1_0(rootSignature1_1.Desc_1_1.NumParameters); |
| for (size_t i = 0; i < rootParameters1_0.size(); ++i) { |
| const D3D12_ROOT_PARAMETER1& rootParameter1_1 = |
| DAWN_UNSAFE_TODO(rootSignature1_1.Desc_1_1.pParameters[i]); |
| |
| rootParameters1_0[i].ParameterType = rootParameter1_1.ParameterType; |
| rootParameters1_0[i].ShaderVisibility = rootParameter1_1.ShaderVisibility; |
| |
| switch (rootParameters1_0[i].ParameterType) { |
| case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: |
| rootParameters1_0[i].Constants = rootParameter1_1.Constants; |
| break; |
| |
| case D3D12_ROOT_PARAMETER_TYPE_CBV: |
| case D3D12_ROOT_PARAMETER_TYPE_SRV: |
| case D3D12_ROOT_PARAMETER_TYPE_UAV: |
| rootParameters1_0[i].Descriptor.RegisterSpace = |
| rootParameter1_1.Descriptor.RegisterSpace; |
| rootParameters1_0[i].Descriptor.ShaderRegister = |
| rootParameter1_1.Descriptor.ShaderRegister; |
| break; |
| |
| case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: |
| rootParameters1_0[i].DescriptorTable.NumDescriptorRanges = |
| rootParameter1_1.DescriptorTable.NumDescriptorRanges; |
| if (rootParameters1_0[i].DescriptorTable.NumDescriptorRanges > 0) { |
| std::vector<D3D12_DESCRIPTOR_RANGE> descriptorRanges1_0( |
| rootParameters1_0[i].DescriptorTable.NumDescriptorRanges); |
| for (uint32_t index = 0; |
| index < rootParameter1_1.DescriptorTable.NumDescriptorRanges; ++index) { |
| const D3D12_DESCRIPTOR_RANGE1& descriptorRange1_1 = DAWN_UNSAFE_TODO( |
| rootParameter1_1.DescriptorTable.pDescriptorRanges[index]); |
| descriptorRanges1_0[index].BaseShaderRegister = |
| descriptorRange1_1.BaseShaderRegister; |
| descriptorRanges1_0[index].NumDescriptors = |
| descriptorRange1_1.NumDescriptors; |
| descriptorRanges1_0[index].OffsetInDescriptorsFromTableStart = |
| descriptorRange1_1.OffsetInDescriptorsFromTableStart; |
| descriptorRanges1_0[index].RangeType = descriptorRange1_1.RangeType; |
| descriptorRanges1_0[index].RegisterSpace = descriptorRange1_1.RegisterSpace; |
| } |
| allDescriptorRanges1_0.push_back(descriptorRanges1_0); |
| rootParameters1_0[i].DescriptorTable.pDescriptorRanges = |
| allDescriptorRanges1_0.back().data(); |
| } |
| break; |
| |
| default: |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| } |
| |
| D3D12_ROOT_SIGNATURE_DESC rootSignatureDescriptor; |
| rootSignatureDescriptor.NumParameters = static_cast<uint32_t>(rootParameters1_0.size()); |
| rootSignatureDescriptor.pParameters = rootParameters1_0.data(); |
| rootSignatureDescriptor.NumStaticSamplers = 0; |
| rootSignatureDescriptor.pStaticSamplers = nullptr; |
| rootSignatureDescriptor.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; |
| |
| return device->GetFunctions()->d3d12SerializeRootSignature( |
| &rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob); |
| } |
| |
| } // anonymous namespace |
| |
| ResultOrError<Ref<PipelineLayout>> PipelineLayout::Create( |
| Device* device, |
| const UnpackedPtr<PipelineLayoutDescriptor>& descriptor) { |
| Ref<PipelineLayout> layout = AcquireRef(new PipelineLayout(device, descriptor)); |
| DAWN_TRY(layout->Initialize()); |
| return layout; |
| } |
| |
| MaybeError PipelineLayout::Initialize() { |
| BindGroupMask bindGroupMask = GetBindGroupLayoutsMask(); |
| BindGroupIndex highestBindGroupIndex = GetHighestBitIndexPlusOne(bindGroupMask); |
| PerBindGroup<const CachedObject*> cachedObjects; |
| for (BindGroupIndex i : Range(highestBindGroupIndex)) { |
| if (bindGroupMask[i]) { |
| cachedObjects[i] = GetBindGroupLayout(i); |
| } else { |
| cachedObjects[i] = GetDevice()->GetEmptyBindGroupLayout()->GetInternalBindGroupLayout(); |
| } |
| } |
| |
| // Record bind group layout objects and user immediate data size into pipeline layout cache key. |
| // It represents pipeline layout base attributes and ignored future changes caused by internal |
| // immediate data size from pipeline. |
| uint32_t numSetLayoutsWithHoles = |
| static_cast<uint32_t>(GetHighestBitIndexPlusOne(bindGroupMask)); |
| StreamIn(&mCacheKey, stream::Iterable(cachedObjects.data(), numSetLayoutsWithHoles), |
| GetImmediateDataRangeByteSize()); |
| |
| DAWN_TRY(BuildBaseRootParameters()); |
| |
| return {}; |
| } |
| |
| ResultOrError<Ref<PipelineLayoutHandle>> PipelineLayout::GetOrCreatePipelineLayoutHandle( |
| const ImmediateMask& pipelineImmediateMask) { |
| // Check cache |
| Ref<PipelineLayoutHandle> pipelineLayoutHandle; |
| mPipelineLayoutHandles.Use([&](auto pipelineLayoutHandles) { |
| auto it = pipelineLayoutHandles->find(pipelineImmediateMask); |
| if (it != pipelineLayoutHandles->end()) { |
| pipelineLayoutHandle = it->second; |
| } |
| }); |
| |
| if (pipelineLayoutHandle != nullptr) { |
| return pipelineLayoutHandle; |
| } |
| |
| DAWN_TRY_ASSIGN(pipelineLayoutHandle, CreatePipelineLayoutHandle(pipelineImmediateMask)); |
| |
| return mPipelineLayoutHandles.Use([&](auto pipelineLayoutHandles) { |
| return pipelineLayoutHandles |
| ->insert({pipelineImmediateMask, std::move(pipelineLayoutHandle)}) |
| .first->second; |
| }); |
| } |
| |
| MaybeError PipelineLayout::BuildBaseRootParameters() { |
| // Parameters are D3D12_ROOT_PARAMETER_TYPE which is either a root table, constant, or |
| // descriptor. |
| std::vector<D3D12_ROOT_PARAMETER1> rootParameters; |
| std::vector<D3D12_STATIC_SAMPLER_DESC> staticSamplers; |
| |
| size_t rangesCount = 0; |
| size_t staticSamplerCount = 0; |
| for (BindGroupIndex group : GetBindGroupLayoutsMask()) { |
| const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group)); |
| rangesCount += bindGroupLayout->GetCbvUavSrvDescriptorRanges().size() + |
| bindGroupLayout->GetSamplerDescriptorRanges().size(); |
| staticSamplerCount += bindGroupLayout->GetStaticSamplerCount(); |
| } |
| |
| ResourceTable::DescriptorRanges resourceTableRanges; |
| if (UsesResourceTable()) { |
| DAWN_TRY_ASSIGN(resourceTableRanges, ResourceTable::GetDescriptorRanges(*this)); |
| rangesCount += resourceTableRanges.cbvUavSrvs.size() + resourceTableRanges.samplers.size(); |
| } |
| |
| // We are taking pointers to `ranges`, so we cannot let it resize while we're pushing to it. |
| std::vector<D3D12_DESCRIPTOR_RANGE1> ranges(rangesCount); |
| staticSamplers.reserve(staticSamplerCount); |
| |
| uint32_t rangeIndex = 0; |
| |
| // Set the root descriptor table parameter and copy ranges. An optional registerSpace can be |
| // passed in and is set only on ranges with kRegisterSpacePlaceholder. Returns whether or not |
| // the parameter was set. A root parameter is not set if the number of ranges is 0. |
| auto SetRootDescriptorTable = [&](std::span<const D3D12_DESCRIPTOR_RANGE1> descriptorRanges, |
| uint32_t registerSpace = |
| kRegisterSpacePlaceholder) -> std::optional<uint32_t> { |
| auto rangeCount = descriptorRanges.size(); |
| if (rangeCount == 0) { |
| return {}; |
| } |
| |
| D3D12_ROOT_PARAMETER1 rootParameter = {}; |
| rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; |
| rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; |
| rootParameter.DescriptorTable.NumDescriptorRanges = static_cast<uint32_t>(rangeCount); |
| rootParameter.DescriptorTable.pDescriptorRanges = &ranges[rangeIndex]; |
| |
| for (auto& range : descriptorRanges) { |
| ranges[rangeIndex] = range; |
| if (range.RegisterSpace == kRegisterSpacePlaceholder) { |
| ranges[rangeIndex].RegisterSpace = registerSpace; |
| } |
| rangeIndex++; |
| } |
| |
| rootParameters.emplace_back(rootParameter); |
| return static_cast<uint32_t>(rootParameters.size() - 1); |
| }; |
| |
| mResourceTableCbvUavSrvRootParameterIndex = kInvalidResourceTableRootParameterIndex; |
| mResourceTableSamplerRootParameterIndex = kInvalidResourceTableRootParameterIndex; |
| if (UsesResourceTable()) { |
| if (auto paramIndex = SetRootDescriptorTable(resourceTableRanges.cbvUavSrvs)) { |
| mResourceTableCbvUavSrvRootParameterIndex = *paramIndex; |
| } |
| if (auto paramIndex = SetRootDescriptorTable(resourceTableRanges.samplers)) { |
| mResourceTableSamplerRootParameterIndex = *paramIndex; |
| } |
| } |
| |
| for (BindGroupIndex group : GetBindGroupLayoutsMask()) { |
| const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group)); |
| |
| // Note that CbvUavSrvDescriptorRanges includes dynamic storage buffers. |
| if (auto paramIndex = SetRootDescriptorTable( |
| bindGroupLayout->GetCbvUavSrvDescriptorRanges(), static_cast<uint32_t>(group))) { |
| mCbvUavSrvRootParameterIndices[group] = *paramIndex; |
| } |
| if (auto paramIndex = SetRootDescriptorTable(bindGroupLayout->GetSamplerDescriptorRanges(), |
| static_cast<uint32_t>(group))) { |
| mSamplerRootParameterIndices[group] = *paramIndex; |
| } |
| |
| // Combine the static samplers from the all of the bind group layouts to one vector. |
| for (auto& samplerDesc : bindGroupLayout->GetStaticSamplers()) { |
| auto& newSampler = staticSamplers.emplace_back(samplerDesc); |
| newSampler.RegisterSpace = static_cast<uint32_t>(group); |
| } |
| |
| // Init root descriptors in root signatures for dynamic uniform buffer bindings. |
| // Dynamic buffer bindings are packed at the beginning of the layout binding info. |
| // Resize the vector to include entries for both dynamic uniform and storage buffers, even |
| // though we only write to the uniform entries, so that we can index them correctly. |
| mDynamicUniformRootParameterIndices[group].resize( |
| bindGroupLayout->GetDynamicBufferCount(), kInvalidDynamicUniformBufferParameterIndex); |
| for (BindingIndex dynamicBindingIndex : bindGroupLayout->GetDynamicBufferIndices()) { |
| if (GetBindGroupLayout(group)->IsStorageBufferBinding(dynamicBindingIndex)) { |
| // Dynamic storage buffers are stored in the root descriptor table |
| continue; |
| } |
| |
| 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_PARAMETER1 rootParameter = {}; |
| |
| // Setup root descriptor. |
| D3D12_ROOT_DESCRIPTOR1 rootDescriptor; |
| rootDescriptor.ShaderRegister = bindGroupLayout->GetShaderRegister(dynamicBindingIndex); |
| rootDescriptor.RegisterSpace = static_cast<uint32_t>(group); |
| // Using D3D12_ROOT_DESCRIPTOR_FLAG_NONE means using DATA_STATIC_WHILE_SET_AT_EXECUTE |
| // for CBV/SRV and using DATA_VOLATILE for UAV, which is allowed because currently in |
| // Dawn the views with dynamic offsets are always re-applied every time before a draw or |
| // a dispatch call. |
| rootDescriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE; |
| |
| // Set root descriptors in root signatures. |
| rootParameter.Descriptor = rootDescriptor; |
| |
| // Set parameter types according to bind group layout descriptor. |
| rootParameter.ParameterType = |
| RootParameterType(std::get<BufferBindingInfo>(bindingInfo.bindingLayout).type); |
| |
| // Set visibilities according to bind group layout descriptor. |
| rootParameter.ShaderVisibility = ShaderVisibilityType(bindingInfo.visibility); |
| |
| mDynamicUniformRootParameterIndices[group][dynamicBindingIndex] = rootParameters.size(); |
| 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. |
| DAWN_ASSERT(rangeIndex == rangesCount); |
| |
| // For dynamic storage buffers, we store the length and offset of each binding as root |
| // constants. Lengths and offsets are bound to separate groups, but share the same binding value |
| // (aka register offset). Here we populate mDynamicStorageBufferInfo with this mapping of |
| // dynamic storage buffer bind group to register offset, which will be used to update the root |
| // constant values, as well as to tell Tint to emit loads from these root constant values for |
| // lengths and offsets. Each bind group's length/offset data is stored contiguously in the root |
| // constant, so we also compute and store the first register offset for each group where the |
| // data should start. |
| uint32_t dynamicStorageBufferInfoShaderRegisterOffset = 0; |
| for (BindGroupIndex group : GetBindGroupLayoutsMask()) { |
| const BindGroupLayoutInternalBase* bgl = GetBindGroupLayout(group); |
| const size_t dynamicStorageBufferCount = |
| static_cast<size_t>(bgl->GetDynamicStorageBufferCount()); |
| |
| BindGroupDynamicStorageBufferInfo info; |
| info.firstRegisterOffset = dynamicStorageBufferInfoShaderRegisterOffset; |
| info.bindingAndRegisterOffsets.reserve(dynamicStorageBufferCount); |
| |
| for (BindingIndex bindingIndex : bgl->GetDynamicBufferIndices()) { |
| if (bgl->IsStorageBufferBinding(bindingIndex)) { |
| info.bindingAndRegisterOffsets.push_back( |
| {bgl->GetBindingInfo(bindingIndex).binding, |
| dynamicStorageBufferInfoShaderRegisterOffset++}); |
| } |
| } |
| DAWN_ASSERT(info.bindingAndRegisterOffsets.size() == dynamicStorageBufferCount); |
| mDynamicStorageBufferInfo[group] = std::move(info); |
| } |
| |
| if (dynamicStorageBufferInfoShaderRegisterOffset > 0) { |
| auto createRootConstants = [&](uint32_t num32BitValues, uint32_t registerSpace, |
| uint32_t shaderRegister) -> uint32_t { |
| D3D12_ROOT_PARAMETER1 rootParam{}; |
| rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; |
| rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; |
| rootParam.Constants.Num32BitValues = num32BitValues; |
| rootParam.Constants.RegisterSpace = registerSpace; |
| rootParam.Constants.ShaderRegister = shaderRegister; |
| rootParameters.emplace_back(rootParam); |
| return static_cast<uint32_t>(rootParameters.size() - 1); |
| }; |
| |
| // Create the same number of root constants for both the lengths and the offsets of each |
| // dynamic storage buffer |
| mDynamicStorageBufferLengthsParameterIndex = createRootConstants( |
| dynamicStorageBufferInfoShaderRegisterOffset, kDynamicStorageBufferLengthsRegisterSpace, |
| kDynamicStorageBufferLengthsBaseRegister); |
| mDynamicStorageBufferOffsetsParameterIndex = createRootConstants( |
| dynamicStorageBufferInfoShaderRegisterOffset, kDynamicStorageBufferOffsetsRegisterSpace, |
| kDynamicStorageBufferOffsetsBaseRegister); |
| } else { |
| mDynamicStorageBufferLengthsParameterIndex = |
| kInvalidDynamicStorageBufferLengthsParameterIndex; |
| mDynamicStorageBufferOffsetsParameterIndex = |
| kInvalidDynamicStorageBufferOffsetsParameterIndex; |
| } |
| |
| // Stash the layout-invariant parameters so each PipelineLayoutHandle can be built from them |
| // without recomputing or mutating shared state. The descriptor table entries in rootParameters |
| // point into ranges; moving the vectors into the const struct preserves those pointers and |
| // keeps them immutable thereafter. |
| mInvariantParams.emplace(std::move(rootParameters), std::move(ranges), |
| std::move(staticSamplers)); |
| |
| return {}; |
| } |
| |
| ResultOrError<Ref<PipelineLayoutHandle>> PipelineLayout::CreatePipelineLayoutHandle( |
| const ImmediateMask& pipelineImmediateMask) { |
| Device* device = ToBackend(GetDevice()); |
| DAWN_ASSERT(mInvariantParams.has_value()); |
| |
| // Start from the layout-invariant base parameters and append the immediates root parameter, |
| // whose size depends on the pipeline's internal immediate usage. Everything here operates on |
| // locals so that this method has no side effects and can run concurrently on pipeline-creation |
| // worker threads. The descriptor table entries copied from rootParameters keep pointing into |
| // mInvariantParams->ranges, which is stable for the lifetime of the layout. |
| std::vector<D3D12_ROOT_PARAMETER1> rootParameters = mInvariantParams->rootParameters; |
| uint32_t immediatesParameterIndex = kInvalidImmediatesParameterIndex; |
| if (pipelineImmediateMask.count() > 0) { |
| D3D12_ROOT_PARAMETER1 immediates{}; |
| immediates.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; |
| immediates.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; |
| immediates.Constants.Num32BitValues = pipelineImmediateMask.count(); |
| immediates.Constants.RegisterSpace = kImmediatesRegisterSpace; |
| immediates.Constants.ShaderRegister = kImmediatesBaseRegister; |
| immediatesParameterIndex = static_cast<uint32_t>(rootParameters.size()); |
| rootParameters.emplace_back(immediates); |
| } |
| |
| D3D12_VERSIONED_ROOT_SIGNATURE_DESC versionedRootSignatureDescriptor = {}; |
| versionedRootSignatureDescriptor.Version = D3D_ROOT_SIGNATURE_VERSION_1_1; |
| versionedRootSignatureDescriptor.Desc_1_1.NumParameters = |
| static_cast<uint32_t>(rootParameters.size()); |
| versionedRootSignatureDescriptor.Desc_1_1.pParameters = rootParameters.data(); |
| versionedRootSignatureDescriptor.Desc_1_1.NumStaticSamplers = |
| static_cast<uint32_t>(mInvariantParams->staticSamplers.size()); |
| versionedRootSignatureDescriptor.Desc_1_1.pStaticSamplers = |
| mInvariantParams->staticSamplers.data(); |
| versionedRootSignatureDescriptor.Desc_1_1.Flags = |
| D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; |
| |
| ComPtr<ID3DBlob> rootSignatureBlob; |
| DAWN_TRY([&]() -> MaybeError { |
| ComPtr<ID3DBlob> error; |
| if (device->IsToggleEnabled(Toggle::D3D12UseRootSignatureVersion1_1) && |
| SUCCEEDED(device->GetFunctions()->SerializeVersionedRootSignature( |
| &versionedRootSignatureDescriptor, &rootSignatureBlob, &error))) [[likely]] { |
| return {}; |
| } |
| // If using root signature version 1.1 failed, try again with root signature version 1.0. |
| // Some drivers appear to run an outdated version of the DXIL validator and can't support |
| // 1.1. |
| // Note that retrying again is OK because whether we use version 1.0 or 1.1 doesn't |
| // affect anything else except pipeline layout creation. Nothing else in Dawn cares |
| // what decision we made here. |
| // TODO(crbug.com/1512318): Add some telemetry so we log how often/when this happens. |
| std::ostringstream messageStream; |
| if (error) { |
| messageStream << static_cast<const char*>(error->GetBufferPointer()) << "\n"; |
| } |
| HRESULT hr = SerializeRootParameter1_0(device, versionedRootSignatureDescriptor, |
| &rootSignatureBlob, &error); |
| if (SUCCEEDED(hr)) [[likely]] { |
| return {}; |
| } |
| if (error) { |
| messageStream << static_cast<const char*>(error->GetBufferPointer()) << "\n"; |
| } |
| messageStream << "D3D12 serialize root signature"; |
| DAWN_TRY(CheckHRESULT(hr, messageStream.str().c_str())); |
| return {}; |
| }()); |
| |
| ComPtr<ID3D12RootSignature> rootSignature; |
| DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateRootSignature( |
| 0, rootSignatureBlob->GetBufferPointer(), |
| rootSignatureBlob->GetBufferSize(), IID_PPV_ARGS(&rootSignature)), |
| "D3D12 create root signature")); |
| |
| return PipelineLayoutHandle::Create(ToBackend(GetDevice()), std::move(rootSignature), |
| std::move(rootSignatureBlob), immediatesParameterIndex, |
| pipelineImmediateMask); |
| } |
| |
| void PipelineLayout::DestroyImpl(DestroyReason reason) { |
| PipelineLayoutBase::DestroyImpl(reason); |
| mPipelineLayoutHandles->clear(); |
| } |
| |
| uint32_t PipelineLayout::GetResourceTableCbvUavSrvRootParameterIndex() const { |
| DAWN_ASSERT(mResourceTableCbvUavSrvRootParameterIndex != |
| kInvalidResourceTableRootParameterIndex); |
| return mResourceTableCbvUavSrvRootParameterIndex; |
| } |
| |
| uint32_t PipelineLayout::GetResourceTableSamplerRootParameterIndex() const { |
| DAWN_ASSERT(mResourceTableSamplerRootParameterIndex != kInvalidResourceTableRootParameterIndex); |
| return mResourceTableSamplerRootParameterIndex; |
| } |
| |
| uint32_t PipelineLayout::GetBaseResourceTableRegisterSpace() const { |
| return kBaseResourceTableRegisterSpace; |
| } |
| |
| uint32_t PipelineLayout::GetCbvUavSrvRootParameterIndex(BindGroupIndex group) const { |
| DAWN_ASSERT(group < kMaxBindGroupsTyped); |
| return mCbvUavSrvRootParameterIndices[group]; |
| } |
| |
| uint32_t PipelineLayout::GetSamplerRootParameterIndex(BindGroupIndex group) const { |
| DAWN_ASSERT(group < kMaxBindGroupsTyped); |
| return mSamplerRootParameterIndices[group]; |
| } |
| |
| const PipelineLayout::DynamicStorageBufferInfo& PipelineLayout::GetDynamicStorageBufferInfo() |
| const { |
| return mDynamicStorageBufferInfo; |
| } |
| |
| uint32_t PipelineLayout::GetDynamicUniformRootParameterIndex(BindGroupIndex group, |
| BindingIndex bindingIndex) const { |
| DAWN_ASSERT(group < kMaxBindGroupsTyped); |
| DAWN_ASSERT(std::get<BufferBindingInfo>( |
| GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).bindingLayout) |
| .hasDynamicOffset); |
| DAWN_ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).visibility != |
| wgpu::ShaderStage::None); |
| DAWN_ASSERT(std::get<BufferBindingInfo>( |
| GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).bindingLayout) |
| .type == wgpu::BufferBindingType::Uniform); |
| |
| return mDynamicUniformRootParameterIndices[group][bindingIndex]; |
| } |
| |
| uint32_t PipelineLayout::GetFirstIndexOffsetRegisterSpace() const { |
| return kRenderOrComputeInternalRegisterSpace; |
| } |
| |
| uint32_t PipelineLayout::GetFirstIndexOffsetShaderRegister() const { |
| return kRenderOrComputeInternalBaseRegister; |
| } |
| |
| uint32_t PipelineLayout::GetNumWorkgroupsRegisterSpace() const { |
| return kRenderOrComputeInternalRegisterSpace; |
| } |
| |
| uint32_t PipelineLayout::GetNumWorkgroupsShaderRegister() const { |
| return kRenderOrComputeInternalBaseRegister; |
| } |
| |
| uint32_t PipelineLayout::GetDynamicStorageBufferLengthsRegisterSpace() const { |
| return kDynamicStorageBufferLengthsRegisterSpace; |
| } |
| |
| uint32_t PipelineLayout::GetDynamicStorageBufferLengthsShaderRegister() const { |
| return kDynamicStorageBufferLengthsBaseRegister; |
| } |
| |
| uint32_t PipelineLayout::GetDynamicStorageBufferLengthsParameterIndex() const { |
| DAWN_ASSERT(mDynamicStorageBufferLengthsParameterIndex != |
| kInvalidDynamicStorageBufferLengthsParameterIndex); |
| return mDynamicStorageBufferLengthsParameterIndex; |
| } |
| |
| uint32_t PipelineLayout::GetDynamicStorageBufferOffsetsRegisterSpace() const { |
| return kDynamicStorageBufferOffsetsRegisterSpace; |
| } |
| |
| uint32_t PipelineLayout::GetDynamicStorageBufferOffsetsShaderRegister() const { |
| return kDynamicStorageBufferOffsetsBaseRegister; |
| } |
| |
| uint32_t PipelineLayout::GetDynamicStorageBufferOffsetsParameterIndex() const { |
| DAWN_ASSERT(mDynamicStorageBufferOffsetsParameterIndex != |
| kInvalidDynamicStorageBufferOffsetsParameterIndex); |
| return mDynamicStorageBufferOffsetsParameterIndex; |
| } |
| |
| uint32_t PipelineLayout::GetImmediatesRegisterSpace() const { |
| return kImmediatesRegisterSpace; |
| } |
| |
| uint32_t PipelineLayout::GetImmediatesShaderRegister() const { |
| return kImmediatesBaseRegister; |
| } |
| |
| } // namespace dawn::native::d3d12 |