基于DirectX 11实现高效GetPixel,替代GDI+慢像素获取函数
Why This Beats GDI+
- GDI+ relies on cross-context surface access which introduces heavy overhead for repeated sampling. DX11 uses staging textures that directly map to GPU memory, cutting down on latency drastically.
- Since you’re already hooking
IDXGISwapChain::Present, you can reuse existing DX11 device/swap chain resources instead of spinning up new ones, eliminating redundant initialization costs.
Prerequisites
You’ll need access to these resources from your Present hook:
ID3D11Device* pDeviceID3D11DeviceContext* pContext- Swap chain back buffer dimensions and format
Core Implementation
1. Initialize Staging Texture (Run Once)
Create a staging texture matching the back buffer’s specs. Do this once after hook initialization, not per frame.
ID3D11Texture2D* pStagingTexture = nullptr; D3D11_TEXTURE2D_DESC g_StagingDesc = {}; void InitDX11StagingTexture(ID3D11Device* pDevice, UINT width, UINT height, DXGI_FORMAT format) { if (pStagingTexture) pStagingTexture->Release(); ZeroMemory(&g_StagingDesc, sizeof(g_StagingDesc)); g_StagingDesc.Width = width; g_StagingDesc.Height = height; g_StagingDesc.MipLevels = 1; g_StagingDesc.ArraySize = 1; g_StagingDesc.Format = format; g_StagingDesc.SampleDesc.Count = 1; g_StagingDesc.Usage = D3D11_USAGE_STAGING; g_StagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; pDevice->CreateTexture2D(&g_StagingDesc, nullptr, &pStagingTexture); }
2. Capture Back Buffer (Per Frame)
Copy the current back buffer to the staging texture once per frame—do this before any pixel sampling to avoid redundant GPU copies.
void CaptureBackBuffer(IDXGISwapChain* pSwapChain, ID3D11DeviceContext* pContext) { ID3D11Texture2D* pBackBuffer = nullptr; pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer); pContext->CopyResource(pStagingTexture, pBackBuffer); pBackBuffer->Release(); }
3. Single Pixel Color Retrieval
Fixes coordinate issues by accounting for GPU memory row stride (critical for correct pixel indexing):
bool GetPixelColorDX11(UINT x, UINT y, UINT& outColor) { if (!pStagingTexture || x >= g_StagingDesc.Width || y >= g_StagingDesc.Height) return false; D3D11_MAPPED_SUBRESOURCE mappedRes; HRESULT hr = pContext->Map(pStagingTexture, 0, D3D11_MAP_READ, 0, &mappedRes); if (FAILED(hr)) return false; // Calculate pixel position (adjust data type if using non-RGBA8 formats) UINT* pPixels = (UINT*)mappedRes.pData; UINT rowStride = mappedRes.RowPitch / sizeof(UINT); outColor = pPixels[y * rowStride + x]; pContext->Unmap(pStagingTexture, 0); return true; }
4. Multiple Pixels Retrieval (Most Efficient)
Process all required coordinates in one map/unmap pass to minimize CPU-GPU sync overhead:
bool GetMultiplePixelsDX11(const std::vector<std::pair<UINT, UINT>>& coords, std::vector<UINT>& outColors) { if (!pStagingTexture || coords.empty()) return false; outColors.resize(coords.size()); D3D11_MAPPED_SUBRESOURCE mappedRes; HRESULT hr = pContext->Map(pStagingTexture, 0, D3D11_MAP_READ, 0, &mappedRes); if (FAILED(hr)) return false; UINT* pPixels = (UINT*)mappedRes.pData; UINT rowStride = mappedRes.RowPitch / sizeof(UINT); for (size_t i = 0; i < coords.size(); ++i) { UINT x = coords[i].first; UINT y = coords[i].second; if (x >= g_StagingDesc.Width || y >= g_StagingDesc.Height) { outColors[i] = 0; // Handle invalid coordinates as needed continue; } outColors[i] = pPixels[y * rowStride + x]; } pContext->Unmap(pStagingTexture, 0); return true; }
Usage Guidelines
- First Hook Call: Retrieve the swap chain’s back buffer dimensions and format, then run
InitDX11StagingTexture. - Per Frame: Call
CaptureBackBufferonce before sampling any pixels. - Sample Efficiently: Use
GetMultiplePixelsDX11for your 1-11 coordinates instead of calling the single-pixel function repeatedly—this cuts down on map/unmap overhead. - Cleanup: Release
pStagingTexturewhen your hook is unloaded to avoid memory leaks.
This implementation will eliminate the FPS drops from your GDI+ calls, as it avoids slow cross-context surface access and leverages DX11’s optimized memory handling.
内容的提问来源于stack exchange,提问作者marcoluca987

