#include "XCUIBackend/XCUIRHIRenderBackend.h" #include "XCUIBackend/XCUIRHICommandCompiler.h" #include #include #include #include #include #include namespace XCEngine { namespace Editor { namespace XCUIBackend { namespace { using CommandCompiler = XCUIRHICommandCompiler; struct OverlayConstants { Math::Vector4 viewportSize = Math::Vector4::Zero(); }; class TextGlyphProvider final : public CommandCompiler::TextGlyphProvider { public: TextGlyphProvider( const IXCUITextAtlasProvider& atlasProvider, IXCUITextAtlasProvider::FontHandle fontHandle, const UI::UITextureHandle& textureHandle) : m_atlasProvider(atlasProvider), m_fontHandle(fontHandle), m_textureHandle(textureHandle) {} bool BeginText(float requestedFontSize, CommandCompiler::TextRunContext& outContext) const override { outContext = {}; if (!m_fontHandle.IsValid() || !m_textureHandle.IsValid()) return false; IXCUITextAtlasProvider::FontInfo fontInfo = {}; if (!m_atlasProvider.GetFontInfo(m_fontHandle, fontInfo)) return false; const float resolvedFontSize = requestedFontSize > 0.0f ? requestedFontSize : (fontInfo.nominalSize > 0.0f ? fontInfo.nominalSize : 16.0f); IXCUITextAtlasProvider::BakedFontInfo baked = {}; if (!m_atlasProvider.GetBakedFontInfo(m_fontHandle, resolvedFontSize, baked)) return false; outContext.requestedFontSize = requestedFontSize; outContext.resolvedFontSize = resolvedFontSize; outContext.lineHeight = baked.lineHeight; outContext.texture = m_textureHandle; return true; } bool ResolveGlyph( const CommandCompiler::TextRunContext& context, std::uint32_t codepoint, CommandCompiler::TextGlyph& outGlyph) const override { outGlyph = {}; IXCUITextAtlasProvider::GlyphInfo glyph = {}; if (!m_atlasProvider.FindGlyph(m_fontHandle, context.resolvedFontSize, codepoint, glyph)) return false; outGlyph.x0 = glyph.x0; outGlyph.y0 = glyph.y0; outGlyph.x1 = glyph.x1; outGlyph.y1 = glyph.y1; outGlyph.u0 = glyph.u0; outGlyph.v0 = glyph.v0; outGlyph.u1 = glyph.u1; outGlyph.v1 = glyph.v1; outGlyph.advanceX = glyph.advanceX; outGlyph.visible = glyph.visible; return true; } private: const IXCUITextAtlasProvider& m_atlasProvider; IXCUITextAtlasProvider::FontHandle m_fontHandle = {}; UI::UITextureHandle m_textureHandle = {}; }; constexpr char kColorShader[] = "cbuffer OverlayConstants : register(b0) { float4 gViewportSize; };" "struct VSInput { float2 position : POSITION; float4 color : COLOR0; };" "struct VSOutput { float4 position : SV_POSITION; float4 color : COLOR0; };" "VSOutput MainVS(VSInput input) { VSOutput output;" " float2 viewport = max(gViewportSize.xy, float2(1.0, 1.0));" " float2 ndc = float2(input.position.x / viewport.x * 2.0 - 1.0, 1.0 - input.position.y / viewport.y * 2.0);" " output.position = float4(ndc, 0.0, 1.0); output.color = input.color; return output; }" "float4 MainPS(VSOutput input) : SV_TARGET0 { return input.color; }"; constexpr char kTexturedShader[] = "cbuffer OverlayConstants : register(b0) { float4 gViewportSize; };" "Texture2D gOverlayTexture : register(t0); SamplerState gOverlaySampler : register(s0);" "struct VSInput { float2 position : POSITION; float2 uv : TEXCOORD0; float4 color : COLOR0; };" "struct VSOutput { float4 position : SV_POSITION; float2 uv : TEXCOORD0; float4 color : COLOR0; };" "VSOutput MainVS(VSInput input) { VSOutput output;" " float2 viewport = max(gViewportSize.xy, float2(1.0, 1.0));" " float2 ndc = float2(input.position.x / viewport.x * 2.0 - 1.0, 1.0 - input.position.y / viewport.y * 2.0);" " output.position = float4(ndc, 0.0, 1.0); output.uv = input.uv; output.color = input.color; return output; }" "float4 MainPS(VSOutput input) : SV_TARGET0 {" " const float4 sampled = gOverlayTexture.Sample(gOverlaySampler, input.uv);" " float4 color = sampled * input.color; if (color.a <= 0.001f) { discard; } return color; }"; RHI::Rect MakeSurfaceScissorRect(const ::XCEngine::Rendering::RenderSurface& surface) { return RHI::Rect{ 0, 0, static_cast(surface.GetWidth()), static_cast(surface.GetHeight()) }; } RHI::Rect ClampBatchClipRect( const ::XCEngine::Rendering::RenderSurface& surface, const UI::UIRect& clipRect) { const float surfaceWidth = static_cast(surface.GetWidth()); const float surfaceHeight = static_cast(surface.GetHeight()); const float minX = (std::max)(0.0f, (std::min)(clipRect.x, surfaceWidth)); const float minY = (std::max)(0.0f, (std::min)(clipRect.y, surfaceHeight)); const float maxX = (std::max)(minX, (std::min)(clipRect.x + clipRect.width, surfaceWidth)); const float maxY = (std::max)(minY, (std::min)(clipRect.y + clipRect.height, surfaceHeight)); return RHI::Rect{ static_cast(std::floor(minX)), static_cast(std::floor(minY)), static_cast(std::ceil(maxX)), static_cast(std::ceil(maxY)) }; } bool RectEquals(const RHI::Rect& lhs, const RHI::Rect& rhs) { return lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right && lhs.bottom == rhs.bottom; } } // namespace void XCUIRHIRenderBackend::SetTextAtlasProvider(const IXCUITextAtlasProvider* provider) { if (m_textAtlasProvider == provider) return; m_textAtlasProvider = provider; ResetFontAtlasResources(); } void XCUIRHIRenderBackend::Shutdown() { DestroyResources(); } void XCUIRHIRenderBackend::ResetStats() { m_lastOverlayStats = {}; } bool XCUIRHIRenderBackend::Render( const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::Rendering::RenderSurface& surface, const ::XCEngine::UI::UIDrawData& drawData) { ResetStats(); if (!renderContext.IsValid() || renderContext.backendType != RHI::RHIType::D3D12) return false; const auto& colorAttachments = surface.GetColorAttachments(); if (colorAttachments.empty() || colorAttachments[0] == nullptr || renderContext.commandList == nullptr) return false; if (!EnsureInitialized(renderContext)) return false; bool fontAtlasReady = false; const IXCUITextAtlasProvider* atlasProvider = ResolveActiveTextAtlasProvider(fontAtlasReady); return RenderCompiledDrawData( *renderContext.commandList, colorAttachments[0], surface, drawData, atlasProvider, fontAtlasReady); } const IXCUITextAtlasProvider* XCUIRHIRenderBackend::ResolveActiveTextAtlasProvider( bool& outFontAtlasReady) { outFontAtlasReady = false; if (m_textAtlasProvider == nullptr) { return nullptr; } outFontAtlasReady = EnsureFontAtlasResources(*m_textAtlasProvider); return m_textAtlasProvider; } bool XCUIRHIRenderBackend::EnsureInitialized(const ::XCEngine::Rendering::RenderContext& renderContext) { if (m_overlayPipelineState != nullptr && m_overlayPipelineLayout != nullptr && m_overlayConstantPool != nullptr && m_overlayConstantSet != nullptr && m_texturedOverlayPipelineState != nullptr && m_texturedOverlayPipelineLayout != nullptr && m_overlayTexturePool != nullptr && m_overlaySamplerPool != nullptr && m_overlaySamplerSet != nullptr && m_overlaySampler != nullptr && m_device == renderContext.device && m_backendType == renderContext.backendType) { return true; } DestroyResources(); return CreateResources(renderContext); } bool XCUIRHIRenderBackend::CreateResources(const ::XCEngine::Rendering::RenderContext& renderContext) { if (!renderContext.IsValid() || renderContext.backendType != RHI::RHIType::D3D12) return false; m_device = renderContext.device; m_backendType = renderContext.backendType; return CreateOverlayResources() && CreateTexturedOverlayResources(); } bool XCUIRHIRenderBackend::CreateOverlayResources() { RHI::DescriptorSetLayoutBinding binding = { 0, static_cast(RHI::DescriptorType::CBV), 1 }; RHI::DescriptorSetLayoutDesc layout = { &binding, 1 }; RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; pipelineLayoutDesc.setLayouts = &layout; pipelineLayoutDesc.setLayoutCount = 1; m_overlayPipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); if (m_overlayPipelineLayout == nullptr) { DestroyResources(); return false; } RHI::DescriptorPoolDesc poolDesc = {}; poolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; poolDesc.descriptorCount = 1; poolDesc.shaderVisible = false; m_overlayConstantPool = m_device->CreateDescriptorPool(poolDesc); if (m_overlayConstantPool == nullptr) { DestroyResources(); return false; } m_overlayConstantSet = m_overlayConstantPool->AllocateSet(layout); if (m_overlayConstantSet == nullptr) { DestroyResources(); return false; } RHI::GraphicsPipelineDesc desc = {}; desc.pipelineLayout = m_overlayPipelineLayout; desc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); desc.renderTargetCount = 1; desc.renderTargetFormats[0] = static_cast(RHI::Format::R8G8B8A8_UNorm); desc.depthStencilFormat = static_cast(RHI::Format::Unknown); desc.sampleCount = 1; desc.inputLayout.elements = { { "POSITION", 0, static_cast(RHI::Format::R32G32_Float), 0, 0, 0, 0 }, { "COLOR", 0, static_cast(RHI::Format::R32G32B32A32_Float), 0, 8, 0, 0 } }; desc.rasterizerState.fillMode = static_cast(RHI::FillMode::Solid); desc.rasterizerState.cullMode = static_cast(RHI::CullMode::None); desc.rasterizerState.frontFace = static_cast(RHI::FrontFace::CounterClockwise); desc.rasterizerState.depthClipEnable = true; desc.blendState.blendEnable = true; desc.blendState.srcBlend = static_cast(RHI::BlendFactor::SrcAlpha); desc.blendState.dstBlend = static_cast(RHI::BlendFactor::InvSrcAlpha); desc.blendState.srcBlendAlpha = static_cast(RHI::BlendFactor::One); desc.blendState.dstBlendAlpha = static_cast(RHI::BlendFactor::InvSrcAlpha); desc.blendState.blendOp = static_cast(RHI::BlendOp::Add); desc.blendState.blendOpAlpha = static_cast(RHI::BlendOp::Add); desc.blendState.colorWriteMask = 0xF; desc.depthStencilState.depthTestEnable = false; desc.depthStencilState.depthWriteEnable = false; desc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::Always); desc.vertexShader.source.assign(kColorShader, kColorShader + sizeof(kColorShader) - 1); desc.vertexShader.sourceLanguage = RHI::ShaderLanguage::HLSL; desc.vertexShader.entryPoint = L"MainVS"; desc.vertexShader.profile = L"vs_5_0"; desc.fragmentShader.source.assign(kColorShader, kColorShader + sizeof(kColorShader) - 1); desc.fragmentShader.sourceLanguage = RHI::ShaderLanguage::HLSL; desc.fragmentShader.entryPoint = L"MainPS"; desc.fragmentShader.profile = L"ps_5_0"; m_overlayPipelineState = m_device->CreatePipelineState(desc); if (m_overlayPipelineState == nullptr || !m_overlayPipelineState->IsValid()) { DestroyResources(); return false; } return true; } bool XCUIRHIRenderBackend::CreateTexturedOverlayResources() { RHI::DescriptorSetLayoutBinding constantBinding = { 0, static_cast(RHI::DescriptorType::CBV), 1 }; RHI::DescriptorSetLayoutBinding textureBinding = { 0, static_cast(RHI::DescriptorType::SRV), 1 }; RHI::DescriptorSetLayoutBinding samplerBinding = { 0, static_cast(RHI::DescriptorType::Sampler), 1 }; RHI::DescriptorSetLayoutDesc setLayouts[3] = { { &constantBinding, 1 }, { &textureBinding, 1 }, { &samplerBinding, 1 } }; RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; pipelineLayoutDesc.setLayouts = setLayouts; pipelineLayoutDesc.setLayoutCount = 3; m_texturedOverlayPipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); if (m_texturedOverlayPipelineLayout == nullptr) { DestroyResources(); return false; } RHI::DescriptorPoolDesc texturePoolDesc = {}; texturePoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; texturePoolDesc.descriptorCount = 64; texturePoolDesc.shaderVisible = true; m_overlayTexturePool = m_device->CreateDescriptorPool(texturePoolDesc); if (m_overlayTexturePool == nullptr) { DestroyResources(); return false; } RHI::DescriptorPoolDesc samplerPoolDesc = {}; samplerPoolDesc.type = RHI::DescriptorHeapType::Sampler; samplerPoolDesc.descriptorCount = 1; samplerPoolDesc.shaderVisible = true; m_overlaySamplerPool = m_device->CreateDescriptorPool(samplerPoolDesc); if (m_overlaySamplerPool == nullptr) { DestroyResources(); return false; } m_overlaySamplerSet = m_overlaySamplerPool->AllocateSet(setLayouts[2]); if (m_overlaySamplerSet == nullptr) { DestroyResources(); return false; } RHI::SamplerDesc samplerDesc = {}; samplerDesc.filter = static_cast(RHI::FilterMode::Linear); samplerDesc.addressU = static_cast(RHI::TextureAddressMode::Clamp); samplerDesc.addressV = static_cast(RHI::TextureAddressMode::Clamp); samplerDesc.addressW = static_cast(RHI::TextureAddressMode::Clamp); samplerDesc.comparisonFunc = static_cast(RHI::ComparisonFunc::Always); samplerDesc.maxLod = 1000.0f; m_overlaySampler = m_device->CreateSampler(samplerDesc); if (m_overlaySampler == nullptr) { DestroyResources(); return false; } m_overlaySamplerSet->UpdateSampler(0, m_overlaySampler); RHI::GraphicsPipelineDesc desc = {}; desc.pipelineLayout = m_texturedOverlayPipelineLayout; desc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); desc.renderTargetCount = 1; desc.renderTargetFormats[0] = static_cast(RHI::Format::R8G8B8A8_UNorm); desc.depthStencilFormat = static_cast(RHI::Format::Unknown); desc.sampleCount = 1; desc.inputLayout.elements = { { "POSITION", 0, static_cast(RHI::Format::R32G32_Float), 0, 0, 0, 0 }, { "TEXCOORD", 0, static_cast(RHI::Format::R32G32_Float), 0, 8, 0, 0 }, { "COLOR", 0, static_cast(RHI::Format::R32G32B32A32_Float), 0, 16, 0, 0 } }; desc.rasterizerState.fillMode = static_cast(RHI::FillMode::Solid); desc.rasterizerState.cullMode = static_cast(RHI::CullMode::None); desc.rasterizerState.frontFace = static_cast(RHI::FrontFace::CounterClockwise); desc.rasterizerState.depthClipEnable = true; desc.blendState.blendEnable = true; desc.blendState.srcBlend = static_cast(RHI::BlendFactor::SrcAlpha); desc.blendState.dstBlend = static_cast(RHI::BlendFactor::InvSrcAlpha); desc.blendState.srcBlendAlpha = static_cast(RHI::BlendFactor::One); desc.blendState.dstBlendAlpha = static_cast(RHI::BlendFactor::InvSrcAlpha); desc.blendState.blendOp = static_cast(RHI::BlendOp::Add); desc.blendState.blendOpAlpha = static_cast(RHI::BlendOp::Add); desc.blendState.colorWriteMask = 0xF; desc.depthStencilState.depthTestEnable = false; desc.depthStencilState.depthWriteEnable = false; desc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::Always); desc.vertexShader.source.assign(kTexturedShader, kTexturedShader + sizeof(kTexturedShader) - 1); desc.vertexShader.sourceLanguage = RHI::ShaderLanguage::HLSL; desc.vertexShader.entryPoint = L"MainVS"; desc.vertexShader.profile = L"vs_5_0"; desc.fragmentShader.source.assign(kTexturedShader, kTexturedShader + sizeof(kTexturedShader) - 1); desc.fragmentShader.sourceLanguage = RHI::ShaderLanguage::HLSL; desc.fragmentShader.entryPoint = L"MainPS"; desc.fragmentShader.profile = L"ps_5_0"; m_texturedOverlayPipelineState = m_device->CreatePipelineState(desc); if (m_texturedOverlayPipelineState == nullptr || !m_texturedOverlayPipelineState->IsValid()) { DestroyResources(); return false; } return true; } bool XCUIRHIRenderBackend::EnsureOverlayVertexBufferCapacity(std::size_t requiredVertexCount) { const std::uint64_t requiredBytes = static_cast(requiredVertexCount * sizeof(CommandCompiler::ColorVertex)); if (requiredBytes == 0u) return true; if (m_overlayVertexBuffer != nullptr && m_overlayVertexBufferCapacity >= requiredBytes) return true; if (m_overlayVertexBufferView != nullptr) { m_overlayVertexBufferView->Shutdown(); delete m_overlayVertexBufferView; m_overlayVertexBufferView = nullptr; } if (m_overlayVertexBuffer != nullptr) { m_overlayVertexBuffer->Shutdown(); delete m_overlayVertexBuffer; m_overlayVertexBuffer = nullptr; } m_overlayVertexBufferCapacity = (std::max)(requiredBytes, 4096u); RHI::BufferDesc bufferDesc = {}; bufferDesc.size = m_overlayVertexBufferCapacity; bufferDesc.stride = static_cast(sizeof(CommandCompiler::ColorVertex)); bufferDesc.bufferType = static_cast(RHI::BufferType::Vertex); m_overlayVertexBuffer = m_device->CreateBuffer(bufferDesc); if (m_overlayVertexBuffer == nullptr) { m_overlayVertexBufferCapacity = 0u; return false; } m_overlayVertexBuffer->SetStride(bufferDesc.stride); m_overlayVertexBuffer->SetBufferType(RHI::BufferType::Vertex); RHI::ResourceViewDesc viewDesc = {}; viewDesc.dimension = RHI::ResourceViewDimension::Buffer; viewDesc.structureByteStride = bufferDesc.stride; m_overlayVertexBufferView = m_device->CreateVertexBufferView(m_overlayVertexBuffer, viewDesc); return m_overlayVertexBufferView != nullptr; } bool XCUIRHIRenderBackend::EnsureTexturedOverlayVertexBufferCapacity(std::size_t requiredVertexCount) { const std::uint64_t requiredBytes = static_cast(requiredVertexCount * sizeof(CommandCompiler::TexturedVertex)); if (requiredBytes == 0u) return true; if (m_texturedOverlayVertexBuffer != nullptr && m_texturedOverlayVertexBufferCapacity >= requiredBytes) return true; if (m_texturedOverlayVertexBufferView != nullptr) { m_texturedOverlayVertexBufferView->Shutdown(); delete m_texturedOverlayVertexBufferView; m_texturedOverlayVertexBufferView = nullptr; } if (m_texturedOverlayVertexBuffer != nullptr) { m_texturedOverlayVertexBuffer->Shutdown(); delete m_texturedOverlayVertexBuffer; m_texturedOverlayVertexBuffer = nullptr; } m_texturedOverlayVertexBufferCapacity = (std::max)(requiredBytes, 4096u); RHI::BufferDesc bufferDesc = {}; bufferDesc.size = m_texturedOverlayVertexBufferCapacity; bufferDesc.stride = static_cast(sizeof(CommandCompiler::TexturedVertex)); bufferDesc.bufferType = static_cast(RHI::BufferType::Vertex); m_texturedOverlayVertexBuffer = m_device->CreateBuffer(bufferDesc); if (m_texturedOverlayVertexBuffer == nullptr) { m_texturedOverlayVertexBufferCapacity = 0u; return false; } m_texturedOverlayVertexBuffer->SetStride(bufferDesc.stride); m_texturedOverlayVertexBuffer->SetBufferType(RHI::BufferType::Vertex); RHI::ResourceViewDesc viewDesc = {}; viewDesc.dimension = RHI::ResourceViewDimension::Buffer; viewDesc.structureByteStride = bufferDesc.stride; m_texturedOverlayVertexBufferView = m_device->CreateVertexBufferView(m_texturedOverlayVertexBuffer, viewDesc); return m_texturedOverlayVertexBufferView != nullptr; } bool XCUIRHIRenderBackend::EnsureFontAtlasResources(const IXCUITextAtlasProvider& atlasProvider) { if (m_overlayTexturePool == nullptr) return false; IXCUITextAtlasProvider::AtlasTextureView atlasView = {}; if (!atlasProvider.GetAtlasTextureView(IXCUITextAtlasProvider::PixelFormat::RGBA32, atlasView) || !atlasView.IsValid() || atlasView.bytesPerPixel != 4) { return false; } const TextFontHandle font = atlasProvider.GetDefaultFont(); if (!font.IsValid()) return false; if (m_overlayFontTexture != nullptr && m_overlayFontTextureView != nullptr && m_overlayFontTextureSet != nullptr && m_overlayFont.value == font.value && m_fontAtlasStorageKey == atlasView.atlasStorageKey && m_fontAtlasPixelDataKey == atlasView.pixelDataKey) { return true; } ResetFontAtlasResources(); RHI::TextureDesc textureDesc = {}; textureDesc.width = static_cast(atlasView.width); textureDesc.height = static_cast(atlasView.height); textureDesc.depth = 1; textureDesc.mipLevels = 1; textureDesc.arraySize = 1; textureDesc.format = static_cast(RHI::Format::R8G8B8A8_UNorm); textureDesc.textureType = static_cast(RHI::TextureType::Texture2D); textureDesc.sampleCount = 1; m_overlayFontTexture = m_device->CreateTexture( textureDesc, atlasView.pixels, static_cast(atlasView.width) * static_cast(atlasView.height) * static_cast(atlasView.bytesPerPixel), static_cast(atlasView.stride)); if (m_overlayFontTexture == nullptr) { ResetFontAtlasResources(); return false; } RHI::ResourceViewDesc viewDesc = {}; viewDesc.format = static_cast(RHI::Format::R8G8B8A8_UNorm); viewDesc.dimension = RHI::ResourceViewDimension::Texture2D; viewDesc.mipLevel = 0; m_overlayFontTextureView = m_device->CreateShaderResourceView(m_overlayFontTexture, viewDesc); if (m_overlayFontTextureView == nullptr) { ResetFontAtlasResources(); return false; } RHI::DescriptorSetLayoutBinding textureBinding = { 0, static_cast(RHI::DescriptorType::SRV), 1 }; RHI::DescriptorSetLayoutDesc textureLayout = { &textureBinding, 1 }; m_overlayFontTextureSet = m_overlayTexturePool->AllocateSet(textureLayout); if (m_overlayFontTextureSet == nullptr) { ResetFontAtlasResources(); return false; } m_overlayFontTextureSet->Update(0, m_overlayFontTextureView); m_overlayFont = font; m_overlayFontTextureHandle.nativeHandle = reinterpret_cast(m_overlayFontTextureView); m_overlayFontTextureHandle.width = static_cast(atlasView.width); m_overlayFontTextureHandle.height = static_cast(atlasView.height); m_overlayFontTextureHandle.kind = UI::UITextureHandleKind::ShaderResourceView; m_fontAtlasStorageKey = atlasView.atlasStorageKey; m_fontAtlasPixelDataKey = atlasView.pixelDataKey; return true; } ::XCEngine::RHI::RHIDescriptorSet* XCUIRHIRenderBackend::ResolveTextureSet( const ::XCEngine::UI::UITextureHandle& texture, bool* outCacheHit) { if (outCacheHit != nullptr) { *outCacheHit = false; } if (!texture.IsValid() || texture.kind != UI::UITextureHandleKind::ShaderResourceView || m_overlayTexturePool == nullptr) return nullptr; if (m_overlayFontTextureSet != nullptr && m_overlayFontTextureHandle.IsValid() && texture.nativeHandle == m_overlayFontTextureHandle.nativeHandle) { if (outCacheHit != nullptr) { *outCacheHit = true; } return m_overlayFontTextureSet; } for (const ExternalTextureBinding& binding : m_externalTextureBindings) { if (binding.handleKey == texture.nativeHandle && binding.textureSet != nullptr && binding.shaderView != nullptr) { if (outCacheHit != nullptr) { *outCacheHit = true; } return binding.textureSet; } } RHI::RHIResourceView* shaderView = reinterpret_cast(texture.nativeHandle); if (shaderView == nullptr || !shaderView->IsValid() || shaderView->GetViewType() != RHI::ResourceViewType::ShaderResource) return nullptr; RHI::DescriptorSetLayoutBinding textureBinding = { 0, static_cast(RHI::DescriptorType::SRV), 1 }; RHI::DescriptorSetLayoutDesc textureLayout = { &textureBinding, 1 }; RHI::RHIDescriptorSet* textureSet = m_overlayTexturePool->AllocateSet(textureLayout); if (textureSet == nullptr) return nullptr; textureSet->Update(0, shaderView); m_externalTextureBindings.push_back({ texture.nativeHandle, shaderView, textureSet }); return textureSet; } bool XCUIRHIRenderBackend::RenderCompiledDrawData( ::XCEngine::RHI::RHICommandList& commandList, ::XCEngine::RHI::RHIResourceView* renderTarget, const ::XCEngine::Rendering::RenderSurface& surface, const ::XCEngine::UI::UIDrawData& drawData, const IXCUITextAtlasProvider* atlasProvider, bool fontAtlasReady) { CommandCompiler compiler = {}; CommandCompiler::CompileConfig config = {}; config.surfaceClipRect = UI::UIRect(0.0f, 0.0f, static_cast(surface.GetWidth()), static_cast(surface.GetHeight())); std::optional glyphProvider = std::nullopt; if (fontAtlasReady && atlasProvider != nullptr) { glyphProvider.emplace(*atlasProvider, m_overlayFont, m_overlayFontTextureHandle); config.textGlyphProvider = &(*glyphProvider); } CommandCompiler::CompiledDrawData compiled = {}; compiler.Compile(drawData, config, compiled); m_lastOverlayStats.drawListCount = compiled.stats.drawListCount; m_lastOverlayStats.commandCount = compiled.stats.commandCount; m_lastOverlayStats.batchCount = compiled.stats.batchCount; m_lastOverlayStats.skippedCommandCount = compiled.stats.skippedCommandCount; m_lastOverlayStats.vertexCount = compiled.stats.colorVertexCount + compiled.stats.texturedVertexCount; m_lastOverlayStats.triangleCount = compiled.stats.triangleCount; if (compiled.Empty()) return true; if (!compiled.colorVertices.empty() && !EnsureOverlayVertexBufferCapacity(compiled.colorVertices.size())) return false; if (!compiled.texturedVertices.empty() && !EnsureTexturedOverlayVertexBufferCapacity(compiled.texturedVertices.size())) return false; if (!compiled.colorVertices.empty()) m_overlayVertexBuffer->SetData(compiled.colorVertices.data(), compiled.colorVertices.size() * sizeof(CommandCompiler::ColorVertex)); if (!compiled.texturedVertices.empty()) m_texturedOverlayVertexBuffer->SetData(compiled.texturedVertices.data(), compiled.texturedVertices.size() * sizeof(CommandCompiler::TexturedVertex)); OverlayConstants constants = {}; constants.viewportSize = Math::Vector4(static_cast(surface.GetWidth()), static_cast(surface.GetHeight()), 0.0f, 0.0f); m_overlayConstantSet->WriteConstant(0, &constants, sizeof(constants)); commandList.SetRenderTargets(1, &renderTarget, nullptr); const RHI::Viewport viewport = { 0.0f, 0.0f, static_cast(surface.GetWidth()), static_cast(surface.GetHeight()), 0.0f, 1.0f }; const RHI::Rect fullSurfaceScissorRect = MakeSurfaceScissorRect(surface); commandList.SetViewport(viewport); commandList.SetScissorRect(fullSurfaceScissorRect); commandList.SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); RHI::Rect currentScissorRect = fullSurfaceScissorRect; for (const CommandCompiler::Batch& batch : compiled.batches) { if (batch.vertexCount == 0u) continue; const RHI::Rect batchScissorRect = ClampBatchClipRect(surface, batch.clipRect); if (!RectEquals(currentScissorRect, batchScissorRect)) { commandList.SetScissorRect(batchScissorRect); currentScissorRect = batchScissorRect; } if (!RectEquals(batchScissorRect, fullSurfaceScissorRect)) { ++m_lastOverlayStats.scissoredBatchCount; } if (batch.kind == CommandCompiler::BatchKind::Color) { ++m_lastOverlayStats.colorBatchCount; m_lastOverlayStats.renderedCommandCount += batch.commandCount; commandList.SetPipelineState(m_overlayPipelineState); RHI::RHIResourceView* vertexBuffers[] = { m_overlayVertexBufferView }; const std::uint64_t offsets[] = { 0u }; const std::uint32_t strides[] = { static_cast(sizeof(CommandCompiler::ColorVertex)) }; commandList.SetVertexBuffers(0, 1, vertexBuffers, offsets, strides); RHI::RHIDescriptorSet* descriptorSets[] = { m_overlayConstantSet }; commandList.SetGraphicsDescriptorSets(0, 1, descriptorSets, m_overlayPipelineLayout); commandList.Draw(batch.vertexCount, 1, batch.firstVertex, 0); continue; } ++m_lastOverlayStats.texturedBatchCount; ++m_lastOverlayStats.textureResolveCount; bool textureCacheHit = false; RHI::RHIDescriptorSet* textureSet = ResolveTextureSet(batch.texture, &textureCacheHit); if (textureCacheHit) { ++m_lastOverlayStats.textureCacheHitCount; } if (textureSet == nullptr || m_overlaySamplerSet == nullptr) { m_lastOverlayStats.skippedCommandCount += batch.commandCount; continue; } m_lastOverlayStats.renderedCommandCount += batch.commandCount; commandList.SetPipelineState(m_texturedOverlayPipelineState); RHI::RHIResourceView* vertexBuffers[] = { m_texturedOverlayVertexBufferView }; const std::uint64_t offsets[] = { 0u }; const std::uint32_t strides[] = { static_cast(sizeof(CommandCompiler::TexturedVertex)) }; commandList.SetVertexBuffers(0, 1, vertexBuffers, offsets, strides); RHI::RHIDescriptorSet* descriptorSets[] = { m_overlayConstantSet, textureSet, m_overlaySamplerSet }; commandList.SetGraphicsDescriptorSets(0, 3, descriptorSets, m_texturedOverlayPipelineLayout); commandList.Draw(batch.vertexCount, 1, batch.firstVertex, 0); } if (!RectEquals(currentScissorRect, fullSurfaceScissorRect)) { commandList.SetScissorRect(fullSurfaceScissorRect); } return true; } void XCUIRHIRenderBackend::ResetFontAtlasResources() { if (m_overlayFontTextureSet != nullptr) { m_overlayFontTextureSet->Shutdown(); delete m_overlayFontTextureSet; m_overlayFontTextureSet = nullptr; } if (m_overlayFontTextureView != nullptr) { m_overlayFontTextureView->Shutdown(); delete m_overlayFontTextureView; m_overlayFontTextureView = nullptr; } if (m_overlayFontTexture != nullptr) { m_overlayFontTexture->Shutdown(); delete m_overlayFontTexture; m_overlayFontTexture = nullptr; } m_overlayFont = {}; m_overlayFontTextureHandle = {}; m_fontAtlasStorageKey = 0; m_fontAtlasPixelDataKey = 0; } void XCUIRHIRenderBackend::DestroyResources() { for (ExternalTextureBinding& binding : m_externalTextureBindings) { if (binding.textureSet != nullptr) { binding.textureSet->Shutdown(); delete binding.textureSet; binding.textureSet = nullptr; } binding.shaderView = nullptr; binding.handleKey = 0; } m_externalTextureBindings.clear(); if (m_texturedOverlayVertexBufferView != nullptr) { m_texturedOverlayVertexBufferView->Shutdown(); delete m_texturedOverlayVertexBufferView; m_texturedOverlayVertexBufferView = nullptr; } if (m_texturedOverlayVertexBuffer != nullptr) { m_texturedOverlayVertexBuffer->Shutdown(); delete m_texturedOverlayVertexBuffer; m_texturedOverlayVertexBuffer = nullptr; } if (m_overlayVertexBufferView != nullptr) { m_overlayVertexBufferView->Shutdown(); delete m_overlayVertexBufferView; m_overlayVertexBufferView = nullptr; } if (m_overlayVertexBuffer != nullptr) { m_overlayVertexBuffer->Shutdown(); delete m_overlayVertexBuffer; m_overlayVertexBuffer = nullptr; } ResetFontAtlasResources(); if (m_texturedOverlayPipelineState != nullptr) { m_texturedOverlayPipelineState->Shutdown(); delete m_texturedOverlayPipelineState; m_texturedOverlayPipelineState = nullptr; } if (m_overlaySamplerSet != nullptr) { m_overlaySamplerSet->Shutdown(); delete m_overlaySamplerSet; m_overlaySamplerSet = nullptr; } if (m_overlaySampler != nullptr) { m_overlaySampler->Shutdown(); delete m_overlaySampler; m_overlaySampler = nullptr; } if (m_overlaySamplerPool != nullptr) { m_overlaySamplerPool->Shutdown(); delete m_overlaySamplerPool; m_overlaySamplerPool = nullptr; } if (m_overlayTexturePool != nullptr) { m_overlayTexturePool->Shutdown(); delete m_overlayTexturePool; m_overlayTexturePool = nullptr; } if (m_texturedOverlayPipelineLayout != nullptr) { m_texturedOverlayPipelineLayout->Shutdown(); delete m_texturedOverlayPipelineLayout; m_texturedOverlayPipelineLayout = nullptr; } if (m_overlayPipelineState != nullptr) { m_overlayPipelineState->Shutdown(); delete m_overlayPipelineState; m_overlayPipelineState = nullptr; } if (m_overlayConstantSet != nullptr) { m_overlayConstantSet->Shutdown(); delete m_overlayConstantSet; m_overlayConstantSet = nullptr; } if (m_overlayConstantPool != nullptr) { m_overlayConstantPool->Shutdown(); delete m_overlayConstantPool; m_overlayConstantPool = nullptr; } if (m_overlayPipelineLayout != nullptr) { m_overlayPipelineLayout->Shutdown(); delete m_overlayPipelineLayout; m_overlayPipelineLayout = nullptr; } m_overlayVertexBufferCapacity = 0u; m_texturedOverlayVertexBufferCapacity = 0u; m_lastOverlayStats = {}; m_device = nullptr; m_backendType = RHI::RHIType::D3D12; } } // namespace XCUIBackend } // namespace Editor } // namespace XCEngine