forked from mpv-player/mpv
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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. - Performance seems promising. It's faster than the ANGLE and dxinterop backends (on my machine.) - screenshot window (Ctrl+S) - Software rendering with WARP. 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.
- Loading branch information
Showing
13 changed files
with
2,755 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#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, | ||
}; |
Oops, something went wrong.