1 // Copyright 2019 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/AdapterD3D12.h" 16 17 #include "common/Constants.h" 18 #include "common/WindowsUtils.h" 19 #include "dawn_native/Instance.h" 20 #include "dawn_native/d3d12/BackendD3D12.h" 21 #include "dawn_native/d3d12/D3D12Error.h" 22 #include "dawn_native/d3d12/DeviceD3D12.h" 23 #include "dawn_native/d3d12/PlatformFunctions.h" 24 25 #include <sstream> 26 27 namespace dawn_native { namespace d3d12 { 28 Adapter(Backend * backend,ComPtr<IDXGIAdapter3> hardwareAdapter)29 Adapter::Adapter(Backend* backend, ComPtr<IDXGIAdapter3> hardwareAdapter) 30 : AdapterBase(backend->GetInstance(), wgpu::BackendType::D3D12), 31 mHardwareAdapter(hardwareAdapter), 32 mBackend(backend) { 33 } 34 ~Adapter()35 Adapter::~Adapter() { 36 CleanUpDebugLayerFilters(); 37 } 38 SupportsExternalImages() const39 bool Adapter::SupportsExternalImages() const { 40 // Via dawn_native::d3d12::ExternalImageDXGI::Create 41 return true; 42 } 43 GetDeviceInfo() const44 const D3D12DeviceInfo& Adapter::GetDeviceInfo() const { 45 return mDeviceInfo; 46 } 47 GetHardwareAdapter() const48 IDXGIAdapter3* Adapter::GetHardwareAdapter() const { 49 return mHardwareAdapter.Get(); 50 } 51 GetBackend() const52 Backend* Adapter::GetBackend() const { 53 return mBackend; 54 } 55 GetDevice() const56 ComPtr<ID3D12Device> Adapter::GetDevice() const { 57 return mD3d12Device; 58 } 59 GetDriverVersion() const60 const gpu_info::D3DDriverVersion& Adapter::GetDriverVersion() const { 61 return mDriverVersion; 62 } 63 InitializeImpl()64 MaybeError Adapter::InitializeImpl() { 65 // D3D12 cannot check for feature support without a device. 66 // Create the device to populate the adapter properties then reuse it when needed for actual 67 // rendering. 68 const PlatformFunctions* functions = GetBackend()->GetFunctions(); 69 if (FAILED(functions->d3d12CreateDevice(GetHardwareAdapter(), D3D_FEATURE_LEVEL_11_0, 70 _uuidof(ID3D12Device), &mD3d12Device))) { 71 return DAWN_INTERNAL_ERROR("D3D12CreateDevice failed"); 72 } 73 74 DAWN_TRY(InitializeDebugLayerFilters()); 75 76 DXGI_ADAPTER_DESC1 adapterDesc; 77 mHardwareAdapter->GetDesc1(&adapterDesc); 78 79 mPCIInfo.deviceId = adapterDesc.DeviceId; 80 mPCIInfo.vendorId = adapterDesc.VendorId; 81 mPCIInfo.name = WCharToUTF8(adapterDesc.Description); 82 83 DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this)); 84 85 if (adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { 86 mAdapterType = wgpu::AdapterType::CPU; 87 } else { 88 mAdapterType = (mDeviceInfo.isUMA) ? wgpu::AdapterType::IntegratedGPU 89 : wgpu::AdapterType::DiscreteGPU; 90 } 91 92 // Convert the adapter's D3D12 driver version to a readable string like "24.21.13.9793". 93 LARGE_INTEGER umdVersion; 94 if (mHardwareAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &umdVersion) != 95 DXGI_ERROR_UNSUPPORTED) { 96 uint64_t encodedVersion = umdVersion.QuadPart; 97 98 std::ostringstream o; 99 o << "D3D12 driver version "; 100 for (size_t i = 0; i < mDriverVersion.size(); ++i) { 101 mDriverVersion[i] = (encodedVersion >> (48 - 16 * i)) & 0xFFFF; 102 o << mDriverVersion[i] << "."; 103 } 104 mDriverDescription = o.str(); 105 } 106 107 return {}; 108 } 109 AreTimestampQueriesSupported() const110 bool Adapter::AreTimestampQueriesSupported() const { 111 D3D12_COMMAND_QUEUE_DESC queueDesc = {}; 112 queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 113 queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 114 ComPtr<ID3D12CommandQueue> d3d12CommandQueue; 115 HRESULT hr = mD3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&d3d12CommandQueue)); 116 if (FAILED(hr)) { 117 return false; 118 } 119 120 // GetTimestampFrequency returns an error HRESULT when there are bugs in Windows container 121 // and vGPU implementations. 122 uint64_t timeStampFrequency; 123 hr = d3d12CommandQueue->GetTimestampFrequency(&timeStampFrequency); 124 if (FAILED(hr)) { 125 return false; 126 } 127 128 return true; 129 } 130 InitializeSupportedFeaturesImpl()131 MaybeError Adapter::InitializeSupportedFeaturesImpl() { 132 if (AreTimestampQueriesSupported()) { 133 mSupportedFeatures.EnableFeature(Feature::TimestampQuery); 134 } 135 mSupportedFeatures.EnableFeature(Feature::TextureCompressionBC); 136 mSupportedFeatures.EnableFeature(Feature::PipelineStatisticsQuery); 137 mSupportedFeatures.EnableFeature(Feature::MultiPlanarFormats); 138 return {}; 139 } 140 InitializeSupportedLimitsImpl(CombinedLimits * limits)141 MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) { 142 D3D12_FEATURE_DATA_D3D12_OPTIONS featureData = {}; 143 144 DAWN_TRY(CheckHRESULT(mD3d12Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, 145 &featureData, sizeof(featureData)), 146 "CheckFeatureSupport D3D12_FEATURE_D3D12_OPTIONS")); 147 148 // Check if the device is at least D3D_FEATURE_LEVEL_11_1 or D3D_FEATURE_LEVEL_11_0 149 const D3D_FEATURE_LEVEL levelsToQuery[]{D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0}; 150 151 D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevels; 152 featureLevels.NumFeatureLevels = sizeof(levelsToQuery) / sizeof(D3D_FEATURE_LEVEL); 153 featureLevels.pFeatureLevelsRequested = levelsToQuery; 154 DAWN_TRY( 155 CheckHRESULT(mD3d12Device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, 156 &featureLevels, sizeof(featureLevels)), 157 "CheckFeatureSupport D3D12_FEATURE_FEATURE_LEVELS")); 158 159 if (featureLevels.MaxSupportedFeatureLevel == D3D_FEATURE_LEVEL_11_0 && 160 featureData.ResourceBindingTier < D3D12_RESOURCE_BINDING_TIER_2) { 161 return DAWN_VALIDATION_ERROR( 162 "At least Resource Binding Tier 2 is required for D3D12 Feature Level 11.0 " 163 "devices."); 164 } 165 166 GetDefaultLimits(&limits->v1); 167 168 // https://docs.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels 169 170 // Limits that are the same across D3D feature levels 171 limits->v1.maxTextureDimension1D = D3D12_REQ_TEXTURE1D_U_DIMENSION; 172 limits->v1.maxTextureDimension2D = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; 173 limits->v1.maxTextureDimension3D = D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION; 174 limits->v1.maxTextureArrayLayers = D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION; 175 // Slot values can be 0-15, inclusive: 176 // https://docs.microsoft.com/en-ca/windows/win32/api/d3d12/ns-d3d12-d3d12_input_element_desc 177 limits->v1.maxVertexBuffers = 16; 178 limits->v1.maxVertexAttributes = D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT; 179 180 // Note: WebGPU requires FL11.1+ 181 // https://docs.microsoft.com/en-us/windows/win32/direct3d12/hardware-support 182 // Resource Binding Tier: 1 2 3 183 184 // Max(CBV+UAV+SRV) 1M 1M 1M+ 185 // Max CBV per stage 14 14 full 186 // Max SRV per stage 128 full full 187 // Max UAV in all stages 64 64 full 188 // Max Samplers per stage 16 2048 2048 189 190 // https://docs.microsoft.com/en-us/windows-hardware/test/hlk/testref/efad06e8-51d1-40ce-ad5c-573a134b4bb6 191 // "full" means the full heap can be used. This is tested 192 // to work for 1 million descriptors, and 1.1M for tier 3. 193 uint32_t maxCBVsPerStage; 194 uint32_t maxSRVsPerStage; 195 uint32_t maxUAVsAllStages; 196 uint32_t maxSamplersPerStage; 197 switch (featureData.ResourceBindingTier) { 198 case D3D12_RESOURCE_BINDING_TIER_1: 199 maxCBVsPerStage = 14; 200 maxSRVsPerStage = 128; 201 maxUAVsAllStages = 64; 202 maxSamplersPerStage = 16; 203 break; 204 case D3D12_RESOURCE_BINDING_TIER_2: 205 maxCBVsPerStage = 14; 206 maxSRVsPerStage = 1'000'000; 207 maxUAVsAllStages = 64; 208 maxSamplersPerStage = 2048; 209 break; 210 case D3D12_RESOURCE_BINDING_TIER_3: 211 default: 212 maxCBVsPerStage = 1'100'000; 213 maxSRVsPerStage = 1'100'000; 214 maxUAVsAllStages = 1'100'000; 215 maxSamplersPerStage = 2048; 216 break; 217 } 218 219 ASSERT(maxUAVsAllStages / 4 > limits->v1.maxStorageTexturesPerShaderStage); 220 ASSERT(maxUAVsAllStages / 4 > limits->v1.maxStorageBuffersPerShaderStage); 221 uint32_t maxUAVsPerStage = maxUAVsAllStages / 2; 222 223 limits->v1.maxUniformBuffersPerShaderStage = maxCBVsPerStage; 224 // Allocate half of the UAVs to storage buffers, and half to storage textures. 225 limits->v1.maxStorageTexturesPerShaderStage = maxUAVsPerStage / 2; 226 limits->v1.maxStorageBuffersPerShaderStage = maxUAVsPerStage - maxUAVsPerStage / 2; 227 limits->v1.maxSampledTexturesPerShaderStage = maxSRVsPerStage; 228 limits->v1.maxSamplersPerShaderStage = maxSamplersPerStage; 229 230 // https://docs.microsoft.com/en-us/windows/win32/direct3d12/root-signature-limits 231 // In DWORDS. Descriptor tables cost 1, Root constants cost 1, Root descriptors cost 2. 232 static constexpr uint32_t kMaxRootSignatureSize = 64u; 233 // Dawn maps WebGPU's binding model by: 234 // - (maxBindGroups) 235 // CBVs/UAVs/SRVs for bind group are a root descriptor table 236 // - (maxBindGroups) 237 // Samplers for each bind group are a root descriptor table 238 // - (2 * maxDynamicBuffers) 239 // Each dynamic buffer is a root descriptor 240 // RESERVED: 241 // - 3 = max of: 242 // - 2 root constants for the baseVertex/baseInstance constants. 243 // - 3 root constants for num workgroups X, Y, Z 244 // - 4 root constants (kMaxDynamicStorageBuffersPerPipelineLayout) for dynamic storage 245 // buffer lengths. 246 static constexpr uint32_t kReservedSlots = 7; 247 248 // Available slots after base limits considered. 249 uint32_t availableRootSignatureSlots = 250 kMaxRootSignatureSize - kReservedSlots - 251 2 * (limits->v1.maxBindGroups + limits->v1.maxDynamicUniformBuffersPerPipelineLayout + 252 limits->v1.maxDynamicStorageBuffersPerPipelineLayout); 253 254 // Because we need either: 255 // - 1 cbv/uav/srv table + 1 sampler table 256 // - 2 slots for a root descriptor 257 uint32_t availableDynamicBufferOrBindGroup = availableRootSignatureSlots / 2; 258 259 // We can either have a bind group, a dyn uniform buffer or a dyn storage buffer. 260 // Distribute evenly. 261 limits->v1.maxBindGroups += availableDynamicBufferOrBindGroup / 3; 262 limits->v1.maxDynamicUniformBuffersPerPipelineLayout += 263 availableDynamicBufferOrBindGroup / 3; 264 limits->v1.maxDynamicStorageBuffersPerPipelineLayout += 265 (availableDynamicBufferOrBindGroup - 2 * (availableDynamicBufferOrBindGroup / 3)); 266 267 ASSERT(2 * (limits->v1.maxBindGroups + 268 limits->v1.maxDynamicUniformBuffersPerPipelineLayout + 269 limits->v1.maxDynamicStorageBuffersPerPipelineLayout) <= 270 kMaxRootSignatureSize - kReservedSlots); 271 272 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-numthreads 273 limits->v1.maxComputeWorkgroupSizeX = D3D12_CS_THREAD_GROUP_MAX_X; 274 limits->v1.maxComputeWorkgroupSizeY = D3D12_CS_THREAD_GROUP_MAX_Y; 275 limits->v1.maxComputeWorkgroupSizeZ = D3D12_CS_THREAD_GROUP_MAX_Z; 276 limits->v1.maxComputeInvocationsPerWorkgroup = D3D12_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP; 277 278 // https://docs.maxComputeWorkgroupSizeXmicrosoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_dispatch_arguments 279 limits->v1.maxComputeWorkgroupsPerDimension = 280 D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; 281 282 // https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-downlevel-compute-shaders 283 // Thread Group Shared Memory is limited to 16Kb on downlevel hardware. This is less than 284 // the 32Kb that is available to Direct3D 11 hardware. D3D12 is also 32kb. 285 limits->v1.maxComputeWorkgroupStorageSize = 32768; 286 287 // Max number of "constants" where each constant is a 16-byte float4 288 limits->v1.maxUniformBufferBindingSize = D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16; 289 // D3D12 has no documented limit on the size of a storage buffer binding. 290 limits->v1.maxStorageBufferBindingSize = 4294967295; 291 292 // TODO(crbug.com/dawn/685): 293 // LIMITS NOT SET: 294 // - maxInterStageShaderComponents 295 // - maxVertexBufferArrayStride 296 297 return {}; 298 } 299 InitializeDebugLayerFilters()300 MaybeError Adapter::InitializeDebugLayerFilters() { 301 if (!GetInstance()->IsBackendValidationEnabled()) { 302 return {}; 303 } 304 305 D3D12_MESSAGE_ID denyIds[] = { 306 307 // 308 // Permanent IDs: list of warnings that are not applicable 309 // 310 311 // Resource sub-allocation partially maps pre-allocated heaps. This means the 312 // entire physical addresses space may have no resources or have many resources 313 // assigned the same heap. 314 D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_HAS_NO_RESOURCE, 315 D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_INTERSECTS_MULTIPLE_BUFFERS, 316 317 // The debug layer validates pipeline objects when they are created. Dawn validates 318 // them when them when they are set. Therefore, since the issue is caught at a later 319 // time, we can silence this warnings. 320 D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET, 321 322 // Adding a clear color during resource creation would require heuristics or delayed 323 // creation. 324 // https://crbug.com/dawn/418 325 D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, 326 D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE, 327 328 // Dawn enforces proper Unmaps at a later time. 329 // https://crbug.com/dawn/422 330 D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_GPU_WRITTEN_READBACK_RESOURCE_MAPPED, 331 332 // WebGPU allows empty scissors without empty viewports. 333 D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE, 334 335 // 336 // Temporary IDs: list of warnings that should be fixed or promoted 337 // 338 339 // Remove after warning have been addressed 340 // https://crbug.com/dawn/421 341 D3D12_MESSAGE_ID_GPU_BASED_VALIDATION_INCOMPATIBLE_RESOURCE_STATE, 342 343 // For small placed resource alignment, we first request the small alignment, which may 344 // get rejected and generate a debug error. Then, we request 0 to get the allowed 345 // allowed alignment. 346 D3D12_MESSAGE_ID_CREATERESOURCE_INVALIDALIGNMENT, 347 }; 348 349 // Create a retrieval filter with a deny list to suppress messages. 350 // Any messages remaining will be converted to Dawn errors. 351 D3D12_INFO_QUEUE_FILTER filter{}; 352 // Filter out info/message and only create errors from warnings or worse. 353 D3D12_MESSAGE_SEVERITY severities[] = { 354 D3D12_MESSAGE_SEVERITY_INFO, 355 D3D12_MESSAGE_SEVERITY_MESSAGE, 356 }; 357 filter.DenyList.NumSeverities = ARRAYSIZE(severities); 358 filter.DenyList.pSeverityList = severities; 359 filter.DenyList.NumIDs = ARRAYSIZE(denyIds); 360 filter.DenyList.pIDList = denyIds; 361 362 ComPtr<ID3D12InfoQueue> infoQueue; 363 DAWN_TRY(CheckHRESULT(mD3d12Device.As(&infoQueue), 364 "D3D12 QueryInterface ID3D12Device to ID3D12InfoQueue")); 365 366 // To avoid flooding the console, a storage-filter is also used to 367 // prevent messages from getting logged. 368 DAWN_TRY(CheckHRESULT(infoQueue->PushStorageFilter(&filter), 369 "ID3D12InfoQueue::PushStorageFilter")); 370 371 DAWN_TRY(CheckHRESULT(infoQueue->PushRetrievalFilter(&filter), 372 "ID3D12InfoQueue::PushRetrievalFilter")); 373 374 return {}; 375 } 376 CleanUpDebugLayerFilters()377 void Adapter::CleanUpDebugLayerFilters() { 378 if (!GetInstance()->IsBackendValidationEnabled()) { 379 return; 380 } 381 382 // The device may not exist if this adapter failed to initialize. 383 if (mD3d12Device == nullptr) { 384 return; 385 } 386 387 // If the debug layer is not installed, return immediately to avoid crashing the process. 388 ComPtr<ID3D12InfoQueue> infoQueue; 389 if (FAILED(mD3d12Device.As(&infoQueue))) { 390 return; 391 } 392 393 infoQueue->PopRetrievalFilter(); 394 infoQueue->PopStorageFilter(); 395 } 396 CreateDeviceImpl(const DawnDeviceDescriptor * descriptor)397 ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl(const DawnDeviceDescriptor* descriptor) { 398 return Device::Create(this, descriptor); 399 } 400 401 // Resets the backend device and creates a new one. If any D3D12 objects belonging to the 402 // current ID3D12Device have not been destroyed, a non-zero value will be returned upon Reset() 403 // and the subequent call to CreateDevice will return a handle the existing device instead of 404 // creating a new one. ResetInternalDeviceForTestingImpl()405 MaybeError Adapter::ResetInternalDeviceForTestingImpl() { 406 ASSERT(mD3d12Device.Reset() == 0); 407 DAWN_TRY(Initialize()); 408 409 return {}; 410 } 411 412 }} // namespace dawn_native::d3d12 413