1 /*****************************************************************************
2 * d3d11_fmt.c : D3D11 helper calls
3 *****************************************************************************
4 * Copyright © 2017 VLC authors, VideoLAN and VideoLabs
6 * Authors: Steve Lhomme <robux4@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
27 #include <vlc_common.h>
28 #include <vlc_picture.h>
33 #if !defined(NDEBUG) && defined(HAVE_DXGIDEBUG_H)
34 # include <initguid.h>
35 # include <dxgidebug.h>
38 #include "d3d11_fmt.h"
40 #include "../codec/avcodec/va_surface.h"
42 picture_sys_t
*ActivePictureSys(picture_t
*p_pic
)
44 struct va_pic_context
*pic_ctx
= (struct va_pic_context
*)p_pic
->context
;
45 return pic_ctx
? &pic_ctx
->picsys
: p_pic
->p_sys
;
48 void AcquirePictureSys(picture_sys_t
*p_sys
)
50 for (int i
=0; i
<D3D11_MAX_SHADER_VIEW
; i
++) {
51 if (p_sys
->resourceView
[i
])
52 ID3D11ShaderResourceView_AddRef(p_sys
->resourceView
[i
]);
53 if (p_sys
->texture
[i
])
54 ID3D11Texture2D_AddRef(p_sys
->texture
[i
]);
57 ID3D11DeviceContext_AddRef(p_sys
->context
);
59 ID3D11VideoDecoderOutputView_AddRef(p_sys
->decoder
);
60 if (p_sys
->processorInput
)
61 ID3D11VideoProcessorInputView_AddRef(p_sys
->processorInput
);
62 if (p_sys
->processorOutput
)
63 ID3D11VideoProcessorOutputView_AddRef(p_sys
->processorOutput
);
66 void ReleasePictureSys(picture_sys_t
*p_sys
)
68 for (int i
=0; i
<D3D11_MAX_SHADER_VIEW
; i
++) {
69 if (p_sys
->resourceView
[i
])
70 ID3D11ShaderResourceView_Release(p_sys
->resourceView
[i
]);
71 if (p_sys
->texture
[i
])
72 ID3D11Texture2D_Release(p_sys
->texture
[i
]);
75 ID3D11DeviceContext_Release(p_sys
->context
);
77 ID3D11VideoDecoderOutputView_Release(p_sys
->decoder
);
78 if (p_sys
->processorInput
)
79 ID3D11VideoProcessorInputView_Release(p_sys
->processorInput
);
80 if (p_sys
->processorOutput
)
81 ID3D11VideoProcessorOutputView_Release(p_sys
->processorOutput
);
84 /* map texture planes to resource views */
85 int AllocateShaderView(vlc_object_t
*obj
, ID3D11Device
*d3ddevice
,
86 const d3d_format_t
*format
,
87 ID3D11Texture2D
*p_texture
[D3D11_MAX_SHADER_VIEW
], UINT slice_index
,
88 ID3D11ShaderResourceView
*resourceView
[D3D11_MAX_SHADER_VIEW
])
92 D3D11_SHADER_RESOURCE_VIEW_DESC resviewDesc
= { 0 };
93 D3D11_TEXTURE2D_DESC texDesc
;
94 ID3D11Texture2D_GetDesc(p_texture
[0], &texDesc
);
95 assert(texDesc
.BindFlags
& D3D11_BIND_SHADER_RESOURCE
);
97 if (texDesc
.ArraySize
== 1)
99 resviewDesc
.ViewDimension
= D3D11_SRV_DIMENSION_TEXTURE2D
;
100 resviewDesc
.Texture2D
.MipLevels
= 1;
104 resviewDesc
.ViewDimension
= D3D11_SRV_DIMENSION_TEXTURE2DARRAY
;
105 resviewDesc
.Texture2DArray
.MipLevels
= -1;
106 resviewDesc
.Texture2DArray
.ArraySize
= 1;
107 resviewDesc
.Texture2DArray
.FirstArraySlice
= slice_index
;
109 for (i
=0; i
<D3D11_MAX_SHADER_VIEW
; i
++)
111 resviewDesc
.Format
= format
->resourceFormat
[i
];
112 if (resviewDesc
.Format
== DXGI_FORMAT_UNKNOWN
)
113 resourceView
[i
] = NULL
;
116 hr
= ID3D11Device_CreateShaderResourceView(d3ddevice
, (ID3D11Resource
*)p_texture
[i
], &resviewDesc
, &resourceView
[i
]);
118 msg_Err(obj
, "Could not Create the Texture ResourceView %d slice %d. (hr=0x%lX)", i
, slice_index
, hr
);
124 if (i
!= D3D11_MAX_SHADER_VIEW
)
128 ID3D11ShaderResourceView_Release(resourceView
[i
]);
129 resourceView
[i
] = NULL
;
137 void D3D11_ReleaseDevice(d3d11_device_t
*d3d_dev
)
139 if (d3d_dev
->d3dcontext
)
141 ID3D11DeviceContext_Flush(d3d_dev
->d3dcontext
);
142 ID3D11DeviceContext_Release(d3d_dev
->d3dcontext
);
143 d3d_dev
->d3dcontext
= NULL
;
145 if (d3d_dev
->d3ddevice
)
147 ID3D11Device_Release(d3d_dev
->d3ddevice
);
148 d3d_dev
->d3ddevice
= NULL
;
152 #undef D3D11_CreateDevice
153 HRESULT
D3D11_CreateDevice(vlc_object_t
*obj
, d3d11_handle_t
*hd3d
,
154 bool hw_decoding
, d3d11_device_t
*out
)
156 #if !VLC_WINSTORE_APP
157 # define D3D11CreateDevice(args...) pf_CreateDevice(args)
159 PFN_D3D11_CREATE_DEVICE pf_CreateDevice
;
160 pf_CreateDevice
= (void *)GetProcAddress(hd3d
->hdll
, "D3D11CreateDevice");
161 if (!pf_CreateDevice
) {
162 msg_Err(obj
, "Cannot locate reference to D3D11CreateDevice ABI in DLL");
163 return E_NOINTERFACE
;
167 HRESULT hr
= E_NOTIMPL
;
168 UINT creationFlags
= 0;
170 if (hw_decoding
|| !obj
->obj
.force
)
171 creationFlags
|= D3D11_CREATE_DEVICE_VIDEO_SUPPORT
;
174 # if !VLC_WINSTORE_APP
175 if (IsDebuggerPresent())
178 HINSTANCE sdklayer_dll
= LoadLibrary(TEXT("d3d11_1sdklayers.dll"));
180 creationFlags
|= D3D11_CREATE_DEVICE_DEBUG
;
181 FreeLibrary(sdklayer_dll
);
186 static const D3D_DRIVER_TYPE driverAttempts
[] = {
187 D3D_DRIVER_TYPE_HARDWARE
,
188 #if 0 /* ifndef NDEBUG */
189 D3D_DRIVER_TYPE_REFERENCE
,
193 static D3D_FEATURE_LEVEL D3D11_features
[] = {
194 D3D_FEATURE_LEVEL_11_1
, D3D_FEATURE_LEVEL_11_0
,
195 D3D_FEATURE_LEVEL_10_1
, D3D_FEATURE_LEVEL_10_0
,
196 D3D_FEATURE_LEVEL_9_3
, D3D_FEATURE_LEVEL_9_2
, D3D_FEATURE_LEVEL_9_1
199 for (UINT driver
= 0; driver
< ARRAY_SIZE(driverAttempts
); driver
++) {
200 D3D_FEATURE_LEVEL i_feature_level
;
201 hr
= D3D11CreateDevice(NULL
, driverAttempts
[driver
], NULL
, creationFlags
,
202 D3D11_features
, ARRAY_SIZE(D3D11_features
), D3D11_SDK_VERSION
,
203 &out
->d3ddevice
, &i_feature_level
, &out
->d3dcontext
);
206 msg_Dbg(obj
, "Created the D3D11 device 0x%p ctx 0x%p type %d level %x.",
207 (void *)out
->d3ddevice
, (void *)out
->d3dcontext
,
208 driverAttempts
[driver
], i_feature_level
);
210 /* we can work with legacy levels but only if forced */
211 if ( obj
->obj
.force
|| i_feature_level
>= D3D_FEATURE_LEVEL_11_0
)
213 msg_Dbg(obj
, "Incompatible feature level %x", i_feature_level
);
214 ID3D11DeviceContext_Release(out
->d3dcontext
);
215 ID3D11Device_Release(out
->d3ddevice
);
216 out
->d3dcontext
= NULL
;
217 out
->d3ddevice
= NULL
;
230 IDXGIAdapter
*D3D11DeviceAdapter(ID3D11Device
*d3ddev
)
232 IDXGIDevice
*pDXGIDevice
= NULL
;
233 HRESULT hr
= ID3D11Device_QueryInterface(d3ddev
, &IID_IDXGIDevice
, (void **)&pDXGIDevice
);
238 IDXGIAdapter
*p_adapter
;
239 hr
= IDXGIDevice_GetAdapter(pDXGIDevice
, &p_adapter
);
240 IDXGIDevice_Release(pDXGIDevice
);
247 bool isXboxHardware(ID3D11Device
*d3ddev
)
249 IDXGIAdapter
*p_adapter
= D3D11DeviceAdapter(d3ddev
);
254 DXGI_ADAPTER_DESC adapterDesc
;
255 if (SUCCEEDED(IDXGIAdapter_GetDesc(p_adapter
, &adapterDesc
))) {
256 if (adapterDesc
.VendorId
== 0 &&
257 adapterDesc
.DeviceId
== 0 &&
258 !wcscmp(L
"ROOT\\SraKmd\\0000", adapterDesc
.Description
))
262 IDXGIAdapter_Release(p_adapter
);
266 bool isNvidiaHardware(ID3D11Device
*d3ddev
)
268 IDXGIAdapter
*p_adapter
= D3D11DeviceAdapter(d3ddev
);
273 DXGI_ADAPTER_DESC adapterDesc
;
274 if (SUCCEEDED(IDXGIAdapter_GetDesc(p_adapter
, &adapterDesc
)))
275 result
= adapterDesc
.VendorId
== GPU_MANUFACTURER_NVIDIA
;
277 IDXGIAdapter_Release(p_adapter
);
281 #if !VLC_WINSTORE_APP
282 static HKEY
GetAdapterRegistry(DXGI_ADAPTER_DESC
*adapterDesc
)
286 TCHAR szData
[256], lookup
[256];
289 _sntprintf(lookup
, 256, TEXT("pci\\ven_%04x&dev_%04x"), adapterDesc
->VendorId
, adapterDesc
->DeviceId
);
292 _sntprintf(key
, 128, TEXT("SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\%04d"), i
);
293 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE
, key
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
296 len
= sizeof(szData
);
297 if( RegQueryValueEx( hKey
, TEXT("MatchingDeviceId"), NULL
, NULL
, (LPBYTE
) &szData
, &len
) == ERROR_SUCCESS
) {
298 if (_tcsncmp(lookup
, szData
, _tcslen(lookup
)) == 0)
308 int D3D11CheckDriverVersion(ID3D11Device
*d3ddev
, UINT vendorId
, const struct wdmm_version
*min_ver
)
310 IDXGIAdapter
*pAdapter
= D3D11DeviceAdapter(d3ddev
);
314 DXGI_ADAPTER_DESC adapterDesc
;
315 HRESULT hr
= IDXGIAdapter_GetDesc(pAdapter
, &adapterDesc
);
316 IDXGIAdapter_Release(pAdapter
);
320 if (vendorId
&& adapterDesc
.VendorId
!= vendorId
)
323 LONG err
= ERROR_ACCESS_DENIED
;
329 HKEY hKey
= GetAdapterRegistry(&adapterDesc
);
333 err
= RegQueryValueEx( hKey
, TEXT("DriverVersion"), NULL
, NULL
, (LPBYTE
) &szData
, &len
);
336 if (err
!= ERROR_SUCCESS
)
339 int wddm
, d3d_features
, revision
, build
;
340 /* see https://msdn.microsoft.com/windows/hardware/commercialize/design/compatibility/device-graphics */
341 if (_stscanf(szData
, TEXT("%d.%d.%d.%d"), &wddm
, &d3d_features
, &revision
, &build
) != 4)
345 wddm
> min_ver
->wddm
||
346 (wddm
== min_ver
->wddm
&& (d3d_features
> min_ver
->d3d_features
||
347 (d3d_features
== min_ver
->d3d_features
&&
348 (revision
> min_ver
->revision
||
349 (revision
== min_ver
->revision
&&
350 build
> min_ver
->build
)))));
352 return newer
? VLC_SUCCESS
: VLC_EGENERIC
;
356 const d3d_format_t
*FindD3D11Format(ID3D11Device
*d3ddevice
,
357 vlc_fourcc_t i_src_chroma
,
358 uint8_t bits_per_channel
,
362 supportFlags
|= D3D11_FORMAT_SUPPORT_TEXTURE2D
;
363 for (const d3d_format_t
*output_format
= GetRenderFormatList();
364 output_format
->name
!= NULL
; ++output_format
)
366 if (i_src_chroma
&& i_src_chroma
!= output_format
->fourcc
)
368 if (bits_per_channel
&& bits_per_channel
> output_format
->bitsPerChannel
)
370 if (!allow_opaque
&& is_d3d11_opaque(output_format
->fourcc
))
373 DXGI_FORMAT textureFormat
;
374 if (output_format
->formatTexture
== DXGI_FORMAT_UNKNOWN
)
375 textureFormat
= output_format
->resourceFormat
[0];
377 textureFormat
= output_format
->formatTexture
;
379 if( DeviceSupportsFormat( d3ddevice
, textureFormat
, supportFlags
) )
380 return output_format
;
385 int AllocateTextures( vlc_object_t
*obj
, d3d11_device_t
*d3d_dev
,
386 const d3d_format_t
*cfg
, const video_format_t
*fmt
,
387 unsigned pool_size
, ID3D11Texture2D
*textures
[] )
389 plane_t planes
[PICTURE_PLANE_MAX
];
390 int plane
, plane_count
;
392 ID3D11Texture2D
*slicedTexture
= NULL
;
393 D3D11_TEXTURE2D_DESC texDesc
;
394 ZeroMemory(&texDesc
, sizeof(texDesc
));
395 texDesc
.MipLevels
= 1;
396 texDesc
.SampleDesc
.Count
= 1;
397 texDesc
.MiscFlags
= 0; //D3D11_RESOURCE_MISC_SHARED;
398 texDesc
.BindFlags
= D3D11_BIND_SHADER_RESOURCE
;
399 if (is_d3d11_opaque(fmt
->i_chroma
)) {
400 texDesc
.BindFlags
|= D3D11_BIND_DECODER
;
401 texDesc
.Usage
= D3D11_USAGE_DEFAULT
;
402 texDesc
.CPUAccessFlags
= 0;
404 texDesc
.Usage
= D3D11_USAGE_DYNAMIC
;
405 texDesc
.CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
;
407 texDesc
.ArraySize
= pool_size
;
409 const vlc_chroma_description_t
*p_chroma_desc
= vlc_fourcc_GetChromaDescription( fmt
->i_chroma
);
413 if (cfg
->formatTexture
== DXGI_FORMAT_UNKNOWN
) {
414 if (p_chroma_desc
->plane_count
== 0)
416 msg_Dbg(obj
, "failed to get the pixel format planes for %4.4s", (char *)&fmt
->i_chroma
);
419 assert(p_chroma_desc
->plane_count
<= D3D11_MAX_SHADER_VIEW
);
420 plane_count
= p_chroma_desc
->plane_count
;
422 texDesc
.Format
= cfg
->resourceFormat
[0];
423 assert(cfg
->resourceFormat
[1] == cfg
->resourceFormat
[0]);
424 assert(cfg
->resourceFormat
[2] == cfg
->resourceFormat
[0]);
426 for( int i
= 0; i
< plane_count
; i
++ )
428 plane_t
*p
= &planes
[i
];
430 p
->i_lines
= fmt
->i_height
* p_chroma_desc
->p
[i
].h
.num
/ p_chroma_desc
->p
[i
].h
.den
;
431 p
->i_visible_lines
= fmt
->i_visible_height
* p_chroma_desc
->p
[i
].h
.num
/ p_chroma_desc
->p
[i
].h
.den
;
432 p
->i_pitch
= fmt
->i_width
* p_chroma_desc
->p
[i
].w
.num
/ p_chroma_desc
->p
[i
].w
.den
* p_chroma_desc
->pixel_size
;
433 p
->i_visible_pitch
= fmt
->i_visible_width
* p_chroma_desc
->p
[i
].w
.num
/ p_chroma_desc
->p
[i
].w
.den
* p_chroma_desc
->pixel_size
;
434 p
->i_pixel_pitch
= p_chroma_desc
->pixel_size
;
438 texDesc
.Format
= cfg
->formatTexture
;
439 texDesc
.Height
= fmt
->i_height
;
440 texDesc
.Width
= fmt
->i_width
;
442 hr
= ID3D11Device_CreateTexture2D( d3d_dev
->d3ddevice
, &texDesc
, NULL
, &slicedTexture
);
444 msg_Err(obj
, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size
, hr
);
449 for (unsigned picture_count
= 0; picture_count
< pool_size
; picture_count
++) {
450 for (plane
= 0; plane
< plane_count
; plane
++)
453 textures
[picture_count
* D3D11_MAX_SHADER_VIEW
+ plane
] = slicedTexture
;
454 ID3D11Texture2D_AddRef(slicedTexture
);
456 texDesc
.Height
= planes
[plane
].i_lines
;
457 texDesc
.Width
= planes
[plane
].i_pitch
;
458 hr
= ID3D11Device_CreateTexture2D( d3d_dev
->d3ddevice
, &texDesc
, NULL
, &textures
[picture_count
* D3D11_MAX_SHADER_VIEW
+ plane
] );
460 msg_Err(obj
, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size
, hr
);
465 for (; plane
< D3D11_MAX_SHADER_VIEW
; plane
++) {
466 if (!cfg
->resourceFormat
[plane
])
467 textures
[picture_count
* D3D11_MAX_SHADER_VIEW
+ plane
] = NULL
;
470 textures
[picture_count
* D3D11_MAX_SHADER_VIEW
+ plane
] = textures
[picture_count
* D3D11_MAX_SHADER_VIEW
];
471 ID3D11Texture2D_AddRef(textures
[picture_count
* D3D11_MAX_SHADER_VIEW
+ plane
]);
476 if (!is_d3d11_opaque(fmt
->i_chroma
) && cfg
->formatTexture
!= DXGI_FORMAT_UNKNOWN
) {
477 D3D11_MAPPED_SUBRESOURCE mappedResource
;
478 hr
= ID3D11DeviceContext_Map(d3d_dev
->d3dcontext
, (ID3D11Resource
*)textures
[0], 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
480 msg_Err(obj
, "The texture cannot be mapped. (hr=0x%lX)", hr
);
483 ID3D11DeviceContext_Unmap(d3d_dev
->d3dcontext
, (ID3D11Resource
*)textures
[0], 0);
484 if (mappedResource
.RowPitch
< p_chroma_desc
->pixel_size
* texDesc
.Width
) {
485 msg_Err( obj
, "The texture row pitch is too small (%d instead of %d)", mappedResource
.RowPitch
,
486 p_chroma_desc
->pixel_size
* texDesc
.Width
);
489 if ( mappedResource
.RowPitch
>=
490 2* (fmt
->i_width
* p_chroma_desc
->p
[0].w
.num
/ p_chroma_desc
->p
[0].w
.den
* p_chroma_desc
->pixel_size
) )
492 msg_Err(obj
, "Bogus %4.4s pitch detected. %d vs %d", (const char*)&fmt
->i_chroma
,
493 mappedResource
.RowPitch
,
494 (fmt
->i_width
* p_chroma_desc
->p
[0].w
.num
/ p_chroma_desc
->p
[0].w
.den
* p_chroma_desc
->pixel_size
));
500 ID3D11Texture2D_Release(slicedTexture
);
504 ID3D11Texture2D_Release(slicedTexture
);
509 int D3D11_Create(vlc_object_t
*obj
, d3d11_handle_t
*hd3d
)
511 #if !VLC_WINSTORE_APP
512 hd3d
->hdll
= LoadLibrary(TEXT("D3D11.DLL"));
515 msg_Warn(obj
, "cannot load d3d11.dll, aborting");
519 # if !defined(NDEBUG) && defined(HAVE_DXGIDEBUG_H)
520 if (IsDebuggerPresent())
522 hd3d
->dxgidebug_dll
= LoadLibrary(TEXT("DXGIDEBUG.DLL"));
523 HRESULT (WINAPI
* pf_DXGIGetDebugInterface
)(const GUID
*riid
, void **ppDebug
) = NULL
;
524 if (hd3d
->dxgidebug_dll
)
525 pf_DXGIGetDebugInterface
=
526 (void *)GetProcAddress(hd3d
->dxgidebug_dll
, "DXGIGetDebugInterface");
527 if (pf_DXGIGetDebugInterface
) {
528 IDXGIDebug
*pDXGIDebug
;
529 if (SUCCEEDED(pf_DXGIGetDebugInterface(&IID_IDXGIDebug
, (void**)&pDXGIDebug
)))
530 IDXGIDebug_ReportLiveObjects(pDXGIDebug
, DXGI_DEBUG_ALL
, DXGI_DEBUG_RLO_ALL
);
538 void D3D11_Destroy(d3d11_handle_t
*hd3d
)
540 #if !VLC_WINSTORE_APP
542 FreeLibrary(hd3d
->hdll
);
544 #if !defined(NDEBUG) && defined(HAVE_DXGIDEBUG_H)
545 if (hd3d
->dxgidebug_dll
)
546 FreeLibrary(hd3d
->dxgidebug_dll
);
552 #undef D3D11_LogProcessorSupport
553 void D3D11_LogProcessorSupport(vlc_object_t
*o
,
554 ID3D11VideoProcessorEnumerator
*processorEnumerator
)
558 for (int format
= 0; format
< 188; format
++) {
559 hr
= ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator
, format
, &flags
);
562 const char *name
= DxgiFormatToStr(format
);
563 const char *support
= NULL
;
564 if ((flags
& (D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT
|D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT
))
565 == (D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT
|D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT
))
566 support
= "input/output";
567 else if (flags
& D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT
)
569 else if (flags
& D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT
)
574 msg_Dbg(o
, "processor format %s is supported for %s", name
, support
);
576 msg_Dbg(o
, "processor format (%d) is supported for %s", format
, support
);