diff options
author | James Ross-Gowan <rossy@jrg.systems> | 2017-09-07 20:18:06 +1000 |
---|---|---|
committer | James Ross-Gowan <rossy@jrg.systems> | 2017-11-07 20:27:13 +1100 |
commit | 68eac1a1e7ac931576a8b59dd159a7961189ca48 (patch) | |
tree | ef25b97dd381bc5429138f256ae113339a9f353a /video/out/opengl | |
parent | 8020a62953926cd6d672e9151290bd4d65e8ee08 (diff) | |
download | mpv-68eac1a1e7ac931576a8b59dd159a7961189ca48.tar.bz2 mpv-68eac1a1e7ac931576a8b59dd159a7961189ca48.tar.xz |
vo_gpu: d3d11: initial implementation
This is a new RA/vo_gpu backend that uses Direct3D 11. The GLSL
generated by vo_gpu is cross-compiled to HLSL with SPIRV-Cross.
What works:
- All of mpv's internal shaders should work, including compute shaders.
- Some external shaders have been tested and work, including RAVU and
adaptive-sharpen.
- Non-dumb mode works, even on very old hardware. Most features work at
feature level 9_3 and all features work at feature level 10_0. Some
features also work at feature level 9_1 and 9_2, but without high-bit-
depth FBOs, it's not very useful. (Hardware this old is probably not
fast enough for advanced features anyway.)
Note: This is more compatible than ANGLE, which requires 9_3 to work
at all (GLES 2.0,) and 10_1 for non-dumb-mode (GLES 3.0.)
- Hardware decoding with D3D11VA, including decoding of 10-bit formats
without truncation to 8-bit.
What doesn't work / can be improved:
- PBO upload and direct rendering does not work yet. Direct rendering
requires persistent-mapped PBOs because the decoder needs to be able
to read data from images that have already been decoded and uploaded.
Unfortunately, it seems like persistent-mapped PBOs are fundamentally
incompatible with D3D11, which requires all resources to use driver-
managed memory and requires memory to be unmapped (and hence pointers
to be invalidated) when a resource is used in a draw or copy
operation.
However it might be possible to use D3D11's limited multithreading
capabilities to emulate some features of PBOs, like asynchronous
texture uploading.
- The blit() and clear() operations don't have equivalents in the D3D11
API that handle all cases, so in most cases, they have to be emulated
with a shader. This is currently done inside ra_d3d11, but ideally it
would be done in generic code, so it can take advantage of mpv's
shader generation utilities.
- SPIRV-Cross is used through a NIH C-compatible wrapper library, since
it does not expose a C interface itself.
The library is available here: https://github.com/rossy/crossc
- The D3D11 context could be made to support more modern DXGI features
in future. For example, it should be possible to add support for
high-bit-depth and HDR output with DXGI 1.5/1.6.
Diffstat (limited to 'video/out/opengl')
-rw-r--r-- | video/out/opengl/context_angle.c | 2 | ||||
-rw-r--r-- | video/out/opengl/d3d11_helpers.c | 383 | ||||
-rw-r--r-- | video/out/opengl/d3d11_helpers.h | 73 |
3 files changed, 1 insertions, 457 deletions
diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c index 0b665e27bf..b504293db5 100644 --- a/video/out/opengl/context_angle.c +++ b/video/out/opengl/context_angle.c @@ -24,7 +24,7 @@ #include "angle_dynamic.h" #include "egl_helpers.h" -#include "d3d11_helpers.h" +#include "video/out/gpu/d3d11_helpers.h" #include "common/common.h" #include "options/m_config.h" diff --git a/video/out/opengl/d3d11_helpers.c b/video/out/opengl/d3d11_helpers.c deleted file mode 100644 index d9b7fc2804..0000000000 --- a/video/out/opengl/d3d11_helpers.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <windows.h> -#include <d3d11.h> -#include <dxgi1_2.h> -#include <pthread.h> - -#include "common/common.h" -#include "common/msg.h" -#include "osdep/io.h" -#include "osdep/windows_utils.h" - -#include "d3d11_helpers.h" - -// Windows 8 enum value, not present in mingw-w64 headers -#define DXGI_ADAPTER_FLAG_SOFTWARE (2) - -static pthread_once_t d3d11_once = PTHREAD_ONCE_INIT; -static PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = NULL; -static void d3d11_load(void) -{ - HMODULE d3d11 = LoadLibraryW(L"d3d11.dll"); - if (!d3d11) - return; - pD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) - GetProcAddress(d3d11, "D3D11CreateDevice"); -} - -// Get a const array of D3D_FEATURE_LEVELs from max_fl to min_fl (inclusive) -static int get_feature_levels(int max_fl, int min_fl, - const D3D_FEATURE_LEVEL **out) -{ - static const D3D_FEATURE_LEVEL levels[] = { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1, - }; - static const int levels_len = MP_ARRAY_SIZE(levels); - - int start = 0; - for (; start < levels_len; start++) { - if (levels[start] <= max_fl) - break; - } - int len = 0; - for (; start + len < levels_len; len++) { - if (levels[start + len] < min_fl) - break; - } - *out = &levels[start]; - return len; -} - -static HRESULT create_device(struct mp_log *log, bool warp, bool bgra, - int max_fl, int min_fl, ID3D11Device **dev) -{ - const D3D_FEATURE_LEVEL *levels; - int levels_len = get_feature_levels(max_fl, min_fl, &levels); - if (!levels_len) { - mp_fatal(log, "No suitable Direct3D feature level found\n"); - return E_FAIL; - } - - D3D_DRIVER_TYPE type = warp ? D3D_DRIVER_TYPE_WARP - : D3D_DRIVER_TYPE_HARDWARE; - UINT flags = bgra ? D3D11_CREATE_DEVICE_BGRA_SUPPORT : 0; - return pD3D11CreateDevice(NULL, type, NULL, flags, levels, levels_len, - D3D11_SDK_VERSION, dev, NULL, NULL); -} - -// Create a Direct3D 11 device for rendering and presentation. This is meant to -// reduce boilerplate in backends that D3D11, while also making sure they share -// the same device creation logic and log the same information. -bool mp_d3d11_create_present_device(struct mp_log *log, - struct d3d11_device_opts *opts, - ID3D11Device **dev_out) -{ - bool warp = opts->force_warp; - bool bgra = true; - int max_fl = opts->max_feature_level; - int min_fl = opts->min_feature_level; - ID3D11Device *dev = NULL; - IDXGIDevice1 *dxgi_dev = NULL; - IDXGIAdapter1 *adapter = NULL; - bool success = false; - HRESULT hr; - - pthread_once(&d3d11_once, d3d11_load); - if (!pD3D11CreateDevice) { - mp_fatal(log, "Failed to load d3d11.dll\n"); - goto done; - } - - // Return here to retry creating the device - do { - // Use these default feature levels if they are not set - max_fl = max_fl ? max_fl : D3D_FEATURE_LEVEL_11_0; - min_fl = min_fl ? min_fl : D3D_FEATURE_LEVEL_9_1; - - hr = create_device(log, warp, bgra, max_fl, min_fl, &dev); - if (SUCCEEDED(hr)) - break; - - // BGRA is recommended, but FL 10_0 hardware may not support it - if (bgra) { - mp_dbg(log, "Failed to create D3D device with BGRA support\n"); - bgra = false; - continue; - } - - // Trying to create a D3D_FEATURE_LEVEL_11_1 device on Windows 7 - // without the platform update will not succeed. Try a 11_0 device. - if (max_fl >= D3D_FEATURE_LEVEL_11_1 && - min_fl <= D3D_FEATURE_LEVEL_11_0) - { - mp_dbg(log, "Failed to create 11_1+ device, trying 11_0\n"); - max_fl = D3D_FEATURE_LEVEL_11_0; - bgra = true; - continue; - } - - // Retry with WARP if allowed - if (!warp && opts->allow_warp) { - mp_dbg(log, "Failed to create hardware device, trying WARP\n"); - warp = true; - max_fl = opts->max_feature_level; - min_fl = opts->min_feature_level; - bgra = true; - continue; - } - - mp_fatal(log, "Failed to create Direct3D 11 device: %s\n", - mp_HRESULT_to_str(hr)); - goto done; - } while (true); - - hr = ID3D11Device_QueryInterface(dev, &IID_IDXGIDevice1, (void**)&dxgi_dev); - if (FAILED(hr)) { - mp_fatal(log, "Failed to get DXGI device\n"); - goto done; - } - hr = IDXGIDevice1_GetParent(dxgi_dev, &IID_IDXGIAdapter1, (void**)&adapter); - if (FAILED(hr)) { - mp_fatal(log, "Failed to get DXGI adapter\n"); - goto done; - } - - IDXGIDevice1_SetMaximumFrameLatency(dxgi_dev, opts->max_frame_latency); - - DXGI_ADAPTER_DESC1 desc; - hr = IDXGIAdapter1_GetDesc1(adapter, &desc); - if (FAILED(hr)) { - mp_fatal(log, "Failed to get adapter description\n"); - goto done; - } - - D3D_FEATURE_LEVEL selected_level = ID3D11Device_GetFeatureLevel(dev); - mp_verbose(log, "Using Direct3D 11 feature level %u_%u\n", - ((unsigned)selected_level) >> 12, - (((unsigned)selected_level) >> 8) & 0xf); - - char *dev_name = mp_to_utf8(NULL, desc.Description); - mp_verbose(log, "Device: %s\n" - "VendorId: 0x%04d\n" - "DeviceId: 0x%04d\n" - "LUID: %08lx%08lx\n", - dev_name, desc.VendorId, desc.DeviceId, - desc.AdapterLuid.HighPart, desc.AdapterLuid.LowPart); - talloc_free(dev_name); - - if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) - warp = true; - // If the primary display adapter is a software adapter, the - // DXGI_ADAPTER_FLAG_SOFTWARE flag won't be set, but the device IDs should - // still match the Microsoft Basic Render Driver - if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) - warp = true; - if (warp) { - mp_msg(log, opts->force_warp ? MSGL_V : MSGL_WARN, - "Using a software adapter\n"); - } - - *dev_out = dev; - dev = NULL; - success = true; - -done: - SAFE_RELEASE(adapter); - SAFE_RELEASE(dxgi_dev); - SAFE_RELEASE(dev); - return success; -} - -static HRESULT create_swapchain_1_2(ID3D11Device *dev, IDXGIFactory2 *factory, - struct mp_log *log, - struct d3d11_swapchain_opts *opts, - bool flip, DXGI_FORMAT format, - IDXGISwapChain **swapchain_out) -{ - IDXGISwapChain *swapchain = NULL; - IDXGISwapChain1 *swapchain1 = NULL; - HRESULT hr; - - DXGI_SWAP_CHAIN_DESC1 desc = { - .Width = opts->width ? opts->width : 1, - .Height = opts->height ? opts->height : 1, - .Format = format, - .SampleDesc = { .Count = 1 }, - .BufferUsage = opts->usage, - }; - - if (flip) { - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - desc.BufferCount = opts->length; - } else { - desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - desc.BufferCount = 1; - } - - hr = IDXGIFactory2_CreateSwapChainForHwnd(factory, (IUnknown*)dev, - opts->window, &desc, NULL, NULL, &swapchain1); - if (FAILED(hr)) - goto done; - hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain, - (void**)&swapchain); - if (FAILED(hr)) - goto done; - - *swapchain_out = swapchain; - swapchain = NULL; - -done: - SAFE_RELEASE(swapchain1); - SAFE_RELEASE(swapchain); - return hr; -} - -static HRESULT create_swapchain_1_1(ID3D11Device *dev, IDXGIFactory1 *factory, - struct mp_log *log, - struct d3d11_swapchain_opts *opts, - DXGI_FORMAT format, - IDXGISwapChain **swapchain_out) -{ - DXGI_SWAP_CHAIN_DESC desc = { - .BufferDesc = { - .Width = opts->width ? opts->width : 1, - .Height = opts->height ? opts->height : 1, - .Format = format, - }, - .SampleDesc = { .Count = 1 }, - .BufferUsage = opts->usage, - .BufferCount = 1, - .OutputWindow = opts->window, - .Windowed = TRUE, - .SwapEffect = DXGI_SWAP_EFFECT_DISCARD, - }; - - return IDXGIFactory1_CreateSwapChain(factory, (IUnknown*)dev, &desc, - swapchain_out); -} - -// Create a Direct3D 11 swapchain -bool mp_d3d11_create_swapchain(ID3D11Device *dev, struct mp_log *log, - struct d3d11_swapchain_opts *opts, - IDXGISwapChain **swapchain_out) -{ - IDXGIDevice1 *dxgi_dev = NULL; - IDXGIAdapter1 *adapter = NULL; - IDXGIFactory1 *factory = NULL; - IDXGIFactory2 *factory2 = NULL; - IDXGISwapChain *swapchain = NULL; - bool success = false; - HRESULT hr; - - hr = ID3D11Device_QueryInterface(dev, &IID_IDXGIDevice1, (void**)&dxgi_dev); - if (FAILED(hr)) { - mp_fatal(log, "Failed to get DXGI device\n"); - goto done; - } - hr = IDXGIDevice1_GetParent(dxgi_dev, &IID_IDXGIAdapter1, (void**)&adapter); - if (FAILED(hr)) { - mp_fatal(log, "Failed to get DXGI adapter\n"); - goto done; - } - hr = IDXGIAdapter1_GetParent(adapter, &IID_IDXGIFactory1, (void**)&factory); - if (FAILED(hr)) { - mp_fatal(log, "Failed to get DXGI factory\n"); - goto done; - } - hr = IDXGIFactory1_QueryInterface(factory, &IID_IDXGIFactory2, - (void**)&factory2); - if (FAILED(hr)) - factory2 = NULL; - - // Try B8G8R8A8_UNORM first, since at least in Windows 8, it's always the - // format of the desktop image - static const DXGI_FORMAT formats[] = { - DXGI_FORMAT_B8G8R8A8_UNORM, - DXGI_FORMAT_R8G8B8A8_UNORM, - }; - static const int formats_len = MP_ARRAY_SIZE(formats); - bool flip = factory2 && opts->flip; - - // Return here to retry creating the swapchain - do { - for (int i = 0; i < formats_len; i++) { - if (factory2) { - // Create a DXGI 1.2+ (Windows 8+) swap chain if possible - hr = create_swapchain_1_2(dev, factory2, log, opts, flip, - formats[i], &swapchain); - } else { - // Fall back to DXGI 1.1 (Windows 7) - hr = create_swapchain_1_1(dev, factory, log, opts, formats[i], - &swapchain); - } - if (SUCCEEDED(hr)) - break; - } - if (SUCCEEDED(hr)) - break; - - if (flip) { - mp_dbg(log, "Failed to create flip-model swapchain, trying bitblt\n"); - flip = false; - continue; - } - - mp_fatal(log, "Failed to create swapchain: %s\n", mp_HRESULT_to_str(hr)); - goto done; - } while (true); - - // Prevent DXGI from making changes to the VO window, otherwise it will - // hook the Alt+Enter keystroke and make it trigger an ugly transition to - // exclusive fullscreen mode instead of running the user-set command. - IDXGIFactory_MakeWindowAssociation(factory, opts->window, - DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | - DXGI_MWA_NO_PRINT_SCREEN); - - if (factory2) { - mp_verbose(log, "Using DXGI 1.2+\n"); - } else { - mp_verbose(log, "Using DXGI 1.1\n"); - } - - DXGI_SWAP_CHAIN_DESC scd = {0}; - IDXGISwapChain_GetDesc(swapchain, &scd); - if (scd.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) { - mp_verbose(log, "Using flip-model presentation\n"); - } else { - mp_verbose(log, "Using bitblt-model presentation\n"); - } - - *swapchain_out = swapchain; - swapchain = NULL; - success = true; - -done: - SAFE_RELEASE(swapchain); - SAFE_RELEASE(factory2); - SAFE_RELEASE(factory); - SAFE_RELEASE(adapter); - SAFE_RELEASE(dxgi_dev); - return success; -} diff --git a/video/out/opengl/d3d11_helpers.h b/video/out/opengl/d3d11_helpers.h deleted file mode 100644 index f34d1d4def..0000000000 --- a/video/out/opengl/d3d11_helpers.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MP_D3D11_HELPERS_H_ -#define MP_D3D11_HELPERS_H_ - -#include <stdbool.h> -#include <windows.h> -#include <d3d11.h> -#include <dxgi1_2.h> - -struct d3d11_device_opts { - // Allow a software (WARP) adapter. Note, sometimes a software adapter will - // be used even when allow_warp is false. This is because, on Windows 8 and - // up, if there are no hardware adapters, Windows will pretend the WARP - // adapter is the primary hardware adapter. - bool allow_warp; - - // Always use a WARP adapter. This is mainly for testing purposes. - bool force_warp; - - // The maximum number of pending frames allowed to be queued to a swapchain - int max_frame_latency; - - // The maximum Direct3D 11 feature level to attempt to create - // If unset, defaults to D3D_FEATURE_LEVEL_11_0 - int max_feature_level; - - // The minimum Direct3D 11 feature level to attempt to create. If this is - // not supported, device creation will fail. - // If unset, defaults to D3D_FEATURE_LEVEL_9_1 - int min_feature_level; -}; - -bool mp_d3d11_create_present_device(struct mp_log *log, - struct d3d11_device_opts *opts, - ID3D11Device **dev_out); - -struct d3d11_swapchain_opts { - HWND window; - int width; - int height; - - // Use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL if possible - bool flip; - - // Number of surfaces in the swapchain - int length; - - // The BufferUsage value for swapchain surfaces. This should probably - // contain DXGI_USAGE_RENDER_TARGET_OUTPUT. - DXGI_USAGE usage; -}; - -bool mp_d3d11_create_swapchain(ID3D11Device *dev, struct mp_log *log, - struct d3d11_swapchain_opts *opts, - IDXGISwapChain **swapchain_out); - -#endif |