FreeRDP
Loading...
Searching...
No Matches
wf_dxgi.c
1
20#include <freerdp/config.h>
21
22#include "wf_interface.h"
23
24#ifdef WITH_DXGI_1_2
25
26#define CINTERFACE
27
28#include <D3D11.h>
29#include <dxgi1_2.h>
30
31#include <tchar.h>
32#include "wf_dxgi.h"
33
34#include <freerdp/log.h>
35#define TAG SERVER_TAG("windows")
36
37/* Driver types supported */
38D3D_DRIVER_TYPE DriverTypes[] = {
39 D3D_DRIVER_TYPE_HARDWARE,
40 D3D_DRIVER_TYPE_WARP,
41 D3D_DRIVER_TYPE_REFERENCE,
42};
43UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
44
45/* Feature levels supported */
46D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,
47 D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1 };
48
49UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
50
51D3D_FEATURE_LEVEL FeatureLevel;
52
53ID3D11Device* gDevice = NULL;
54ID3D11DeviceContext* gContext = NULL;
55IDXGIOutputDuplication* gOutputDuplication = NULL;
56ID3D11Texture2D* gAcquiredDesktopImage = NULL;
57
58IDXGISurface* surf;
59ID3D11Texture2D* sStage;
60
61DXGI_OUTDUPL_FRAME_INFO FrameInfo;
62
63int wf_dxgi_init(wfInfo* wfi)
64{
65 gAcquiredDesktopImage = NULL;
66
67 if (wf_dxgi_createDevice(wfi) != 0)
68 {
69 return 1;
70 }
71
72 if (wf_dxgi_getDuplication(wfi) != 0)
73 {
74 return 1;
75 }
76
77 return 0;
78}
79
80int wf_dxgi_createDevice(wfInfo* wfi)
81{
82 HRESULT status;
83 UINT DriverTypeIndex;
84
85 for (DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
86 {
87 status = D3D11CreateDevice(NULL, DriverTypes[DriverTypeIndex], NULL, 0, FeatureLevels,
88 NumFeatureLevels, D3D11_SDK_VERSION, &gDevice, &FeatureLevel,
89 &gContext);
90 if (SUCCEEDED(status))
91 break;
92
93 WLog_INFO(TAG, "D3D11CreateDevice returned [%ld] for Driver Type %d", status,
94 DriverTypes[DriverTypeIndex]);
95 }
96
97 if (FAILED(status))
98 {
99 WLog_ERR(TAG, "Failed to create device in InitializeDx");
100 return 1;
101 }
102
103 return 0;
104}
105
106int wf_dxgi_getDuplication(wfInfo* wfi)
107{
108 HRESULT status;
109 UINT dTop, i = 0;
110 DXGI_OUTPUT_DESC desc = { 0 };
111 IDXGIOutput* pOutput;
112 IDXGIDevice* DxgiDevice = NULL;
113 IDXGIAdapter* DxgiAdapter = NULL;
114 IDXGIOutput* DxgiOutput = NULL;
115 IDXGIOutput1* DxgiOutput1 = NULL;
116
117 status = gDevice->lpVtbl->QueryInterface(gDevice, &IID_IDXGIDevice, (void**)&DxgiDevice);
118
119 if (FAILED(status))
120 {
121 WLog_ERR(TAG, "Failed to get QI for DXGI Device");
122 return 1;
123 }
124
125 status = DxgiDevice->lpVtbl->GetParent(DxgiDevice, &IID_IDXGIAdapter, (void**)&DxgiAdapter);
126 DxgiDevice->lpVtbl->Release(DxgiDevice);
127 DxgiDevice = NULL;
128
129 if (FAILED(status))
130 {
131 WLog_ERR(TAG, "Failed to get parent DXGI Adapter");
132 return 1;
133 }
134
135 pOutput = NULL;
136
137 while (DxgiAdapter->lpVtbl->EnumOutputs(DxgiAdapter, i, &pOutput) != DXGI_ERROR_NOT_FOUND)
138 {
139 DXGI_OUTPUT_DESC* pDesc = &desc;
140
141 status = pOutput->lpVtbl->GetDesc(pOutput, pDesc);
142
143 if (FAILED(status))
144 {
145 WLog_ERR(TAG, "Failed to get description");
146 return 1;
147 }
148
149 WLog_INFO(TAG, "Output %u: [%s] [%d]", i, pDesc->DeviceName, pDesc->AttachedToDesktop);
150
151 if (pDesc->AttachedToDesktop)
152 dTop = i;
153
154 pOutput->lpVtbl->Release(pOutput);
155 ++i;
156 }
157
158 dTop = wfi->screenID;
159
160 status = DxgiAdapter->lpVtbl->EnumOutputs(DxgiAdapter, dTop, &DxgiOutput);
161 DxgiAdapter->lpVtbl->Release(DxgiAdapter);
162 DxgiAdapter = NULL;
163
164 if (FAILED(status))
165 {
166 WLog_ERR(TAG, "Failed to get output");
167 return 1;
168 }
169
170 status =
171 DxgiOutput->lpVtbl->QueryInterface(DxgiOutput, &IID_IDXGIOutput1, (void**)&DxgiOutput1);
172 DxgiOutput->lpVtbl->Release(DxgiOutput);
173 DxgiOutput = NULL;
174
175 if (FAILED(status))
176 {
177 WLog_ERR(TAG, "Failed to get IDXGIOutput1");
178 return 1;
179 }
180
181 status =
182 DxgiOutput1->lpVtbl->DuplicateOutput(DxgiOutput1, (IUnknown*)gDevice, &gOutputDuplication);
183 DxgiOutput1->lpVtbl->Release(DxgiOutput1);
184 DxgiOutput1 = NULL;
185
186 if (FAILED(status))
187 {
188 if (status == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
189 {
190 WLog_ERR(
191 TAG,
192 "There is already the maximum number of applications using the Desktop Duplication "
193 "API running, please close one of those applications and then try again.");
194 return 1;
195 }
196
197 WLog_ERR(TAG, "Failed to get duplicate output. Status = %ld", status);
198 return 1;
199 }
200
201 return 0;
202}
203
204int wf_dxgi_cleanup(wfInfo* wfi)
205{
206 if (wfi->framesWaiting > 0)
207 {
208 wf_dxgi_releasePixelData(wfi);
209 }
210
211 if (gAcquiredDesktopImage)
212 {
213 gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
214 gAcquiredDesktopImage = NULL;
215 }
216
217 if (gOutputDuplication)
218 {
219 gOutputDuplication->lpVtbl->Release(gOutputDuplication);
220 gOutputDuplication = NULL;
221 }
222
223 if (gContext)
224 {
225 gContext->lpVtbl->Release(gContext);
226 gContext = NULL;
227 }
228
229 if (gDevice)
230 {
231 gDevice->lpVtbl->Release(gDevice);
232 gDevice = NULL;
233 }
234
235 return 0;
236}
237
238int wf_dxgi_nextFrame(wfInfo* wfi, UINT timeout)
239{
240 HRESULT status = 0;
241 UINT i = 0;
242 UINT DataBufferSize = 0;
243 BYTE* DataBuffer = NULL;
244 IDXGIResource* DesktopResource = NULL;
245
246 if (wfi->framesWaiting > 0)
247 {
248 wf_dxgi_releasePixelData(wfi);
249 }
250
251 if (gAcquiredDesktopImage)
252 {
253 gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
254 gAcquiredDesktopImage = NULL;
255 }
256
257 status = gOutputDuplication->lpVtbl->AcquireNextFrame(gOutputDuplication, timeout, &FrameInfo,
258 &DesktopResource);
259
260 if (status == DXGI_ERROR_WAIT_TIMEOUT)
261 {
262 return 1;
263 }
264
265 if (FAILED(status))
266 {
267 if (status == DXGI_ERROR_ACCESS_LOST)
268 {
269 WLog_ERR(TAG, "Failed to acquire next frame with status=%ld", status);
270 WLog_ERR(TAG, "Trying to reinitialize due to ACCESS LOST...");
271
272 if (gAcquiredDesktopImage)
273 {
274 gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
275 gAcquiredDesktopImage = NULL;
276 }
277
278 if (gOutputDuplication)
279 {
280 gOutputDuplication->lpVtbl->Release(gOutputDuplication);
281 gOutputDuplication = NULL;
282 }
283
284 wf_dxgi_getDuplication(wfi);
285
286 return 1;
287 }
288 else
289 {
290 WLog_ERR(TAG, "Failed to acquire next frame with status=%ld", status);
291 status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
292
293 if (FAILED(status))
294 {
295 WLog_ERR(TAG, "Failed to release frame with status=%ld", status);
296 }
297
298 return 1;
299 }
300 }
301
302 status = DesktopResource->lpVtbl->QueryInterface(DesktopResource, &IID_ID3D11Texture2D,
303 (void**)&gAcquiredDesktopImage);
304 DesktopResource->lpVtbl->Release(DesktopResource);
305 DesktopResource = NULL;
306
307 if (FAILED(status))
308 {
309 return 1;
310 }
311
312 wfi->framesWaiting = FrameInfo.AccumulatedFrames;
313
314 if (FrameInfo.AccumulatedFrames == 0)
315 {
316 status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
317
318 if (FAILED(status))
319 {
320 WLog_ERR(TAG, "Failed to release frame with status=%ld", status);
321 }
322 }
323
324 return 0;
325}
326
327int wf_dxgi_getPixelData(wfInfo* wfi, BYTE** data, int* pitch, RECT* invalid)
328{
329 HRESULT status;
330 D3D11_BOX Box;
331 DXGI_MAPPED_RECT mappedRect;
332 D3D11_TEXTURE2D_DESC tDesc;
333
334 tDesc.Width = (invalid->right - invalid->left);
335 tDesc.Height = (invalid->bottom - invalid->top);
336 tDesc.MipLevels = 1;
337 tDesc.ArraySize = 1;
338 tDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
339 tDesc.SampleDesc.Count = 1;
340 tDesc.SampleDesc.Quality = 0;
341 tDesc.Usage = D3D11_USAGE_STAGING;
342 tDesc.BindFlags = 0;
343 tDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
344 tDesc.MiscFlags = 0;
345
346 Box.top = invalid->top;
347 Box.left = invalid->left;
348 Box.right = invalid->right;
349 Box.bottom = invalid->bottom;
350 Box.front = 0;
351 Box.back = 1;
352
353 status = gDevice->lpVtbl->CreateTexture2D(gDevice, &tDesc, NULL, &sStage);
354
355 if (FAILED(status))
356 {
357 WLog_ERR(TAG, "Failed to create staging surface");
358 exit(1);
359 return 1;
360 }
361
362 gContext->lpVtbl->CopySubresourceRegion(gContext, (ID3D11Resource*)sStage, 0, 0, 0, 0,
363 (ID3D11Resource*)gAcquiredDesktopImage, 0, &Box);
364
365 status = sStage->lpVtbl->QueryInterface(sStage, &IID_IDXGISurface, (void**)&surf);
366
367 if (FAILED(status))
368 {
369 WLog_ERR(TAG, "Failed to QI staging surface");
370 exit(1);
371 return 1;
372 }
373
374 surf->lpVtbl->Map(surf, &mappedRect, DXGI_MAP_READ);
375
376 if (FAILED(status))
377 {
378 WLog_ERR(TAG, "Failed to map staging surface");
379 exit(1);
380 return 1;
381 }
382
383 *data = mappedRect.pBits;
384 *pitch = mappedRect.Pitch;
385
386 return 0;
387}
388
389int wf_dxgi_releasePixelData(wfInfo* wfi)
390{
391 HRESULT status;
392
393 surf->lpVtbl->Unmap(surf);
394 surf->lpVtbl->Release(surf);
395 surf = NULL;
396 sStage->lpVtbl->Release(sStage);
397 sStage = NULL;
398
399 status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
400
401 if (FAILED(status))
402 {
403 WLog_ERR(TAG, "Failed to release frame");
404 return 1;
405 }
406
407 wfi->framesWaiting = 0;
408
409 return 0;
410}
411
412int wf_dxgi_getInvalidRegion(RECT* invalid)
413{
414 HRESULT status;
415 UINT dirty;
416 UINT BufSize;
417 RECT* pRect;
418 BYTE* DirtyRects;
419 UINT DataBufferSize = 0;
420 BYTE* DataBuffer = NULL;
421
422 if (FrameInfo.AccumulatedFrames == 0)
423 {
424 return 1;
425 }
426
427 if (FrameInfo.TotalMetadataBufferSize)
428 {
429
430 if (FrameInfo.TotalMetadataBufferSize > DataBufferSize)
431 {
432 if (DataBuffer)
433 {
434 free(DataBuffer);
435 DataBuffer = NULL;
436 }
437
438 DataBuffer = (BYTE*)malloc(FrameInfo.TotalMetadataBufferSize);
439
440 if (!DataBuffer)
441 {
442 DataBufferSize = 0;
443 WLog_ERR(TAG, "Failed to allocate memory for metadata");
444 exit(1);
445 }
446
447 DataBufferSize = FrameInfo.TotalMetadataBufferSize;
448 }
449
450 BufSize = FrameInfo.TotalMetadataBufferSize;
451
452 status = gOutputDuplication->lpVtbl->GetFrameMoveRects(
453 gOutputDuplication, BufSize, (DXGI_OUTDUPL_MOVE_RECT*)DataBuffer, &BufSize);
454
455 if (FAILED(status))
456 {
457 WLog_ERR(TAG, "Failed to get frame move rects");
458 return 1;
459 }
460
461 DirtyRects = DataBuffer + BufSize;
462 BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
463
464 status = gOutputDuplication->lpVtbl->GetFrameDirtyRects(gOutputDuplication, BufSize,
465 (RECT*)DirtyRects, &BufSize);
466
467 if (FAILED(status))
468 {
469 WLog_ERR(TAG, "Failed to get frame dirty rects");
470 return 1;
471 }
472 dirty = BufSize / sizeof(RECT);
473
474 pRect = (RECT*)DirtyRects;
475
476 for (UINT i = 0; i < dirty; ++i)
477 {
478 UnionRect(invalid, invalid, pRect);
479 ++pRect;
480 }
481 }
482
483 return 0;
484}
485
486#endif