urenai 发表于 2025-1-15 20:12:20

尝试了很多次,无法封装D3D 截图,希望分享一下,谢谢!

大致分为2部分,
1、获取数据。
2、数据还原成bmp图片

不管怎么优化,普通截图速度都不可能降到50毫秒以下。

fengshangren 发表于 2025-1-15 22:30:05

D3D截图我一直不太清楚是什么,有什么优势,和DXGI有什么区别,哪个快

urenai 发表于 2025-1-15 22:45:35

fengshangren 发表于 2025-1-15 22:30
D3D截图我一直不太清楚是什么,有什么优势,和DXGI有什么区别,哪个快

兄弟分享一下 DXGI

uuyyhhjj 发表于 2025-1-16 20:46:21

Desktop Duplication API 我用AI试了下,始终是黑屏,没接触过D3D,表面上看不出来哪个环节出问题了

#include <windows.h>
#include <d3d11.h>
#include <dxgi1_2.h>
#include <wrl/client.h> // 仅用于异常处理,不用于智能指针
#include <iostream>

#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")

#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = nullptr; } }

// 窗口尺寸
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;

// 全局变量
ID3D11Device* g_pD3DDevice = nullptr;
ID3D11DeviceContext* g_pD3DDeviceContext = nullptr;
IDXGIOutputDuplication* g_pDeskDupl = nullptr;
ID3D11Texture2D* g_pAcquiredDesktopImage = nullptr;
ID3D11Texture2D* g_pRenderTargetTexture = nullptr;
ID3D11RenderTargetView* g_pRenderTargetView = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;

// 函数声明
bool InitializeDirect3D(HWND hwnd);
bool InitializeDesktopDuplication();
void CaptureFrame();
void RenderFrame();
void Cleanup();

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

// 主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    // 注册窗口类
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, nullptr, nullptr, nullptr, nullptr, L"ScreenCaptureClass", nullptr };
    RegisterClassEx(&wc);

    // 创建窗口
    HWND hwnd = CreateWindow(wc.lpszClassName, L"DirectX Desktop Duplication API Example", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, nullptr, nullptr, hInstance, nullptr);
    if (!hwnd)
    {
      std::cerr << "Failed to create window!" << std::endl;
      return -1;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // 初始化 Direct3D
    if (!InitializeDirect3D(hwnd))
    {
      std::cerr << "Failed to initialize Direct3D!" << std::endl;
      Cleanup();
      return -1;
    }

    // 初始化桌面复制
    if (!InitializeDesktopDuplication())
    {
      std::cerr << "Failed to initialize desktop duplication!" << std::endl;
      Cleanup();
      return -1;
    }

    // 主消息循环
    MSG msg = { 0 };
    while (msg.message != WM_QUIT)
    {
      if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
      {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
      }
      else
      {
            CaptureFrame();
            RenderFrame();
      }
    }

    Cleanup();
    return static_cast<int>(msg.wParam);
}

// 初始化 Direct3D
bool InitializeDirect3D(HWND hwnd)
{
    // 创建设备和交换链
    DXGI_SWAP_CHAIN_DESC scd = {};
    scd.BufferCount = 1;
    scd.BufferDesc.Width = WINDOW_WIDTH;
    scd.BufferDesc.Height = WINDOW_HEIGHT;
    scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    scd.BufferDesc.RefreshRate.Numerator = 60;
    scd.BufferDesc.RefreshRate.Denominator = 1;
    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    scd.OutputWindow = hwnd;
    scd.SampleDesc.Count = 1;
    scd.SampleDesc.Quality = 0;
    scd.Windowed = TRUE;

    HRESULT hr = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &scd, &g_pSwapChain, &g_pD3DDevice, nullptr, &g_pD3DDeviceContext);
    if (FAILED(hr))
    {
      std::cerr << "Failed to create device and swap chain!" << std::endl;
      return false;
    }

    // 创建渲染目标视图
    ID3D11Texture2D* pBackBuffer = nullptr;
    hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pBackBuffer));
    if (FAILED(hr))
    {
      std::cerr << "Failed to get back buffer!" << std::endl;
      return false;
    }

    hr = g_pD3DDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_pRenderTargetView);
    SAFE_RELEASE(pBackBuffer);
    if (FAILED(hr))
    {
      std::cerr << "Failed to create render target view!" << std::endl;
      return false;
    }

    g_pD3DDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, nullptr);

    // 设置视口
    D3D11_VIEWPORT vp = {};
    vp.Width = static_cast<float>(WINDOW_WIDTH);
    vp.Height = static_cast<float>(WINDOW_HEIGHT);
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    g_pD3DDeviceContext->RSSetViewports(1, &vp);

    return true;
}

// 初始化桌面复制
bool InitializeDesktopDuplication()
{
    // 获取 DXGI 设备
    IDXGIDevice* pDxgiDevice = nullptr;
    HRESULT hr = g_pD3DDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&pDxgiDevice));
    if (FAILED(hr))
    {
      std::cerr << "Failed to get DXGI device!" << std::endl;
      return false;
    }

    // 获取 DXGI 适配器
    IDXGIAdapter* pDxgiAdapter = nullptr;
    hr = pDxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&pDxgiAdapter));
    SAFE_RELEASE(pDxgiDevice);
    if (FAILED(hr))
    {
      std::cerr << "Failed to get DXGI adapter!" << std::endl;
      return false;
    }

    // 获取 DXGI 输出
    IDXGIOutput* pDxgiOutput = nullptr;
    hr = pDxgiAdapter->EnumOutputs(0, &pDxgiOutput);
    SAFE_RELEASE(pDxgiAdapter);
    if (FAILED(hr))
    {
      std::cerr << "Failed to get DXGI output!" << std::endl;
      return false;
    }

    // 获取 DXGI 输出1
    IDXGIOutput1* pDxgiOutput1 = nullptr;
    hr = pDxgiOutput->QueryInterface(__uuidof(IDXGIOutput1), reinterpret_cast<void**>(&pDxgiOutput1));
    SAFE_RELEASE(pDxgiOutput);
    if (FAILED(hr))
    {
      std::cerr << "Failed to get DXGI output1!" << std::endl;
      return false;
    }

    // 创建桌面复制接口
    hr = pDxgiOutput1->DuplicateOutput(g_pD3DDevice, &g_pDeskDupl);
    SAFE_RELEASE(pDxgiOutput1);
    if (FAILED(hr))
    {
      std::cerr << "Failed to create desktop duplication interface!" << std::endl;
      return false;
    }

    return true;
}

// 捕获桌面帧
void CaptureFrame()
{
    DXGI_OUTDUPL_FRAME_INFO frameInfo;
    IDXGIResource* pDesktopResource = nullptr;

    // 获取桌面帧
    HRESULT hr = g_pDeskDupl->AcquireNextFrame(0, &frameInfo, &pDesktopResource);
    if (FAILED(hr))
    {
      if (hr == DXGI_ERROR_WAIT_TIMEOUT)
      {
            return; // 没有新的帧
      }
      std::cerr << "Failed to acquire next frame!" << std::endl;
      return;
    }

    // 获取桌面纹理
    hr = pDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&g_pAcquiredDesktopImage));
    SAFE_RELEASE(pDesktopResource);
    if (FAILED(hr))
    {
      std::cerr << "Failed to get desktop texture!" << std::endl;
      g_pDeskDupl->ReleaseFrame();
      return;
    }

    // 释放帧
    g_pDeskDupl->ReleaseFrame();
}

// 渲染帧
void RenderFrame()
{
    if (!g_pAcquiredDesktopImage)
    {
      return;
    }

    // 创建渲染目标纹理
    D3D11_TEXTURE2D_DESC desc;
    g_pAcquiredDesktopImage->GetDesc(&desc);

    if (!g_pRenderTargetTexture)
    {
      desc.Width = WINDOW_WIDTH;
      desc.Height = WINDOW_HEIGHT;
      desc.BindFlags = D3D11_BIND_RENDER_TARGET;
      desc.MiscFlags = 0;

      HRESULT hr = g_pD3DDevice->CreateTexture2D(&desc, nullptr, &g_pRenderTargetTexture);
      if (FAILED(hr))
      {
            std::cerr << "Failed to create render target texture!" << std::endl;
            return;
      }
    }

    // 将桌面图像复制到渲染目标纹理
    g_pD3DDeviceContext->CopyResource(g_pRenderTargetTexture, g_pAcquiredDesktopImage);

    // 清除渲染目标
    float clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
    g_pD3DDeviceContext->ClearRenderTargetView(g_pRenderTargetView, clearColor);

    // 渲染纹理到窗口
    g_pD3DDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, nullptr);
    g_pD3DDeviceContext->Draw(4, 0);

    // 呈现交换链
    g_pSwapChain->Present(0, 0);

    // 释放桌面图像
    SAFE_RELEASE(g_pAcquiredDesktopImage);
}

// 清理资源
void Cleanup()
{
    SAFE_RELEASE(g_pRenderTargetTexture);
    SAFE_RELEASE(g_pRenderTargetView);
    SAFE_RELEASE(g_pSwapChain);
    SAFE_RELEASE(g_pDeskDupl);
    SAFE_RELEASE(g_pD3DDeviceContext);
    SAFE_RELEASE(g_pD3DDevice);
}
页: [1]
查看完整版本: 尝试了很多次,无法封装D3D 截图,希望分享一下,谢谢!