- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 546
- Reaction score
- 7
Digging into the DXGI API for a screen capture module and I've hit a wall that's likely familiar to anyone messing with the Desktop Duplication API. I'm trying to pull frames for an external project, and while the code executes without a single HRESULT error, the actual output is a void.
The Symptom:
Everything returns S_OK, but the buffer is essentially zeroed out. If I save as BGR, it's a black void. If I save as BGRA, it's 100% transparent. This usually smells like a staging texture issue or a synchronization failure between the GPU and CPU buffers.
The Current Setup:
Frame Capture Logic:
Main Invocation:
The Core Problem:
I've verified that AcquireNextFrame is returning S_OK and the pointer isn't null. I'm using the standard DXGI_FORMAT_B8G8R8A8_UNORM. Despite this, the pixel data is just not there. I suspect the issue might be in how the resource is being mapped or the staging texture is handled within the library helper, but I've tried a few different implementations with the same result.
Could this be related to multi-GPU setups or specifically how Windows handles the desktop duplication slot?
anyone encountered this with multi-monitor setups or specific TDR settings?
The Symptom:
Everything returns S_OK, but the buffer is essentially zeroed out. If I save as BGR, it's a black void. If I save as BGRA, it's 100% transparent. This usually smells like a staging texture issue or a synchronization failure between the GPU and CPU buffers.
The Current Setup:
- D3D11 Device and DXGI Device initialization.
- Desktop Duplication setup via IDXGIOutputDuplication.
- Acquiring the frame using AcquireNextFrame.
- Copying to a CPU-accessible texture and saving to BMP.
Frame Capture Logic:
Code:
BYTE* GetFrameAsBitmap(CAPTURE& capturer, int& width, int& height) {
DXGI_OUTDUPL_FRAME_INFO frameInfo;
CComPtr<IDXGIResource> desktopResource;
HRESULT hr = capturer.lDeskDupl->AcquireNextFrame(0, &frameInfo, &desktopResource);
if (SUCCEEDED(hr)) {
if (!capturer.Get(desktopResource, false)) {
capturer.lDeskDupl->ReleaseFrame();
return nullptr;
}
capturer.lDeskDupl->ReleaseFrame();
}
width = capturer.lOutputDuplDesc.ModeDesc.Width;
height = capturer.lOutputDuplDesc.ModeDesc.Height;
return capturer.buf.data();
}
Main Invocation:
Code:
void main() {
CAPTURE capture = CAPTURE();
HRESULT hr = capture.CreateDirect3DDevice(nullptr);
if (FAILED(hr)) {
std::cerr << "Failed to create D3D device!" << std::endl;
return;
}
if (!capture.Prepare(0)) {
std::cerr << "Failed to prepare screen capture!" << std::endl;
return;
}
int width, height;
BYTE* bitmap = GetFrameAsBitmap(capture, width, height);
if (bitmap) {
SaveBitmapToFile(bitmap, width, height, "screenshot.bmp");
}
}
Code:
void SaveBitmapToFile(const BYTE* bitmap, int width, int height, const char* filename) {
int bytesPerPixel = 4;
int rowSize = width * bytesPerPixel;
int padding = (4 - (rowSize % 4)) % 4;
std::vector<BYTE> bmpData((rowSize + padding) * height);
for (int y = 0; y < height; y++) {
int srcIndex = (height - 1 - y) * width * bytesPerPixel;
int dstIndex = y * (width * bytesPerPixel + padding);
for (int x = 0; x < width; x++) {
bmpData[dstIndex + x * 3 + 0] = bitmap[srcIndex + x * 4 + 0]; // B
bmpData[dstIndex + x * 3 + 1] = bitmap[srcIndex + x * 4 + 1]; // G
bmpData[dstIndex + x * 3 + 2] = bitmap[srcIndex + x * 4 + 2]; // R
}
}
BITMAPFILEHEADER fileHeader = {0x4D42, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bmpData.size(), 0, 0, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)};
BITMAPINFOHEADER infoHeader = {sizeof(BITMAPINFOHEADER), width, height, 1, 24, BI_RGB, (DWORD)bmpData.size(), 0, 0, 0, 0};
std::ofstream file(filename, std::ios::binary);
file.write((char*)&fileHeader, sizeof(fileHeader));
file.write((char*)&infoHeader, sizeof(infoHeader));
file.write((char*)bmpData.data(), bmpData.size());
}
The Core Problem:
I've verified that AcquireNextFrame is returning S_OK and the pointer isn't null. I'm using the standard DXGI_FORMAT_B8G8R8A8_UNORM. Despite this, the pixel data is just not there. I suspect the issue might be in how the resource is being mapped or the staging texture is handled within the library helper, but I've tried a few different implementations with the same result.
Could this be related to multi-GPU setups or specifically how Windows handles the desktop duplication slot?
anyone encountered this with multi-monitor setups or specific TDR settings?