// Copyright (c) Wojciech Figat. All rights reserved. #if GRAPHICS_API_DIRECTX12 #include "GPUDeviceDX12.h" #include "GPUShaderDX12.h" #include "GPUContextDX12.h" #include "GPUPipelineStateDX12.h" #include "GPUTextureDX12.h" #include "GPUTimerQueryDX12.h" #include "GPUBufferDX12.h" #include "GPUSamplerDX12.h" #include "GPUVertexLayoutDX12.h" #include "GPUSwapChainDX12.h" #include "RootSignatureDX12.h" #include "UploadBufferDX12.h" #include "CommandQueueDX12.h" #include "Engine/Engine/Engine.h" #include "Engine/Engine/CommandLine.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Core/Log.h" #include "Engine/Core/Config/PlatformSettings.h" #include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Utilities.h" #include "Engine/Threading/Threading.h" #include "CommandSignatureDX12.h" static bool CheckDX12Support(IDXGIAdapter* adapter) { #if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE return true; #else // Try to create device if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { return true; } return false; #endif } GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements, bool explicitOffsets) : GPUResourceDX12(device, StringView::Empty) , InputElementsCount(elements.Count()) { SetElements(elements, explicitOffsets); for (int32 i = 0; i < elements.Count(); i++) { const VertexElement& src = GetElements().Get()[i]; D3D12_INPUT_ELEMENT_DESC& dst = InputElements[i]; dst.SemanticName = RenderToolsDX::GetVertexInputSemantic(src.Type, dst.SemanticIndex); dst.Format = RenderToolsDX::ToDxgiFormat(src.Format); dst.InputSlot = src.Slot; dst.AlignedByteOffset = src.Offset; dst.InputSlotClass = src.PerInstance ? D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA : D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; dst.InstanceDataStepRate = src.PerInstance ? 1 : 0; } } RootSignatureDX12::RootSignatureDX12() { // Clear structures Platform::MemoryClear(this, sizeof(*this)); // Descriptor tables { // SRVs D3D12_DESCRIPTOR_RANGE& range = _ranges[0]; range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; range.NumDescriptors = GPU_MAX_SR_BINDED; range.BaseShaderRegister = 0; range.RegisterSpace = 0; range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } { // UAVs D3D12_DESCRIPTOR_RANGE& range = _ranges[1]; range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; range.NumDescriptors = GPU_MAX_UA_BINDED; range.BaseShaderRegister = 0; range.RegisterSpace = 0; range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } { // Samplers D3D12_DESCRIPTOR_RANGE& range = _ranges[2]; range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT; range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT; range.RegisterSpace = 0; range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } // Root parameters for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++) { // CBs D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_CB + i]; param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; param.Descriptor.ShaderRegister = i; param.Descriptor.RegisterSpace = 0; } { // SRVs D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SR]; param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; param.DescriptorTable.NumDescriptorRanges = 1; param.DescriptorTable.pDescriptorRanges = &_ranges[0]; } { // UAVs D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_UA]; param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; param.DescriptorTable.NumDescriptorRanges = 1; param.DescriptorTable.pDescriptorRanges = &_ranges[1]; } { // Samplers D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SAMPLER]; param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; param.DescriptorTable.NumDescriptorRanges = 1; param.DescriptorTable.pDescriptorRanges = &_ranges[2]; } // Static samplers static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(_staticSamplers), "Update static samplers setup."); // Linear Clamp InitSampler(0, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // Point Clamp InitSampler(1, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // Linear Wrap InitSampler(2, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_WRAP); // Point Wrap InitSampler(3, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_WRAP); // Shadow InitSampler(4, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL); // Shadow PCF InitSampler(5, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL); // Init _desc.NumParameters = ARRAY_COUNT(_parameters); _desc.pParameters = _parameters; _desc.NumStaticSamplers = ARRAY_COUNT(_staticSamplers); _desc.pStaticSamplers = _staticSamplers; _desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; } void RootSignatureDX12::InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc) { auto& sampler = _staticSamplers[i]; sampler.Filter = filter; sampler.AddressU = address; sampler.AddressV = address; sampler.AddressW = address; sampler.MipLODBias = 0.0f; sampler.MaxAnisotropy = 1; sampler.ComparisonFunc = comparisonFunc; sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; sampler.MinLOD = 0; sampler.MaxLOD = D3D12_FLOAT32_MAX; sampler.ShaderRegister = i; sampler.RegisterSpace = 0; sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; } ComPtr RootSignatureDX12::Serialize() const { ComPtr signature; ComPtr error; VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&_desc, D3D_ROOT_SIGNATURE_VERSION_1_0, &signature, &error)); if (error.Get()) { LOG(Error, "D3D12SerializeRootSignature failed with error: {}", String((const char*)error->GetBufferPointer())); } return signature; } #if USE_EDITOR const Char* GetRootSignatureShaderVisibility(D3D12_SHADER_VISIBILITY visibility) { switch (visibility) { case D3D12_SHADER_VISIBILITY_VERTEX: return TEXT(", visibility=SHADER_VISIBILITY_VERTEX"); case D3D12_SHADER_VISIBILITY_HULL: return TEXT(", visibility=SHADER_VISIBILITY_HULL"); case D3D12_SHADER_VISIBILITY_DOMAIN: return TEXT(", visibility=SHADER_VISIBILITY_DOMAIN"); case D3D12_SHADER_VISIBILITY_GEOMETRY: return TEXT(", visibility=SHADER_VISIBILITY_GEOMETRY"); case D3D12_SHADER_VISIBILITY_PIXEL: return TEXT(", visibility=SHADER_VISIBILITY_PIXEL"); case D3D12_SHADER_VISIBILITY_AMPLIFICATION: return TEXT(", visibility=SHADER_VISIBILITY_AMPLIFICATION"); case D3D12_SHADER_VISIBILITY_MESH: return TEXT(", visibility=SHADER_VISIBILITY_MESH"); case D3D12_SHADER_VISIBILITY_ALL: default: return TEXT(""); // Default } } const Char* GetRootSignatureSamplerFilter(D3D12_FILTER filter) { switch (filter) { case D3D12_FILTER_MIN_MAG_MIP_POINT: return TEXT("FILTER_MIN_MAG_MIP_POINT"); case D3D12_FILTER_MIN_MAG_MIP_LINEAR: return TEXT("FILTER_MIN_MAG_MIP_LINEAR"); case D3D12_FILTER_ANISOTROPIC: return TEXT("FILTER_ANISOTROPIC"); case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT: return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_POINT"); case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR: return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_LINEAR"); default: CRASH; // Not implemented } } const Char* GetRootSignatureSamplerAddress(D3D12_TEXTURE_ADDRESS_MODE address) { switch (address) { case D3D12_TEXTURE_ADDRESS_MODE_WRAP: return TEXT("TEXTURE_ADDRESS_WRAP"); case D3D12_TEXTURE_ADDRESS_MODE_MIRROR: return TEXT("TEXTURE_ADDRESS_MIRROR"); case D3D12_TEXTURE_ADDRESS_MODE_CLAMP: return TEXT("TEXTURE_ADDRESS_CLAMP"); case D3D12_TEXTURE_ADDRESS_MODE_BORDER: return TEXT("TEXTURE_ADDRESS_BORDER"); case D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE: return TEXT("TEXTURE_ADDRESS_MIRROR_ONCE"); default: return TEXT(""); } } const Char* GetRootSignatureSamplerComparisonFunc(D3D12_COMPARISON_FUNC func) { switch (func) { case D3D12_COMPARISON_FUNC_NEVER: return TEXT("COMPARISON_NEVER"); case D3D12_COMPARISON_FUNC_LESS: return TEXT("COMPARISON_LESS"); case D3D12_COMPARISON_FUNC_EQUAL: return TEXT("COMPARISON_EQUAL"); case D3D12_COMPARISON_FUNC_LESS_EQUAL: return TEXT("COMPARISON_LESS_EQUAL"); case D3D12_COMPARISON_FUNC_GREATER: return TEXT("COMPARISON_GREATER"); case D3D12_COMPARISON_FUNC_NOT_EQUAL: return TEXT("COMPARISON_NOT_EQUAL"); case D3D12_COMPARISON_FUNC_GREATER_EQUAL: return TEXT("COMPARISON_GREATER_EQUAL"); case D3D12_COMPARISON_FUNC_ALWAYS: default: return TEXT("COMPARISON_ALWAYS"); } } void RootSignatureDX12::ToString(StringBuilder& sb, bool singleLine) const { // Flags sb.Append(TEXT("RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)")); // Parameters const Char newLine = singleLine ? ' ' : '\n'; for (const D3D12_ROOT_PARAMETER& param : _parameters) { const Char* visibility = GetRootSignatureShaderVisibility(param.ShaderVisibility); switch (param.ParameterType) { case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: sb.AppendFormat(TEXT(",{}DescriptorTable("), newLine); for (uint32 rangeIndex = 0; rangeIndex < param.DescriptorTable.NumDescriptorRanges; rangeIndex++) { if (rangeIndex) sb.Append(TEXT(", ")); const D3D12_DESCRIPTOR_RANGE& range = param.DescriptorTable.pDescriptorRanges[rangeIndex]; switch (range.RangeType) { case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: sb.AppendFormat(TEXT("SRV(t{}"), range.BaseShaderRegister); break; case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: sb.AppendFormat(TEXT("UAV(u{}"), range.BaseShaderRegister); break; case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: sb.AppendFormat(TEXT("CBV(b{}"), range.BaseShaderRegister); break; case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: sb.AppendFormat(TEXT("Sampler(s{}"), range.BaseShaderRegister); break; } if (range.NumDescriptors != 1) { if (range.NumDescriptors == UINT_MAX) sb.Append(TEXT(", numDescriptors=unbounded")); else sb.AppendFormat(TEXT(", numDescriptors={}"), range.NumDescriptors); } if (range.OffsetInDescriptorsFromTableStart != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) sb.AppendFormat(TEXT(", offset={}"), range.OffsetInDescriptorsFromTableStart); sb.Append(')'); } sb.AppendFormat(TEXT("{})"), visibility); break; case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: sb.AppendFormat(TEXT(",{}RootConstants(num32BitConstants={}, b{}{})"), newLine, param.Constants.Num32BitValues, param.Constants.ShaderRegister, visibility); break; case D3D12_ROOT_PARAMETER_TYPE_CBV: sb.AppendFormat(TEXT(",{}CBV(b{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); break; case D3D12_ROOT_PARAMETER_TYPE_SRV: sb.AppendFormat(TEXT(",{}SRV(t{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); break; case D3D12_ROOT_PARAMETER_TYPE_UAV: sb.AppendFormat(TEXT(",{}UAV(u{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); break; } } // Static Samplers for (const D3D12_STATIC_SAMPLER_DESC& sampler : _staticSamplers) { const Char* visibility = GetRootSignatureShaderVisibility(sampler.ShaderVisibility); sb.AppendFormat(TEXT(",{}StaticSampler(s{}"), newLine, sampler.ShaderRegister); sb.AppendFormat(TEXT(", filter={}"), GetRootSignatureSamplerFilter(sampler.Filter)); sb.AppendFormat(TEXT(", addressU={}"), GetRootSignatureSamplerAddress(sampler.AddressU)); sb.AppendFormat(TEXT(", addressV={}"), GetRootSignatureSamplerAddress(sampler.AddressV)); sb.AppendFormat(TEXT(", addressW={}"), GetRootSignatureSamplerAddress(sampler.AddressW)); sb.AppendFormat(TEXT(", comparisonFunc={}"), GetRootSignatureSamplerComparisonFunc(sampler.ComparisonFunc)); sb.AppendFormat(TEXT(", maxAnisotropy={}"), sampler.MaxAnisotropy); sb.Append(TEXT(", borderColor=STATIC_BORDER_COLOR_OPAQUE_BLACK")); sb.AppendFormat(TEXT("{})"), visibility); } } String RootSignatureDX12::ToString() const { StringBuilder sb; ToString(sb); return sb.ToString(); } StringAnsi RootSignatureDX12::ToStringAnsi() const { StringBuilder sb; ToString(sb); return sb.ToStringAnsi(); } #endif GPUDevice* GPUDeviceDX12::Create() { #if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE IDXGIFactory4* dxgiFactory = nullptr; GPUAdapterDX selectedAdapter; selectedAdapter.Index = 0; selectedAdapter.MaxFeatureLevel = D3D_FEATURE_LEVEL_12_0; Platform::MemoryClear(&selectedAdapter.Description, sizeof(selectedAdapter.Description)); selectedAdapter.Description.VendorId = GPU_VENDOR_ID_AMD; #else #if !USE_EDITOR && PLATFORM_WINDOWS auto winSettings = WindowsPlatformSettings::Get(); if (!winSettings->SupportDX12) { // Skip if there is no support LOG(Warning, "Cannot use DirectX 12 (support disabled)."); return nullptr; } #endif // Debug Layer #if GPU_ENABLE_DIAGNOSTICS ComPtr debugLayer; D3D12GetDebugInterface(IID_PPV_ARGS(&debugLayer)); if (debugLayer) { debugLayer->EnableDebugLayer(); LOG(Info, "DirectX debugging layer enabled"); } #if 0 #ifdef __ID3D12Debug1_FWD_DEFINED__ ComPtr debugLayer1; D3D12GetDebugInterface(IID_PPV_ARGS(&debugLayer1)); if (debugLayer1) { // GPU-based validation and synchronized validation for debugging only debugLayer1->SetEnableGPUBasedValidation(true); debugLayer1->SetEnableSynchronizedCommandQueueValidation(true); } #endif #endif #ifdef __ID3D12DeviceRemovedExtendedDataSettings_FWD_DEFINED__ ComPtr dredSettings; D3D12GetDebugInterface(IID_PPV_ARGS(&dredSettings)); if (dredSettings) { // Turn on AutoBreadcrumbs and Page Fault reporting dredSettings->SetAutoBreadcrumbsEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON); dredSettings->SetPageFaultEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON); } #endif #endif // Create DXGI factory (CreateDXGIFactory2 is supported on Windows 8.1 or newer) IDXGIFactory4* dxgiFactory; IDXGIFactory6* dxgiFactory6; HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory6)); if (hr == S_OK) dxgiFactory = dxgiFactory6; else { dxgiFactory6 = nullptr; hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)); } if (hr != S_OK) { LOG(Error, "Cannot create DXGI adapter. Error code: {0:x}.", hr); return nullptr; } // Enumerate the DXGIFactory's adapters int32 selectedAdapterIndex = -1; Array adapters; IDXGIAdapter* tempAdapter; for (uint32 index = 0; dxgiFactory->EnumAdapters(index, &tempAdapter) != DXGI_ERROR_NOT_FOUND; index++) { // Try to use that adapter GPUAdapterDX adapter; if (tempAdapter && CheckDX12Support(tempAdapter)) { adapter.Index = index; adapter.MaxFeatureLevel = D3D_FEATURE_LEVEL_12_0; VALIDATE_DIRECTX_CALL(tempAdapter->GetDesc(&adapter.Description)); uint32 outputs = RenderToolsDX::CountAdapterOutputs(tempAdapter); // Send that info to the log LOG(Info, "Adapter {1}: '{0}', DirectX {2}", adapter.Description.Description, index, RenderToolsDX::GetFeatureLevelString(adapter.MaxFeatureLevel)); LOG(Info, " Dedicated Video Memory: {0}, Dedicated System Memory: {1}, Shared System Memory: {2}, Output(s): {3}", Utilities::BytesToText(adapter.Description.DedicatedVideoMemory), Utilities::BytesToText(adapter.Description.DedicatedSystemMemory), Utilities::BytesToText(adapter.Description.SharedSystemMemory), outputs); adapters.Add(adapter); } } // Find the best performing adapter and prefer using it instead of the first device const auto gpuPreference = DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE; if (dxgiFactory6 != nullptr && selectedAdapterIndex == -1) { if (dxgiFactory6->EnumAdapterByGpuPreference(0, gpuPreference, IID_PPV_ARGS(&tempAdapter)) != DXGI_ERROR_NOT_FOUND) { GPUAdapterDX adapter; if (tempAdapter && CheckDX12Support(tempAdapter)) { DXGI_ADAPTER_DESC desc; VALIDATE_DIRECTX_CALL(tempAdapter->GetDesc(&desc)); for (int i = 0; i < adapters.Count(); i++) { if (adapters[i].Description.AdapterLuid.LowPart == desc.AdapterLuid.LowPart && adapters[i].Description.AdapterLuid.HighPart == desc.AdapterLuid.HighPart) { selectedAdapterIndex = i; break; } } } } } // Select the adapter to use if (selectedAdapterIndex < 0) selectedAdapterIndex = 0; if (adapters.Count() == 0 || selectedAdapterIndex >= adapters.Count()) { LOG(Error, "Failed to find valid DirectX adapter!"); return nullptr; } GPUAdapterDX selectedAdapter = adapters[selectedAdapterIndex]; uint32 vendorId = 0; if (CommandLine::Options.NVIDIA.IsTrue()) vendorId = GPU_VENDOR_ID_NVIDIA; else if (CommandLine::Options.AMD.IsTrue()) vendorId = GPU_VENDOR_ID_AMD; else if (CommandLine::Options.Intel.IsTrue()) vendorId = GPU_VENDOR_ID_INTEL; if (vendorId != 0) { for (const auto& adapter : adapters) { if (adapter.GetVendorId() == vendorId) { selectedAdapter = adapter; break; } } } if (!selectedAdapter.IsValid()) { LOG(Error, "Failed to choose valid DirectX adapter!"); return nullptr; } if (selectedAdapter.MaxFeatureLevel < D3D_FEATURE_LEVEL_12_0) { LOG(Error, "Failed to choose valid DirectX adapter!"); return nullptr; } #endif // Create device auto device = New(dxgiFactory, New(selectedAdapter)); if (device->Init()) { LOG(Warning, "Graphics Device init failed"); Delete(device); return nullptr; } return device; } static MSAALevel GetMaximumMultisampleCount(ID3D12Device* device, DXGI_FORMAT dxgiFormat) { int32 maxCount = 1; D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS qualityLevels = { dxgiFormat }; for (int32 i = 2; i <= 8; i *= 2) { qualityLevels.SampleCount = i; if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &qualityLevels, sizeof(qualityLevels))) && qualityLevels.NumQualityLevels > 0) maxCount = i; } return static_cast(maxCount); } GPUDeviceDX12::GPUDeviceDX12(IDXGIFactory4* dxgiFactory, GPUAdapterDX* adapter) : GPUDeviceDX(RendererType::DirectX12, ShaderProfile::DirectX_SM6, adapter) , _device(nullptr) , _factoryDXGI(dxgiFactory) , _res2Dispose(256) , _rootSignature(nullptr) , _commandQueue(nullptr) , _mainContext(nullptr) , UploadBuffer(this) , TimestampQueryHeap(this, D3D12_QUERY_HEAP_TYPE_TIMESTAMP, DX12_BACK_BUFFER_COUNT * 1024) , Heap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 4 * 1024, false) , Heap_RTV(this, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1 * 1024, false) , Heap_DSV(this, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 64, false) , Heap_Sampler(this, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 128, false) , RingHeap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 512 * 1024, true) , RingHeap_Sampler(this, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 1 * 1024, true) { } bool GPUDeviceDX12::Init() { #if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE // Create DirectX device D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; params.Version = D3D12_SDK_VERSION; #if GPU_ENABLE_DIAGNOSTICS params.ProcessDebugFlags = D3D12_PROCESS_DEBUG_FLAG_DEBUG_LAYER_ENABLED; #elif !BUILD_RELEASE params.ProcessDebugFlags = D3D12XBOX_PROCESS_DEBUG_FLAG_INSTRUMENTED; #endif params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); #if PLATFORM_XBOX_SCARLETT params.DisableDXR = TRUE; #endif VALIDATE_DIRECTX_CALL(D3D12XboxCreateDevice(nullptr, ¶ms, IID_GRAPHICS_PPV_ARGS(&_device))); // Setup adapter D3D12XBOX_GPU_HARDWARE_CONFIGURATION hwConfig = {}; _device->GetGpuHardwareConfigurationX(&hwConfig); const wchar_t* hwVer = L"Unknown"; switch (hwConfig.HardwareVersion) { case D3D12XBOX_HARDWARE_VERSION_XBOX_ONE: hwVer = L"Xbox One"; break; case D3D12XBOX_HARDWARE_VERSION_XBOX_ONE_S: hwVer = L"Xbox One S"; break; case D3D12XBOX_HARDWARE_VERSION_XBOX_ONE_X: hwVer = L"Xbox One X"; break; case D3D12XBOX_HARDWARE_VERSION_XBOX_ONE_X_DEVKIT: hwVer = L"Xbox One X (DevKit)"; break; #ifdef _GAMING_XBOX_SCARLETT case D3D12XBOX_HARDWARE_VERSION_XBOX_SCARLETT_LOCKHART: hwVer = L"Scarlett Lockhart"; break; case D3D12XBOX_HARDWARE_VERSION_XBOX_SCARLETT_ANACONDA: hwVer = L"Scarlett Anaconda"; break; case D3D12XBOX_HARDWARE_VERSION_XBOX_SCARLETT_DEVKIT: hwVer = L"Scarlett Dev Kit"; break; #endif } LOG(Info, "Hardware Version: {0}", hwVer); updateFrameEvents(); // Setup display output auto& videoOutput = VideoOutputs.AddOne(); videoOutput.Name = hwVer; ComPtr dxgiDevice; VALIDATE_DIRECTX_CALL(_device->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxgiDevice))); ComPtr dxgiAdapter; VALIDATE_DIRECTX_CALL(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf())); ComPtr dxgiOutput; VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); DXGI_FORMAT backbufferFormat = RenderToolsDX::ToDxgiFormat(GPU_BACK_BUFFER_PIXEL_FORMAT); UINT modesCount = 0; #ifdef _GAMING_XBOX_SCARLETT VALIDATE_DIRECTX_CALL(dxgiOutput->GetDisplayModeList(backbufferFormat, 0, &modesCount, NULL)); Array modes; modes.Resize((int32)modesCount); VALIDATE_DIRECTX_CALL(dxgiOutput->GetDisplayModeListX(backbufferFormat, 0, &modesCount, modes.Get())); for (const DXGIXBOX_MODE_DESC& mode : modes) { if (mode.Width > videoOutput.Width) { videoOutput.Width = mode.Width; videoOutput.Height = mode.Height; } videoOutput.RefreshRate = Math::Max(videoOutput.RefreshRate, mode.RefreshRate.Numerator / (float)mode.RefreshRate.Denominator); } modes.Resize(0); #else videoOutput.Width = 1920; videoOutput.Height = 1080; videoOutput.RefreshRate = 60; #endif #if PLATFORM_GDK GDKPlatform::Suspended.Bind(this); GDKPlatform::Resumed.Bind(this); #endif #else // Get DXGI adapter IDXGIAdapter1* adapter; ASSERT(_factoryDXGI); if (_factoryDXGI->EnumAdapters1(_adapter->Index, &adapter) == DXGI_ERROR_NOT_FOUND || adapter == nullptr) { LOG(Warning, "Cannot get the adapter."); return true; } UpdateOutputs(adapter); // Create DirectX device VALIDATE_DIRECTX_CALL(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&_device))); #if PLATFORM_WINDOWS // Detect RenderDoc usage (UUID {A7AA6116-9C8D-4BBA-9083-B4D816B71B78}) IUnknown* unknown = nullptr; const GUID uuidRenderDoc = { 0xa7aa6116, 0x9c8d, 0x4bba, { 0x90, 0x83, 0xb4, 0xd8, 0x16, 0xb7, 0x1b, 0x78 } }; HRESULT hr = _device->QueryInterface(uuidRenderDoc, (void**)&unknown); if (SUCCEEDED(hr) && unknown) { IsDebugToolAttached = true; unknown->Release(); } if (!IsDebugToolAttached && GetModuleHandleA("renderdoc.dll")) IsDebugToolAttached = true; if (!IsDebugToolAttached && (GetModuleHandleA("Nvda.Graphics.Interception.dll") || GetModuleHandleA("WarpViz.Injection.dll") || GetModuleHandleA("nvperf_grfx_target.dll"))) IsDebugToolAttached = true; #endif // Check if can use screen tearing on a swapchain ComPtr factory5; _factoryDXGI->QueryInterface(IID_PPV_ARGS(&factory5)); if (factory5) { BOOL allowTearing; if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))) && allowTearing #if PLATFORM_WINDOWS && !IsDebugToolAttached // Disable tearing with RenderDoc (prevents crashing) #endif ) AllowTearing = true; } // Debug Layer #if GPU_ENABLE_DIAGNOSTICS ComPtr infoQueue; HRESULT result = _device->QueryInterface(IID_PPV_ARGS(&infoQueue)); LOG_DIRECTX_RESULT(result); if (infoQueue) { D3D12_INFO_QUEUE_FILTER filter; Platform::MemoryClear(&filter, sizeof(filter)); D3D12_MESSAGE_SEVERITY denySeverity = D3D12_MESSAGE_SEVERITY_INFO; filter.DenyList.NumSeverities = 1; filter.DenyList.pSeverityList = &denySeverity; D3D12_MESSAGE_ID disabledMessages[] = { D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, D3D12_MESSAGE_ID_INVALID_DESCRIPTOR_HANDLE, D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_PS_OUTPUT_RT_OUTPUT_MISMATCH, D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE, D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_EMPTY_LAYOUT, D3D12_MESSAGE_ID_RESOURCE_BARRIER_DUPLICATE_SUBRESOURCE_TRANSITIONS, D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE, }; filter.DenyList.NumIDs = ARRAY_COUNT(disabledMessages); filter.DenyList.pIDList = disabledMessages; infoQueue->AddStorageFilterEntries(&filter); infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE); infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE); //infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE); } #endif #endif // Change state _state = DeviceState::Created; // Spawn some info about the hardware D3D12_FEATURE_DATA_D3D12_OPTIONS options; VALIDATE_DIRECTX_CALL(_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))); LOG(Info, "Tiled Resources Tier: {0}", (int32)options.TiledResourcesTier); LOG(Info, "Resource Binding Tier: {0}", (int32)options.ResourceBindingTier); LOG(Info, "Conservative Rasterization Tier: {0}", (int32)options.ConservativeRasterizationTier); LOG(Info, "Resource Heap Tier: {0}", (int32)options.ResourceHeapTier); // Init device limits { auto& limits = Limits; limits.HasCompute = true; limits.HasTessellation = GPU_ALLOW_TESSELLATION_SHADERS; limits.HasGeometryShaders = GPU_ALLOW_GEOMETRY_SHADERS; limits.HasInstancing = true; limits.HasVolumeTextureRendering = true; limits.HasDrawIndirect = true; limits.HasAppendConsumeBuffers = true; limits.HasSeparateRenderTargetBlendState = true; limits.HasDepthAsSRV = true; limits.HasDepthClip = true; limits.HasReadOnlyDepth = true; limits.HasMultisampleDepthAsSRV = true; limits.HasTypedUAVLoad = options.TypedUAVLoadAdditionalFormats != 0; limits.MaximumMipLevelsCount = D3D12_REQ_MIP_LEVELS; limits.MaximumTexture1DSize = D3D12_REQ_TEXTURE1D_U_DIMENSION; limits.MaximumTexture1DArraySize = D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION; limits.MaximumTexture2DSize = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; limits.MaximumTexture2DArraySize = D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION; limits.MaximumTexture3DSize = D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION; limits.MaximumTextureCubeSize = D3D12_REQ_TEXTURECUBE_DIMENSION; limits.MaximumSamplerAnisotropy = D3D12_DEFAULT_MAX_ANISOTROPY; for (int32 i = 0; i < static_cast(PixelFormat::MAX); i++) { const PixelFormat format = static_cast(i); const DXGI_FORMAT dxgiFormat = RenderToolsDX::ToDxgiFormat(format); D3D12_FEATURE_DATA_FORMAT_SUPPORT formatInfo = { dxgiFormat }; if (FAILED(_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatInfo, sizeof(formatInfo)))) formatInfo.Support1 = D3D12_FORMAT_SUPPORT1_NONE; const MSAALevel maximumMultisampleCount = GetMaximumMultisampleCount(_device, dxgiFormat); FeaturesPerFormat[i] = FormatFeatures(format, maximumMultisampleCount, (FormatSupport)formatInfo.Support1); } } #if !BUILD_RELEASE // Prevent the GPU from overclocking or under-clocking to get consistent timings if (CommandLine::Options.ShaderProfile.IsTrue()) { _device->SetStablePowerState(TRUE); } #endif // Setup resources _commandQueue = New(this, D3D12_COMMAND_LIST_TYPE_DIRECT); if (_commandQueue->Init()) return true; _mainContext = New(this, D3D12_COMMAND_LIST_TYPE_DIRECT); if (RingHeap_CBV_SRV_UAV.Init()) return true; if (RingHeap_Sampler.Init()) return true; // Create empty views D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; for (int32 i = 0; i < ARRAY_COUNT(_nullSrv); i++) { srvDesc.ViewDimension = (D3D12_SRV_DIMENSION)i; switch (srvDesc.ViewDimension) { case D3D12_SRV_DIMENSION_BUFFER: srvDesc.Buffer.FirstElement = 0; srvDesc.Buffer.NumElements = 0; srvDesc.Buffer.StructureByteStride = 0; srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; break; case D3D12_SRV_DIMENSION_TEXTURE1D: srvDesc.Texture1D.MostDetailedMip = 0; srvDesc.Texture1D.MipLevels = 1; srvDesc.Texture1D.ResourceMinLODClamp = 0.0f; break; case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: srvDesc.Texture1DArray.MostDetailedMip = 0; srvDesc.Texture1DArray.MipLevels = 1; srvDesc.Texture1DArray.FirstArraySlice = 0; srvDesc.Texture1DArray.ArraySize = 1; srvDesc.Texture1DArray.ResourceMinLODClamp = 0.0f; break; case D3D12_SRV_DIMENSION_UNKNOWN: // Map Unknown into Texture2D case D3D12_SRV_DIMENSION_TEXTURE2D: srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Texture2D.MipLevels = 1; srvDesc.Texture2D.PlaneSlice = 0; srvDesc.Texture2D.ResourceMinLODClamp = 0.0f; break; case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: srvDesc.Texture2DArray.MostDetailedMip = 0; srvDesc.Texture2DArray.MipLevels = 1; srvDesc.Texture2DArray.FirstArraySlice = 0; srvDesc.Texture2DArray.ArraySize = 0; srvDesc.Texture2DArray.PlaneSlice = 0; srvDesc.Texture2DArray.ResourceMinLODClamp = 0.0f; break; case D3D12_SRV_DIMENSION_TEXTURE3D: srvDesc.Texture3D.MostDetailedMip = 0; srvDesc.Texture3D.MipLevels = 1; srvDesc.Texture3D.ResourceMinLODClamp = 0.0f; break; case D3D12_SRV_DIMENSION_TEXTURECUBE: srvDesc.TextureCube.MostDetailedMip = 0; srvDesc.TextureCube.MipLevels = 1; srvDesc.TextureCube.ResourceMinLODClamp = 0.0f; break; case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY: srvDesc.TextureCubeArray.MostDetailedMip = 0; srvDesc.TextureCubeArray.MipLevels = 1; srvDesc.TextureCubeArray.First2DArrayFace = 0; srvDesc.TextureCubeArray.NumCubes = 0; srvDesc.TextureCubeArray.ResourceMinLODClamp = 0.0f; break; default: continue; } _nullSrv[i].CreateSRV(this, nullptr, &srvDesc); } { D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc; uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; uavDesc.Texture2D.MipSlice = 0; uavDesc.Texture2D.PlaneSlice = 0; _nullUav.CreateUAV(this, nullptr, &uavDesc); } // Create root signature { RootSignatureDX12 signature; ComPtr signatureBlob = signature.Serialize(); VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&_rootSignature))); } if (TimestampQueryHeap.Init()) return true; // Cached command signatures { DrawIndirectCommandSignature = New(this, 1); DrawIndirectCommandSignature->At(0).Draw(); DrawIndirectCommandSignature->Finalize(); } { DrawIndexedIndirectCommandSignature = New(this, 1); DrawIndexedIndirectCommandSignature->At(0).DrawIndexed(); DrawIndexedIndirectCommandSignature->Finalize(); } { DispatchIndirectCommandSignature = New(this, 1); DispatchIndirectCommandSignature->At(0).Dispatch(); DispatchIndirectCommandSignature->Finalize(); } _state = DeviceState::Ready; return GPUDeviceDX::Init(); } void GPUDeviceDX12::DrawBegin() { { PROFILE_CPU_NAMED("Wait For GPU"); //_commandQueue->WaitForGPU(); _commandQueue->WaitForFence(_mainContext->FrameFenceValues[1]); } // Base GPUDeviceDX::DrawBegin(); updateRes2Dispose(); UploadBuffer.BeginGeneration(Engine::FrameCount); } void GPUDeviceDX12::RenderEnd() { // Base GPUDeviceDX::RenderEnd(); // Resolve the timestamp queries TimestampQueryHeap.EndQueryBatchAndResolveQueryData(_mainContext); } GPUDeviceDX12::~GPUDeviceDX12() { // Ensure to be disposed Dispose(); } D3D12_CPU_DESCRIPTOR_HANDLE GPUDeviceDX12::NullSRV(D3D12_SRV_DIMENSION dimension) const { return _nullSrv[dimension].CPU(); } D3D12_CPU_DESCRIPTOR_HANDLE GPUDeviceDX12::NullUAV() const { return _nullUav.CPU(); } ID3D12GraphicsCommandList* GPUDeviceDX12::GetCommandList() const { return _mainContext->GetCommandList(); } ID3D12CommandQueue* GPUDeviceDX12::GetCommandQueueDX12() const { return _commandQueue->GetCommandQueue(); } void GPUDeviceDX12::Dispose() { GPUDeviceLock lock(this); // Check if has been disposed already if (_state == DeviceState::Disposed) return; // Set current state _state = DeviceState::Disposing; // Wait for rendering end WaitForGPU(); // Pre dispose preDispose(); // Release all late dispose resources (if state is Disposing all are released) updateRes2Dispose(); // Clear pipeline objects SAFE_DELETE_GPU_RESOURCE(DummyVB); for (auto& srv : _nullSrv) srv.Release(); _nullUav.Release(); TimestampQueryHeap.Destroy(); DX_SAFE_RELEASE_CHECK(_rootSignature, 0); Heap_CBV_SRV_UAV.ReleaseGPU(); Heap_RTV.ReleaseGPU(); Heap_DSV.ReleaseGPU(); Heap_Sampler.ReleaseGPU(); RingHeap_CBV_SRV_UAV.ReleaseGPU(); RingHeap_Sampler.ReleaseGPU(); UploadBuffer.ReleaseGPU(); SAFE_DELETE(DrawIndirectCommandSignature); SAFE_DELETE(_mainContext); SAFE_DELETE(_commandQueue); // Clear DirectX stuff SAFE_DELETE(_adapter); SAFE_RELEASE(_device); SAFE_RELEASE(_factoryDXGI); // Base GPUDeviceDX::Dispose(); // Set current state _state = DeviceState::Disposed; } void GPUDeviceDX12::WaitForGPU() { _commandQueue->WaitForGPU(); } GPUTexture* GPUDeviceDX12::CreateTexture(const StringView& name) { PROFILE_MEM(GraphicsTextures); return New(this, name); } GPUShader* GPUDeviceDX12::CreateShader(const StringView& name) { PROFILE_MEM(GraphicsShaders); return New(this, name); } GPUPipelineState* GPUDeviceDX12::CreatePipelineState() { PROFILE_MEM(GraphicsCommands); return New(this); } GPUTimerQuery* GPUDeviceDX12::CreateTimerQuery() { return New(this); } GPUBuffer* GPUDeviceDX12::CreateBuffer(const StringView& name) { PROFILE_MEM(GraphicsBuffers); return New(this, name); } GPUSampler* GPUDeviceDX12::CreateSampler() { return New(this); } GPUVertexLayout* GPUDeviceDX12::CreateVertexLayout(const VertexElements& elements, bool explicitOffsets) { return New(this, elements, explicitOffsets); } GPUSwapChain* GPUDeviceDX12::CreateSwapChain(Window* window) { return New(this, window); } GPUConstantBuffer* GPUDeviceDX12::CreateConstantBuffer(uint32 size, const StringView& name) { PROFILE_MEM(GraphicsShaders); return New(this, size, name); } void GPUDeviceDX12::AddResourceToLateRelease(IGraphicsUnknown* resource, uint32 safeFrameCount) { if (resource == nullptr) return; ScopeLock lock(_res2DisposeLock); // Add to the list DisposeResourceEntry entry; entry.Resource = resource; entry.TargetFrame = Engine::FrameCount + safeFrameCount; _res2Dispose.Add(entry); } void GPUDeviceDX12::updateRes2Dispose() { uint64 currentFrame = Engine::FrameCount; if (_state == DeviceState::Disposing) { // During device disposing we want to remove all resources currentFrame = MAX_uint32; } _res2DisposeLock.Lock(); for (int32 i = _res2Dispose.Count() - 1; i >= 0 && i < _res2Dispose.Count(); i--) { const DisposeResourceEntry& entry = _res2Dispose[i]; if (entry.TargetFrame <= currentFrame) { auto refs = entry.Resource->Release(); if (refs != 0) { LOG(Error, "Late release resource has not been fully released. References left: {0}", refs); } _res2Dispose.RemoveAt(i); } } _res2DisposeLock.Unlock(); } #if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE void GPUDeviceDX12::OnSuspended() { _commandQueue->GetCommandQueue()->SuspendX(0); } void GPUDeviceDX12::OnResumed() { _commandQueue->GetCommandQueue()->ResumeX(); updateFrameEvents(); } void GPUDeviceDX12::updateFrameEvents() { ComPtr dxgiDevice; VALIDATE_DIRECTX_CALL(_device->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxgiDevice))); ComPtr dxgiAdapter; VALIDATE_DIRECTX_CALL(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf())); dxgiAdapter->GetDesc(&_adapter->Description); ComPtr dxgiOutput; VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); // TODO: support 120/40/30/24 fps VALIDATE_DIRECTX_CALL(_device->SetFrameIntervalX(dxgiOutput.Get(), D3D12XBOX_FRAME_INTERVAL_60_HZ, DX12_BACK_BUFFER_COUNT - 1u, D3D12XBOX_FRAME_INTERVAL_FLAG_NONE)); VALIDATE_DIRECTX_CALL(_device->ScheduleFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, 0U, nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } #endif GPUDevice* CreateGPUDeviceDX12() { return GPUDeviceDX12::Create(); } #endif