如何基于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,
相关产品推荐
相关产品推荐

