• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "dawn_native/d3d12/PipelineLayoutD3D12.h"
16 #include <sstream>
17 
18 #include "common/Assert.h"
19 #include "common/BitSetIterator.h"
20 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
21 #include "dawn_native/d3d12/D3D12Error.h"
22 #include "dawn_native/d3d12/DeviceD3D12.h"
23 #include "dawn_native/d3d12/PlatformFunctions.h"
24 
25 using Microsoft::WRL::ComPtr;
26 
27 namespace dawn_native { namespace d3d12 {
28     namespace {
29 
30         // Reserve register names for internal use. This registers map to bindings in the shader,
31         // but are not directly related to allocation of the root signature.
32         // In the root signature, it the index of the root parameter where these registers are
33         // used that determines the layout of the root signature.
34         static constexpr uint32_t kRenderOrComputeInternalRegisterSpace = kMaxBindGroups + 1;
35         static constexpr uint32_t kRenderOrComputeInternalBaseRegister = 0;
36 
37         static constexpr uint32_t kDynamicStorageBufferLengthsRegisterSpace = kMaxBindGroups + 2;
38         static constexpr uint32_t kDynamicStorageBufferLengthsBaseRegister = 0;
39 
ShaderVisibilityType(wgpu::ShaderStage visibility)40         D3D12_SHADER_VISIBILITY ShaderVisibilityType(wgpu::ShaderStage visibility) {
41             ASSERT(visibility != wgpu::ShaderStage::None);
42 
43             if (visibility == wgpu::ShaderStage::Vertex) {
44                 return D3D12_SHADER_VISIBILITY_VERTEX;
45             }
46 
47             if (visibility == wgpu::ShaderStage::Fragment) {
48                 return D3D12_SHADER_VISIBILITY_PIXEL;
49             }
50 
51             // For compute or any two combination of stages, visibility must be ALL
52             return D3D12_SHADER_VISIBILITY_ALL;
53         }
54 
RootParameterType(wgpu::BufferBindingType type)55         D3D12_ROOT_PARAMETER_TYPE RootParameterType(wgpu::BufferBindingType type) {
56             switch (type) {
57                 case wgpu::BufferBindingType::Uniform:
58                     return D3D12_ROOT_PARAMETER_TYPE_CBV;
59                 case wgpu::BufferBindingType::Storage:
60                 case kInternalStorageBufferBinding:
61                     return D3D12_ROOT_PARAMETER_TYPE_UAV;
62                 case wgpu::BufferBindingType::ReadOnlyStorage:
63                     return D3D12_ROOT_PARAMETER_TYPE_SRV;
64                 case wgpu::BufferBindingType::Undefined:
65                     UNREACHABLE();
66             }
67         }
68 
69     }  // anonymous namespace
70 
Create(Device * device,const PipelineLayoutDescriptor * descriptor)71     ResultOrError<Ref<PipelineLayout>> PipelineLayout::Create(
72         Device* device,
73         const PipelineLayoutDescriptor* descriptor) {
74         Ref<PipelineLayout> layout = AcquireRef(new PipelineLayout(device, descriptor));
75         DAWN_TRY(layout->Initialize());
76         return layout;
77     }
78 
Initialize()79     MaybeError PipelineLayout::Initialize() {
80         Device* device = ToBackend(GetDevice());
81         // Parameters are D3D12_ROOT_PARAMETER_TYPE which is either a root table, constant, or
82         // descriptor.
83         std::vector<D3D12_ROOT_PARAMETER> rootParameters;
84 
85         size_t rangesCount = 0;
86         for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) {
87             const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group));
88             rangesCount += bindGroupLayout->GetCbvUavSrvDescriptorRanges().size() +
89                            bindGroupLayout->GetSamplerDescriptorRanges().size();
90         }
91 
92         // We are taking pointers to `ranges`, so we cannot let it resize while we're pushing to it.
93         std::vector<D3D12_DESCRIPTOR_RANGE> ranges(rangesCount);
94 
95         uint32_t rangeIndex = 0;
96 
97         for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) {
98             const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group));
99 
100             // Set the root descriptor table parameter and copy ranges. Ranges are offset by the
101             // bind group index Returns whether or not the parameter was set. A root parameter is
102             // not set if the number of ranges is 0
103             auto SetRootDescriptorTable =
104                 [&](const std::vector<D3D12_DESCRIPTOR_RANGE>& descriptorRanges) -> bool {
105                 auto rangeCount = descriptorRanges.size();
106                 if (rangeCount == 0) {
107                     return false;
108                 }
109 
110                 D3D12_ROOT_PARAMETER rootParameter = {};
111                 rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
112                 rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
113                 rootParameter.DescriptorTable.NumDescriptorRanges = rangeCount;
114                 rootParameter.DescriptorTable.pDescriptorRanges = &ranges[rangeIndex];
115 
116                 for (auto& range : descriptorRanges) {
117                     ASSERT(range.RegisterSpace == kRegisterSpacePlaceholder);
118                     ranges[rangeIndex] = range;
119                     ranges[rangeIndex].RegisterSpace = static_cast<uint32_t>(group);
120                     rangeIndex++;
121                 }
122 
123                 rootParameters.emplace_back(rootParameter);
124 
125                 return true;
126             };
127 
128             if (SetRootDescriptorTable(bindGroupLayout->GetCbvUavSrvDescriptorRanges())) {
129                 mCbvUavSrvRootParameterInfo[group] = rootParameters.size() - 1;
130             }
131             if (SetRootDescriptorTable(bindGroupLayout->GetSamplerDescriptorRanges())) {
132                 mSamplerRootParameterInfo[group] = rootParameters.size() - 1;
133             }
134 
135             // Init root descriptors in root signatures for dynamic buffer bindings.
136             // These are packed at the beginning of the layout binding info.
137             for (BindingIndex dynamicBindingIndex{0};
138                  dynamicBindingIndex < bindGroupLayout->GetDynamicBufferCount();
139                  ++dynamicBindingIndex) {
140                 const BindingInfo& bindingInfo =
141                     bindGroupLayout->GetBindingInfo(dynamicBindingIndex);
142 
143                 if (bindingInfo.visibility == wgpu::ShaderStage::None) {
144                     // Skip dynamic buffers that are not visible. D3D12 does not have None
145                     // visibility.
146                     continue;
147                 }
148 
149                 D3D12_ROOT_PARAMETER rootParameter = {};
150 
151                 // Setup root descriptor.
152                 D3D12_ROOT_DESCRIPTOR rootDescriptor;
153                 rootDescriptor.ShaderRegister =
154                     bindGroupLayout->GetShaderRegister(dynamicBindingIndex);
155                 rootDescriptor.RegisterSpace = static_cast<uint32_t>(group);
156 
157                 // Set root descriptors in root signatures.
158                 rootParameter.Descriptor = rootDescriptor;
159                 mDynamicRootParameterIndices[group][dynamicBindingIndex] = rootParameters.size();
160 
161                 // Set parameter types according to bind group layout descriptor.
162                 rootParameter.ParameterType = RootParameterType(bindingInfo.buffer.type);
163 
164                 // Set visibilities according to bind group layout descriptor.
165                 rootParameter.ShaderVisibility = ShaderVisibilityType(bindingInfo.visibility);
166 
167                 rootParameters.emplace_back(rootParameter);
168             }
169         }
170 
171         // Make sure that we added exactly the number of elements we expected. If we added more,
172         // |ranges| will have resized and the pointers in the |rootParameter|s will be invalid.
173         ASSERT(rangeIndex == rangesCount);
174 
175         D3D12_ROOT_PARAMETER renderOrComputeInternalConstants{};
176         renderOrComputeInternalConstants.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
177         renderOrComputeInternalConstants.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
178         // Always allocate 3 constants for either:
179         //  - vertex_index and instance_index
180         //  - num_workgroups_x, num_workgroups_y and num_workgroups_z
181         // NOTE: We should consider delaying root signature creation until we know how many values
182         // we need
183         renderOrComputeInternalConstants.Constants.Num32BitValues = 3;
184         renderOrComputeInternalConstants.Constants.RegisterSpace =
185             kRenderOrComputeInternalRegisterSpace;
186         renderOrComputeInternalConstants.Constants.ShaderRegister =
187             kRenderOrComputeInternalBaseRegister;
188         mFirstIndexOffsetParameterIndex = rootParameters.size();
189         mNumWorkgroupsParameterIndex = rootParameters.size();
190         // NOTE: We should consider moving this entry to earlier in the root signature since offsets
191         // would need to be updated often
192         rootParameters.emplace_back(renderOrComputeInternalConstants);
193 
194         // Loops over all of the dynamic storage buffer bindings in the layout and build
195         // a mapping from the binding to the next offset into the root constant array where
196         // that dynamic storage buffer's binding size will be stored. The next register offset
197         // to use is tracked with |dynamicStorageBufferLengthsShaderRegisterOffset|.
198         // This data will be used by shader translation to emit a load from the root constant
199         // array to use as the binding's size in runtime array calculations.
200         // Each bind group's length data is stored contiguously in the root constant array,
201         // so the loop also computes the first register offset for each group where the
202         // data should start.
203         uint32_t dynamicStorageBufferLengthsShaderRegisterOffset = 0;
204         for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) {
205             const BindGroupLayoutBase* bgl = GetBindGroupLayout(group);
206 
207             mDynamicStorageBufferLengthInfo[group].firstRegisterOffset =
208                 dynamicStorageBufferLengthsShaderRegisterOffset;
209             mDynamicStorageBufferLengthInfo[group].bindingAndRegisterOffsets.reserve(
210                 bgl->GetBindingCountInfo().dynamicStorageBufferCount);
211 
212             for (BindingIndex bindingIndex(0); bindingIndex < bgl->GetDynamicBufferCount();
213                  ++bindingIndex) {
214                 if (bgl->IsStorageBufferBinding(bindingIndex)) {
215                     mDynamicStorageBufferLengthInfo[group].bindingAndRegisterOffsets.push_back(
216                         {bgl->GetBindingInfo(bindingIndex).binding,
217                          dynamicStorageBufferLengthsShaderRegisterOffset++});
218                 }
219             }
220 
221             ASSERT(mDynamicStorageBufferLengthInfo[group].bindingAndRegisterOffsets.size() ==
222                    bgl->GetBindingCountInfo().dynamicStorageBufferCount);
223         }
224         ASSERT(dynamicStorageBufferLengthsShaderRegisterOffset <=
225                kMaxDynamicStorageBuffersPerPipelineLayout);
226 
227         D3D12_ROOT_PARAMETER dynamicStorageBufferLengthConstants{};
228         dynamicStorageBufferLengthConstants.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
229         dynamicStorageBufferLengthConstants.ParameterType =
230             D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
231         dynamicStorageBufferLengthConstants.Constants.Num32BitValues =
232             dynamicStorageBufferLengthsShaderRegisterOffset;
233         dynamicStorageBufferLengthConstants.Constants.RegisterSpace =
234             kDynamicStorageBufferLengthsRegisterSpace;
235         dynamicStorageBufferLengthConstants.Constants.ShaderRegister =
236             kDynamicStorageBufferLengthsBaseRegister;
237         mDynamicStorageBufferLengthsParameterIndex = rootParameters.size();
238         rootParameters.emplace_back(dynamicStorageBufferLengthConstants);
239 
240         D3D12_ROOT_SIGNATURE_DESC rootSignatureDescriptor;
241         rootSignatureDescriptor.NumParameters = rootParameters.size();
242         rootSignatureDescriptor.pParameters = rootParameters.data();
243         rootSignatureDescriptor.NumStaticSamplers = 0;
244         rootSignatureDescriptor.pStaticSamplers = nullptr;
245         rootSignatureDescriptor.Flags =
246             D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
247 
248         ComPtr<ID3DBlob> signature;
249         ComPtr<ID3DBlob> error;
250         HRESULT hr = device->GetFunctions()->d3d12SerializeRootSignature(
251             &rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
252         if (DAWN_UNLIKELY(FAILED(hr))) {
253             std::ostringstream messageStream;
254             if (error) {
255                 messageStream << static_cast<const char*>(error->GetBufferPointer());
256 
257                 // |error| is observed to always end with a \n, but is not
258                 // specified to do so, so we add an extra newline just in case.
259                 messageStream << std::endl;
260             }
261             messageStream << "D3D12 serialize root signature";
262             DAWN_TRY(CheckHRESULT(hr, messageStream.str().c_str()));
263         }
264         DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateRootSignature(
265                                   0, signature->GetBufferPointer(), signature->GetBufferSize(),
266                                   IID_PPV_ARGS(&mRootSignature)),
267                               "D3D12 create root signature"));
268         return {};
269     }
270 
GetCbvUavSrvRootParameterIndex(BindGroupIndex group) const271     uint32_t PipelineLayout::GetCbvUavSrvRootParameterIndex(BindGroupIndex group) const {
272         ASSERT(group < kMaxBindGroupsTyped);
273         return mCbvUavSrvRootParameterInfo[group];
274     }
275 
GetSamplerRootParameterIndex(BindGroupIndex group) const276     uint32_t PipelineLayout::GetSamplerRootParameterIndex(BindGroupIndex group) const {
277         ASSERT(group < kMaxBindGroupsTyped);
278         return mSamplerRootParameterInfo[group];
279     }
280 
GetRootSignature() const281     ID3D12RootSignature* PipelineLayout::GetRootSignature() const {
282         return mRootSignature.Get();
283     }
284 
285     const PipelineLayout::DynamicStorageBufferLengthInfo&
GetDynamicStorageBufferLengthInfo() const286     PipelineLayout::GetDynamicStorageBufferLengthInfo() const {
287         return mDynamicStorageBufferLengthInfo;
288     }
289 
GetDynamicRootParameterIndex(BindGroupIndex group,BindingIndex bindingIndex) const290     uint32_t PipelineLayout::GetDynamicRootParameterIndex(BindGroupIndex group,
291                                                           BindingIndex bindingIndex) const {
292         ASSERT(group < kMaxBindGroupsTyped);
293         ASSERT(bindingIndex < kMaxDynamicBuffersPerPipelineLayoutTyped);
294         ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).buffer.hasDynamicOffset);
295         ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).visibility !=
296                wgpu::ShaderStage::None);
297         return mDynamicRootParameterIndices[group][bindingIndex];
298     }
299 
GetFirstIndexOffsetRegisterSpace() const300     uint32_t PipelineLayout::GetFirstIndexOffsetRegisterSpace() const {
301         return kRenderOrComputeInternalRegisterSpace;
302     }
303 
GetFirstIndexOffsetShaderRegister() const304     uint32_t PipelineLayout::GetFirstIndexOffsetShaderRegister() const {
305         return kRenderOrComputeInternalBaseRegister;
306     }
307 
GetFirstIndexOffsetParameterIndex() const308     uint32_t PipelineLayout::GetFirstIndexOffsetParameterIndex() const {
309         return mFirstIndexOffsetParameterIndex;
310     }
311 
GetNumWorkgroupsRegisterSpace() const312     uint32_t PipelineLayout::GetNumWorkgroupsRegisterSpace() const {
313         return kRenderOrComputeInternalRegisterSpace;
314     }
315 
GetNumWorkgroupsShaderRegister() const316     uint32_t PipelineLayout::GetNumWorkgroupsShaderRegister() const {
317         return kRenderOrComputeInternalBaseRegister;
318     }
319 
GetNumWorkgroupsParameterIndex() const320     uint32_t PipelineLayout::GetNumWorkgroupsParameterIndex() const {
321         return mNumWorkgroupsParameterIndex;
322     }
323 
GetDynamicStorageBufferLengthsRegisterSpace() const324     uint32_t PipelineLayout::GetDynamicStorageBufferLengthsRegisterSpace() const {
325         return kDynamicStorageBufferLengthsRegisterSpace;
326     }
327 
GetDynamicStorageBufferLengthsShaderRegister() const328     uint32_t PipelineLayout::GetDynamicStorageBufferLengthsShaderRegister() const {
329         return kDynamicStorageBufferLengthsBaseRegister;
330     }
331 
GetDynamicStorageBufferLengthsParameterIndex() const332     uint32_t PipelineLayout::GetDynamicStorageBufferLengthsParameterIndex() const {
333         return mDynamicStorageBufferLengthsParameterIndex;
334     }
335 
GetDispatchIndirectCommandSignatureWithNumWorkgroups()336     ID3D12CommandSignature* PipelineLayout::GetDispatchIndirectCommandSignatureWithNumWorkgroups() {
337         // mDispatchIndirectCommandSignatureWithNumWorkgroups won't be created until it is needed.
338         if (mDispatchIndirectCommandSignatureWithNumWorkgroups.Get() != nullptr) {
339             return mDispatchIndirectCommandSignatureWithNumWorkgroups.Get();
340         }
341 
342         D3D12_INDIRECT_ARGUMENT_DESC argumentDescs[2] = {};
343         argumentDescs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT;
344         argumentDescs[0].Constant.RootParameterIndex = GetNumWorkgroupsParameterIndex();
345         argumentDescs[0].Constant.Num32BitValuesToSet = 3;
346         argumentDescs[0].Constant.DestOffsetIn32BitValues = 0;
347 
348         // A command signature must contain exactly 1 Draw / Dispatch / DispatchMesh / DispatchRays
349         // command. That command must come last.
350         argumentDescs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
351 
352         D3D12_COMMAND_SIGNATURE_DESC programDesc = {};
353         programDesc.ByteStride = 6 * sizeof(uint32_t);
354         programDesc.NumArgumentDescs = 2;
355         programDesc.pArgumentDescs = argumentDescs;
356 
357         // The root signature must be specified if and only if the command signature changes one of
358         // the root arguments.
359         ToBackend(GetDevice())
360             ->GetD3D12Device()
361             ->CreateCommandSignature(
362                 &programDesc, GetRootSignature(),
363                 IID_PPV_ARGS(&mDispatchIndirectCommandSignatureWithNumWorkgroups));
364         return mDispatchIndirectCommandSignatureWithNumWorkgroups.Get();
365     }
366 
367 }}  // namespace dawn_native::d3d12
368