diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index 59891272da..4383e1343a 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -25,6 +25,8 @@ Interface changes :: --- mpv 0.30.0 --- + - add `--d3d11-output-csp` to enable explicit selection of a D3D11 + swap chain color space. - add `--d3d11-output-format` to enable explicit selection of a D3D11 swap chain format. - rewrite DVB channel switching to use an integer value diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 197423e6b2..c8af27bcb7 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -4746,6 +4746,19 @@ The following video options are currently all specific to ``--vo=gpu`` and from Windows 10. Thus on older systems it will only automatically utilize the rgba8 output format. +``--d3d11-output-csp=`` + Select a specific D3D11 output color space to utilize for D3D11 rendering. + "auto" is the default, which will select the color space of the desktop + on which the swap chain is located. + + Values other than "srgb" and "pq" have had issues in testing, so they + are mostly available for manual testing. + + .. note:: + + Swap chain color space configuration is only available from an API + available from Windows 10. Thus on older systems it will not work. + ``--d3d11va-zero-copy=`` By default, when using hardware decoding with ``--gpu-api=d3d11``, the video image will be copied (GPU-to-GPU) from the decoder surface to a diff --git a/video/out/d3d11/context.c b/video/out/d3d11/context.c index f03f93df92..bdfbacfe54 100644 --- a/video/out/d3d11/context.c +++ b/video/out/d3d11/context.c @@ -37,6 +37,7 @@ struct d3d11_opts { int sync_interval; char *adapter_name; int output_format; + int color_space; }; #define OPT_BASE_STRUCT struct d3d11_opts @@ -66,6 +67,12 @@ const struct m_sub_options d3d11_conf = { {"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})), {0} }, .defaults = &(const struct d3d11_opts) { @@ -75,6 +82,7 @@ const struct m_sub_options d3d11_conf = { .sync_interval = 1, .adapter_name = NULL, .output_format = DXGI_FORMAT_UNKNOWN, + .color_space = -1, }, .size = sizeof(struct d3d11_opts) }; @@ -85,6 +93,7 @@ struct priv { struct ra_tex *backbuffer; ID3D11Device *device; IDXGISwapChain *swapchain; + struct mp_colorspace swapchain_csp; int64_t perf_freq; unsigned last_sync_refresh_count; @@ -188,6 +197,7 @@ static bool d3d11_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo) *out_fbo = (struct ra_fbo) { .tex = p->backbuffer, .flip = false, + .color_space = p->swapchain_csp }; return true; } @@ -382,6 +392,8 @@ static bool d3d11_init(struct ra_ctx *ctx) .width = ctx->vo->dwidth, .height = ctx->vo->dheight, .format = p->opts->output_format, + .color_space = p->opts->color_space, + .configured_csp = &p->swapchain_csp, .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 diff --git a/video/out/gpu/d3d11_helpers.c b/video/out/gpu/d3d11_helpers.c index 11c891b352..33d01e0861 100644 --- a/video/out/gpu/d3d11_helpers.c +++ b/video/out/gpu/d3d11_helpers.c @@ -227,6 +227,52 @@ static const char *d3d11_get_csp_name(DXGI_COLOR_SPACE_TYPE csp) } } +static bool d3d11_get_mp_csp(DXGI_COLOR_SPACE_TYPE csp, + struct mp_colorspace *mp_csp) +{ + if (!mp_csp) + return false; + + // Colorspaces utilizing gamma 2.2 (G22) are set to + // AUTO as that keeps the current default flow regarding + // SDR transfer function handling. + // (no adjustment is done unless the user has a CMS LUT). + // + // Additionally, only set primary information with colorspaces + // utilizing non-709 primaries to keep the current behavior + // regarding not doing conversion from BT.601 to BT.709. + switch (csp) { + case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709: + *mp_csp = (struct mp_colorspace){ + .gamma = MP_CSP_TRC_AUTO, + .primaries = MP_CSP_PRIM_AUTO, + }; + break; + case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709: + *mp_csp = (struct mp_colorspace) { + .gamma = MP_CSP_TRC_LINEAR, + .primaries = MP_CSP_PRIM_AUTO, + }; + break; + case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020: + *mp_csp = (struct mp_colorspace) { + .gamma = MP_CSP_TRC_PQ, + .primaries = MP_CSP_PRIM_BT_2020, + }; + break; + case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020: + *mp_csp = (struct mp_colorspace) { + .gamma = MP_CSP_TRC_AUTO, + .primaries = MP_CSP_PRIM_BT_2020, + }; + break; + default: + return false; + } + + return true; +} + static bool query_output_format_and_colorspace(struct mp_log *log, IDXGISwapChain *swapchain, DXGI_FORMAT *out_fmt, @@ -658,14 +704,82 @@ static bool update_swapchain_format(struct mp_log *log, return true; } +static bool update_swapchain_color_space(struct mp_log *log, + IDXGISwapChain *swapchain, + DXGI_COLOR_SPACE_TYPE color_space) +{ + IDXGISwapChain4 *swapchain4 = NULL; + const char *csp_name = d3d11_get_csp_name(color_space); + bool success = false; + HRESULT hr = E_FAIL; + unsigned int csp_support_flags; + + hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain4, + (void *)&(swapchain4)); + if (FAILED(hr)) { + mp_err(log, "Failed to create v4 swapchain for color space " + "configuration (%s)!\n", + mp_HRESULT_to_str(hr)); + goto done; + } + + hr = IDXGISwapChain4_CheckColorSpaceSupport(swapchain4, + color_space, + &csp_support_flags); + if (FAILED(hr)) { + mp_err(log, "Failed to check color space support for color space " + "%s (%d): %s!\n", + csp_name, color_space, mp_HRESULT_to_str(hr)); + goto done; + } + + mp_verbose(log, + "Swapchain capabilities for color space %s (%d): " + "normal: %s, overlay: %s\n", + csp_name, color_space, + (csp_support_flags & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) ? + "yes" : "no", + (csp_support_flags & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_OVERLAY_PRESENT) ? + "yes" : "no"); + + if (!(csp_support_flags & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { + mp_err(log, "Color space %s (%d) is not supported by this swapchain!\n", + csp_name, color_space); + goto done; + } + + hr = IDXGISwapChain4_SetColorSpace1(swapchain4, color_space); + if (FAILED(hr)) { + mp_err(log, "Failed to set color space %s (%d) for this swapchain " + "(%s)!\n", + csp_name, color_space, mp_HRESULT_to_str(hr)); + goto done; + } + + mp_verbose(log, "Swapchain successfully configured to color space %s (%d)!\n", + csp_name, color_space); + + success = true; + +done: + SAFE_RELEASE(swapchain4); + return success; +} + static bool configure_created_swapchain(struct mp_log *log, IDXGISwapChain *swapchain, - DXGI_FORMAT requested_format) + DXGI_FORMAT requested_format, + DXGI_COLOR_SPACE_TYPE requested_csp, + struct mp_colorspace *configured_csp) { DXGI_FORMAT probed_format = DXGI_FORMAT_UNKNOWN; DXGI_FORMAT selected_format = DXGI_FORMAT_UNKNOWN; - DXGI_COLOR_SPACE_TYPE probed_colorspace; + DXGI_COLOR_SPACE_TYPE probed_colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + DXGI_COLOR_SPACE_TYPE selected_colorspace; const char *format_name = NULL; + const char *csp_name = NULL; + struct mp_colorspace mp_csp = { 0 }; + bool mp_csp_mapped = false; query_output_format_and_colorspace(log, swapchain, &probed_format, @@ -676,13 +790,57 @@ static bool configure_created_swapchain(struct mp_log *log, requested_format : (probed_format != DXGI_FORMAT_UNKNOWN ? probed_format : DXGI_FORMAT_R8G8B8A8_UNORM); - format_name = d3d11_get_format_name(selected_format); + selected_colorspace = requested_csp != -1 ? + requested_csp : probed_colorspace; + format_name = d3d11_get_format_name(selected_format); + csp_name = d3d11_get_csp_name(selected_colorspace); + mp_csp_mapped = d3d11_get_mp_csp(selected_colorspace, &mp_csp); mp_verbose(log, "Selected swapchain format %s (%d), attempting " "to utilize it.\n", format_name, selected_format); - return update_swapchain_format(log, swapchain, selected_format); + if (!update_swapchain_format(log, swapchain, selected_format)) { + return false; + } + + if (!IsWindows10OrGreater()) { + // On older than Windows 10, query_output_format_and_colorspace + // will not change probed_colorspace, and even if a user sets + // a colorspace it will not get applied. Thus warn user in case a + // value was specifically set and finish. + if (requested_csp != -1) { + mp_warn(log, "User selected a D3D11 color space %s (%d), " + "but configuration of color spaces is only supported" + "from Windows 10! The default configuration has been " + "left as-is.\n", + csp_name, selected_colorspace); + } + + return true; + } + + if (!mp_csp_mapped) { + mp_warn(log, "Color space %s (%d) does not have an mpv color space " + "mapping! Overriding to standard sRGB!\n", + csp_name, selected_colorspace); + selected_colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + d3d11_get_mp_csp(selected_colorspace, &mp_csp); + } + + mp_verbose(log, "Selected swapchain color space %s (%d), attempting to " + "utilize it.\n", + csp_name, selected_colorspace); + + if (!update_swapchain_color_space(log, swapchain, selected_colorspace)) { + return false; + } + + if (configured_csp) { + *configured_csp = mp_csp; + } + + return true; } // Create a Direct3D 11 swapchain @@ -757,7 +915,9 @@ bool mp_d3d11_create_swapchain(ID3D11Device *dev, struct mp_log *log, mp_verbose(log, "Using DXGI 1.1\n"); } - configure_created_swapchain(log, swapchain, opts->format); + configure_created_swapchain(log, swapchain, opts->format, + opts->color_space, + opts->configured_csp); DXGI_SWAP_CHAIN_DESC scd = {0}; IDXGISwapChain_GetDesc(swapchain, &scd); diff --git a/video/out/gpu/d3d11_helpers.h b/video/out/gpu/d3d11_helpers.h index 5ce23c68cb..c115d330d5 100644 --- a/video/out/gpu/d3d11_helpers.h +++ b/video/out/gpu/d3d11_helpers.h @@ -78,6 +78,12 @@ struct d3d11_swapchain_opts { int width; int height; DXGI_FORMAT format; + DXGI_COLOR_SPACE_TYPE color_space; + + // mp_colorspace mapping of the configured swapchain colorspace + // shall be written into this memory location if configuration + // succeeds. Will be ignored if NULL. + struct mp_colorspace *configured_csp; // Use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL if possible bool flip;