• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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