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