// Copyright 2019 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dawn_native/d3d12/AdapterD3D12.h" #include "common/Constants.h" #include "common/WindowsUtils.h" #include "dawn_native/Instance.h" #include "dawn_native/d3d12/BackendD3D12.h" #include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" #include namespace dawn_native { namespace d3d12 { Adapter::Adapter(Backend* backend, ComPtr hardwareAdapter) : AdapterBase(backend->GetInstance(), wgpu::BackendType::D3D12), mHardwareAdapter(hardwareAdapter), mBackend(backend) { } Adapter::~Adapter() { CleanUpDebugLayerFilters(); } bool Adapter::SupportsExternalImages() const { // Via dawn_native::d3d12::ExternalImageDXGI::Create return true; } const D3D12DeviceInfo& Adapter::GetDeviceInfo() const { return mDeviceInfo; } IDXGIAdapter3* Adapter::GetHardwareAdapter() const { return mHardwareAdapter.Get(); } Backend* Adapter::GetBackend() const { return mBackend; } ComPtr Adapter::GetDevice() const { return mD3d12Device; } const gpu_info::D3DDriverVersion& Adapter::GetDriverVersion() const { return mDriverVersion; } MaybeError Adapter::Initialize() { // D3D12 cannot check for feature support without a device. // Create the device to populate the adapter properties then reuse it when needed for actual // rendering. const PlatformFunctions* functions = GetBackend()->GetFunctions(); if (FAILED(functions->d3d12CreateDevice(GetHardwareAdapter(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), &mD3d12Device))) { return DAWN_INTERNAL_ERROR("D3D12CreateDevice failed"); } DAWN_TRY(InitializeDebugLayerFilters()); DXGI_ADAPTER_DESC1 adapterDesc; mHardwareAdapter->GetDesc1(&adapterDesc); mPCIInfo.deviceId = adapterDesc.DeviceId; mPCIInfo.vendorId = adapterDesc.VendorId; mPCIInfo.name = WCharToUTF8(adapterDesc.Description); DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this)); if (adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { mAdapterType = wgpu::AdapterType::CPU; } else { mAdapterType = (mDeviceInfo.isUMA) ? wgpu::AdapterType::IntegratedGPU : wgpu::AdapterType::DiscreteGPU; } // Convert the adapter's D3D12 driver version to a readable string like "24.21.13.9793". LARGE_INTEGER umdVersion; if (mHardwareAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &umdVersion) != DXGI_ERROR_UNSUPPORTED) { uint64_t encodedVersion = umdVersion.QuadPart; std::ostringstream o; o << "D3D12 driver version "; for (size_t i = 0; i < mDriverVersion.size(); ++i) { mDriverVersion[i] = (encodedVersion >> (48 - 16 * i)) & 0xFFFF; o << mDriverVersion[i] << "."; } mDriverDescription = o.str(); } InitializeSupportedExtensions(); return {}; } bool Adapter::AreTimestampQueriesSupported() const { D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; ComPtr d3d12CommandQueue; HRESULT hr = mD3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&d3d12CommandQueue)); if (FAILED(hr)) { return false; } // GetTimestampFrequency returns an error HRESULT when there are bugs in Windows container // and vGPU implementations. uint64_t timeStampFrequency; hr = d3d12CommandQueue->GetTimestampFrequency(&timeStampFrequency); if (FAILED(hr)) { return false; } return true; } void Adapter::InitializeSupportedExtensions() { if (AreTimestampQueriesSupported()) { mSupportedExtensions.EnableExtension(Extension::TimestampQuery); } mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC); mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery); mSupportedExtensions.EnableExtension(Extension::MultiPlanarFormats); } MaybeError Adapter::InitializeDebugLayerFilters() { if (!GetInstance()->IsBackendValidationEnabled()) { return {}; } D3D12_MESSAGE_ID denyIds[] = { // // Permanent IDs: list of warnings that are not applicable // // Resource sub-allocation partially maps pre-allocated heaps. This means the // entire physical addresses space may have no resources or have many resources // assigned the same heap. D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_HAS_NO_RESOURCE, D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_INTERSECTS_MULTIPLE_BUFFERS, // The debug layer validates pipeline objects when they are created. Dawn validates // them when them when they are set. Therefore, since the issue is caught at a later // time, we can silence this warnings. D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET, // Adding a clear color during resource creation would require heuristics or delayed // creation. // https://crbug.com/dawn/418 D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE, // Dawn enforces proper Unmaps at a later time. // https://crbug.com/dawn/422 D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_GPU_WRITTEN_READBACK_RESOURCE_MAPPED, // WebGPU allows empty scissors without empty viewports. D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE, // // Temporary IDs: list of warnings that should be fixed or promoted // // Remove after warning have been addressed // https://crbug.com/dawn/421 D3D12_MESSAGE_ID_GPU_BASED_VALIDATION_INCOMPATIBLE_RESOURCE_STATE, // For small placed resource alignment, we first request the small alignment, which may // get rejected and generate a debug error. Then, we request 0 to get the allowed // allowed alignment. D3D12_MESSAGE_ID_CREATERESOURCE_INVALIDALIGNMENT, }; // Create a retrieval filter with a deny list to suppress messages. // Any messages remaining will be converted to Dawn errors. D3D12_INFO_QUEUE_FILTER filter{}; // Filter out info/message and only create errors from warnings or worse. D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO, D3D12_MESSAGE_SEVERITY_MESSAGE, }; filter.DenyList.NumSeverities = ARRAYSIZE(severities); filter.DenyList.pSeverityList = severities; filter.DenyList.NumIDs = ARRAYSIZE(denyIds); filter.DenyList.pIDList = denyIds; ComPtr infoQueue; DAWN_TRY(CheckHRESULT(mD3d12Device.As(&infoQueue), "D3D12 QueryInterface ID3D12Device to ID3D12InfoQueue")); // To avoid flooding the console, a storage-filter is also used to // prevent messages from getting logged. DAWN_TRY(CheckHRESULT(infoQueue->PushStorageFilter(&filter), "ID3D12InfoQueue::PushStorageFilter")); DAWN_TRY(CheckHRESULT(infoQueue->PushRetrievalFilter(&filter), "ID3D12InfoQueue::PushRetrievalFilter")); return {}; } void Adapter::CleanUpDebugLayerFilters() { if (!GetInstance()->IsBackendValidationEnabled()) { return; } // The device may not exist if this adapter failed to initialize. if (mD3d12Device == nullptr) { return; } // If the debug layer is not installed, return immediately to avoid crashing the process. ComPtr infoQueue; if (FAILED(mD3d12Device.As(&infoQueue))) { return; } infoQueue->PopRetrievalFilter(); infoQueue->PopStorageFilter(); } ResultOrError Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) { return Device::Create(this, descriptor); } // Resets the backend device and creates a new one. If any D3D12 objects belonging to the // current ID3D12Device have not been destroyed, a non-zero value will be returned upon Reset() // and the subequent call to CreateDevice will return a handle the existing device instead of // creating a new one. MaybeError Adapter::ResetInternalDeviceForTestingImpl() { ASSERT(mD3d12Device.Reset() == 0); DAWN_TRY(Initialize()); return {}; } }} // namespace dawn_native::d3d12