forked from Averta047/imfx
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathimfx_capture_dxgi.cpp
More file actions
207 lines (174 loc) · 6.72 KB
/
Copy pathimfx_capture_dxgi.cpp
File metadata and controls
207 lines (174 loc) · 6.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#include "imfx_capture_dxgi.h"
#include <stdio.h>
#pragma comment(lib, "dxgi.lib")
ImFXCapture_DXGI::ImFXCapture_DXGI()
{
memset(_LastError, 0, sizeof(_LastError));
}
bool ImFXCapture_DXGI::Init(ID3D11Device* pDevice, ID3D11DeviceContext* pCtx, UINT monitorIndex)
{
_pDevice = pDevice;
_pCtx = pCtx;
// ---- Get the DXGI device from the D3D11 device -------------------------
IDXGIDevice* pDxgiDevice = nullptr;
if (FAILED(pDevice->QueryInterface(IID_PPV_ARGS(&pDxgiDevice))))
{
snprintf(_LastError, sizeof(_LastError), "QueryInterface IDXGIDevice failed");
return false;
}
// ---- Walk adapter -> output --------------------------------------------
IDXGIAdapter* pAdapter = nullptr;
HRESULT hr = pDxgiDevice->GetParent(IID_PPV_ARGS(&pAdapter));
pDxgiDevice->Release();
if (FAILED(hr))
{
snprintf(_LastError, sizeof(_LastError), "GetParent IDXGIAdapter failed (hr=0x%08X)", hr);
return false;
}
IDXGIOutput* pOutput = nullptr;
hr = pAdapter->EnumOutputs(monitorIndex, &pOutput);
pAdapter->Release();
if (FAILED(hr))
{
snprintf(_LastError, sizeof(_LastError),
"EnumOutputs(%u) failed - monitor index out of range? (hr=0x%08X)", monitorIndex, hr);
return false;
}
// ---- QI to IDXGIOutput1 for DuplicateOutput ----------------------------
IDXGIOutput1* pOutput1 = nullptr;
hr = pOutput->QueryInterface(IID_PPV_ARGS(&pOutput1));
pOutput->Release();
if (FAILED(hr))
{
snprintf(_LastError, sizeof(_LastError),
"QI IDXGIOutput1 failed - Windows 8+ required (hr=0x%08X)", hr);
return false;
}
// ---- Store monitor dimensions for reference ----------------------------
DXGI_OUTPUT_DESC outDesc = {};
pOutput1->GetDesc(&outDesc);
_MonitorW = (UINT)(outDesc.DesktopCoordinates.right - outDesc.DesktopCoordinates.left);
_MonitorH = (UINT)(outDesc.DesktopCoordinates.bottom - outDesc.DesktopCoordinates.top);
// ---- Create the duplication object -------------------------------------
// Common failure modes:
// DXGI_ERROR_NOT_CURRENTLY_AVAILABLE - another app holds duplication,
// or this is a Remote Desktop session.
// E_ACCESSDENIED - running without PROCESS_DPI_AWARENESS, rare.
hr = pOutput1->DuplicateOutput(pDevice, &_pDuplication);
pOutput1->Release();
if (FAILED(hr))
{
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
snprintf(_LastError, sizeof(_LastError),
"DuplicateOutput: DXGI_ERROR_NOT_CURRENTLY_AVAILABLE\n"
"Another application may already be using desktop duplication,\n"
"or this is a Remote Desktop / virtual machine session.");
else if (hr == E_ACCESSDENIED)
snprintf(_LastError, sizeof(_LastError),
"DuplicateOutput: E_ACCESSDENIED - try running as administrator");
else
snprintf(_LastError, sizeof(_LastError),
"DuplicateOutput failed (hr=0x%08X)", hr);
return false;
}
// Pre-allocate the staging texture at monitor resolution
RecreateStaging(_MonitorW, _MonitorH);
snprintf(_LastError, sizeof(_LastError), "OK");
return true;
}
void ImFXCapture_DXGI::Shutdown()
{
if (_FrameHeld && _pDuplication)
{
_pDuplication->ReleaseFrame();
_FrameHeld = false;
}
DestroyStaging();
if (_pDuplication) { _pDuplication->Release(); _pDuplication = nullptr; }
_pDevice = nullptr;
_pCtx = nullptr;
}
void ImFXCapture_DXGI::RecreateStaging(UINT w, UINT h)
{
DestroyStaging();
// Must be BGRA to match the desktop duplication format.
// BIND_SHADER_RESOURCE so we can make an SRV directly.
D3D11_TEXTURE2D_DESC td = {};
td.Width = w;
td.Height = h;
td.MipLevels = 1;
td.ArraySize = 1;
td.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
td.SampleDesc.Count = 1;
td.Usage = D3D11_USAGE_DEFAULT;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
if (FAILED(_pDevice->CreateTexture2D(&td, nullptr, &_pStagingTex)))
return;
_pDevice->CreateShaderResourceView(_pStagingTex, nullptr, &_pStagingSrv);
}
void ImFXCapture_DXGI::DestroyStaging()
{
if (_pStagingSrv) { _pStagingSrv->Release(); _pStagingSrv = nullptr; }
if (_pStagingTex) { _pStagingTex->Release(); _pStagingTex = nullptr; }
}
ID3D11ShaderResourceView* ImFXCapture_DXGI::AcquireFrame(UINT timeoutMs)
{
if (!_pDuplication || !_pStagingTex)
return nullptr;
// Should not call AcquireFrame twice without ReleaseFrame in between
if (_FrameHeld)
{
_pDuplication->ReleaseFrame();
_FrameHeld = false;
}
DXGI_OUTDUPL_FRAME_INFO frameInfo = {};
IDXGIResource* pDesktopResource = nullptr;
HRESULT hr = _pDuplication->AcquireNextFrame(timeoutMs, &frameInfo, &pDesktopResource);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
return nullptr; // No new frame this tick - caller reuses last effect output
if (hr == DXGI_ERROR_ACCESS_LOST)
{
// Desktop mode change (resolution, DPI, fullscreen toggle).
// Re-init is required; signal the caller via nullptr.
snprintf(_LastError, sizeof(_LastError), "AcquireNextFrame: ACCESS_LOST - call Shutdown()/Init() again");
_pDuplication->Release();
_pDuplication = nullptr;
return nullptr;
}
if (FAILED(hr))
{
snprintf(_LastError, sizeof(_LastError), "AcquireNextFrame failed (hr=0x%08X)", hr);
return nullptr;
}
_FrameHeld = true;
// ---- Get the desktop texture -------------------------------------------
ID3D11Texture2D* pDesktopTex = nullptr;
hr = pDesktopResource->QueryInterface(IID_PPV_ARGS(&pDesktopTex));
pDesktopResource->Release();
if (FAILED(hr))
return nullptr;
// ---- Check if resolution changed (e.g. DPI scaling event) -------------
D3D11_TEXTURE2D_DESC desktopDesc = {};
pDesktopTex->GetDesc(&desktopDesc);
if (desktopDesc.Width != _MonitorW || desktopDesc.Height != _MonitorH)
{
_MonitorW = desktopDesc.Width;
_MonitorH = desktopDesc.Height;
RecreateStaging(_MonitorW, _MonitorH);
}
// ---- Copy desktop texture -> SRV-capable staging texture ---------------
// CopyResource is a GPU-side blit - no CPU round-trip.
// The desktop texture has no SRV bind flag so we can't use it directly.
if (_pStagingTex)
_pCtx->CopyResource(_pStagingTex, pDesktopTex);
pDesktopTex->Release();
return _pStagingSrv; // valid until next ReleaseFrame()
}
void ImFXCapture_DXGI::ReleaseFrame()
{
if (_FrameHeld && _pDuplication)
{
_pDuplication->ReleaseFrame();
_FrameHeld = false;
}
}