FreeRDP
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 */
38 D3D_DRIVER_TYPE DriverTypes[] = {
39  D3D_DRIVER_TYPE_HARDWARE,
40  D3D_DRIVER_TYPE_WARP,
41  D3D_DRIVER_TYPE_REFERENCE,
42 };
43 UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
44 
45 /* Feature levels supported */
46 D3D_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 
49 UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
50 
51 D3D_FEATURE_LEVEL FeatureLevel;
52 
53 ID3D11Device* gDevice = NULL;
54 ID3D11DeviceContext* gContext = NULL;
55 IDXGIOutputDuplication* gOutputDuplication = NULL;
56 ID3D11Texture2D* gAcquiredDesktopImage = NULL;
57 
58 IDXGISurface* surf;
59 ID3D11Texture2D* sStage;
60 
61 DXGI_OUTDUPL_FRAME_INFO FrameInfo;
62 
63 int 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 
80 int 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 
106 int 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 
204 int 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 
238 int 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 
327 int 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 
389 int 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 
412 int 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