text: // 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 <sstream>
  
  namespace dawn_native { namespace d3d12 {
  
      Adapter::Adapter(Backend* backend, ComPtr<IDXGIAdapter3> 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<ID3D12Device> 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 {};
      }
  
      void Adapter::InitializeSupportedExtensions() {
          mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC);
          mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery);
          mSupportedExtensions.EnableExtension(Extension::TimestampQuery);
          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<ID3D12InfoQueue> 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<ID3D12InfoQueue> infoQueue;
          if (FAILED(mD3d12Device.As(&infoQueue))) {
              return;
          }
  
          infoQueue->PopRetrievalFilter();
          infoQueue->PopStorageFilter();
      }
  
      ResultOrError<DeviceBase*> 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
  