You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DirectX12下Direct2D UI叠加层的分摊渲染及Alpha混合问题

D3D11on12与Direct2D问题解决及Alpha混合实现

问题概述

使用D3D11on12结合Direct2D时遇到两个核心问题:

  1. 偶尔触发设备移除错误:

D3D12 ERROR: ID3D12Device::RemoveDevice: Device removal has been triggered for the following reason (DXGI_ERROR_ACCESS_DENIED: The application attempted to use a resource it does not access to. This could be, for example, rendering to a texture while only having read access.). [ EXECUTION ERROR #232: DEVICE_REMOVAL_PROCESS_AT_FAULT]

  1. 存在卡顿,计划通过UI分摊渲染优化,将UI渲染到独立透明位图后叠加到交换链缓冲区,但不知如何实现Alpha混合。

一、设备移除错误修复

  • 权限检查:确认交换链后台缓冲区创建时,DXGI_USAGE包含DXGI_USAGE_RENDER_TARGET_OUTPUT,确保拥有写入权限;使用ID3D11On12Device::CreateWrappedResource包装D3D12资源时,D3D11_RESOURCE_FLAGS需允许写入操作。
  • 资源状态同步:D3D12资源状态必须匹配当前操作:
    • 渲染到后台缓冲区前,通过ID3D12GraphicsCommandList::ResourceBarrier将资源切换至D3D12_RESOURCE_STATE_RENDER_TARGET;
    • 完成渲染后切换回D3D12_RESOURCE_STATE_PRESENT用于展示;
    • 避免D3D12和D3D11同时操作同一资源,操作前后需通过ID3D11On12Device::AcquireWrappedResources和ReleaseWrappedResources同步。
  • 包装资源生命周期:确保D3D12资源未被提前释放,包装的D3D11资源需与原D3D12资源生命周期保持一致。

二、卡顿优化与UI分摊渲染

  • 分离UI与场景渲染:创建独立的UI专用D3D12纹理,通过CreateWrappedResource转为D3D11资源后,用CreateBitmapFromDxgiSurface创建Direct2D位图。固定间隔更新UI(如每2帧或16ms一次),无需与场景帧完全同步。
  • 缓存UI结果:保留最近一次渲染完成的UI位图,后续帧直接复用该资源叠加,避免每帧重绘全部UI元素。
  • 异步渲染:将UI渲染任务放到独立的D3D11命令队列执行,与场景渲染并行,减少主线程阻塞。

三、Alpha混合实现(适配你的技术栈)

方案1:Direct2D直接叠加(零3D管线知识)

这是最简便的方式,利用Direct2D内置的混合功能:

  1. 将交换链后台缓冲区包装为D3D11渲染目标,再创建Direct2D渲染目标。
  2. 设置混合模式为D2D1_BLEND_MODE_SRC_OVER,直接绘制UI位图:
    // 假设pRenderTarget是交换链对应的Direct2D渲染目标,pUIBitmap是UI透明位图
    pRenderTarget->SetBlendState(D2D1::BlendStateDescription(D2D1_BLEND_MODE_SRC_OVER));
    pRenderTarget->DrawBitmap(
        pUIBitmap,
        D2D1::RectF(0.0f, 0.0f, screenWidth, screenHeight), // 目标区域
        1.0f, // 全局透明度
        D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
        D2D1::RectF(0.0f, 0.0f, uiWidth, uiHeight) // UI源区域
    );
    
    此方式会自动根据UI位图的Alpha通道完成混合,无需手动计算。

方案2:计算着色器实现(灵活定制混合逻辑)

如果你需要自定义混合规则,用熟悉的计算着色器实现:

  1. 为交换链后台缓冲区创建UAV(无序访问视图),为UI位图和后台缓冲区创建SRV(着色器资源视图)。
  2. 编写Alpha混合计算着色器:
    // 注册绑定:UAV对应输出,两个SRV分别是场景和UI
    RWTexture2D<float4> sceneOutput : register(u0);
    Texture2D<float4> sceneInput : register(t0);
    Texture2D<float4> uiTexture : register(t1);
    SamplerState samplerLinear : register(s0);
    
    [numthreads(8, 8, 1)]
    void BlendCS(uint3 dispatchID : SV_DispatchThreadID)
    {
        uint2 coord = dispatchID.xy;
        // 确保坐标在纹理范围内
        if (coord.x >= sceneOutput.Width || coord.y >= sceneOutput.Height) return;
    
        float4 sceneColor = sceneInput[coord];
        float4 uiColor = uiTexture[coord];
    
        // 标准Alpha混合公式:最终颜色 = UI颜色*Alpha + 场景颜色*(1-Alpha)
        float4 finalColor = uiColor * uiColor.a + sceneColor * (1.0f - uiColor.a);
        sceneOutput[coord] = finalColor;
    }
    
  3. 在命令队列中绑定SRV、UAV和采样器,调度计算着色器完成混合:
    // 假设pComputeCommandList是已记录的计算命令列表
    pComputeCommandList->SetComputeShader(pBlendCS);
    pComputeCommandList->SetShaderResources(0, 1, &pSceneSRV);
    pComputeCommandList->SetShaderResources(1, 1, &pUISRV);
    pComputeCommandList->SetUnorderedAccessViews(0, 1, &pSceneUAV, nullptr);
    // 计算调度线程组数量
    UINT groupX = (screenWidth + 7) / 8;
    UINT groupY = (screenHeight + 7) / 8;
    pComputeCommandList->Dispatch(groupX, groupY, 1);
    

关键注意事项

  • 确保UI位图的像素格式包含Alpha通道,推荐DXGI_FORMAT_R8G8B8A8_UNORM。
  • 操作前后正确切换资源状态:使用Direct2D前切换到D3D12_RESOURCE_STATE_RENDER_TARGET,使用计算着色器前切换到D3D12_RESOURCE_STATE_UNORDERED_ACCESS,完成后切换回D3D12_RESOURCE_STATE_PRESENT。

内容的提问来源于stack exchange,提问作者Tom Huntington

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 18:35:23