1 // Copyright (c) 2019 Intel Corporation
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in all
11 // copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 // SOFTWARE.
20 
21 #include "common_directx.h"
22 
23 #include<map>
24 
25 #define D3DFMT_NV12                     (D3DFORMAT)MAKEFOURCC('N','V','1','2')
26 #define D3DFMT_YV12                     (D3DFORMAT)MAKEFOURCC('Y','V','1','2')
27 
28 IDirect3DDeviceManager9*                pDeviceManager9 = NULL;
29 IDirect3DDevice9Ex*                     pD3DD9          = NULL;
30 IDirect3D9Ex*                           pD3D9           = NULL;
31 HANDLE                                  pDeviceHandle   = NULL;
32 IDirectXVideoAccelerationService*       pDXVAServiceDec = NULL;
33 IDirectXVideoAccelerationService*       pDXVAServiceVPP = NULL;
34 
35 bool                                    g_bCreateSharedHandles = false;
36 
37 std::map<mfxMemId*, mfxHDL>             allocResponses;
38 std::map<mfxHDL, mfxFrameAllocResponse> allocDecodeResponses;
39 std::map<mfxHDL, int>                   allocDecodeRefCount;
40 
41 const struct {
42     mfxIMPL impl;       // actual implementation
43     mfxU32  adapterID;  // device adapter number
44 } implTypes[] = {
45     {MFX_IMPL_HARDWARE, 0},
46     {MFX_IMPL_HARDWARE2, 1},
47     {MFX_IMPL_HARDWARE3, 2},
48     {MFX_IMPL_HARDWARE4, 3}
49 };
50 
51 // =================================================================
52 // DirectX functionality required to manage D3D surfaces
53 //
54 
GetIntelDeviceAdapterNum(mfxSession session)55 mfxU32 GetIntelDeviceAdapterNum(mfxSession session)
56 {
57     mfxU32  adapterNum = 0;
58     mfxIMPL impl;
59 
60     MFXQueryIMPL(session, &impl);
61 
62     mfxIMPL baseImpl = MFX_IMPL_BASETYPE(impl); // Extract Media SDK base implementation type
63 
64     // get corresponding adapter number
65     for (mfxU8 i = 0; i < sizeof(implTypes)/sizeof(implTypes[0]); i++) {
66         if (implTypes[i].impl == baseImpl) {
67             adapterNum = implTypes[i].adapterID;
68             break;
69         }
70     }
71 
72     return adapterNum;
73 }
74 
75 // Create HW device context
CreateHWDevice(mfxSession session,mfxHDL * deviceHandle,HWND window,bool bCreateSharedHandles)76 mfxStatus CreateHWDevice(mfxSession session, mfxHDL* deviceHandle, HWND window, bool bCreateSharedHandles)
77 {
78     // If window handle is not supplied, get window handle from coordinate 0,0
79     if (window == NULL) {
80         POINT point = {0, 0};
81         window = WindowFromPoint(point);
82     }
83 
84     g_bCreateSharedHandles = bCreateSharedHandles;
85 
86     HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &pD3D9);
87     if (!pD3D9 || FAILED(hr)) return MFX_ERR_DEVICE_FAILED;
88 
89     RECT rc;
90     GetClientRect(window, &rc);
91 
92     D3DPRESENT_PARAMETERS D3DPP;
93     memset(&D3DPP, 0, sizeof(D3DPP));
94     D3DPP.Windowed                   = true;
95     D3DPP.hDeviceWindow              = window;
96     D3DPP.Flags                      = D3DPRESENTFLAG_VIDEO;
97     D3DPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
98     D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_ONE;
99     D3DPP.BackBufferCount            = 1;
100     D3DPP.BackBufferFormat           = D3DFMT_A8R8G8B8;
101     D3DPP.BackBufferWidth            = rc.right - rc.left;
102     D3DPP.BackBufferHeight           = rc.bottom - rc.top;
103     D3DPP.Flags                     |= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
104     D3DPP.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
105 
106     hr = pD3D9->CreateDeviceEx( GetIntelDeviceAdapterNum(session),
107                                 D3DDEVTYPE_HAL,
108                                 window,
109                                 D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
110                                 &D3DPP,
111                                 NULL,
112                                 &pD3DD9);
113     if (FAILED(hr)) return MFX_ERR_NULL_PTR;
114 
115     hr = pD3DD9->ResetEx(&D3DPP, NULL);
116     if (FAILED(hr)) return MFX_ERR_UNDEFINED_BEHAVIOR;
117 
118     hr = pD3DD9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
119     if (FAILED(hr)) return MFX_ERR_UNDEFINED_BEHAVIOR;
120 
121     UINT resetToken = 0;
122 
123     hr = DXVA2CreateDirect3DDeviceManager9(&resetToken, &pDeviceManager9);
124     if (FAILED(hr)) return MFX_ERR_NULL_PTR;
125 
126     hr = pDeviceManager9->ResetDevice(pD3DD9, resetToken);
127     if (FAILED(hr)) return MFX_ERR_UNDEFINED_BEHAVIOR;
128 
129     *deviceHandle = (mfxHDL)pDeviceManager9;
130 
131     return MFX_ERR_NONE;
132 }
133 
GetDevice()134 IDirect3DDevice9Ex* GetDevice()
135 {
136     return pD3DD9;
137 }
138 
139 
140 // Free HW device context
CleanupHWDevice()141 void CleanupHWDevice()
142 {
143     if (pDeviceManager9)
144         pDeviceManager9->CloseDeviceHandle(pDeviceHandle);
145     MSDK_SAFE_RELEASE(pDXVAServiceDec);
146     MSDK_SAFE_RELEASE(pDXVAServiceVPP);
147     MSDK_SAFE_RELEASE(pDeviceManager9);
148     MSDK_SAFE_RELEASE(pD3DD9);
149     MSDK_SAFE_RELEASE(pD3D9);
150 }
151 
ClearYUVSurfaceD3D(mfxMemId memId)152 void ClearYUVSurfaceD3D(mfxMemId memId)
153 {
154     IDirect3DSurface9* pSurface;
155     D3DSURFACE_DESC desc;
156     D3DLOCKED_RECT locked;
157     pSurface = (IDirect3DSurface9*)memId;
158     pSurface->GetDesc(&desc);
159     pSurface->LockRect(&locked, 0, D3DLOCK_NOSYSLOCK);
160     memset((mfxU8*)locked.pBits, 100, desc.Height*locked.Pitch);   // Y plane
161     memset((mfxU8*)locked.pBits + desc.Height * locked.Pitch, 50, (desc.Height*locked.Pitch)/2);   // UV plane
162     pSurface->UnlockRect();
163 }
164 
ClearRGBSurfaceD3D(mfxMemId memId)165 void ClearRGBSurfaceD3D(mfxMemId memId)
166 {
167     IDirect3DSurface9* pSurface;
168     D3DSURFACE_DESC desc;
169     D3DLOCKED_RECT locked;
170     pSurface = (IDirect3DSurface9*)memId;
171     pSurface->GetDesc(&desc);
172     pSurface->LockRect(&locked, 0, D3DLOCK_NOSYSLOCK);
173     memset((mfxU8*)locked.pBits, 100, desc.Height*locked.Pitch);   // RGBA
174     pSurface->UnlockRect();
175 }
176 
177 //
178 // Media SDK memory allocator entrypoints....
179 //
_simple_alloc(mfxFrameAllocRequest * request,mfxFrameAllocResponse * response)180 mfxStatus _simple_alloc(mfxFrameAllocRequest* request, mfxFrameAllocResponse* response)
181 {
182     DWORD   DxvaType;
183     HRESULT hr = S_OK;
184 
185     // Determine surface format (current simple implementation only supports NV12 and RGB4(32))
186     D3DFORMAT format;
187     if (MFX_FOURCC_NV12 == request->Info.FourCC)
188         format = D3DFMT_NV12;
189     else if (MFX_FOURCC_RGB4 == request->Info.FourCC)
190         format = D3DFMT_A8R8G8B8;
191     else if (MFX_FOURCC_YUY2 == request->Info.FourCC)
192         format = D3DFMT_YUY2;
193     else if (MFX_FOURCC_YV12 == request->Info.FourCC)
194         format = D3DFMT_YV12;
195     else
196         format = (D3DFORMAT)request->Info.FourCC;
197 
198 
199     // Determine render target
200     if (MFX_MEMTYPE_DXVA2_PROCESSOR_TARGET & request->Type)
201         DxvaType = DXVA2_VideoProcessorRenderTarget;
202     else
203         DxvaType = DXVA2_VideoDecoderRenderTarget;
204 
205     // Force use of video processor if color conversion is required (with this simple set of samples we only illustrate RGB4 color converison via VPP)
206     if (D3DFMT_A8R8G8B8 == format) // must use processor service
207         DxvaType = DXVA2_VideoProcessorRenderTarget;
208 
209     mfxMemId* mids = NULL;
210     if (!g_bCreateSharedHandles) {
211         mids = new mfxMemId[request->NumFrameSuggested];
212         if (!mids) return MFX_ERR_MEMORY_ALLOC;
213     } else {
214         mids = new mfxMemId[request->NumFrameSuggested*2];
215         if (!mids) return MFX_ERR_MEMORY_ALLOC;
216 
217         memset(mids, 0, sizeof(mfxMemId)*request->NumFrameSuggested*2);
218     }
219 
220     if (!pDeviceHandle) {
221         hr = pDeviceManager9->OpenDeviceHandle(&pDeviceHandle);
222         if (FAILED(hr)) return MFX_ERR_MEMORY_ALLOC;
223     }
224 
225     IDirectXVideoAccelerationService* pDXVAServiceTmp = NULL;
226 
227     if (DXVA2_VideoDecoderRenderTarget == DxvaType) { // for both decode and encode
228         if (pDXVAServiceDec == NULL)
229             hr = pDeviceManager9->GetVideoService(pDeviceHandle, IID_IDirectXVideoDecoderService, (void**)&pDXVAServiceDec);
230 
231         pDXVAServiceTmp = pDXVAServiceDec;
232     } else { // DXVA2_VideoProcessorRenderTarget ; for VPP
233         if (pDXVAServiceVPP == NULL)
234             hr = pDeviceManager9->GetVideoService(pDeviceHandle, IID_IDirectXVideoProcessorService, (void**)&pDXVAServiceVPP);
235 
236         pDXVAServiceTmp = pDXVAServiceVPP;
237     }
238     if (FAILED(hr)) return MFX_ERR_MEMORY_ALLOC;
239 
240     if (g_bCreateSharedHandles && !(MFX_MEMTYPE_INTERNAL_FRAME & request->Type)) {
241         // Allocate surfaces with shared handles. Commonly used for OpenCL interoperability
242         for (int i=0; i<request->NumFrameSuggested; ++i) {
243             mfxMemId* tmpptr = mids + i + request->NumFrameSuggested;
244 
245             hr = pDXVAServiceTmp->CreateSurface(request->Info.Width,
246                                                 request->Info.Height,
247                                                 0,
248                                                 format,
249                                                 D3DPOOL_DEFAULT,
250                                                 0,
251                                                 DxvaType,
252                                                 (IDirect3DSurface9**)mids+i,
253                                                 (HANDLE*)(tmpptr));
254         }
255     } else {
256         // Allocate surfaces
257         hr = pDXVAServiceTmp->CreateSurface(request->Info.Width,
258                                             request->Info.Height,
259                                             request->NumFrameSuggested - 1,
260                                             format,
261                                             D3DPOOL_DEFAULT,
262                                             0,
263                                             DxvaType,
264                                             (IDirect3DSurface9**)mids,
265                                             NULL);
266     }
267     if (FAILED(hr)) return MFX_ERR_MEMORY_ALLOC;
268 
269     response->mids = mids;
270     response->NumFrameActual = request->NumFrameSuggested;
271 
272     return MFX_ERR_NONE;
273 }
274 
simple_alloc(mfxHDL pthis,mfxFrameAllocRequest * request,mfxFrameAllocResponse * response)275 mfxStatus simple_alloc(mfxHDL pthis, mfxFrameAllocRequest* request, mfxFrameAllocResponse* response)
276 {
277     mfxStatus sts = MFX_ERR_NONE;
278 
279     if (request->Type & MFX_MEMTYPE_SYSTEM_MEMORY)
280         return MFX_ERR_UNSUPPORTED;
281 
282     if (allocDecodeResponses.find(pthis) != allocDecodeResponses.end() &&
283         MFX_MEMTYPE_EXTERNAL_FRAME & request->Type &&
284         MFX_MEMTYPE_FROM_DECODE & request->Type) {
285         // Memory for this request was already allocated during manual allocation stage. Return saved response
286         //   When decode acceleration device (DXVA) is created it requires a list of D3D surfaces to be passed.
287         //   Therefore Media SDK will ask for the surface info/mids again at Init() stage, thus requiring us to return the saved response
288         //   (No such restriction applies to Encode or VPP)
289         *response = allocDecodeResponses[pthis];
290         allocDecodeRefCount[pthis]++;
291     } else {
292         sts = _simple_alloc(request, response);
293 
294         if (MFX_ERR_NONE == sts) {
295             if ( MFX_MEMTYPE_EXTERNAL_FRAME & request->Type &&
296                  MFX_MEMTYPE_FROM_DECODE & request->Type) {
297                 // Decode alloc response handling
298                 allocDecodeResponses[pthis] = *response;
299                 allocDecodeRefCount[pthis]++;
300             } else {
301                 // Encode and VPP alloc response handling
302                 allocResponses[response->mids] = pthis;
303             }
304         }
305     }
306 
307     return sts;
308 }
309 
simple_lock(mfxHDL pthis,mfxMemId mid,mfxFrameData * ptr)310 mfxStatus simple_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData* ptr)
311 {
312     pthis; // To suppress warning for this unused parameter
313 
314     IDirect3DSurface9* pSurface = (IDirect3DSurface9*)mid;
315 
316     if (pSurface == 0) return MFX_ERR_INVALID_HANDLE;
317     if (ptr == 0) return MFX_ERR_LOCK_MEMORY;
318 
319     D3DSURFACE_DESC desc;
320     HRESULT hr = pSurface->GetDesc(&desc);
321     if (FAILED(hr)) return MFX_ERR_LOCK_MEMORY;
322 
323     D3DLOCKED_RECT locked;
324     hr = pSurface->LockRect(&locked, 0, D3DLOCK_NOSYSLOCK);
325     if (FAILED(hr)) return MFX_ERR_LOCK_MEMORY;
326 
327     // In these simple set of samples we only illustrate usage of NV12 and RGB4(32)
328     if (D3DFMT_NV12 == desc.Format) {
329         ptr->Pitch  = (mfxU16)locked.Pitch;
330         ptr->Y      = (mfxU8*)locked.pBits;
331         ptr->U      = (mfxU8*)locked.pBits + desc.Height * locked.Pitch;
332         ptr->V      = ptr->U + 1;
333     } else if (D3DFMT_A8R8G8B8 == desc.Format) {
334         ptr->Pitch = (mfxU16)locked.Pitch;
335         ptr->B = (mfxU8*)locked.pBits;
336         ptr->G = ptr->B + 1;
337         ptr->R = ptr->B + 2;
338         ptr->A = ptr->B + 3;
339     } else if (D3DFMT_YUY2 == desc.Format) {
340         ptr->Pitch = (mfxU16)locked.Pitch;
341         ptr->Y = (mfxU8*)locked.pBits;
342         ptr->U = ptr->Y + 1;
343         ptr->V = ptr->Y + 3;
344     } else if (D3DFMT_YV12 == desc.Format) {
345         ptr->Pitch = (mfxU16)locked.Pitch;
346         ptr->Y = (mfxU8*)locked.pBits;
347         ptr->V = ptr->Y + desc.Height * locked.Pitch;
348         ptr->U = ptr->V + (desc.Height * locked.Pitch) / 4;
349     } else if (D3DFMT_P8 == desc.Format) {
350         ptr->Pitch = (mfxU16)locked.Pitch;
351         ptr->Y = (mfxU8*)locked.pBits;
352         ptr->U = 0;
353         ptr->V = 0;
354     }
355 
356     return MFX_ERR_NONE;
357 }
358 
simple_unlock(mfxHDL pthis,mfxMemId mid,mfxFrameData * ptr)359 mfxStatus simple_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData* ptr)
360 {
361     pthis; // To suppress warning for this unused parameter
362 
363     IDirect3DSurface9* pSurface = (IDirect3DSurface9*)mid;
364 
365     if (pSurface == 0) return MFX_ERR_INVALID_HANDLE;
366 
367     pSurface->UnlockRect();
368 
369     if (NULL != ptr) {
370         ptr->Pitch = 0;
371         ptr->R     = 0;
372         ptr->G     = 0;
373         ptr->B     = 0;
374         ptr->A     = 0;
375     }
376 
377     return MFX_ERR_NONE;
378 }
379 
simple_gethdl(mfxHDL pthis,mfxMemId mid,mfxHDL * handle)380 mfxStatus simple_gethdl(mfxHDL pthis, mfxMemId mid, mfxHDL* handle)
381 {
382     pthis; // To suppress warning for this unused parameter
383 
384     if (handle == 0) return MFX_ERR_INVALID_HANDLE;
385 
386     *handle = mid;
387     return MFX_ERR_NONE;
388 }
389 
390 
_simple_free(mfxFrameAllocResponse * response)391 mfxStatus _simple_free(mfxFrameAllocResponse* response)
392 {
393     if (response->mids) {
394         for (mfxU32 i = 0; i < response->NumFrameActual; i++) {
395             if (response->mids[i]) {
396                 IDirect3DSurface9* handle = (IDirect3DSurface9*)response->mids[i];
397                 handle->Release();
398             }
399         }
400     }
401 
402     delete [] response->mids;
403     response->mids = 0;
404 
405     return MFX_ERR_NONE;
406 }
407 
simple_free(mfxHDL pthis,mfxFrameAllocResponse * response)408 mfxStatus simple_free(mfxHDL pthis, mfxFrameAllocResponse* response)
409 {
410     if (!response) return MFX_ERR_NULL_PTR;
411 
412     if (allocResponses.find(response->mids) == allocResponses.end()) {
413         // Decode free response handling
414         if (--allocDecodeRefCount[pthis] == 0) {
415             _simple_free(response);
416             allocDecodeResponses.erase(pthis);
417             allocDecodeRefCount.erase(pthis);
418         }
419     } else {
420         // Encode and VPP free response handling
421         allocResponses.erase(response->mids);
422         _simple_free(response);
423     }
424 
425     return MFX_ERR_NONE;
426 }
427