From 68eac1a1e7ac931576a8b59dd159a7961189ca48 Mon Sep 17 00:00:00 2001 From: James Ross-Gowan Date: Thu, 7 Sep 2017 20:18:06 +1000 Subject: 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. --- video/out/d3d11/context.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 video/out/d3d11/context.c (limited to 'video/out/d3d11/context.c') diff --git a/video/out/d3d11/context.c b/video/out/d3d11/context.c new file mode 100644 index 0000000000..018fd99934 --- /dev/null +++ b/video/out/d3d11/context.c @@ -0,0 +1,235 @@ +/* + * 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 . + */ + +#include "common/msg.h" +#include "options/m_config.h" +#include "osdep/windows_utils.h" + +#include "video/out/gpu/context.h" +#include "video/out/gpu/d3d11_helpers.h" +#include "video/out/gpu/spirv.h" +#include "video/out/w32_common.h" +#include "ra_d3d11.h" + +struct d3d11_opts { + int feature_level; + int warp; + int flip; + int sync_interval; +}; + +#define OPT_BASE_STRUCT struct d3d11_opts +const struct m_sub_options d3d11_conf = { + .opts = (const struct m_option[]) { + OPT_CHOICE("d3d11-warp", warp, 0, + ({"auto", -1}, + {"no", 0}, + {"yes", 1})), + OPT_CHOICE("d3d11-feature-level", feature_level, 0, + ({"12_1", D3D_FEATURE_LEVEL_12_1}, + {"12_0", D3D_FEATURE_LEVEL_12_0}, + {"11_1", D3D_FEATURE_LEVEL_11_1}, + {"11_0", D3D_FEATURE_LEVEL_11_0}, + {"10_1", D3D_FEATURE_LEVEL_10_1}, + {"10_0", D3D_FEATURE_LEVEL_10_0}, + {"9_3", D3D_FEATURE_LEVEL_9_3}, + {"9_2", D3D_FEATURE_LEVEL_9_2}, + {"9_1", D3D_FEATURE_LEVEL_9_1})), + OPT_FLAG("d3d11-flip", flip, 0), + OPT_INTRANGE("d3d11-sync-interval", sync_interval, 0, 0, 4), + {0} + }, + .defaults = &(const struct d3d11_opts) { + .feature_level = D3D_FEATURE_LEVEL_12_1, + .warp = -1, + .flip = 1, + .sync_interval = 1, + }, + .size = sizeof(struct d3d11_opts) +}; + +struct priv { + struct d3d11_opts *opts; + + struct ra_tex *backbuffer; + ID3D11Device *device; + IDXGISwapChain *swapchain; +}; + +static struct ra_tex *get_backbuffer(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + ID3D11Texture2D *backbuffer = NULL; + struct ra_tex *tex = NULL; + HRESULT hr; + + hr = IDXGISwapChain_GetBuffer(p->swapchain, 0, &IID_ID3D11Texture2D, + (void**)&backbuffer); + if (FAILED(hr)) { + MP_ERR(ctx, "Couldn't get swapchain image\n"); + goto done; + } + + tex = ra_d3d11_wrap_tex(ctx->ra, (ID3D11Resource *)backbuffer); +done: + SAFE_RELEASE(backbuffer); + return tex; +} + +static bool resize(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + HRESULT hr; + + ra_tex_free(ctx->ra, &p->backbuffer); + + hr = IDXGISwapChain_ResizeBuffers(p->swapchain, 0, ctx->vo->dwidth, + ctx->vo->dheight, DXGI_FORMAT_UNKNOWN, 0); + if (FAILED(hr)) { + MP_FATAL(ctx, "Couldn't resize swapchain: %s\n", mp_HRESULT_to_str(hr)); + return false; + } + + p->backbuffer = get_backbuffer(ctx); + + return true; +} + +static bool d3d11_reconfig(struct ra_ctx *ctx) +{ + vo_w32_config(ctx->vo); + return resize(ctx); +} + +static int d3d11_color_depth(struct ra_swapchain *sw) +{ + return 8; +} + +static bool d3d11_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo) +{ + struct priv *p = sw->priv; + *out_fbo = (struct ra_fbo) { + .tex = p->backbuffer, + .flip = false, + }; + return true; +} + +static bool d3d11_submit_frame(struct ra_swapchain *sw, + const struct vo_frame *frame) +{ + ra_d3d11_flush(sw->ctx->ra); + return true; +} + +static void d3d11_swap_buffers(struct ra_swapchain *sw) +{ + struct priv *p = sw->priv; + IDXGISwapChain_Present(p->swapchain, p->opts->sync_interval, 0); +} + +static int d3d11_control(struct ra_ctx *ctx, int *events, int request, void *arg) +{ + int ret = vo_w32_control(ctx->vo, events, request, arg); + if (*events & VO_EVENT_RESIZE) { + if (!resize(ctx)) + return VO_ERROR; + } + return ret; +} + +static void d3d11_uninit(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + + ra_tex_free(ctx->ra, &p->backbuffer); + SAFE_RELEASE(p->swapchain); + vo_w32_uninit(ctx->vo); + SAFE_RELEASE(p->device); + + // Destory the RA last to prevent objects we hold from showing up in D3D's + // leak checker + ctx->ra->fns->destroy(ctx->ra); +} + +static const struct ra_swapchain_fns d3d11_swapchain = { + .color_depth = d3d11_color_depth, + .start_frame = d3d11_start_frame, + .submit_frame = d3d11_submit_frame, + .swap_buffers = d3d11_swap_buffers, +}; + +static bool d3d11_init(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); + p->opts = mp_get_config_group(ctx, ctx->global, &d3d11_conf); + + struct ra_swapchain *sw = ctx->swapchain = talloc_zero(ctx, struct ra_swapchain); + sw->priv = p; + sw->ctx = ctx; + sw->fns = &d3d11_swapchain; + + struct d3d11_device_opts dopts = { + .debug = ctx->opts.debug, + .allow_warp = p->opts->warp != 0, + .force_warp = p->opts->warp == 1, + .max_feature_level = p->opts->feature_level, + .max_frame_latency = ctx->opts.swapchain_depth, + }; + if (!mp_d3d11_create_present_device(ctx->log, &dopts, &p->device)) + goto error; + + if (!spirv_compiler_init(ctx)) + goto error; + ctx->ra = ra_d3d11_create(p->device, ctx->log, ctx->spirv); + if (!ctx->ra) + goto error; + + if (!vo_w32_init(ctx->vo)) + goto error; + + struct d3d11_swapchain_opts scopts = { + .window = vo_w32_hwnd(ctx->vo), + .width = ctx->vo->dwidth, + .height = ctx->vo->dheight, + .flip = p->opts->flip, + // Add one frame for the backbuffer and one frame of "slack" to reduce + // contention with the window manager when acquiring the backbuffer + .length = ctx->opts.swapchain_depth + 2, + .usage = DXGI_USAGE_RENDER_TARGET_OUTPUT, + }; + if (!mp_d3d11_create_swapchain(p->device, ctx->log, &scopts, &p->swapchain)) + goto error; + + p->backbuffer = get_backbuffer(ctx); + + return true; + +error: + d3d11_uninit(ctx); + return false; +} + +const struct ra_ctx_fns ra_ctx_d3d11 = { + .type = "d3d11", + .name = "d3d11", + .reconfig = d3d11_reconfig, + .control = d3d11_control, + .init = d3d11_init, + .uninit = d3d11_uninit, +}; -- cgit v1.2.3