diff options
Diffstat (limited to 'video/out/d3d11/context.c')
-rw-r--r-- | video/out/d3d11/context.c | 354 |
1 files changed, 243 insertions, 111 deletions
diff --git a/video/out/d3d11/context.c b/video/out/d3d11/context.c index bdfbacfe54..f183ae8be5 100644 --- a/video/out/d3d11/context.c +++ b/video/out/d3d11/context.c @@ -24,61 +24,60 @@ #include "video/out/gpu/d3d11_helpers.h" #include "video/out/gpu/spirv.h" #include "video/out/w32_common.h" +#include "context.h" #include "ra_d3d11.h" -static int d3d11_validate_adapter(struct mp_log *log, - const struct m_option *opt, - struct bstr name, struct bstr param); - struct d3d11_opts { int feature_level; int warp; - int flip; + bool flip; int sync_interval; char *adapter_name; int output_format; int color_space; + bool exclusive_fs; }; #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), - OPT_STRING_VALIDATE("d3d11-adapter", adapter_name, 0, - d3d11_validate_adapter), - OPT_CHOICE("d3d11-output-format", output_format, 0, - ({"auto", DXGI_FORMAT_UNKNOWN}, - {"rgba8", DXGI_FORMAT_R8G8B8A8_UNORM}, - {"bgra8", DXGI_FORMAT_B8G8R8A8_UNORM}, - {"rgb10_a2", DXGI_FORMAT_R10G10B10A2_UNORM}, - {"rgba16f", DXGI_FORMAT_R16G16B16A16_FLOAT})), - OPT_CHOICE("d3d11-output-csp", color_space, 0, - ({"auto", -1}, - {"srgb", DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709}, - {"linear", DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709}, - {"pq", DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020}, - {"bt.2020", DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020})), + {"d3d11-warp", OPT_CHOICE(warp, + {"auto", -1}, + {"no", 0}, + {"yes", 1})}, + {"d3d11-feature-level", OPT_CHOICE(feature_level, + {"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})}, + {"d3d11-flip", OPT_BOOL(flip)}, + {"d3d11-sync-interval", OPT_INT(sync_interval), M_RANGE(0, 4)}, + {"d3d11-adapter", OPT_STRING_VALIDATE(adapter_name, + mp_dxgi_validate_adapter)}, + {"d3d11-output-format", OPT_CHOICE(output_format, + {"auto", DXGI_FORMAT_UNKNOWN}, + {"rgba8", DXGI_FORMAT_R8G8B8A8_UNORM}, + {"bgra8", DXGI_FORMAT_B8G8R8A8_UNORM}, + {"rgb10_a2", DXGI_FORMAT_R10G10B10A2_UNORM}, + {"rgba16f", DXGI_FORMAT_R16G16B16A16_FLOAT})}, + {"d3d11-output-csp", OPT_CHOICE(color_space, + {"auto", -1}, + {"srgb", DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709}, + {"linear", DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709}, + {"pq", DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020}, + {"bt.2020", DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020})}, + {"d3d11-exclusive-fs", OPT_BOOL(exclusive_fs)}, {0} }, .defaults = &(const struct d3d11_opts) { .feature_level = D3D_FEATURE_LEVEL_12_1, .warp = -1, - .flip = 1, + .flip = true, .sync_interval = 1, .adapter_name = NULL, .output_format = DXGI_FORMAT_UNKNOWN, @@ -89,49 +88,23 @@ const struct m_sub_options d3d11_conf = { struct priv { struct d3d11_opts *opts; + struct m_config_cache *opts_cache; + + struct mp_vo_opts *vo_opts; + struct m_config_cache *vo_opts_cache; struct ra_tex *backbuffer; ID3D11Device *device; IDXGISwapChain *swapchain; - struct mp_colorspace swapchain_csp; + struct pl_color_space swapchain_csp; int64_t perf_freq; - unsigned last_sync_refresh_count; - int64_t last_sync_qpc_time; + unsigned sync_refresh_count; + int64_t sync_qpc_time; int64_t vsync_duration_qpc; int64_t last_submit_qpc; }; -static int d3d11_validate_adapter(struct mp_log *log, - const struct m_option *opt, - struct bstr name, struct bstr param) -{ - bool help = bstr_equals0(param, "help"); - bool adapter_matched = false; - struct bstr listing = { 0 }; - - if (bstr_equals0(param, "")) { - return 0; - } - - adapter_matched = mp_d3d11_list_or_verify_adapters(log, - help ? bstr0(NULL) : param, - help ? &listing : NULL); - - if (help) { - mp_info(log, "Available D3D11 adapters:\n%.*s", - BSTR_P(listing)); - talloc_free(listing.start); - return M_OPT_EXIT; - } - - if (!adapter_matched) { - mp_err(log, "No adapter matching '%.*s'!\n", BSTR_P(param)); - } - - return adapter_matched ? 0 : M_OPT_INVALID; -} - static struct ra_tex *get_backbuffer(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -157,7 +130,10 @@ static bool resize(struct ra_ctx *ctx) struct priv *p = ctx->priv; HRESULT hr; - ra_tex_free(ctx->ra, &p->backbuffer); + if (p->backbuffer) { + MP_ERR(ctx, "Attempt at resizing while a frame was in progress!\n"); + return false; + } hr = IDXGISwapChain_ResizeBuffers(p->swapchain, 0, ctx->vo->dwidth, ctx->vo->dheight, DXGI_FORMAT_UNKNOWN, 0); @@ -166,8 +142,6 @@ static bool resize(struct ra_ctx *ctx) return false; } - p->backbuffer = get_backbuffer(ctx); - return true; } @@ -181,16 +155,37 @@ static int d3d11_color_depth(struct ra_swapchain *sw) { struct priv *p = sw->priv; - if (!p->backbuffer) + DXGI_OUTPUT_DESC1 desc1; + if (mp_get_dxgi_output_desc(p->swapchain, &desc1)) + return desc1.BitsPerColor; + + DXGI_SWAP_CHAIN_DESC desc; + + HRESULT hr = IDXGISwapChain_GetDesc(p->swapchain, &desc); + if (FAILED(hr)) { + MP_ERR(sw->ctx, "Failed to query swap chain description: %s!\n", + mp_HRESULT_to_str(hr)); + return 0; + } + + const struct ra_format *ra_fmt = + ra_d3d11_get_ra_format(sw->ctx->ra, desc.BufferDesc.Format); + if (!ra_fmt) return 0; - return p->backbuffer->params.format->component_depth[0]; + return ra_fmt->component_depth[0]; } static bool d3d11_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo) { struct priv *p = sw->priv; + if (!out_fbo) + return true; + + assert(!p->backbuffer); + + p->backbuffer = get_backbuffer(sw->ctx); if (!p->backbuffer) return false; @@ -205,32 +200,37 @@ static bool d3d11_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo) static bool d3d11_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame) { + struct priv *p = sw->priv; + ra_d3d11_flush(sw->ctx->ra); + ra_tex_free(sw->ctx->ra, &p->backbuffer); return true; } -static int64_t qpc_to_us(struct ra_swapchain *sw, int64_t qpc) +static int64_t qpc_to_ns(struct ra_swapchain *sw, int64_t qpc) { struct priv *p = sw->priv; - // Convert QPC units (1/perf_freq seconds) to microseconds. This will work + // Convert QPC units (1/perf_freq seconds) to nanoseconds. This will work // without overflow because the QPC value is guaranteed not to roll-over // within 100 years, so perf_freq must be less than 2.9*10^9. - return qpc / p->perf_freq * 1000000 + - qpc % p->perf_freq * 1000000 / p->perf_freq; + return qpc / p->perf_freq * INT64_C(1000000000) + + qpc % p->perf_freq * INT64_C(1000000000) / p->perf_freq; } -static int64_t qpc_us_now(struct ra_swapchain *sw) +static int64_t qpc_ns_now(struct ra_swapchain *sw) { LARGE_INTEGER perf_count; QueryPerformanceCounter(&perf_count); - return qpc_to_us(sw, perf_count.QuadPart); + return qpc_to_ns(sw, perf_count.QuadPart); } static void d3d11_swap_buffers(struct ra_swapchain *sw) { struct priv *p = sw->priv; + m_config_cache_update(p->opts_cache); + LARGE_INTEGER perf_count; QueryPerformanceCounter(&perf_count); p->last_submit_qpc = perf_count.QuadPart; @@ -243,10 +243,21 @@ static void d3d11_get_vsync(struct ra_swapchain *sw, struct vo_vsync_info *info) struct priv *p = sw->priv; HRESULT hr; + m_config_cache_update(p->opts_cache); + // The calculations below are only valid if mpv presents on every vsync if (p->opts->sync_interval != 1) return; + // They're also only valid for flip model swapchains + DXGI_SWAP_CHAIN_DESC desc; + hr = IDXGISwapChain_GetDesc(p->swapchain, &desc); + if (FAILED(hr) || (desc.SwapEffect != DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL && + desc.SwapEffect != DXGI_SWAP_EFFECT_FLIP_DISCARD)) + { + return; + } + // GetLastPresentCount returns a sequential ID for the frame submitted by // the last call to IDXGISwapChain::Present() UINT submit_count; @@ -262,34 +273,39 @@ static void d3d11_get_vsync(struct ra_swapchain *sw, struct vo_vsync_info *info) DXGI_FRAME_STATISTICS stats; hr = IDXGISwapChain_GetFrameStatistics(p->swapchain, &stats); if (hr == DXGI_ERROR_FRAME_STATISTICS_DISJOINT) { - p->last_sync_refresh_count = 0; - p->last_sync_qpc_time = 0; + p->sync_refresh_count = 0; + p->sync_qpc_time = 0; } if (FAILED(hr)) return; + info->last_queue_display_time = 0; + info->vsync_duration = 0; // Detecting skipped vsyncs is possible but not supported yet - info->skipped_vsyncs = 0; + info->skipped_vsyncs = -1; - // Get the number of physical vsyncs that have passed since the last call. + // Get the number of physical vsyncs that have passed since the start of the + // playback or disjoint event. // Check for 0 here, since sometimes GetFrameStatistics returns S_OK but // with 0s in some (all?) members of DXGI_FRAME_STATISTICS. unsigned src_passed = 0; - if (stats.SyncRefreshCount && p->last_sync_refresh_count) - src_passed = stats.SyncRefreshCount - p->last_sync_refresh_count; - p->last_sync_refresh_count = stats.SyncRefreshCount; + if (stats.SyncRefreshCount && p->sync_refresh_count) + src_passed = stats.SyncRefreshCount - p->sync_refresh_count; + if (p->sync_refresh_count == 0) + p->sync_refresh_count = stats.SyncRefreshCount; // Get the elapsed time passed between the above vsyncs unsigned sqt_passed = 0; - if (stats.SyncQPCTime.QuadPart && p->last_sync_qpc_time) - sqt_passed = stats.SyncQPCTime.QuadPart - p->last_sync_qpc_time; - p->last_sync_qpc_time = stats.SyncQPCTime.QuadPart; + if (stats.SyncQPCTime.QuadPart && p->sync_qpc_time) + sqt_passed = stats.SyncQPCTime.QuadPart - p->sync_qpc_time; + if (p->sync_qpc_time == 0) + p->sync_qpc_time = stats.SyncQPCTime.QuadPart; // If any vsyncs have passed, estimate the physical frame rate if (src_passed && sqt_passed) p->vsync_duration_qpc = sqt_passed / src_passed; if (p->vsync_duration_qpc) - info->vsync_duration = qpc_to_us(sw, p->vsync_duration_qpc); + info->vsync_duration = qpc_to_ns(sw, p->vsync_duration_qpc); // If the physical frame rate is known and the other members of // DXGI_FRAME_STATISTICS are non-0, estimate the timing of the next frame @@ -297,10 +313,9 @@ static void d3d11_get_vsync(struct ra_swapchain *sw, struct vo_vsync_info *info) stats.PresentRefreshCount && stats.SyncRefreshCount && stats.SyncQPCTime.QuadPart) { - // PresentRefreshCount and SyncRefreshCount might refer to different - // frames (this can definitely occur in bitblt-mode.) Assuming mpv - // presents on every frame, guess the present count that relates to - // SyncRefreshCount. + // It's not clear if PresentRefreshCount and SyncRefreshCount can refer + // to different frames, but in case they can, assuming mpv presents on + // every frame, guess the present count that relates to SyncRefreshCount. unsigned expected_sync_pc = stats.PresentCount + (stats.SyncRefreshCount - stats.PresentRefreshCount); @@ -313,15 +328,90 @@ static void d3d11_get_vsync(struct ra_swapchain *sw, struct vo_vsync_info *info) // Only set the estimated display time if it's after the last submission // time. It could be before if mpv skips a lot of frames. if (last_queue_display_time_qpc >= p->last_submit_qpc) { - info->last_queue_display_time = mp_time_us() + - (qpc_to_us(sw, last_queue_display_time_qpc) - qpc_us_now(sw)); + info->last_queue_display_time = mp_time_ns() + + (qpc_to_ns(sw, last_queue_display_time_qpc) - qpc_ns_now(sw)); } } } +static bool d3d11_set_fullscreen(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + HRESULT hr; + + m_config_cache_update(p->opts_cache); + + if (!p->swapchain) { + MP_ERR(ctx, "Full screen configuration was requested before D3D11 " + "swap chain was ready!"); + return false; + } + + // we only want exclusive FS if we are entering FS and + // exclusive FS is enabled. Otherwise disable exclusive FS. + bool enable_exclusive_fs = p->vo_opts->fullscreen && + p->opts->exclusive_fs; + + MP_VERBOSE(ctx, "%s full-screen exclusive mode while %s fullscreen\n", + enable_exclusive_fs ? "Enabling" : "Disabling", + ctx->vo->opts->fullscreen ? "entering" : "leaving"); + + hr = IDXGISwapChain_SetFullscreenState(p->swapchain, + enable_exclusive_fs, NULL); + if (FAILED(hr)) + return false; + + if (!resize(ctx)) + return false; + + return true; +} + static int d3d11_control(struct ra_ctx *ctx, int *events, int request, void *arg) { - int ret = vo_w32_control(ctx->vo, events, request, arg); + struct priv *p = ctx->priv; + int ret = -1; + bool fullscreen_switch_needed = false; + + switch (request) { + case VOCTRL_VO_OPTS_CHANGED: { + void *changed_option; + + while (m_config_cache_get_next_changed(p->vo_opts_cache, + &changed_option)) + { + struct mp_vo_opts *vo_opts = p->vo_opts_cache->opts; + + if (changed_option == &vo_opts->fullscreen) { + fullscreen_switch_needed = true; + } + } + + break; + } + default: + break; + } + + // if leaving full screen, handle d3d11 stuff first, then general + // windowing + if (fullscreen_switch_needed && !p->vo_opts->fullscreen) { + if (!d3d11_set_fullscreen(ctx)) + return VO_FALSE; + + fullscreen_switch_needed = false; + } + + ret = vo_w32_control(ctx->vo, events, request, arg); + + // if entering full screen, handle d3d11 after general windowing stuff + if (fullscreen_switch_needed && p->vo_opts->fullscreen) { + if (!d3d11_set_fullscreen(ctx)) + return VO_FALSE; + + fullscreen_switch_needed = false; + } + if (*events & VO_EVENT_RESIZE) { if (!resize(ctx)) return VO_ERROR; @@ -333,13 +423,16 @@ static void d3d11_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; + if (p->swapchain) + IDXGISwapChain_SetFullscreenState(p->swapchain, FALSE, NULL); + if (ctx->ra) 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 + // Destroy the RA last to prevent objects we hold from showing up in D3D's // leak checker if (ctx->ra) ctx->ra->fns->destroy(ctx->ra); @@ -356,7 +449,11 @@ static const struct ra_swapchain_fns d3d11_swapchain = { 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); + p->opts_cache = m_config_cache_alloc(ctx, ctx->global, &d3d11_conf); + p->opts = p->opts_cache->opts; + + p->vo_opts_cache = m_config_cache_alloc(ctx, ctx->vo->global, &vo_sub_opts); + p->vo_opts = p->vo_opts_cache->opts; LARGE_INTEGER perf_freq; QueryPerformanceFrequency(&perf_freq); @@ -387,6 +484,16 @@ static bool d3d11_init(struct ra_ctx *ctx) if (!vo_w32_init(ctx->vo)) goto error; + if (ctx->opts.want_alpha) + vo_w32_set_transparency(ctx->vo, ctx->opts.want_alpha); + + UINT usage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT; + if (ID3D11Device_GetFeatureLevel(p->device) >= D3D_FEATURE_LEVEL_11_0 && + p->opts->output_format != DXGI_FORMAT_B8G8R8A8_UNORM) + { + usage |= DXGI_USAGE_UNORDERED_ACCESS; + } + struct d3d11_swapchain_opts scopts = { .window = vo_w32_hwnd(ctx->vo), .width = ctx->vo->dwidth, @@ -398,15 +505,11 @@ static bool d3d11_init(struct ra_ctx *ctx) // Add one frame for the backbuffer and one frame of "slack" to reduce // contention with the window manager when acquiring the backbuffer .length = ctx->vo->opts->swapchain_depth + 2, - .usage = DXGI_USAGE_RENDER_TARGET_OUTPUT, + .usage = usage, }; if (!mp_d3d11_create_swapchain(p->device, ctx->log, &scopts, &p->swapchain)) goto error; - p->backbuffer = get_backbuffer(ctx); - if (!p->backbuffer) - goto error; - return true; error: @@ -414,11 +517,40 @@ error: return false; } +static void d3d11_update_render_opts(struct ra_ctx *ctx) +{ + vo_w32_set_transparency(ctx->vo, ctx->opts.want_alpha); +} + +IDXGISwapChain *ra_d3d11_ctx_get_swapchain(struct ra_ctx *ra) +{ + if (ra->swapchain->fns != &d3d11_swapchain) + return NULL; + + struct priv *p = ra->priv; + + IDXGISwapChain_AddRef(p->swapchain); + + return p->swapchain; +} + +bool ra_d3d11_ctx_prefer_8bit_output_format(struct ra_ctx *ra) +{ + if (ra->swapchain->fns != &d3d11_swapchain) + return false; + + struct priv *p = ra->priv; + + return p->opts->output_format == DXGI_FORMAT_R8G8B8A8_UNORM; +} + const struct ra_ctx_fns ra_ctx_d3d11 = { - .type = "d3d11", - .name = "d3d11", - .reconfig = d3d11_reconfig, - .control = d3d11_control, - .init = d3d11_init, - .uninit = d3d11_uninit, + .type = "d3d11", + .name = "d3d11", + .description = "Direct3D 11", + .reconfig = d3d11_reconfig, + .control = d3d11_control, + .update_render_opts = d3d11_update_render_opts, + .init = d3d11_init, + .uninit = d3d11_uninit, }; |