Reputation: 19
I am trying to use DirectX11 in C++ to Capture the screen and convert it to a texture I can render with OpenGL or ImGui. I have used the Windows API with BitBlt, this however results in fps lower than 15, so this is not an option.
I am using Windows 11, with a Nvidia graphics card.
Library Import:
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
Initializing DirectX11:
bool DirectX11::initDirectX11() {
HRESULT hr = S_OK;
IDXGIFactory* DXGIFactory = nullptr;
IDXGIAdapter* Adapter = nullptr;
D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_0;
Output = nullptr;
deskDupl = nullptr;
hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&DXGIFactory));
if (FAILED(hr)) {
std::cout << "Failed to create DXGI Factory" << std::endl;
return false;
}
hr = DXGIFactory->EnumAdapters(0, &Adapter);
if (FAILED(hr)) {
std::cout << "Failed to create adapter" << std::endl;
return false;
}
hr = Adapter->EnumOutputs(0, &Output);
if (FAILED(hr)) {
std::cout << "Failed to create output" << std::endl;
return false;
}
hr = Output->QueryInterface(__uuidof(Output), (void**)(&deskDupl));
if (FAILED(hr)) {
std::cout << "Failed to create output1" << std::endl;
return false;
}
hr = Output->GetDesc(&OutputDesc);
if (FAILED(hr)) {
std::cout << "Failed to get output description" << std::endl;
return false;
}
hr = D3D11CreateDevice(Adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &device, &FeatureLevel, &deviceContext);
if (FAILED(hr)) {
std::cout << "Failed to create D3D11 device" << std::endl;
return false;
}
DXGIFactory->Release();
Adapter->Release();
return true;
}
Capturing the Screen:
void DirectX11::captureScreen() {
HRESULT hr = S_OK;
IDXGIResource* DesktopResource;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
// Get new frame
hr = deskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
if (FAILED(hr)) {
std::cout << "Failed to acquire next frame" << std::endl;
return;
}
// Store the frame as a texture
// Trows exception
// Read access violation
// DesktopResource was 0xFFFFFFFFFFFFFFFF
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&AcquiredDesktopImage));
if (FAILED(hr)) {
std::cout << "Failed to query interface" << std::endl;
return;
}
}
The problem is that it keeps throwing a read acces violation exception.
Read access violation
DesktopResource was 0xFFFFFFFFFFFFFFFF
Upvotes: 0
Views: 426
Reputation: 139187
This code
hr = Output->QueryInterface(__uuidof(Output), (void**)(&deskDupl));
Is invalid as it writes an IDXGIOutput
reference to an IDXGIOutputDuplication
reference.
To get an IDXGIOutputDuplication
reference, you must call the IDXGIOutput1::DuplicateOutput method, so you must get an IDXGIOutput1
first, like this:
IDXGIOutput1* o1;
hr = Output->QueryInterface(__uuidof(IDXGIOutput1), (void**)(&o1));
And then
hr = o1->DuplicateOutput(device, &deskDupl);
But for this to succeed, you must create the device from an IDXGIFactory1
or from an IDXGIFactory
factory created from CreateDXGIFactory1 (DXGI 1.1), so replace the call CreateDXGIFactory
by CreateDXGIFactory1
.
Note: to avoid casting to dangerous (void**)
for QueryInterface
and many other COM calls, and so avoid raw casting errors (some of it at least) when working with Visual Studio you can use the IID_PPV_ARGS macro. So you can write this:
hr = Output->QueryInterface(IID_PPV_ARGS(&o1));
And nowadays, IUnknown
defining headers (Windows SDK's Unknwnbase.h
) also defines some more useful C++ templates, so you can even call QueryInterface like this directly:
hr = Output->QueryInterface(&o1);
PS: note sure this exists with other compilers than MSVC.
Upvotes: 0