#include "XCEngine/RHI/D3D12/D3D12Device.h" #include "XCEngine/RHI/D3D12/D3D12CommandQueue.h" #include "XCEngine/RHI/D3D12/D3D12CommandList.h" #include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h" #include "XCEngine/RHI/D3D12/D3D12Fence.h" #include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h" #include "XCEngine/RHI/D3D12/D3D12DescriptorSet.h" #include "XCEngine/RHI/D3D12/D3D12QueryHeap.h" #include "XCEngine/RHI/D3D12/D3D12RootSignature.h" #include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h" #include "XCEngine/RHI/D3D12/D3D12PipelineState.h" #include "XCEngine/RHI/D3D12/D3D12Sampler.h" #include "XCEngine/RHI/D3D12/D3D12Texture.h" #include "XCEngine/RHI/D3D12/D3D12Buffer.h" #include "XCEngine/RHI/D3D12/D3D12SwapChain.h" #include "XCEngine/RHI/D3D12/D3D12Shader.h" #include "XCEngine/RHI/D3D12/D3D12ResourceView.h" #include "XCEngine/RHI/D3D12/D3D12RenderPass.h" #include "XCEngine/RHI/D3D12/D3D12Framebuffer.h" #include "XCEngine/Debug/Logger.h" #include #include #include #include #include #include #include #ifdef _DEBUG #include #endif namespace XCEngine { namespace RHI { namespace { std::string NarrowAscii(const std::wstring& value) { std::string result; result.reserve(value.size()); for (wchar_t ch : value) { result.push_back(static_cast(ch)); } return result; } uint64_t GetVolumeTraceSteadyMs(); void LogVolumeTraceRendering(const std::string& message); bool HasShaderPayload(const ShaderCompileDesc& desc) { return !desc.source.empty() || !desc.fileName.empty(); } bool ShouldTraceVolumetricShaderCompile(const ShaderCompileDesc& desc) { const std::string fileName = NarrowAscii(desc.fileName); if (fileName.find("volumetric") != std::string::npos) { return true; } if (!desc.source.empty()) { const std::string sourceText(desc.source.begin(), desc.source.end()); if (sourceText.find("PNANOVDB_HLSL") != std::string::npos || sourceText.find("VolumeData") != std::string::npos) { return true; } } return false; } std::string DescribeShaderCompileDesc(const ShaderCompileDesc& desc) { std::string description = "entry=" + NarrowAscii(desc.entryPoint) + " profile=" + NarrowAscii(desc.profile) + " source_bytes=" + std::to_string(desc.source.size()) + " macro_count=" + std::to_string(desc.macros.size()); if (!desc.fileName.empty()) { description += " file=" + NarrowAscii(desc.fileName); } return description; } bool CompileD3D12Shader(const ShaderCompileDesc& desc, D3D12Shader& shader) { const bool traceShaderCompile = ShouldTraceVolumetricShaderCompile(desc); const uint64_t compileStartMs = traceShaderCompile ? GetVolumeTraceSteadyMs() : 0u; if (traceShaderCompile) { LogVolumeTraceRendering( "D3D12 shader compile begin steady_ms=" + std::to_string(compileStartMs) + " " + DescribeShaderCompileDesc(desc)); } const std::string entryPoint = NarrowAscii(desc.entryPoint); const std::string profile = NarrowAscii(desc.profile); const char* entryPointPtr = entryPoint.empty() ? nullptr : entryPoint.c_str(); const char* profilePtr = profile.empty() ? nullptr : profile.c_str(); if (!desc.source.empty()) { std::vector macroNames; std::vector macroDefinitions; std::vector macroTable; if (!desc.macros.empty()) { macroNames.reserve(desc.macros.size()); macroDefinitions.reserve(desc.macros.size()); macroTable.reserve(desc.macros.size() + 1u); for (const ShaderCompileMacro& macro : desc.macros) { macroNames.push_back(NarrowAscii(macro.name)); macroDefinitions.push_back(NarrowAscii(macro.definition)); } for (size_t macroIndex = 0; macroIndex < desc.macros.size(); ++macroIndex) { D3D_SHADER_MACRO d3dMacro = {}; d3dMacro.Name = macroNames[macroIndex].c_str(); d3dMacro.Definition = macroDefinitions[macroIndex].empty() ? "1" : macroDefinitions[macroIndex].c_str(); macroTable.push_back(d3dMacro); } macroTable.push_back({ nullptr, nullptr }); } const D3D_SHADER_MACRO* macroPtr = macroTable.empty() ? nullptr : macroTable.data(); const bool compiled = shader.Compile( desc.source.data(), desc.source.size(), desc.fileName.empty() ? nullptr : desc.fileName.c_str(), macroPtr, entryPointPtr, profilePtr); if (traceShaderCompile) { const uint64_t compileEndMs = GetVolumeTraceSteadyMs(); LogVolumeTraceRendering( std::string("D3D12 shader compile ") + (compiled ? "end" : "failed") + " steady_ms=" + std::to_string(compileEndMs) + " total_ms=" + std::to_string(compileEndMs - compileStartMs) + " " + DescribeShaderCompileDesc(desc)); } return compiled; } if (!desc.fileName.empty()) { const bool compiled = shader.CompileFromFile(desc.fileName.c_str(), entryPointPtr, profilePtr); if (traceShaderCompile) { const uint64_t compileEndMs = GetVolumeTraceSteadyMs(); LogVolumeTraceRendering( std::string("D3D12 shader compile ") + (compiled ? "end" : "failed") + " steady_ms=" + std::to_string(compileEndMs) + " total_ms=" + std::to_string(compileEndMs - compileStartMs) + " " + DescribeShaderCompileDesc(desc)); } return compiled; } if (traceShaderCompile) { const uint64_t compileEndMs = GetVolumeTraceSteadyMs(); LogVolumeTraceRendering( "D3D12 shader compile failed steady_ms=" + std::to_string(compileEndMs) + " total_ms=" + std::to_string(compileEndMs - compileStartMs) + " reason=empty_shader_payload " + DescribeShaderCompileDesc(desc)); } return false; } bool WaitForQueueIdle( ID3D12Device* device, ID3D12CommandQueue* commandQueue) { if (device == nullptr || commandQueue == nullptr) { return false; } ID3D12Fence* fence = nullptr; if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)))) { return false; } HANDLE fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (fenceEvent == nullptr) { fence->Release(); return false; } constexpr UINT64 kFenceValue = 1; const HRESULT signalResult = commandQueue->Signal(fence, kFenceValue); if (FAILED(signalResult)) { CloseHandle(fenceEvent); fence->Release(); return false; } if (fence->GetCompletedValue() < kFenceValue) { if (FAILED(fence->SetEventOnCompletion(kFenceValue, fenceEvent))) { CloseHandle(fenceEvent); fence->Release(); return false; } WaitForSingleObject(fenceEvent, INFINITE); } CloseHandle(fenceEvent); fence->Release(); return true; } uint32_t GetFormatBytesPerPixel(Format format) { switch (format) { case Format::R8_UNorm: return 1; case Format::R8G8_UNorm: return 2; case Format::R8G8B8A8_UNorm: case Format::R8G8B8A8_SRGB: return 4; case Format::R16_Float: return 2; case Format::R16G16B16A16_Float: return 8; case Format::R32_Float: return 4; case Format::R32G32_Float: return 8; case Format::R32G32B32_Float: return 12; case Format::R32G32B32A32_Float: return 16; default: return 0; } } TextureType ResolveTextureType(uint32_t rawType) { return static_cast(rawType); } uint16_t ResolveDepthOrArraySize(const TextureDesc& desc, TextureType textureType) { switch (textureType) { case TextureType::Texture3D: return static_cast(std::max(desc.depth, 1)); case TextureType::Texture2DArray: return static_cast(std::max(desc.arraySize, 1)); case TextureType::TextureCube: case TextureType::TextureCubeArray: return static_cast(std::max(desc.arraySize, 6)); default: return 1; } } std::string ShaderModelToString(D3D_SHADER_MODEL shaderModel) { switch (shaderModel) { case D3D_SHADER_MODEL_6_7: return "6.7"; case D3D_SHADER_MODEL_6_6: return "6.6"; case D3D_SHADER_MODEL_6_5: return "6.5"; case D3D_SHADER_MODEL_6_4: return "6.4"; case D3D_SHADER_MODEL_6_3: return "6.3"; case D3D_SHADER_MODEL_6_2: return "6.2"; case D3D_SHADER_MODEL_6_1: return "6.1"; case D3D_SHADER_MODEL_6_0: return "6.0"; case D3D_SHADER_MODEL_5_1: return "5.1"; default: return {}; } } D3D12_RTV_DIMENSION ResolveRTVDimension(const ResourceViewDesc& desc, TextureType textureType) { switch (desc.dimension) { case ResourceViewDimension::Texture1D: return D3D12_RTV_DIMENSION_TEXTURE1D; case ResourceViewDimension::Texture1DArray: return D3D12_RTV_DIMENSION_TEXTURE1DARRAY; case ResourceViewDimension::Texture2DArray: case ResourceViewDimension::TextureCube: case ResourceViewDimension::TextureCubeArray: return D3D12_RTV_DIMENSION_TEXTURE2DARRAY; case ResourceViewDimension::Texture3D: return D3D12_RTV_DIMENSION_TEXTURE3D; case ResourceViewDimension::Texture2DMS: return D3D12_RTV_DIMENSION_TEXTURE2DMS; case ResourceViewDimension::Texture2DMSArray: return D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; case ResourceViewDimension::Unknown: break; default: return D3D12_RTV_DIMENSION_TEXTURE2D; } switch (textureType) { case TextureType::Texture1D: return D3D12_RTV_DIMENSION_TEXTURE1D; case TextureType::Texture2DArray: case TextureType::TextureCube: case TextureType::TextureCubeArray: return D3D12_RTV_DIMENSION_TEXTURE2DARRAY; case TextureType::Texture3D: return D3D12_RTV_DIMENSION_TEXTURE3D; default: return D3D12_RTV_DIMENSION_TEXTURE2D; } } D3D12_SRV_DIMENSION ResolveSRVDimension(const ResourceViewDesc& desc, TextureType textureType) { switch (desc.dimension) { case ResourceViewDimension::Texture1D: return D3D12_SRV_DIMENSION_TEXTURE1D; case ResourceViewDimension::Texture1DArray: return D3D12_SRV_DIMENSION_TEXTURE1DARRAY; case ResourceViewDimension::Texture2DArray: return D3D12_SRV_DIMENSION_TEXTURE2DARRAY; case ResourceViewDimension::Texture3D: return D3D12_SRV_DIMENSION_TEXTURE3D; case ResourceViewDimension::TextureCube: return D3D12_SRV_DIMENSION_TEXTURECUBE; case ResourceViewDimension::TextureCubeArray: return D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; case ResourceViewDimension::Texture2DMS: return D3D12_SRV_DIMENSION_TEXTURE2DMS; case ResourceViewDimension::Texture2DMSArray: return D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY; case ResourceViewDimension::Unknown: break; default: return D3D12_SRV_DIMENSION_TEXTURE2D; } switch (textureType) { case TextureType::Texture1D: return D3D12_SRV_DIMENSION_TEXTURE1D; case TextureType::Texture2DArray: return D3D12_SRV_DIMENSION_TEXTURE2DARRAY; case TextureType::Texture3D: return D3D12_SRV_DIMENSION_TEXTURE3D; case TextureType::TextureCube: return D3D12_SRV_DIMENSION_TEXTURECUBE; case TextureType::TextureCubeArray: return D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; default: return D3D12_SRV_DIMENSION_TEXTURE2D; } } bool IsDepthFormat(Format format) { return format == Format::D16_UNorm || format == Format::D24_UNorm_S8_UInt || format == Format::D32_Float; } DXGI_FORMAT ResolveD3D12ResourceFormat(Format format) { switch (format) { case Format::D16_UNorm: return DXGI_FORMAT_R16_TYPELESS; case Format::D24_UNorm_S8_UInt: return DXGI_FORMAT_R24G8_TYPELESS; case Format::D32_Float: return DXGI_FORMAT_R32_TYPELESS; default: return ToD3D12(format); } } DXGI_FORMAT ResolveD3D12DepthStencilViewFormat(Format format) { switch (format) { case Format::D16_UNorm: return DXGI_FORMAT_D16_UNORM; case Format::D24_UNorm_S8_UInt: return DXGI_FORMAT_D24_UNORM_S8_UINT; case Format::D32_Float: return DXGI_FORMAT_D32_FLOAT; default: return ToD3D12(format); } } DXGI_FORMAT ResolveD3D12ShaderResourceViewFormat(Format format) { switch (format) { case Format::D16_UNorm: return DXGI_FORMAT_R16_UNORM; case Format::D24_UNorm_S8_UInt: return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; case Format::D32_Float: return DXGI_FORMAT_R32_FLOAT; default: return ToD3D12(format); } } bool IsSupportedBufferViewDimension(ResourceViewDimension dimension) { return dimension == ResourceViewDimension::Buffer || dimension == ResourceViewDimension::StructuredBuffer || dimension == ResourceViewDimension::RawBuffer; } uint64_t GetVolumeTraceSteadyMs() { using Clock = std::chrono::steady_clock; static const Clock::time_point s_start = Clock::now(); return static_cast(std::chrono::duration_cast( Clock::now() - s_start).count()); } bool ShouldTraceLargeStorageBuffer(const BufferDesc& desc) { return static_cast(desc.bufferType) == BufferType::Storage && desc.size >= 32ull * 1024ull * 1024ull; } void LogVolumeTraceRendering(const std::string& message) { Containers::String entry("[VolumeTrace] "); entry += message.c_str(); Debug::Logger::Get().Info(Debug::LogCategory::Rendering, entry); } uint32_t ResolveBufferViewElementStride(RHIBuffer* buffer, const ResourceViewDesc& desc) { if (desc.dimension == ResourceViewDimension::RawBuffer) { return 4u; } if (desc.structureByteStride > 0) { return desc.structureByteStride; } if (desc.dimension == ResourceViewDimension::Buffer && desc.format != 0) { return GetFormatBytesPerPixel(static_cast(desc.format)); } return buffer != nullptr ? buffer->GetStride() : 0u; } bool TryResolveBufferViewFirstElement( RHIBuffer* buffer, const ResourceViewDesc& desc, uint32_t& outFirstElement, uint32_t& outElementStride) { outFirstElement = 0; outElementStride = ResolveBufferViewElementStride(buffer, desc); if (outElementStride == 0) { return false; } if ((desc.bufferLocation % outElementStride) != 0) { return false; } outFirstElement = desc.firstElement + static_cast(desc.bufferLocation / outElementStride); return true; } bool TryResolveBufferViewElementCount( RHIBuffer* buffer, const ResourceViewDesc& desc, uint32_t elementStride, uint32_t firstElement, uint32_t& outElementCount) { outElementCount = 0; if (desc.elementCount > 0) { outElementCount = desc.elementCount; return true; } if (buffer == nullptr || elementStride == 0) { return false; } const uint64_t byteOffset = static_cast(firstElement) * elementStride; if (byteOffset >= buffer->GetSize()) { return false; } outElementCount = static_cast((buffer->GetSize() - byteOffset) / elementStride); return outElementCount > 0; } DXGI_FORMAT ResolveD3D12BufferViewFormat(const ResourceViewDesc& desc) { if (desc.dimension == ResourceViewDimension::RawBuffer) { return DXGI_FORMAT_R32_TYPELESS; } if (desc.dimension == ResourceViewDimension::StructuredBuffer) { return DXGI_FORMAT_UNKNOWN; } if (desc.dimension == ResourceViewDimension::Buffer && desc.format != 0) { return ToD3D12(static_cast(desc.format)); } return DXGI_FORMAT_UNKNOWN; } } // namespace D3D12Device::D3D12Device() : m_isDeviceRemoved(false) , m_initialized(false) { } D3D12Device::~D3D12Device() { Shutdown(); } bool D3D12Device::Initialize(const RHIDeviceDesc& desc) { if (m_initialized) { return true; } m_deviceDesc = desc; if (!CreateDXGIFactory(desc.enableDebugLayer)) { return false; } ComPtr adapter; int adapterIndex = desc.adapterIndex; bool adapterFound = false; while (m_factory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND) { DXGI_ADAPTER_DESC1 descAdapter; adapter->GetDesc1(&descAdapter); if (descAdapter.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { adapterIndex++; continue; } HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr); if (SUCCEEDED(hr)) { adapterFound = true; m_adapter = adapter; break; } adapterIndex++; } if (!adapterFound) { return false; } if (!CreateDevice(m_adapter.Get())) { return false; } QueryAdapterInfo(); m_initialized = true; return true; } void D3D12Device::Shutdown() { if (m_device) { m_device.Reset(); } if (m_factory) { m_factory.Reset(); } m_adapter.Reset(); m_initialized = false; } bool D3D12Device::CreateDXGIFactory(bool enableDebugLayer) { UINT dxgiFactoryFlags = 0; if (enableDebugLayer) { ID3D12Debug* debugController = nullptr; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) { debugController->EnableDebugLayer(); dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; debugController->Release(); } } HRESULT hr = CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&m_factory)); return SUCCEEDED(hr); } bool D3D12Device::CreateDevice(IDXGIAdapter1* adapter) { OutputDebugStringA("[DEBUG] CreateDevice: start\n"); HRESULT hr = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); if (FAILED(hr)) { char buf[256]; sprintf(buf, "[DEBUG] CreateDevice: D3D12CreateDevice failed! hr=%08X\n", hr); OutputDebugStringA(buf); } return SUCCEEDED(hr); } void D3D12Device::QueryAdapterInfo() { if (!m_adapter) { return; } DXGI_ADAPTER_DESC1 desc; m_adapter->GetDesc1(&desc); m_adapterInfo.vendorId = desc.VendorId; m_adapterInfo.deviceId = desc.DeviceId; m_adapterInfo.dedicatedVideoMemory = desc.DedicatedVideoMemory; m_adapterInfo.dedicatedSystemMemory = desc.DedicatedSystemMemory; m_adapterInfo.sharedSystemMemory = desc.SharedSystemMemory; m_adapterInfo.description = desc.Description; m_adapterInfo.isSoftware = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0; m_deviceInfo.description = desc.Description; m_deviceInfo.vendorId = desc.VendorId; m_deviceInfo.deviceId = desc.DeviceId; m_deviceInfo.dedicatedVideoMemory = desc.DedicatedVideoMemory; m_deviceInfo.dedicatedSystemMemory = desc.DedicatedSystemMemory; m_deviceInfo.sharedSystemMemory = desc.SharedSystemMemory; m_deviceInfo.isSoftware = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0; switch (desc.VendorId) { case 0x10DE: m_deviceInfo.vendor = L"NVIDIA"; break; case 0x1002: m_deviceInfo.vendor = L"AMD"; break; case 0x8086: m_deviceInfo.vendor = L"Intel"; break; default: m_deviceInfo.vendor = L"Unknown"; break; } m_deviceInfo.renderer = desc.Description; D3D12_FEATURE_DATA_D3D12_OPTIONS5 options5 = {}; if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, sizeof(options5)))) { m_capabilities.bSupportsRayTracing = options5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED; } D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = {}; if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, sizeof(options7)))) { m_capabilities.bSupportsMeshShaders = options7.MeshShaderTier != D3D12_MESH_SHADER_TIER_NOT_SUPPORTED; } D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)))) { m_capabilities.bSupportsConservativeRasterization = options.ConservativeRasterizationTier != D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED; } m_capabilities.bSupportsComputeShaders = true; D3D12_FEATURE_DATA_SHADER_MODEL shaderModel = {}; const D3D_SHADER_MODEL shaderModelCandidates[] = { D3D_SHADER_MODEL_6_7, D3D_SHADER_MODEL_6_6, D3D_SHADER_MODEL_6_5, D3D_SHADER_MODEL_6_4, D3D_SHADER_MODEL_6_3, D3D_SHADER_MODEL_6_2, D3D_SHADER_MODEL_6_1, D3D_SHADER_MODEL_6_0, D3D_SHADER_MODEL_5_1 }; for (D3D_SHADER_MODEL candidate : shaderModelCandidates) { shaderModel.HighestShaderModel = candidate; if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof(shaderModel)))) { m_capabilities.shaderModel = ShaderModelToString(shaderModel.HighestShaderModel); break; } } D3D12_FEATURE_DATA_D3D12_OPTIONS3 options3 = {}; if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3)))) { } D3D12_FEATURE_DATA_D3D12_OPTIONS4 options4 = {}; if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS4, &options4, sizeof(options4)))) { } m_capabilities.maxRenderTargets = 8; m_capabilities.maxViewports = 16; m_capabilities.maxVertexAttribs = 8; m_capabilities.maxColorAttachments = 8; m_capabilities.maxConstantBufferSize = D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16; m_capabilities.maxAnisotropy = D3D12_MAX_MAXANISOTROPY; m_capabilities.maxTexture2DSize = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; m_capabilities.maxTexture3DSize = D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION; m_capabilities.maxTextureCubeSize = D3D12_REQ_TEXTURECUBE_DIMENSION; } bool D3D12Device::CheckFeatureSupport(D3D12_FEATURE feature, void* featureSupportData, uint32_t featureSupportDataSize) { return SUCCEEDED(m_device->CheckFeatureSupport(feature, featureSupportData, featureSupportDataSize)); } std::vector D3D12Device::EnumerateAdapters() { std::vector adapters; if (!m_factory) { return adapters; } ComPtr adapter; int adapterIndex = 0; while (m_factory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND) { DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); AdapterInfo info; info.description = desc.Description; info.dedicatedVideoMemory = desc.DedicatedVideoMemory; info.dedicatedSystemMemory = desc.DedicatedSystemMemory; info.sharedSystemMemory = desc.SharedSystemMemory; info.vendorId = desc.VendorId; info.deviceId = desc.DeviceId; info.isSoftware = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0; adapters.push_back(info); adapterIndex++; } return adapters; } UINT D3D12Device::GetDescriptorHandleIncrementSize(DescriptorHeapType type) const { return m_device->GetDescriptorHandleIncrementSize(ToD3D12(type)); } void* D3D12Device::GetNativeHandle() const { return m_device.Get(); } void* D3D12Device::GetNativeDevice() { return m_device.Get(); } bool D3D12Device::ReadTexturePixelRGBA8( RHICommandQueue* commandQueue, RHITexture* texture, ResourceStates sourceState, uint32_t pixelX, uint32_t pixelY, std::array& outRgba) { outRgba = {}; auto* d3d12Queue = static_cast(commandQueue); auto* d3d12Texture = static_cast(texture); if (m_device == nullptr || d3d12Queue == nullptr || d3d12Texture == nullptr || d3d12Texture->GetResource() == nullptr || pixelX >= d3d12Texture->GetWidth() || pixelY >= d3d12Texture->GetHeight()) { return false; } ID3D12CommandAllocator* commandAllocator = nullptr; if (FAILED(m_device->CreateCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)))) { return false; } ID3D12GraphicsCommandList* commandList = nullptr; if (FAILED(m_device->CreateCommandList( 0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, nullptr, IID_PPV_ARGS(&commandList)))) { commandAllocator->Release(); return false; } constexpr UINT kRowPitch = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT; D3D12_HEAP_PROPERTIES heapProperties = {}; heapProperties.Type = D3D12_HEAP_TYPE_READBACK; D3D12_RESOURCE_DESC readbackDesc = {}; readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; readbackDesc.Width = kRowPitch; readbackDesc.Height = 1; readbackDesc.DepthOrArraySize = 1; readbackDesc.MipLevels = 1; readbackDesc.SampleDesc.Count = 1; readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; ID3D12Resource* readbackBuffer = nullptr; if (FAILED(m_device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &readbackDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&readbackBuffer)))) { commandList->Release(); commandAllocator->Release(); return false; } ID3D12Resource* sourceResource = d3d12Texture->GetResource(); D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Transition.pResource = sourceResource; barrier.Transition.StateBefore = ToD3D12(sourceState); barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; commandList->ResourceBarrier(1, &barrier); D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; srcLocation.pResource = sourceResource; srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; srcLocation.SubresourceIndex = 0; D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; dstLocation.pResource = readbackBuffer; dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dstLocation.PlacedFootprint.Offset = 0; dstLocation.PlacedFootprint.Footprint.Format = sourceResource->GetDesc().Format; dstLocation.PlacedFootprint.Footprint.Width = 1; dstLocation.PlacedFootprint.Footprint.Height = 1; dstLocation.PlacedFootprint.Footprint.Depth = 1; dstLocation.PlacedFootprint.Footprint.RowPitch = kRowPitch; D3D12_BOX sourceBox = {}; sourceBox.left = pixelX; sourceBox.top = pixelY; sourceBox.front = 0; sourceBox.right = pixelX + 1u; sourceBox.bottom = pixelY + 1u; sourceBox.back = 1; commandList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, &sourceBox); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; barrier.Transition.StateAfter = ToD3D12(sourceState); commandList->ResourceBarrier(1, &barrier); if (FAILED(commandList->Close())) { readbackBuffer->Release(); commandList->Release(); commandAllocator->Release(); return false; } ID3D12CommandList* commandLists[] = { commandList }; d3d12Queue->GetCommandQueue()->ExecuteCommandLists(1, commandLists); if (!WaitForQueueIdle(m_device.Get(), d3d12Queue->GetCommandQueue())) { readbackBuffer->Release(); commandList->Release(); commandAllocator->Release(); return false; } void* mappedData = nullptr; D3D12_RANGE readRange = { 0, 4 }; if (FAILED(readbackBuffer->Map(0, &readRange, &mappedData))) { readbackBuffer->Release(); commandList->Release(); commandAllocator->Release(); return false; } std::memcpy(outRgba.data(), mappedData, outRgba.size()); D3D12_RANGE writeRange = { 0, 0 }; readbackBuffer->Unmap(0, &writeRange); readbackBuffer->Release(); commandList->Release(); commandAllocator->Release(); return true; } const RHICapabilities& D3D12Device::GetCapabilities() const { return m_capabilities; } const RHIDeviceInfo& D3D12Device::GetDeviceInfo() const { return m_deviceInfo; } RHIBuffer* D3D12Device::CreateBuffer(const BufferDesc& desc) { auto* buffer = new D3D12Buffer(); const BufferType bufferType = static_cast(desc.bufferType); const BufferFlags bufferFlags = static_cast(desc.flags); D3D12_HEAP_TYPE heapType = D3D12_HEAP_TYPE_DEFAULT; D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON; D3D12_RESOURCE_FLAGS resourceFlags = D3D12_RESOURCE_FLAG_NONE; if (bufferType == BufferType::ReadBack) { heapType = D3D12_HEAP_TYPE_READBACK; initialState = D3D12_RESOURCE_STATE_COPY_DEST; } else if (bufferType == BufferType::Constant || bufferType == BufferType::Vertex || bufferType == BufferType::Index) { heapType = D3D12_HEAP_TYPE_UPLOAD; initialState = D3D12_RESOURCE_STATE_GENERIC_READ; } else if (bufferType == BufferType::Storage) { const bool allowUnorderedAccess = (bufferFlags & BufferFlags::AllowUnorderedAccess) == BufferFlags::AllowUnorderedAccess; if (allowUnorderedAccess) { resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } else { heapType = D3D12_HEAP_TYPE_UPLOAD; initialState = D3D12_RESOURCE_STATE_GENERIC_READ; } } if (ShouldTraceLargeStorageBuffer(desc)) { LogVolumeTraceRendering( "D3D12 CreateBuffer legacy size_bytes=" + std::to_string(desc.size) + " heap=" + std::to_string(static_cast(heapType)) + " flags=" + std::to_string(static_cast(desc.flags))); } if (buffer->Initialize(m_device.Get(), desc.size, initialState, heapType, resourceFlags)) { buffer->SetStride(desc.stride); buffer->SetBufferType(bufferType); buffer->SetState(ResourceStates::Common); return buffer; } delete buffer; return nullptr; } RHIBuffer* D3D12Device::CreateBuffer( const BufferDesc& desc, const void* initialData, size_t initialDataSize, ResourceStates finalState) { const bool traceLargeStorageBuffer = ShouldTraceLargeStorageBuffer(desc); const uint64_t traceStartMs = traceLargeStorageBuffer ? GetVolumeTraceSteadyMs() : 0u; if (traceLargeStorageBuffer) { LogVolumeTraceRendering( "D3D12 CreateBuffer(initialData) begin steady_ms=" + std::to_string(traceStartMs) + " size_bytes=" + std::to_string(desc.size) + " initial_bytes=" + std::to_string(initialDataSize)); } if (initialData == nullptr || initialDataSize == 0u) { return CreateBuffer(desc); } if (m_device == nullptr || desc.size == 0u || initialDataSize > desc.size || desc.size > static_cast(SIZE_MAX)) { if (traceLargeStorageBuffer) { LogVolumeTraceRendering("D3D12 CreateBuffer(initialData) rejected invalid parameters"); } return nullptr; } const BufferType bufferType = static_cast(desc.bufferType); if (bufferType == BufferType::ReadBack) { if (traceLargeStorageBuffer) { LogVolumeTraceRendering("D3D12 CreateBuffer(initialData) rejected readback buffer"); } return nullptr; } const BufferFlags bufferFlags = static_cast(desc.flags); D3D12_RESOURCE_FLAGS resourceFlags = D3D12_RESOURCE_FLAG_NONE; if ((bufferFlags & BufferFlags::AllowUnorderedAccess) == BufferFlags::AllowUnorderedAccess) { resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } D3D12CommandQueue uploadQueue; if (!uploadQueue.Initialize(m_device.Get(), CommandQueueType::Direct)) { if (traceLargeStorageBuffer) { LogVolumeTraceRendering("D3D12 CreateBuffer(initialData) failed stage=init_queue"); } return nullptr; } D3D12CommandAllocator uploadAllocator; if (!uploadAllocator.Initialize(m_device.Get(), CommandQueueType::Direct)) { if (traceLargeStorageBuffer) { LogVolumeTraceRendering("D3D12 CreateBuffer(initialData) failed stage=init_allocator"); } uploadQueue.Shutdown(); return nullptr; } D3D12CommandList uploadCommandList; if (!uploadCommandList.Initialize(m_device.Get(), CommandQueueType::Direct, uploadAllocator.GetCommandAllocator())) { if (traceLargeStorageBuffer) { LogVolumeTraceRendering("D3D12 CreateBuffer(initialData) failed stage=init_command_list"); } uploadAllocator.Shutdown(); uploadQueue.Shutdown(); return nullptr; } auto shutdownUploadContext = [&]() { uploadCommandList.Shutdown(); uploadAllocator.Shutdown(); uploadQueue.Shutdown(); }; uploadAllocator.Reset(); uploadCommandList.Reset(); const uint64_t commandSetupEndMs = traceLargeStorageBuffer ? GetVolumeTraceSteadyMs() : 0u; auto* buffer = new D3D12Buffer(); if (!buffer->Initialize( m_device.Get(), desc.size, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_HEAP_TYPE_DEFAULT, resourceFlags)) { if (traceLargeStorageBuffer) { LogVolumeTraceRendering("D3D12 CreateBuffer(initialData) failed stage=create_default_buffer"); } delete buffer; shutdownUploadContext(); return nullptr; } const uint64_t defaultBufferEndMs = traceLargeStorageBuffer ? GetVolumeTraceSteadyMs() : 0u; buffer->SetStride(desc.stride); buffer->SetBufferType(bufferType); buffer->SetState(ResourceStates::CopyDst); D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; uploadHeapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; uploadHeapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; uploadHeapProperties.CreationNodeMask = 0; uploadHeapProperties.VisibleNodeMask = 0; D3D12_RESOURCE_DESC uploadBufferDesc = {}; uploadBufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; uploadBufferDesc.Alignment = 0; uploadBufferDesc.Width = desc.size; uploadBufferDesc.Height = 1; uploadBufferDesc.DepthOrArraySize = 1; uploadBufferDesc.MipLevels = 1; uploadBufferDesc.Format = DXGI_FORMAT_UNKNOWN; uploadBufferDesc.SampleDesc.Count = 1; uploadBufferDesc.SampleDesc.Quality = 0; uploadBufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; uploadBufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE; ComPtr uploadBuffer; if (FAILED(m_device->CreateCommittedResource( &uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &uploadBufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)))) { if (traceLargeStorageBuffer) { LogVolumeTraceRendering("D3D12 CreateBuffer(initialData) failed stage=create_upload_buffer"); } buffer->Shutdown(); delete buffer; shutdownUploadContext(); return nullptr; } const uint64_t uploadBufferEndMs = traceLargeStorageBuffer ? GetVolumeTraceSteadyMs() : 0u; void* mappedData = nullptr; D3D12_RANGE readRange = { 0, 0 }; if (FAILED(uploadBuffer->Map(0, &readRange, &mappedData)) || mappedData == nullptr) { if (traceLargeStorageBuffer) { LogVolumeTraceRendering("D3D12 CreateBuffer(initialData) failed stage=map_upload_buffer"); } buffer->Shutdown(); delete buffer; shutdownUploadContext(); return nullptr; } std::memset(mappedData, 0, static_cast(desc.size)); std::memcpy(mappedData, initialData, initialDataSize); uploadBuffer->Unmap(0, nullptr); const uint64_t cpuFillEndMs = traceLargeStorageBuffer ? GetVolumeTraceSteadyMs() : 0u; ID3D12GraphicsCommandList* const commandList = uploadCommandList.GetCommandList(); commandList->CopyBufferRegion( buffer->GetResource(), 0, uploadBuffer.Get(), 0, desc.size); const D3D12_RESOURCE_STATES resolvedFinalState = ToD3D12(finalState); if (resolvedFinalState != D3D12_RESOURCE_STATE_COPY_DEST) { D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = buffer->GetResource(); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; barrier.Transition.StateAfter = resolvedFinalState; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; commandList->ResourceBarrier(1, &barrier); } const uint64_t recordCommandsEndMs = traceLargeStorageBuffer ? GetVolumeTraceSteadyMs() : 0u; uploadCommandList.Close(); ID3D12CommandList* commandLists[] = { commandList }; uploadQueue.ExecuteCommandListsInternal(1, commandLists); const uint64_t submitEndMs = traceLargeStorageBuffer ? GetVolumeTraceSteadyMs() : 0u; if (traceLargeStorageBuffer) { LogVolumeTraceRendering( "D3D12 CreateBuffer(initialData) waiting_for_idle steady_ms=" + std::to_string(submitEndMs) + " size_bytes=" + std::to_string(desc.size)); } uploadQueue.WaitForIdle(); const uint64_t waitEndMs = traceLargeStorageBuffer ? GetVolumeTraceSteadyMs() : 0u; buffer->SetState(finalState); if (traceLargeStorageBuffer) { LogVolumeTraceRendering( "D3D12 CreateBuffer(initialData) end steady_ms=" + std::to_string(waitEndMs) + " total_ms=" + std::to_string(waitEndMs - traceStartMs) + " setup_ms=" + std::to_string(commandSetupEndMs - traceStartMs) + " default_buffer_ms=" + std::to_string(defaultBufferEndMs - commandSetupEndMs) + " upload_buffer_ms=" + std::to_string(uploadBufferEndMs - defaultBufferEndMs) + " cpu_fill_ms=" + std::to_string(cpuFillEndMs - uploadBufferEndMs) + " record_ms=" + std::to_string(recordCommandsEndMs - cpuFillEndMs) + " submit_ms=" + std::to_string(submitEndMs - recordCommandsEndMs) + " wait_ms=" + std::to_string(waitEndMs - submitEndMs)); } shutdownUploadContext(); return buffer; } RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc) { auto* texture = new D3D12Texture(); D3D12_RESOURCE_DESC d3d12Desc = {}; const TextureType textureType = ResolveTextureType(desc.textureType); const Format format = static_cast(desc.format); const bool isDepthFormat = IsDepthFormat(format); d3d12Desc.Dimension = ToD3D12(textureType); d3d12Desc.Width = desc.width; d3d12Desc.Height = desc.height; d3d12Desc.DepthOrArraySize = ResolveDepthOrArraySize(desc, textureType); d3d12Desc.MipLevels = desc.mipLevels > 0 ? desc.mipLevels : 1; d3d12Desc.Format = ResolveD3D12ResourceFormat(format); d3d12Desc.SampleDesc.Count = desc.sampleCount > 0 ? desc.sampleCount : 1; d3d12Desc.SampleDesc.Quality = desc.sampleQuality; d3d12Desc.Flags = static_cast(desc.flags); if (isDepthFormat) { d3d12Desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; } else if (d3d12Desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && desc.width > 0 && desc.height > 0) { d3d12Desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; } d3d12Desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; const D3D12_RESOURCE_STATES initialState = isDepthFormat ? D3D12_RESOURCE_STATE_DEPTH_WRITE : D3D12_RESOURCE_STATE_COMMON; if (texture->Initialize(m_device.Get(), d3d12Desc, initialState)) { texture->SetFormat(format); texture->SetTextureType(textureType); if (isDepthFormat) { texture->SetState(ResourceStates::DepthWrite); } return texture; } delete texture; return nullptr; } RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc, const void* initialData, size_t initialDataSize, uint32_t rowPitch) { (void)initialDataSize; if (initialData == nullptr) { return CreateTexture(desc); } const Format format = static_cast(desc.format); uint32_t resolvedRowPitch = rowPitch; if (resolvedRowPitch == 0) { const uint32_t bytesPerPixel = GetFormatBytesPerPixel(format); if (bytesPerPixel == 0) { return nullptr; } resolvedRowPitch = desc.width * bytesPerPixel; } D3D12CommandQueue uploadQueue; if (!uploadQueue.Initialize(m_device.Get(), CommandQueueType::Direct)) { return nullptr; } D3D12CommandAllocator uploadAllocator; if (!uploadAllocator.Initialize(m_device.Get(), CommandQueueType::Direct)) { uploadQueue.Shutdown(); return nullptr; } D3D12CommandList uploadCommandList; if (!uploadCommandList.Initialize(m_device.Get(), CommandQueueType::Direct, uploadAllocator.GetCommandAllocator())) { uploadAllocator.Shutdown(); uploadQueue.Shutdown(); return nullptr; } uploadAllocator.Reset(); uploadCommandList.Reset(); auto* texture = new D3D12Texture(); D3D12_RESOURCE_DESC textureDesc = {}; const TextureType textureType = ResolveTextureType(desc.textureType); textureDesc.Dimension = ToD3D12(textureType); textureDesc.Alignment = 0; textureDesc.Width = desc.width; textureDesc.Height = desc.height; textureDesc.DepthOrArraySize = ResolveDepthOrArraySize(desc, textureType); textureDesc.MipLevels = desc.mipLevels > 0 ? static_cast(desc.mipLevels) : 1; textureDesc.Format = ResolveD3D12ResourceFormat(format); textureDesc.SampleDesc.Count = desc.sampleCount > 0 ? desc.sampleCount : 1; textureDesc.SampleDesc.Quality = desc.sampleQuality; textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; textureDesc.Flags = static_cast(desc.flags); ComPtr uploadBuffer; if (!texture->InitializeFromData( m_device.Get(), uploadCommandList.GetCommandList(), textureDesc, textureType, initialData, initialDataSize, resolvedRowPitch, &uploadBuffer)) { delete texture; uploadCommandList.Shutdown(); uploadAllocator.Shutdown(); uploadQueue.Shutdown(); return nullptr; } texture->SetState(ResourceStates::PixelShaderResource); texture->SetFormat(format); texture->SetTextureType(textureType); uploadCommandList.Close(); ID3D12CommandList* commandLists[] = { uploadCommandList.GetCommandList() }; uploadQueue.ExecuteCommandListsInternal(1, commandLists); uploadQueue.WaitForIdle(); uploadBuffer.Reset(); uploadCommandList.Shutdown(); uploadAllocator.Shutdown(); uploadQueue.Shutdown(); return texture; } RHIShader* D3D12Device::CreateShader(const ShaderCompileDesc& desc) { auto* shader = new D3D12Shader(); const std::string entryPoint = NarrowAscii(desc.entryPoint); const std::string profile = NarrowAscii(desc.profile); const char* entryPointPtr = entryPoint.empty() ? nullptr : entryPoint.c_str(); const char* profilePtr = profile.empty() ? nullptr : profile.c_str(); bool success = false; if (!desc.source.empty()) { success = shader->Compile( desc.source.data(), desc.source.size(), desc.fileName.empty() ? nullptr : desc.fileName.c_str(), nullptr, entryPointPtr, profilePtr); } else if (!desc.fileName.empty()) { success = shader->CompileFromFile(desc.fileName.c_str(), entryPointPtr, profilePtr); } if (success) { return shader; } delete shader; return nullptr; } RHISampler* D3D12Device::CreateSampler(const SamplerDesc& desc) { auto* sampler = new D3D12Sampler(); D3D12_SAMPLER_DESC d3d12Desc = {}; d3d12Desc.Filter = ToD3D12(static_cast(desc.filter)); d3d12Desc.AddressU = ToD3D12(static_cast(desc.addressU)); d3d12Desc.AddressV = ToD3D12(static_cast(desc.addressV)); d3d12Desc.AddressW = ToD3D12(static_cast(desc.addressW)); d3d12Desc.MipLODBias = desc.mipLodBias; d3d12Desc.MaxAnisotropy = desc.maxAnisotropy; d3d12Desc.ComparisonFunc = ToD3D12(static_cast(desc.comparisonFunc)); d3d12Desc.BorderColor[0] = desc.borderColorR; d3d12Desc.BorderColor[1] = desc.borderColorG; d3d12Desc.BorderColor[2] = desc.borderColorB; d3d12Desc.BorderColor[3] = desc.borderColorA; d3d12Desc.MinLOD = desc.minLod; d3d12Desc.MaxLOD = desc.maxLod; if (sampler->Initialize(m_device.Get(), d3d12Desc)) { return sampler; } delete sampler; return nullptr; } RHIDescriptorPool* D3D12Device::CreateDescriptorPool(const DescriptorPoolDesc& desc) { auto* pool = new D3D12DescriptorHeap(); DescriptorPoolDesc poolDesc = desc; poolDesc.device = m_device.Get(); if (pool->Initialize(poolDesc)) { return pool; } delete pool; return nullptr; } RHIRenderPass* D3D12Device::CreateRenderPass( uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments, const AttachmentDesc* depthStencilAttachment) { auto* renderPass = new D3D12RenderPass(); if (!renderPass->Initialize(colorAttachmentCount, colorAttachments, depthStencilAttachment)) { delete renderPass; return nullptr; } return renderPass; } RHIFramebuffer* D3D12Device::CreateFramebuffer( class RHIRenderPass* renderPass, uint32_t width, uint32_t height, uint32_t colorAttachmentCount, RHIResourceView** colorAttachments, RHIResourceView* depthStencilAttachment) { auto* framebuffer = new D3D12Framebuffer(); if (!framebuffer->Initialize(renderPass, width, height, colorAttachmentCount, colorAttachments, depthStencilAttachment)) { delete framebuffer; return nullptr; } return framebuffer; } RHIDescriptorSet* D3D12Device::CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) { if (pool == nullptr) { return nullptr; } return pool->AllocateSet(layout); } RHIFence* D3D12Device::CreateFence(const FenceDesc& desc) { auto* fence = new D3D12Fence(); if (fence->Initialize(m_device.Get(), desc.initialValue)) { return fence; } delete fence; return nullptr; } RHIResourceView* D3D12Device::CreateVertexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) { if (!buffer) { return nullptr; } auto* view = new D3D12ResourceView(); view->InitializeAsVertexBuffer(static_cast(buffer), desc); if (!view->IsValid()) { delete view; return nullptr; } return view; } RHIResourceView* D3D12Device::CreateIndexBufferView(RHIBuffer* buffer, const ResourceViewDesc& desc) { if (!buffer) { return nullptr; } auto* view = new D3D12ResourceView(); view->InitializeAsIndexBuffer(static_cast(buffer), desc); if (!view->IsValid()) { delete view; return nullptr; } return view; } RHISwapChain* D3D12Device::CreateSwapChain(const SwapChainDesc& desc, RHICommandQueue* presentQueue) { if (presentQueue == nullptr) { return nullptr; } auto* nativeQueue = static_cast(presentQueue->GetNativeHandle()); if (nativeQueue == nullptr) { return nullptr; } auto* swapChain = new D3D12SwapChain(); HWND hwnd = static_cast(desc.windowHandle); if (swapChain->Initialize(m_factory.Get(), nativeQueue, hwnd, desc.width, desc.height, desc.bufferCount)) { return swapChain; } delete swapChain; return nullptr; } RHICommandList* D3D12Device::CreateCommandList(const CommandListDesc& desc) { if (!m_device) { return nullptr; } auto allocator = std::make_unique(); if (!allocator->Initialize(m_device.Get(), static_cast(desc.commandListType))) { return nullptr; } auto* cmdList = new D3D12CommandList(); if (!cmdList->Initialize(m_device.Get(), static_cast(desc.commandListType), allocator->GetCommandAllocator())) { delete cmdList; return nullptr; } return cmdList; } RHICommandQueue* D3D12Device::CreateCommandQueue(const CommandQueueDesc& desc) { auto* queue = new D3D12CommandQueue(); if (queue->Initialize(m_device.Get(), static_cast(desc.queueType))) { return queue; } delete queue; return nullptr; } RHIPipelineState* D3D12Device::CreatePipelineState(const GraphicsPipelineDesc& desc) { const bool traceVolumetricPipeline = ShouldTraceVolumetricShaderCompile(desc.vertexShader) || ShouldTraceVolumetricShaderCompile(desc.fragmentShader) || ShouldTraceVolumetricShaderCompile(desc.geometryShader); const uint64_t pipelineStartMs = traceVolumetricPipeline ? GetVolumeTraceSteadyMs() : 0u; if (traceVolumetricPipeline) { LogVolumeTraceRendering( "D3D12 CreatePipelineState begin steady_ms=" + std::to_string(pipelineStartMs) + " has_vs=" + std::to_string(HasShaderPayload(desc.vertexShader) ? 1 : 0) + " has_ps=" + std::to_string(HasShaderPayload(desc.fragmentShader) ? 1 : 0) + " has_gs=" + std::to_string(HasShaderPayload(desc.geometryShader) ? 1 : 0)); } auto* pso = new D3D12PipelineState(m_device.Get()); pso->SetInputLayout(desc.inputLayout); pso->SetRasterizerState(desc.rasterizerState); pso->SetBlendState(desc.blendState); pso->SetDepthStencilState(desc.depthStencilState); pso->SetTopology(desc.topologyType); pso->SetRenderTargetFormats(desc.renderTargetCount, desc.renderTargetFormats, desc.depthStencilFormat); pso->SetSampleCount(desc.sampleCount); pso->SetSampleQuality(desc.sampleQuality); const bool hasVertexShader = HasShaderPayload(desc.vertexShader); const bool hasFragmentShader = HasShaderPayload(desc.fragmentShader); const bool hasGeometryShader = HasShaderPayload(desc.geometryShader); if (!hasVertexShader && !hasFragmentShader && !hasGeometryShader) { return pso; } if (!hasVertexShader || !hasFragmentShader) { delete pso; return nullptr; } D3D12RootSignature* rootSignature = nullptr; if (desc.pipelineLayout != nullptr) { auto* pipelineLayout = static_cast(desc.pipelineLayout); pso->SetRootSignature(pipelineLayout->GetRootSignature()); } else { rootSignature = CreateRootSignature({}); if (rootSignature == nullptr) { delete pso; return nullptr; } pso->SetRootSignature(rootSignature->GetRootSignature()); } D3D12Shader vertexShader; D3D12Shader fragmentShader; D3D12Shader geometryShader; const bool vertexCompiled = CompileD3D12Shader(desc.vertexShader, vertexShader); const bool fragmentCompiled = CompileD3D12Shader(desc.fragmentShader, fragmentShader); const bool geometryCompiled = !hasGeometryShader || CompileD3D12Shader(desc.geometryShader, geometryShader); if (!vertexCompiled || !fragmentCompiled || !geometryCompiled) { if (traceVolumetricPipeline) { const uint64_t failureMs = GetVolumeTraceSteadyMs(); LogVolumeTraceRendering( "D3D12 CreatePipelineState failed steady_ms=" + std::to_string(failureMs) + " total_ms=" + std::to_string(failureMs - pipelineStartMs) + " vertex_ok=" + std::to_string(vertexCompiled ? 1 : 0) + " fragment_ok=" + std::to_string(fragmentCompiled ? 1 : 0) + " geometry_ok=" + std::to_string(geometryCompiled ? 1 : 0)); } if (rootSignature != nullptr) { rootSignature->Shutdown(); delete rootSignature; } delete pso; return nullptr; } pso->SetShaderBytecodes( vertexShader.GetD3D12Bytecode(), fragmentShader.GetD3D12Bytecode(), hasGeometryShader ? geometryShader.GetD3D12Bytecode() : D3D12_SHADER_BYTECODE{}); const uint64_t finalizeStartMs = traceVolumetricPipeline ? GetVolumeTraceSteadyMs() : 0u; if (traceVolumetricPipeline) { LogVolumeTraceRendering( "D3D12 CreatePipelineState finalize begin steady_ms=" + std::to_string(finalizeStartMs)); } pso->EnsureValid(); if (traceVolumetricPipeline) { const uint64_t finalizeEndMs = GetVolumeTraceSteadyMs(); LogVolumeTraceRendering( "D3D12 CreatePipelineState finalize end steady_ms=" + std::to_string(finalizeEndMs) + " total_ms=" + std::to_string(finalizeEndMs - finalizeStartMs) + " valid=" + std::to_string(pso->IsValid() ? 1 : 0)); } if (rootSignature != nullptr) { rootSignature->Shutdown(); delete rootSignature; } if (!pso->IsValid()) { if (traceVolumetricPipeline) { const uint64_t failureMs = GetVolumeTraceSteadyMs(); LogVolumeTraceRendering( "D3D12 CreatePipelineState invalid steady_ms=" + std::to_string(failureMs) + " total_ms=" + std::to_string(failureMs - pipelineStartMs)); } delete pso; return nullptr; } if (traceVolumetricPipeline) { const uint64_t pipelineEndMs = GetVolumeTraceSteadyMs(); LogVolumeTraceRendering( "D3D12 CreatePipelineState end steady_ms=" + std::to_string(pipelineEndMs) + " total_ms=" + std::to_string(pipelineEndMs - pipelineStartMs)); } return pso; } RHIPipelineLayout* D3D12Device::CreatePipelineLayout(const RHIPipelineLayoutDesc& desc) { auto* pipelineLayout = new D3D12PipelineLayout(); if (!pipelineLayout->InitializeWithDevice(this, desc)) { delete pipelineLayout; return nullptr; } return pipelineLayout; } RHIResourceView* D3D12Device::CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) { if (!texture) { return nullptr; } auto* view = new D3D12ResourceView(); auto* d3d12Texture = static_cast(texture); ID3D12Resource* resource = d3d12Texture->GetResource(); if (!resource) { delete view; return nullptr; } D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; const Format format = desc.format != 0 ? static_cast(desc.format) : texture->GetFormat(); const TextureType textureType = texture->GetTextureType(); rtvDesc.Format = ToD3D12(format); rtvDesc.ViewDimension = ResolveRTVDimension(desc, textureType); if (rtvDesc.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE2DARRAY) { rtvDesc.Texture2DArray.MipSlice = desc.mipLevel; rtvDesc.Texture2DArray.FirstArraySlice = desc.firstArraySlice; rtvDesc.Texture2DArray.ArraySize = desc.arraySize > 0 ? desc.arraySize : 1; rtvDesc.Texture2DArray.PlaneSlice = desc.planeSlice; } else if (rtvDesc.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE3D) { rtvDesc.Texture3D.MipSlice = desc.mipLevel; rtvDesc.Texture3D.FirstWSlice = desc.firstArraySlice; rtvDesc.Texture3D.WSize = desc.arraySize > 0 ? desc.arraySize : 1; } else { rtvDesc.Texture2D.MipSlice = desc.mipLevel; rtvDesc.Texture2D.PlaneSlice = desc.planeSlice; } auto heap = std::make_unique(); if (!heap->Initialize(m_device.Get(), DescriptorHeapType::RTV, 1, false)) { delete view; return nullptr; } view->InitializeAsRenderTarget(m_device.Get(), resource, &rtvDesc, heap.get(), 0); view->SetOwnedHeap(std::move(heap)); return view; } RHIResourceView* D3D12Device::CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) { auto* view = new D3D12ResourceView(); auto* d3d12Texture = static_cast(texture); ID3D12Resource* resource = d3d12Texture->GetResource(); D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; const Format format = desc.format != 0 ? static_cast(desc.format) : texture->GetFormat(); dsvDesc.Format = ResolveD3D12DepthStencilViewFormat(format); dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; auto heap = std::make_unique(); if (!heap->Initialize(m_device.Get(), DescriptorHeapType::DSV, 1, false)) { delete view; return nullptr; } view->InitializeAsDepthStencil(m_device.Get(), resource, &dsvDesc, heap.get(), 0); view->SetOwnedHeap(std::move(heap)); return view; } RHIResourceView* D3D12Device::CreateShaderResourceView(RHIBuffer* buffer, const ResourceViewDesc& desc) { if (buffer == nullptr || m_device == nullptr || !IsSupportedBufferViewDimension(desc.dimension)) { return nullptr; } if (desc.dimension == ResourceViewDimension::Buffer && desc.format == 0) { return nullptr; } uint32_t firstElement = 0; uint32_t elementStride = 0; if (!TryResolveBufferViewFirstElement(buffer, desc, firstElement, elementStride)) { return nullptr; } uint32_t elementCount = 0; if (!TryResolveBufferViewElementCount(buffer, desc, elementStride, firstElement, elementCount)) { return nullptr; } D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.Format = ResolveD3D12BufferViewFormat(desc); srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srvDesc.Buffer.FirstElement = firstElement; srvDesc.Buffer.NumElements = elementCount; srvDesc.Buffer.StructureByteStride = desc.dimension == ResourceViewDimension::StructuredBuffer ? elementStride : 0u; srvDesc.Buffer.Flags = desc.dimension == ResourceViewDimension::RawBuffer ? D3D12_BUFFER_SRV_FLAG_RAW : D3D12_BUFFER_SRV_FLAG_NONE; auto heap = std::make_unique(); if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, false)) { return nullptr; } auto* view = new D3D12ResourceView(); view->InitializeAsShaderResourceBuffer( m_device.Get(), static_cast(buffer), desc, &srvDesc, heap.get(), 0); if (!view->IsValid()) { delete view; return nullptr; } view->SetOwnedHeap(std::move(heap)); return view; } RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) { auto* view = new D3D12ResourceView(); auto* d3d12Texture = static_cast(texture); ID3D12Resource* resource = d3d12Texture->GetResource(); D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; const Format format = desc.format != 0 ? static_cast(desc.format) : texture->GetFormat(); const TextureType textureType = texture->GetTextureType(); srvDesc.Format = ResolveD3D12ShaderResourceViewFormat(format); srvDesc.ViewDimension = ResolveSRVDimension(desc, textureType); srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; switch (srvDesc.ViewDimension) { case D3D12_SRV_DIMENSION_TEXTURE1D: srvDesc.Texture1D.MostDetailedMip = desc.mipLevel; srvDesc.Texture1D.MipLevels = 1; break; case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: srvDesc.Texture1DArray.MostDetailedMip = desc.mipLevel; srvDesc.Texture1DArray.MipLevels = 1; srvDesc.Texture1DArray.FirstArraySlice = desc.firstArraySlice; srvDesc.Texture1DArray.ArraySize = desc.arraySize > 0 ? desc.arraySize : 1; break; case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: srvDesc.Texture2DArray.MostDetailedMip = desc.mipLevel; srvDesc.Texture2DArray.MipLevels = 1; srvDesc.Texture2DArray.FirstArraySlice = desc.firstArraySlice; srvDesc.Texture2DArray.ArraySize = desc.arraySize > 0 ? desc.arraySize : 1; srvDesc.Texture2DArray.PlaneSlice = desc.planeSlice; break; case D3D12_SRV_DIMENSION_TEXTURE3D: srvDesc.Texture3D.MostDetailedMip = desc.mipLevel; srvDesc.Texture3D.MipLevels = 1; break; case D3D12_SRV_DIMENSION_TEXTURECUBE: srvDesc.TextureCube.MostDetailedMip = desc.mipLevel; srvDesc.TextureCube.MipLevels = 1; break; case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY: srvDesc.TextureCubeArray.MostDetailedMip = desc.mipLevel; srvDesc.TextureCubeArray.MipLevels = 1; srvDesc.TextureCubeArray.First2DArrayFace = desc.firstArraySlice; srvDesc.TextureCubeArray.NumCubes = desc.arraySize > 0 ? std::max(desc.arraySize / 6u, 1u) : 1u; break; case D3D12_SRV_DIMENSION_TEXTURE2DMS: case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY: break; case D3D12_SRV_DIMENSION_TEXTURE2D: default: srvDesc.Texture2D.MostDetailedMip = desc.mipLevel; srvDesc.Texture2D.MipLevels = 1; srvDesc.Texture2D.PlaneSlice = desc.planeSlice; break; } auto heap = std::make_unique(); if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, false)) { delete view; return nullptr; } view->InitializeAsShaderResource(m_device.Get(), resource, &srvDesc, heap.get(), 0); if (IsDepthFormat(format)) { view->SetFormat(format); } view->SetOwnedHeap(std::move(heap)); return view; } RHIResourceView* D3D12Device::CreateUnorderedAccessView(RHIBuffer* buffer, const ResourceViewDesc& desc) { if (buffer == nullptr || m_device == nullptr || !IsSupportedBufferViewDimension(desc.dimension)) { return nullptr; } if (desc.dimension == ResourceViewDimension::Buffer && desc.format == 0) { return nullptr; } uint32_t firstElement = 0; uint32_t elementStride = 0; if (!TryResolveBufferViewFirstElement(buffer, desc, firstElement, elementStride)) { return nullptr; } uint32_t elementCount = 0; if (!TryResolveBufferViewElementCount(buffer, desc, elementStride, firstElement, elementCount)) { return nullptr; } D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; uavDesc.Format = ResolveD3D12BufferViewFormat(desc); uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; uavDesc.Buffer.FirstElement = firstElement; uavDesc.Buffer.NumElements = elementCount; uavDesc.Buffer.StructureByteStride = desc.dimension == ResourceViewDimension::StructuredBuffer ? elementStride : 0u; uavDesc.Buffer.Flags = desc.dimension == ResourceViewDimension::RawBuffer ? D3D12_BUFFER_UAV_FLAG_RAW : D3D12_BUFFER_UAV_FLAG_NONE; auto heap = std::make_unique(); if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, false)) { return nullptr; } auto* view = new D3D12ResourceView(); view->InitializeAsUnorderedAccessBuffer( m_device.Get(), static_cast(buffer), desc, &uavDesc, heap.get(), 0); if (!view->IsValid()) { delete view; return nullptr; } view->SetOwnedHeap(std::move(heap)); return view; } RHIResourceView* D3D12Device::CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) { auto* view = new D3D12ResourceView(); auto* d3d12Texture = static_cast(texture); ID3D12Resource* resource = d3d12Texture->GetResource(); D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; uavDesc.Format = static_cast(desc.format); uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; auto heap = std::make_unique(); if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, false)) { delete view; return nullptr; } view->InitializeAsUnorderedAccess(m_device.Get(), resource, &uavDesc, heap.get(), 0); view->SetOwnedHeap(std::move(heap)); return view; } D3D12DescriptorHeap* D3D12Device::CreateDescriptorHeap(const DescriptorHeapDesc& desc) { auto* heap = new D3D12DescriptorHeap(); if (!heap->Initialize(m_device.Get(), static_cast(desc.heapType), desc.descriptorCount, desc.shaderVisible)) { delete heap; return nullptr; } return heap; } D3D12QueryHeap* D3D12Device::CreateQueryHeap(const QueryHeapDesc& desc) { auto* queryHeap = new D3D12QueryHeap(); if (!queryHeap->Initialize(m_device.Get(), static_cast(desc.queryType), desc.count)) { delete queryHeap; return nullptr; } return queryHeap; } D3D12RootSignature* D3D12Device::CreateRootSignature(const RootSignatureDesc& desc) { auto* rootSig = new D3D12RootSignature(); D3D12_ROOT_SIGNATURE_DESC rootSigDesc = {}; rootSigDesc.NumParameters = 0; rootSigDesc.NumStaticSamplers = 0; rootSigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; if (!rootSig->Initialize(m_device.Get(), rootSigDesc)) { delete rootSig; return nullptr; } return rootSig; } D3D12CommandQueue* D3D12Device::CreateCommandQueueImpl(const CommandQueueDesc& desc) { return nullptr; } D3D12CommandList* D3D12Device::CreateCommandListImpl(const CommandListDesc& desc) { return nullptr; } } // namespace RHI } // namespace XCEngine