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

如何基于WinRT Windows.Graphics.Capture实现屏幕捕获并应用像素着色器

代码修改方案

现有代码核心问题

  • 一次性可复用的D3D资源(顶点缓冲区、采样器、输入布局、着色器等)每帧重复创建,导致性能低下、显存泄漏
  • 捕获帧纹理复制逻辑错误:创建D3D11_USAGE_IMMUTABLE类型纹理时未传入初始数据,会直接创建失败,且无必要额外创建新纹理存储捕获帧
  • 着色器资源视图参数配置错误,无法正确绑定捕获帧纹理到像素着色器
  • 缺少临时COM资源的释放逻辑,运行一段时间后会显存溢出

具体修改步骤

1. 先补充类成员变量定义(在SimpleCapture.h中添加)

#define NUMVERTICES 6
// 原有成员变量基础上新增
struct _VERTEX
{
    DirectX::XMFLOAT3 Pos;
    DirectX::XMFLOAT2 TexCoord;
};
winrt::com_ptr<ID3D11RenderTargetView> m_RTV;
winrt::com_ptr<ID3D11SamplerState> m_SamplerLinear;
winrt::com_ptr<ID3D11Buffer> m_VertexBuffer;
winrt::com_ptr<ID3D11PixelShader> m_PixelShader;
winrt::com_ptr<ID3D11VertexShader> m_VertexShader;
winrt::com_ptr<ID3D11InputLayout> m_InputLayout;
SIZE m_lastSize{};
std::atomic<bool> m_closed = false;

所有COM指针改用winrt::com_ptr自动管理生命周期,避免手动泄漏

2. 修改构造函数逻辑(把一次性初始化逻辑全部移入)

SimpleCapture::SimpleCapture(
    IDirect3DDevice const& device,
    GraphicsCaptureItem const& item)
{
    m_item = item;
    m_device = device;
    m_lastSize = m_item.Size();

    // 获取D3D设备和上下文
    m_d3ddevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
    m_d3ddevice->GetImmediateContext(m_d3dContext.put());

    // 创建交换链
    m_swapChain = CreateDXGISwapChain(
        m_d3ddevice,
        static_cast<uint32_t>(m_lastSize.Width),
        static_cast<uint32_t>(m_lastSize.Height),
        static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized),
        2);

    // 初始化顶点着色器和输入布局
    UINT vsSize = ARRAYSIZE(g_VS1);
    check_hresult(m_d3ddevice->CreateVertexShader(g_VS1, vsSize, nullptr, m_VertexShader.put()));
    D3D11_INPUT_ELEMENT_DESC Layout[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
    };
    UINT numElements = ARRAYSIZE(Layout);
    check_hresult(m_d3ddevice->CreateInputLayout(Layout, numElements, g_VS1, vsSize, m_InputLayout.put()));

    // 初始化像素着色器
    UINT psSize = ARRAYSIZE(g_main);
    check_hresult(m_d3ddevice->CreatePixelShader(g_main, psSize, nullptr, m_PixelShader.put()));

    // 初始化采样器
    D3D11_SAMPLER_DESC SampDesc{};
    SampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    SampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
    SampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
    SampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
    SampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
    SampDesc.MinLOD = 0;
    SampDesc.MaxLOD = D3D11_FLOAT32_MAX;
    check_hresult(m_d3ddevice->CreateSamplerState(&SampDesc, m_SamplerLinear.put()));

    // 初始化顶点缓冲区
    _VERTEX Vertices[NUMVERTICES] =
    {
        {XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)},
        {XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
        {XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
        {XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
        {XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
        {XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)},
    };
    D3D11_BUFFER_DESC BufferDesc{};
    BufferDesc.Usage = D3D11_USAGE_DEFAULT;
    BufferDesc.ByteWidth = sizeof(_VERTEX) * NUMVERTICES;
    BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    D3D11_SUBRESOURCE_DATA InitData{};
    InitData.pSysMem = Vertices;
    check_hresult(m_d3ddevice->CreateBuffer(&BufferDesc, &InitData, m_VertexBuffer.put()));

    // 初始化捕获帧池和会话
    m_framePool = Direct3D11CaptureFramePool::Create(
        m_device,
        DirectXPixelFormat::B8G8R8A8UIntNormalized,
        2,
        m_lastSize);
    m_session = m_framePool.CreateCaptureSession(m_item);
    m_frameArrived = m_framePool.FrameArrived(auto_revoke, { this, &SimpleCapture::OnFrameArrived });
}

3. 重写OnFrameArrived帧处理逻辑

void SimpleCapture::OnFrameArrived(
    Direct3D11CaptureFramePool const& sender,
    winrt::Windows::Foundation::IInspectable const&)
{
    auto frame = sender.TryGetNextFrame();
    if (!frame) return;

    auto frameContentSize = frame.ContentSize();
    // 处理窗口大小变化
    if (frameContentSize.Width != m_lastSize.Width || frameContentSize.Height != m_lastSize.Height)
    {
        m_lastSize = frameContentSize;
        // 重设交换链缓冲区
        m_swapChain->ResizeBuffers(
            2,
            static_cast<uint32_t>(m_lastSize.Width),
            static_cast<uint32_t>(m_lastSize.Height),
            static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized),
            0);
        // 重设帧池大小
        m_framePool.Recreate(
            m_device,
            DirectXPixelFormat::B8G8R8A8UIntNormalized,
            2,
            m_lastSize);
        // 释放旧的RTV,下次会自动重建
        m_RTV = nullptr;
    }

    // 获取交换链后台缓冲,重建RTV(仅大小变化时需要重建)
    winrt::com_ptr<ID3D11Texture2D> backBuffer;
    check_hresult(m_swapChain->GetBuffer(0, winrt::guid_of<ID3D11Texture2D>(), backBuffer.put_void()));
    if (!m_RTV)
    {
        check_hresult(m_d3ddevice->CreateRenderTargetView(backBuffer.get(), nullptr, m_RTV.put()));
    }

    // 直接拿捕获帧纹理建SRV,无需额外复制
    auto frameSurface = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
    winrt::com_ptr<ID3D11ShaderResourceView> frameSrv;
    check_hresult(m_d3ddevice->CreateShaderResourceView(frameSurface.get(), nullptr, frameSrv.put()));

    // 配置渲染管线
    UINT stride = sizeof(_VERTEX);
    UINT offset = 0;
    m_d3dContext->OMSetRenderTargets(1, m_RTV.put(), nullptr);
    m_d3dContext->IASetInputLayout(m_InputLayout.get());
    m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    m_d3dContext->IASetVertexBuffers(0, 1, m_VertexBuffer.put(), &stride, &offset);
    m_d3dContext->VSSetShader(m_VertexShader.get(), nullptr, 0);
    m_d3dContext->PSSetShader(m_PixelShader.get(), nullptr, 0);
    m_d3dContext->PSSetShaderResources(0, 1, frameSrv.put());
    m_d3dContext->PSSetSamplers(0, 1, m_SamplerLinear.put());

    // 绘制(像素着色器会自动处理捕获帧)
    m_d3dContext->Draw(NUMVERTICES, 0);

    // 解绑资源,避免后续冲突
    ID3D11ShaderResourceView* nullSrv = nullptr;
    m_d3dContext->PSSetShaderResources(0, 1, &nullSrv);

    // 提交帧
    DXGI_PRESENT_PARAMETERS presentParameters{};
    m_swapChain->Present1(1, 0, &presentParameters);
}

4. 测试用像素着色器示例(可放到PixelShader.h中验证效果)

以下是反色效果的HLSL像素着色器编译后的字节码,替换你原来的g_main即可测试着色器是否生效:

const BYTE g_main[] = {
    0x44,0x58,0x42,0x43,0x9c,0x96,0x31,0x8f,0x97,0x2a,0x47,0xa0,0x91,0x43,0x15,0x83,
    0x54,0x7d,0x38,0x82,0x02,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00,
    0x34,0x00,0x00,0x00,0x0c,0x02,0x00,0x00,0x44,0x02,0x00,0x00,0x7c,0x02,0x00,0x00,
    0x94,0x01,0x00,0x00,0x58,0x01,0x00,0x00,0x80,0x00,0x00,0x00,0x2c,0x01,0x00,0x00,
    0xa0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x38,0x00,0x00,0x00,
    0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
    0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,
    0x54,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
    0x0d,0x00,0x00,0x00,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65,0x74,0x00,0x50,0x53,
    0x00,0x03,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,
    0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,
    0x00,0x01,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x49,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,
    0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x4f,0x53,0x47,0x4e,0x1c,0x00,0x00,0x00,0x01,0x00,0x00,
    0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x48,0x44,0x52,0x98,0x00,0x00,
    0x00,0x40,0x00,
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 11:45:00