wfreerdp-server: Less hackish win8 support
[platform/upstream/freerdp.git] / server / Windows / wf_dxgi.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * FreeRDP Windows Server
4  *
5  * Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #define CINTERFACE
21
22 #include <D3D11.h>
23 #include <dxgi1_2.h>
24
25 #include <tchar.h>
26 #include "wf_dxgi.h"
27
28 // Driver types supported
29 D3D_DRIVER_TYPE DriverTypes[] =
30 {
31         D3D_DRIVER_TYPE_HARDWARE,
32         D3D_DRIVER_TYPE_WARP,
33         D3D_DRIVER_TYPE_REFERENCE,
34 };
35 UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
36
37 // Feature levels supported
38 D3D_FEATURE_LEVEL FeatureLevels[] =
39 {
40         D3D_FEATURE_LEVEL_11_0,
41         D3D_FEATURE_LEVEL_10_1,
42         D3D_FEATURE_LEVEL_10_0,
43         D3D_FEATURE_LEVEL_9_1
44 };
45 UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
46
47 D3D_FEATURE_LEVEL FeatureLevel;
48
49 ID3D11Device* MeinDevice = NULL;
50 ID3D11DeviceContext* MeinContext = NULL;
51 IDXGIOutputDuplication* MeinDeskDupl = NULL;
52 ID3D11Texture2D* MeinAcquiredDesktopImage = NULL;
53
54 IDXGISurface* surf;
55 ID3D11Texture2D * sStage;
56
57 DXGI_OUTDUPL_FRAME_INFO FrameInfo;
58
59 int wf_dxgi_init(wfInfo* context)
60 {
61         HRESULT hr;
62
63         UINT DriverTypeIndex;
64         IDXGIDevice* DxgiDevice = NULL;
65         IDXGIAdapter* DxgiAdapter = NULL;
66         DXGI_OUTPUT_DESC desc;
67         UINT dTop, i = 0;
68         IDXGIOutput * pOutput;
69
70         IDXGIOutput* DxgiOutput = NULL;
71
72         IDXGIOutput1* DxgiOutput1 = NULL;
73
74
75         /////////////////////////////////////////////////////////
76
77         //guessing this must be null
78         MeinAcquiredDesktopImage = NULL;
79
80
81         _tprintf(_T("Hallo, welt!\n"));
82
83         _tprintf(_T("Trying to create a DX11 Device...\n"));
84         // Create device
85         for (DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
86         {
87                 hr = D3D11CreateDevice(NULL, DriverTypes[DriverTypeIndex], NULL, D3D11_CREATE_DEVICE_DEBUG, FeatureLevels, NumFeatureLevels,
88                                                                 D3D11_SDK_VERSION, &MeinDevice, &FeatureLevel, &MeinContext);
89                 if (SUCCEEDED(hr))
90                 {
91                         // Device creation success, no need to loop anymore
92                         break;
93                 }
94         }
95         if (FAILED(hr))
96         {
97                 _tprintf(_T("Failed to create device in InitializeDx\n"));
98                 return 1;
99         }
100         _tprintf(_T("Gut!\n"));
101
102         ///////////////////////////////////////////////////////
103
104         
105         _tprintf(_T("Trying to get QI for DXGI Device...\n"));
106         
107         hr = MeinDevice->lpVtbl->QueryInterface(MeinDevice, &IID_IDXGIDevice, (void**) &DxgiDevice);
108         if (FAILED(hr))
109     {
110                 _tprintf(_T("Failed to get QI for DXGI Device\n"));
111         return 1;
112     }
113         _tprintf(_T("Gut!\n"));
114
115         //////////////////////////////////////////////////////////
116
117         _tprintf(_T("Trying to get adapter for DXGI Device...\n"));
118         
119         hr = DxgiDevice->lpVtbl->GetParent(DxgiDevice, &IID_IDXGIAdapter, (void**) &DxgiAdapter);
120         DxgiDevice->lpVtbl->Release(DxgiDevice);
121     DxgiDevice = NULL;
122     if (FAILED(hr))
123     {
124         _tprintf(_T("Failed to get parent DXGI Adapter\n"));
125                 return 1;
126     }
127         _tprintf(_T("Gut!\n"));
128         
129         ////////////////////////////////////////////////////////////
130
131         
132         memset(&desc, 0, sizeof(desc));
133         pOutput = NULL;
134         _tprintf(_T("\nLooping through ouputs on DXGI adapter...\n"));
135         while(DxgiAdapter->lpVtbl->EnumOutputs(DxgiAdapter, i, &pOutput) != DXGI_ERROR_NOT_FOUND)
136         {
137                 DXGI_OUTPUT_DESC* pDesc = &desc;
138
139                 hr = pOutput->lpVtbl->GetDesc(pOutput, pDesc);
140                 if (FAILED(hr))
141                 {
142                         _tprintf(_T("Failed to get description\n"));
143                         return 1;
144                 }
145
146                 _tprintf(_T("Output %d: [%s] [%d]\n"), i, pDesc->DeviceName, pDesc->AttachedToDesktop);
147
148                 if(pDesc->AttachedToDesktop)
149                         dTop = i;
150
151                 pOutput->lpVtbl->Release(pOutput);
152                 ++i;
153         }
154
155         //for now stick to the first one
156         dTop = 0;
157
158
159         _tprintf(_T("\nTrying to get output...\n"));
160     hr = DxgiAdapter->lpVtbl->EnumOutputs(DxgiAdapter, dTop, &DxgiOutput);
161     DxgiAdapter->lpVtbl->Release(DxgiAdapter);
162     DxgiAdapter = NULL;
163     if (FAILED(hr))
164     {
165         _tprintf(_T("Failed to get output\n"));
166                 return 1;
167         }
168         _tprintf(_T("Gut!\n"));
169         
170         //////////////////////////////////////////////
171
172         _tprintf(_T("Trying to get IDXGIOutput1...\n"));
173         hr = DxgiOutput->lpVtbl->QueryInterface(DxgiOutput, &IID_IDXGIOutput1, (void**) &DxgiOutput1);
174         DxgiOutput->lpVtbl->Release(DxgiOutput);
175     DxgiOutput = NULL;
176     if (FAILED(hr))
177     {
178          _tprintf(_T("Failed to get IDXGIOutput1\n"));
179                 return 1;
180     }
181         _tprintf(_T("Gut!\n"));
182         
183         //////////////////////////////////////////////
184
185
186         _tprintf(_T("Trying to duplicate the output...\n"));
187         hr = DxgiOutput1->lpVtbl->DuplicateOutput(DxgiOutput1, (IUnknown*)MeinDevice, &MeinDeskDupl);
188         DxgiOutput1->lpVtbl->Release(DxgiOutput1);
189     DxgiOutput1 = NULL;
190     if (FAILED(hr))
191     {
192         if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
193         {
194              _tprintf(_T("There is already the maximum number of applications using the Desktop Duplication API running, please close one of those applications and then try again.\n"));
195                         return 1;
196         }
197                 _tprintf(_T("Failed to get duplicate output\n"));
198                 return 1;
199     }
200         _tprintf(_T("Gut! Init Complete!\n"));
201 }
202
203 int wf_dxgi_cleanup(wfInfo* context)
204 {
205         if(MeinAcquiredDesktopImage)
206         {
207                 MeinAcquiredDesktopImage->lpVtbl->Release(MeinAcquiredDesktopImage);
208                 MeinAcquiredDesktopImage = NULL;
209         }
210
211         if(MeinDeskDupl)
212         {
213                 MeinDeskDupl->lpVtbl->Release(MeinDeskDupl);
214                 MeinDeskDupl = NULL;
215         }
216
217         if(MeinContext)
218         {
219                 MeinContext->lpVtbl->Release(MeinContext);
220                 MeinContext = NULL;
221         }
222
223         if(MeinDevice)
224         {
225                 MeinDevice->lpVtbl->Release(MeinDevice);
226                 MeinDevice = NULL;
227         }
228
229         return 0;
230 }
231
232 int wf_dxgi_nextFrame(wfInfo* context, UINT timeout)
233 {
234         HRESULT hr;
235         IDXGIResource* DesktopResource = NULL;
236         BYTE* DirtyRects;
237         UINT dirty;
238         RECT* pRect;
239         BYTE* MeinMetaDataBuffer = NULL;
240         UINT MeinMetaDataSize = 0;
241         UINT i = 0, BufSize;
242
243         if(MeinAcquiredDesktopImage)
244         {
245                 MeinAcquiredDesktopImage->lpVtbl->Release(MeinAcquiredDesktopImage);
246                 MeinAcquiredDesktopImage = NULL;
247         }
248
249         _tprintf(_T("\nTrying to acquire a frame...\n"));
250         hr = MeinDeskDupl->lpVtbl->AcquireNextFrame(MeinDeskDupl, timeout, &FrameInfo, &DesktopResource);
251         _tprintf(_T("hr = %#0X\n"), hr);
252         if (hr == DXGI_ERROR_WAIT_TIMEOUT)
253         {
254                 _tprintf(_T("Timeout\n"));
255                 return 1;
256         }
257         if (FAILED(hr))
258         {
259                 _tprintf(_T("Failed to acquire next frame\n"));
260
261                 hr = MeinDeskDupl->lpVtbl->ReleaseFrame(MeinDeskDupl);
262                 if (FAILED(hr))
263                 {
264                         _tprintf(_T("Failed to release frame\n"));
265                 }
266                 return 1;
267         }
268         _tprintf(_T("Gut!\n"));
269
270         ///////////////////////////////////////////////
271
272                 
273         _tprintf(_T("Trying to QI for ID3D11Texture2D...\n"));
274         hr = DesktopResource->lpVtbl->QueryInterface(DesktopResource, &IID_ID3D11Texture2D, (void**) &MeinAcquiredDesktopImage);
275         DesktopResource->lpVtbl->Release(DesktopResource);
276         DesktopResource = NULL;
277         if (FAILED(hr))
278         {
279                 _tprintf(_T("Failed to QI for ID3D11Texture2D from acquired IDXGIResource\n"));
280                         return 1;
281         }
282         _tprintf(_T("Gut!\n"));
283
284         if(FrameInfo.AccumulatedFrames == 0)
285         {
286                 //we dont care
287                 return 1;
288         }
289
290         _tprintf(_T("FrameInfo\n"));
291         _tprintf(_T("\tAccumulated Frames: %d\n"), FrameInfo.AccumulatedFrames);
292         _tprintf(_T("\tCoalesced Rectangles: %d\n"), FrameInfo.RectsCoalesced);
293         _tprintf(_T("\tMetadata buffer size: %d\n"), FrameInfo.TotalMetadataBufferSize);
294
295
296         if(FrameInfo.TotalMetadataBufferSize)
297         {
298
299                 if (FrameInfo.TotalMetadataBufferSize > MeinMetaDataSize)
300                 {
301                         if (MeinMetaDataBuffer)
302                         {
303                                 free(MeinMetaDataBuffer);
304                                 MeinMetaDataBuffer = NULL;
305                         }
306                         MeinMetaDataBuffer = (BYTE*) malloc(FrameInfo.TotalMetadataBufferSize);
307                         if (!MeinMetaDataBuffer)
308                         {
309                                 MeinMetaDataSize = 0;
310                                 _tprintf(_T("Failed to allocate memory for metadata\n"));
311                                 return 1;
312                         }
313                         MeinMetaDataSize = FrameInfo.TotalMetadataBufferSize;
314                 }
315
316                 BufSize = FrameInfo.TotalMetadataBufferSize;
317
318                 // Get move rectangles
319                 hr = MeinDeskDupl->lpVtbl->GetFrameMoveRects(MeinDeskDupl, BufSize, (DXGI_OUTDUPL_MOVE_RECT*) MeinMetaDataBuffer, &BufSize);
320                 if (FAILED(hr))
321                 {
322                         _tprintf(_T("Failed to get frame move rects\n"));
323                         return 1;
324                 }
325                 _tprintf(_T("Move rects: %d\n"), BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT));
326
327                 DirtyRects = MeinMetaDataBuffer + BufSize;
328                 BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
329
330                         // Get dirty rectangles
331                 hr = MeinDeskDupl->lpVtbl->GetFrameDirtyRects(MeinDeskDupl, BufSize, (RECT*) DirtyRects, &BufSize);
332                 if (FAILED(hr))
333                 {
334                         _tprintf(_T("Failed to get frame dirty rects\n"));
335                         return 1;
336                 }
337                 dirty = BufSize / sizeof(RECT);
338                 _tprintf(_T("Dirty rects: %d\n"), dirty);
339
340                 pRect = (RECT*) DirtyRects;
341                 for(i = 0; i<dirty; ++i)
342                 {
343                         _tprintf(_T("\tRect: (%d, %d), (%d, %d)\n"),
344                                 pRect->left,
345                                 pRect->top,
346                                 pRect->right,
347                                 pRect->bottom);
348
349                         ++pRect;
350                 }
351                         
352
353         }
354
355         return 0;
356
357 }
358
359 int wf_dxgi_getPixelData(wfInfo* context, BYTE** data, int* pitch, RECT* invalid)
360 {
361         HRESULT hr;
362         DXGI_MAPPED_RECT MeinData;
363         D3D11_TEXTURE2D_DESC tDesc;
364         D3D11_BOX Box;
365
366         if(wf_dxgi_getInvalidRegion(invalid))
367         {
368                 _tprintf(_T("dxgi_getInvalidRegion failed\n"));
369                 exit(1);
370         }
371
372         tDesc.Width = (invalid->right - invalid->left) + 1;
373         tDesc.Height = (invalid->bottom - invalid->top) + 1;
374         tDesc.MipLevels = 1;
375         tDesc.ArraySize = 1;
376         tDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
377         tDesc.SampleDesc.Count = 1;
378         tDesc.SampleDesc.Quality = 0;
379         tDesc.Usage = D3D11_USAGE_STAGING;
380         tDesc.BindFlags = 0;
381         tDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;// | D3D11_CPU_ACCESS_WRITE;
382         tDesc.MiscFlags = 0;
383
384         Box.top = invalid->top;
385         Box.left = invalid->left;
386         Box.right = invalid->right;
387         Box.bottom = invalid->bottom;
388         Box.front = 0;
389         Box.back = 1;
390
391         _tprintf(_T("Trying to create staging surface\n"));
392         hr = MeinDevice->lpVtbl->CreateTexture2D(MeinDevice, &tDesc, NULL, &sStage);
393         if (FAILED(hr))
394         {
395                 _tprintf(_T("Failed to create staging surface\n"));
396                 exit(1);
397         }
398         _tprintf(_T("Gut!\n"));
399
400         MeinContext->lpVtbl->CopySubresourceRegion(MeinContext, (ID3D11Resource*)sStage, 0,0,0,0, (ID3D11Resource*)MeinAcquiredDesktopImage, 0, &Box);   
401                 
402         _tprintf(_T("Trying to QI staging surface\n"));
403         hr = sStage->lpVtbl->QueryInterface(sStage, &IID_IDXGISurface, (void**)&surf);
404         if (FAILED(hr))
405         {
406                 _tprintf(_T("Failed to QI staging surface\n"));
407                 exit(1);
408         }
409         _tprintf(_T("Gut!\n"));
410
411         _tprintf(_T("Trying to map staging surface\n"));
412         surf->lpVtbl->Map(surf, &MeinData, DXGI_MAP_READ);
413         if (FAILED(hr))
414         {
415                 _tprintf(_T("Failed to map staging surface\n"));
416                 exit(1);
417         }
418         _tprintf(_T("Gut!\n"));
419                 
420         //access pixel data 
421
422         *data = MeinData.pBits;
423         *pitch = MeinData.Pitch;
424
425         
426 }
427
428 int wf_dxgi_releasePixelData(wfInfo* context)
429 {
430         HRESULT hr;
431
432         surf->lpVtbl->Unmap(surf);
433         surf->lpVtbl->Release(surf);
434         surf = NULL;
435         sStage->lpVtbl->Release(sStage);
436         sStage = NULL;
437
438         hr = MeinDeskDupl->lpVtbl->ReleaseFrame(MeinDeskDupl);
439         if (FAILED(hr))
440         {
441                 _tprintf(_T("Failed to release frame\n"));
442                 return 1;
443         }
444 }
445
446 int wf_dxgi_getInvalidRegion(RECT* invalid)
447 {
448         HRESULT hr;
449         UINT MeinMetaDataSize = 0;
450         UINT BufSize;
451         UINT i;
452         BYTE* DirtyRects;
453         UINT dirty;
454         RECT* pRect;
455
456         //optimization note: make this buffer global and allocate only once (or grow only when needed)
457         BYTE* MeinMetaDataBuffer = NULL;
458
459
460
461         _tprintf(_T("FrameInfo\n"));
462         _tprintf(_T("\tAccumulated Frames: %d\n"), FrameInfo.AccumulatedFrames);
463         _tprintf(_T("\tCoalesced Rectangles: %d\n"), FrameInfo.RectsCoalesced);
464         _tprintf(_T("\tMetadata buffer size: %d\n"), FrameInfo.TotalMetadataBufferSize);
465
466
467         if(FrameInfo.TotalMetadataBufferSize)
468         {
469
470                 if (FrameInfo.TotalMetadataBufferSize > MeinMetaDataSize)
471                 {
472                         if (MeinMetaDataBuffer)
473                         {
474                                 free(MeinMetaDataBuffer);
475                                 MeinMetaDataBuffer = NULL;
476                         }
477                         MeinMetaDataBuffer = (BYTE*) malloc(FrameInfo.TotalMetadataBufferSize);
478                         if (!MeinMetaDataBuffer)
479                         {
480                                 MeinMetaDataSize = 0;
481                                 _tprintf(_T("Failed to allocate memory for metadata\n"));
482                                 exit(1);
483                         }
484                         MeinMetaDataSize = FrameInfo.TotalMetadataBufferSize;
485                 }
486
487                 BufSize = FrameInfo.TotalMetadataBufferSize;
488
489                 // Get move rectangles
490                 hr = MeinDeskDupl->lpVtbl->GetFrameMoveRects(MeinDeskDupl, BufSize, (DXGI_OUTDUPL_MOVE_RECT*) MeinMetaDataBuffer, &BufSize);
491                 if (FAILED(hr))
492                 {
493                         _tprintf(_T("Failed to get frame move rects\n"));
494                         return 1;
495                 }
496                 _tprintf(_T("Move rects: %d\n"), BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT));
497
498                 DirtyRects = MeinMetaDataBuffer + BufSize;
499                 BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
500
501                         // Get dirty rectangles
502                 hr = MeinDeskDupl->lpVtbl->GetFrameDirtyRects(MeinDeskDupl, BufSize, (RECT*) DirtyRects, &BufSize);
503                 if (FAILED(hr))
504                 {
505                         _tprintf(_T("Failed to get frame dirty rects\n"));
506                         return 1;
507                 }
508                 dirty = BufSize / sizeof(RECT);
509                 _tprintf(_T("Dirty rects: %d\n"), dirty);
510
511                 pRect = (RECT*) DirtyRects;
512                 for(i = 0; i<dirty; ++i)
513                 {
514                         _tprintf(_T("\tRect: (%d, %d), (%d, %d)\n"),
515                                 pRect->left,
516                                 pRect->top,
517                                 pRect->right,
518                                 pRect->bottom);
519
520                         UnionRect(invalid, invalid, pRect);
521
522                         ++pRect;
523                 }
524         }
525
526         return 0;
527 }