summaryrefslogtreecommitdiffstats
path: root/src/d3d11/swapchain.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/d3d11/swapchain.c')
-rw-r--r--src/d3d11/swapchain.c667
1 files changed, 667 insertions, 0 deletions
diff --git a/src/d3d11/swapchain.c b/src/d3d11/swapchain.c
new file mode 100644
index 0000000..8a53632
--- /dev/null
+++ b/src/d3d11/swapchain.c
@@ -0,0 +1,667 @@
+/*
+ * This file is part of libplacebo.
+ *
+ * libplacebo 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.
+ *
+ * libplacebo 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 libplacebo. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <windows.h>
+#include <versionhelpers.h>
+#include <math.h>
+
+#include "gpu.h"
+#include "swapchain.h"
+#include "utils.h"
+
+struct d3d11_csp_mapping {
+ DXGI_COLOR_SPACE_TYPE d3d11_csp;
+ DXGI_FORMAT d3d11_fmt;
+ struct pl_color_space out_csp;
+};
+
+static struct d3d11_csp_mapping map_pl_csp_to_d3d11(const struct pl_color_space *hint,
+ bool use_8bit_sdr)
+{
+ if (pl_color_space_is_hdr(hint) &&
+ hint->transfer != PL_COLOR_TRC_LINEAR)
+ {
+ struct pl_color_space pl_csp = pl_color_space_hdr10;
+ pl_csp.hdr = (struct pl_hdr_metadata) {
+ // Whitelist only values that we support signalling metadata for
+ .prim = hint->hdr.prim,
+ .min_luma = hint->hdr.min_luma,
+ .max_luma = hint->hdr.max_luma,
+ .max_cll = hint->hdr.max_cll,
+ .max_fall = hint->hdr.max_fall,
+ };
+
+ return (struct d3d11_csp_mapping){
+ .d3d11_csp = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020,
+ .d3d11_fmt = DXGI_FORMAT_R10G10B10A2_UNORM,
+ .out_csp = pl_csp,
+ };
+ } else if (pl_color_primaries_is_wide_gamut(hint->primaries) ||
+ hint->transfer == PL_COLOR_TRC_LINEAR)
+ {
+ // scRGB a la VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT,
+ // so could be utilized for HDR/wide gamut content as well
+ // with content that goes beyond 0.0-1.0.
+ return (struct d3d11_csp_mapping){
+ .d3d11_csp = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709,
+ .d3d11_fmt = DXGI_FORMAT_R16G16B16A16_FLOAT,
+ .out_csp = {
+ .primaries = PL_COLOR_PRIM_BT_709,
+ .transfer = PL_COLOR_TRC_LINEAR,
+ }
+ };
+ }
+
+ return (struct d3d11_csp_mapping){
+ .d3d11_csp = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709,
+ .d3d11_fmt = use_8bit_sdr ? DXGI_FORMAT_R8G8B8A8_UNORM :
+ DXGI_FORMAT_R10G10B10A2_UNORM,
+ .out_csp = pl_color_space_monitor,
+ };
+}
+
+struct priv {
+ struct pl_sw_fns impl;
+
+ struct d3d11_ctx *ctx;
+ IDXGISwapChain *swapchain;
+ pl_tex backbuffer;
+
+ // Currently requested or applied swap chain configuration.
+ // Affected by received colorspace hints.
+ struct d3d11_csp_mapping csp_map;
+
+ // Whether a swapchain backbuffer format reconfiguration has been
+ // requested by means of an additional resize action.
+ bool update_swapchain_format;
+
+ // Whether 10-bit backbuffer format is disabled for SDR content.
+ bool disable_10bit_sdr;
+
+ // Fallback to 8-bit RGB was triggered due to lack of compatiblity
+ bool fallback_8bit_rgb;
+};
+
+static void d3d11_sw_destroy(pl_swapchain sw)
+{
+ struct priv *p = PL_PRIV(sw);
+
+ pl_tex_destroy(sw->gpu, &p->backbuffer);
+ SAFE_RELEASE(p->swapchain);
+ pl_free((void *) sw);
+}
+
+static int d3d11_sw_latency(pl_swapchain sw)
+{
+ struct priv *p = PL_PRIV(sw);
+ struct d3d11_ctx *ctx = p->ctx;
+
+ UINT max_latency;
+ IDXGIDevice1_GetMaximumFrameLatency(ctx->dxgi_dev, &max_latency);
+ return max_latency;
+}
+
+static pl_tex get_backbuffer(pl_swapchain sw)
+{
+ struct priv *p = PL_PRIV(sw);
+ struct d3d11_ctx *ctx = p->ctx;
+ ID3D11Texture2D *backbuffer = NULL;
+ pl_tex tex = NULL;
+
+ D3D(IDXGISwapChain_GetBuffer(p->swapchain, 0, &IID_ID3D11Texture2D,
+ (void **) &backbuffer));
+
+ tex = pl_d3d11_wrap(sw->gpu, pl_d3d11_wrap_params(
+ .tex = (ID3D11Resource *) backbuffer,
+ ));
+
+error:
+ SAFE_RELEASE(backbuffer);
+ return tex;
+}
+
+static bool d3d11_sw_resize(pl_swapchain sw, int *width, int *height)
+{
+ struct priv *p = PL_PRIV(sw);
+ struct d3d11_ctx *ctx = p->ctx;
+
+ DXGI_SWAP_CHAIN_DESC desc = {0};
+ IDXGISwapChain_GetDesc(p->swapchain, &desc);
+ int w = PL_DEF(*width, desc.BufferDesc.Width);
+ int h = PL_DEF(*height, desc.BufferDesc.Height);
+ bool format_changed = p->csp_map.d3d11_fmt != desc.BufferDesc.Format;
+ if (format_changed) {
+ PL_INFO(ctx, "Attempting to reconfigure swap chain format: %s -> %s",
+ pl_get_dxgi_format_name(desc.BufferDesc.Format),
+ pl_get_dxgi_format_name(p->csp_map.d3d11_fmt));
+ }
+
+ if (w != desc.BufferDesc.Width || h != desc.BufferDesc.Height ||
+ format_changed)
+ {
+ if (p->backbuffer) {
+ PL_ERR(sw, "Tried resizing the swapchain while a frame was in "
+ "progress! Please submit the current frame first.");
+ return false;
+ }
+
+ HRESULT hr = IDXGISwapChain_ResizeBuffers(p->swapchain, 0, w, h,
+ p->csp_map.d3d11_fmt, desc.Flags);
+
+ if (hr == E_INVALIDARG && p->csp_map.d3d11_fmt != DXGI_FORMAT_R8G8B8A8_UNORM)
+ {
+ PL_WARN(sw, "Reconfiguring the swapchain failed, re-trying with R8G8B8A8_UNORM fallback.");
+ D3D(IDXGISwapChain_ResizeBuffers(p->swapchain, 0, w, h,
+ DXGI_FORMAT_R8G8B8A8_UNORM, desc.Flags));
+
+ // re-configure the colorspace to 8-bit RGB SDR fallback
+ p->csp_map = map_pl_csp_to_d3d11(&pl_color_space_unknown, true);
+ p->fallback_8bit_rgb = true;
+ }
+ else if (FAILED(hr))
+ {
+ PL_ERR(sw, "Reconfiguring the swapchain failed with error: %s", pl_hresult_to_str(hr));
+ return false;
+ }
+ }
+
+ *width = w;
+ *height = h;
+ p->update_swapchain_format = false;
+ return true;
+
+error:
+ return false;
+}
+
+static bool d3d11_sw_start_frame(pl_swapchain sw,
+ struct pl_swapchain_frame *out_frame)
+{
+ struct priv *p = PL_PRIV(sw);
+ struct d3d11_ctx *ctx = p->ctx;
+
+ if (ctx->is_failed)
+ return false;
+ if (p->backbuffer) {
+ PL_ERR(sw, "Attempted calling `pl_swapchain_start_frame` while a frame "
+ "was already in progress! Call `pl_swapchain_submit_frame` first.");
+ return false;
+ }
+
+ if (p->update_swapchain_format) {
+ int w = 0, h = 0;
+ if (!d3d11_sw_resize(sw, &w, &h))
+ return false;
+ }
+
+ p->backbuffer = get_backbuffer(sw);
+ if (!p->backbuffer)
+ return false;
+
+ int bits = 0;
+ pl_fmt fmt = p->backbuffer->params.format;
+ for (int i = 0; i < fmt->num_components; i++)
+ bits = PL_MAX(bits, fmt->component_depth[i]);
+
+ *out_frame = (struct pl_swapchain_frame) {
+ .fbo = p->backbuffer,
+ .flipped = false,
+ .color_repr = {
+ .sys = PL_COLOR_SYSTEM_RGB,
+ .levels = PL_COLOR_LEVELS_FULL,
+ .alpha = PL_ALPHA_UNKNOWN,
+ .bits = {
+ .sample_depth = bits,
+ .color_depth = bits,
+ },
+ },
+ .color_space = p->csp_map.out_csp,
+ };
+
+ return true;
+}
+
+static bool d3d11_sw_submit_frame(pl_swapchain sw)
+{
+ struct priv *p = PL_PRIV(sw);
+ struct d3d11_ctx *ctx = p->ctx;
+
+ // Release the backbuffer. We shouldn't hold onto it unnecessarily, because
+ // it prevents external code from resizing the swapchain, which we'd
+ // otherwise support just fine.
+ pl_tex_destroy(sw->gpu, &p->backbuffer);
+
+ return !ctx->is_failed;
+}
+
+static void d3d11_sw_swap_buffers(pl_swapchain sw)
+{
+ struct priv *p = PL_PRIV(sw);
+ struct d3d11_ctx *ctx = p->ctx;
+
+ // Present can fail with a device removed error
+ D3D(IDXGISwapChain_Present(p->swapchain, 1, 0));
+
+error:
+ return;
+}
+
+static DXGI_HDR_METADATA_HDR10 set_hdr10_metadata(const struct pl_hdr_metadata *hdr)
+{
+ return (DXGI_HDR_METADATA_HDR10) {
+ .RedPrimary = { roundf(hdr->prim.red.x * 50000),
+ roundf(hdr->prim.red.y * 50000) },
+ .GreenPrimary = { roundf(hdr->prim.green.x * 50000),
+ roundf(hdr->prim.green.y * 50000) },
+ .BluePrimary = { roundf(hdr->prim.blue.x * 50000),
+ roundf(hdr->prim.blue.y * 50000) },
+ .WhitePoint = { roundf(hdr->prim.white.x * 50000),
+ roundf(hdr->prim.white.y * 50000) },
+ .MaxMasteringLuminance = roundf(hdr->max_luma),
+ .MinMasteringLuminance = roundf(hdr->min_luma * 10000),
+ .MaxContentLightLevel = roundf(hdr->max_cll),
+ .MaxFrameAverageLightLevel = roundf(hdr->max_fall),
+ };
+}
+
+static bool set_swapchain_metadata(struct d3d11_ctx *ctx,
+ IDXGISwapChain3 *swapchain3,
+ struct d3d11_csp_mapping *csp_map)
+{
+ IDXGISwapChain4 *swapchain4 = NULL;
+ bool ret = false;
+ bool is_hdr = pl_color_space_is_hdr(&csp_map->out_csp);
+ DXGI_HDR_METADATA_HDR10 hdr10 = is_hdr ?
+ set_hdr10_metadata(&csp_map->out_csp.hdr) : (DXGI_HDR_METADATA_HDR10){ 0 };
+
+ D3D(IDXGISwapChain3_SetColorSpace1(swapchain3, csp_map->d3d11_csp));
+
+ // if we succeeded to set the color space, it's good enough,
+ // since older versions of Windows 10 will not have swapchain v4 available.
+ ret = true;
+
+ if (FAILED(IDXGISwapChain3_QueryInterface(swapchain3, &IID_IDXGISwapChain4,
+ (void **)&swapchain4)))
+ {
+ PL_TRACE(ctx, "v4 swap chain interface is not available, skipping HDR10 "
+ "metadata configuration.");
+ goto error;
+ }
+
+ D3D(IDXGISwapChain4_SetHDRMetaData(swapchain4,
+ is_hdr ?
+ DXGI_HDR_METADATA_TYPE_HDR10 :
+ DXGI_HDR_METADATA_TYPE_NONE,
+ is_hdr ? sizeof(hdr10) : 0,
+ is_hdr ? &hdr10 : NULL));
+
+ goto success;
+
+error:
+ csp_map->out_csp.hdr = (struct pl_hdr_metadata) { 0 };
+success:
+ SAFE_RELEASE(swapchain4);
+ return ret;
+}
+
+static bool d3d11_format_supported(struct d3d11_ctx *ctx, DXGI_FORMAT fmt)
+{
+ UINT sup = 0;
+ UINT wanted_sup =
+ D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY |
+ D3D11_FORMAT_SUPPORT_SHADER_SAMPLE | D3D11_FORMAT_SUPPORT_RENDER_TARGET |
+ D3D11_FORMAT_SUPPORT_BLENDABLE;
+
+ D3D(ID3D11Device_CheckFormatSupport(ctx->dev, fmt, &sup));
+
+ return (sup & wanted_sup) == wanted_sup;
+
+error:
+ return false;
+}
+
+static bool d3d11_csp_supported(struct d3d11_ctx *ctx,
+ IDXGISwapChain3 *swapchain3,
+ DXGI_COLOR_SPACE_TYPE color_space)
+{
+ UINT csp_support_flags = 0;
+
+ D3D(IDXGISwapChain3_CheckColorSpaceSupport(swapchain3,
+ color_space,
+ &csp_support_flags));
+
+ return (csp_support_flags & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
+
+error:
+ return false;
+}
+
+static void update_swapchain_color_config(pl_swapchain sw,
+ const struct pl_color_space *csp,
+ bool is_internal)
+{
+ struct priv *p = PL_PRIV(sw);
+ struct d3d11_ctx *ctx = p->ctx;
+ IDXGISwapChain3 *swapchain3 = NULL;
+ struct d3d11_csp_mapping old_map = p->csp_map;
+
+ // ignore config changes in fallback mode
+ if (p->fallback_8bit_rgb)
+ goto cleanup;
+
+ HRESULT hr = IDXGISwapChain_QueryInterface(p->swapchain, &IID_IDXGISwapChain3,
+ (void **)&swapchain3);
+ if (FAILED(hr)) {
+ PL_TRACE(ctx, "v3 swap chain interface is not available, skipping "
+ "color space configuration.");
+ swapchain3 = NULL;
+ }
+
+ // Lack of swap chain v3 means we cannot control swap chain color space;
+ // Only effective formats are the 8 and 10 bit RGB ones.
+ struct d3d11_csp_mapping csp_map =
+ map_pl_csp_to_d3d11(swapchain3 ? csp : &pl_color_space_unknown,
+ p->disable_10bit_sdr);
+
+ if (p->csp_map.d3d11_fmt == csp_map.d3d11_fmt &&
+ p->csp_map.d3d11_csp == csp_map.d3d11_csp &&
+ pl_color_space_equal(&p->csp_map.out_csp, &csp_map.out_csp))
+ goto cleanup;
+
+ PL_INFO(ctx, "%s swap chain configuration%s: format: %s, color space: %s.",
+ is_internal ? "Initial" : "New",
+ is_internal ? "" : " received from hint",
+ pl_get_dxgi_format_name(csp_map.d3d11_fmt),
+ pl_get_dxgi_csp_name(csp_map.d3d11_csp));
+
+ bool fmt_supported = d3d11_format_supported(ctx, csp_map.d3d11_fmt);
+ bool csp_supported = swapchain3 ?
+ d3d11_csp_supported(ctx, swapchain3, csp_map.d3d11_csp) : true;
+ if (!fmt_supported || !csp_supported) {
+ PL_ERR(ctx, "New swap chain configuration was deemed not supported: "
+ "format: %s, color space: %s. Failling back to 8bit RGB.",
+ fmt_supported ? "supported" : "unsupported",
+ csp_supported ? "supported" : "unsupported");
+ // fall back to 8bit sRGB if requested configuration is not supported
+ csp_map = map_pl_csp_to_d3d11(&pl_color_space_unknown, true);
+ }
+
+ p->csp_map = csp_map;
+ p->update_swapchain_format = true;
+
+ if (!swapchain3)
+ goto cleanup;
+
+ if (!set_swapchain_metadata(ctx, swapchain3, &p->csp_map)) {
+ // format succeeded, but color space configuration failed
+ p->csp_map = old_map;
+ p->csp_map.d3d11_fmt = csp_map.d3d11_fmt;
+ }
+
+ pl_d3d11_flush_message_queue(ctx, "After colorspace hint");
+
+cleanup:
+ SAFE_RELEASE(swapchain3);
+}
+
+static void d3d11_sw_colorspace_hint(pl_swapchain sw,
+ const struct pl_color_space *csp)
+{
+ update_swapchain_color_config(sw, csp, false);
+}
+
+IDXGISwapChain *pl_d3d11_swapchain_unwrap(pl_swapchain sw)
+{
+ struct priv *p = PL_PRIV(sw);
+ IDXGISwapChain_AddRef(p->swapchain);
+ return p->swapchain;
+}
+
+static const struct pl_sw_fns d3d11_swapchain = {
+ .destroy = d3d11_sw_destroy,
+ .latency = d3d11_sw_latency,
+ .resize = d3d11_sw_resize,
+ .colorspace_hint = d3d11_sw_colorspace_hint,
+ .start_frame = d3d11_sw_start_frame,
+ .submit_frame = d3d11_sw_submit_frame,
+ .swap_buffers = d3d11_sw_swap_buffers,
+};
+
+static HRESULT create_swapchain_1_2(struct d3d11_ctx *ctx,
+ IDXGIFactory2 *factory, const struct pl_d3d11_swapchain_params *params,
+ bool flip, UINT width, UINT height, DXGI_FORMAT format,
+ IDXGISwapChain **swapchain_out)
+{
+ IDXGISwapChain *swapchain = NULL;
+ IDXGISwapChain1 *swapchain1 = NULL;
+ HRESULT hr;
+
+ DXGI_SWAP_CHAIN_DESC1 desc = {
+ .Width = width,
+ .Height = height,
+ .Format = format,
+ .SampleDesc.Count = 1,
+ .BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT,
+ .Flags = params->flags,
+ };
+
+ if (ID3D11Device_GetFeatureLevel(ctx->dev) >= D3D_FEATURE_LEVEL_11_0)
+ desc.BufferUsage |= DXGI_USAGE_UNORDERED_ACCESS;
+
+ if (flip) {
+ UINT max_latency;
+ IDXGIDevice1_GetMaximumFrameLatency(ctx->dxgi_dev, &max_latency);
+
+ // Make sure we have at least enough buffers to allow `max_latency`
+ // frames in-flight at once, plus one frame for the frontbuffer
+ desc.BufferCount = max_latency + 1;
+
+ if (IsWindows10OrGreater()) {
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ } else {
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ }
+
+ desc.BufferCount = PL_MIN(desc.BufferCount, DXGI_MAX_SWAP_CHAIN_BUFFERS);
+ } else {
+ desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+ desc.BufferCount = 1;
+ }
+
+ if (params->window) {
+ hr = IDXGIFactory2_CreateSwapChainForHwnd(factory, (IUnknown *) ctx->dev,
+ params->window, &desc, NULL, NULL, &swapchain1);
+ } else if (params->core_window) {
+ hr = IDXGIFactory2_CreateSwapChainForCoreWindow(factory,
+ (IUnknown *) ctx->dev, params->core_window, &desc, NULL, &swapchain1);
+ } else {
+ hr = IDXGIFactory2_CreateSwapChainForComposition(factory,
+ (IUnknown *) ctx->dev, &desc, 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(struct d3d11_ctx *ctx,
+ IDXGIFactory1 *factory, const struct pl_d3d11_swapchain_params *params,
+ UINT width, UINT height, DXGI_FORMAT format, IDXGISwapChain **swapchain_out)
+{
+ DXGI_SWAP_CHAIN_DESC desc = {
+ .BufferDesc = {
+ .Width = width,
+ .Height = height,
+ .Format = format,
+ },
+ .SampleDesc.Count = 1,
+ .BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT,
+ .BufferCount = 1,
+ .OutputWindow = params->window,
+ .Windowed = TRUE,
+ .SwapEffect = DXGI_SWAP_EFFECT_DISCARD,
+ .Flags = params->flags,
+ };
+
+ return IDXGIFactory1_CreateSwapChain(factory, (IUnknown *) ctx->dev, &desc,
+ swapchain_out);
+}
+
+static IDXGISwapChain *create_swapchain(struct d3d11_ctx *ctx,
+ const struct pl_d3d11_swapchain_params *params, DXGI_FORMAT format)
+{
+ IDXGIDevice1 *dxgi_dev = NULL;
+ IDXGIAdapter1 *adapter = NULL;
+ IDXGIFactory1 *factory = NULL;
+ IDXGIFactory2 *factory2 = NULL;
+ IDXGISwapChain *swapchain = NULL;
+ bool success = false;
+ HRESULT hr;
+
+ D3D(ID3D11Device_QueryInterface(ctx->dev, &IID_IDXGIDevice1,
+ (void **) &dxgi_dev));
+ D3D(IDXGIDevice1_GetParent(dxgi_dev, &IID_IDXGIAdapter1, (void **) &adapter));
+ D3D(IDXGIAdapter1_GetParent(adapter, &IID_IDXGIFactory1, (void **) &factory));
+
+ hr = IDXGIFactory1_QueryInterface(factory, &IID_IDXGIFactory2,
+ (void **) &factory2);
+ if (FAILED(hr))
+ factory2 = NULL;
+
+ bool flip = factory2 && !params->blit;
+ UINT width = PL_DEF(params->width, 1);
+ UINT height = PL_DEF(params->height, 1);
+
+ // If both width and height are unset, the default size is the window size
+ if (params->window && params->width == 0 && params->height == 0) {
+ RECT rc;
+ if (GetClientRect(params->window, &rc)) {
+ width = PL_DEF(rc.right - rc.left, 1);
+ height = PL_DEF(rc.bottom - rc.top, 1);
+ }
+ }
+
+ // Return here to retry creating the swapchain
+ do {
+ if (factory2) {
+ // Create a DXGI 1.2+ (Windows 8+) swap chain if possible
+ hr = create_swapchain_1_2(ctx, factory2, params, flip, width,
+ height, format, &swapchain);
+ } else {
+ // Fall back to DXGI 1.1 (Windows 7)
+ hr = create_swapchain_1_1(ctx, factory, params, width, height,
+ format, &swapchain);
+ }
+ if (SUCCEEDED(hr))
+ break;
+
+ pl_d3d11_after_error(ctx, hr);
+ if (flip) {
+ PL_DEBUG(ctx, "Failed to create flip-model swapchain, trying bitblt");
+ flip = false;
+ continue;
+ }
+
+ PL_FATAL(ctx, "Failed to create swapchain: %s", pl_hresult_to_str(hr));
+ goto error;
+ } while (true);
+
+ // Prevent DXGI from making changes to the window, otherwise it will hook
+ // the Alt+Enter keystroke and make it trigger an ugly transition to
+ // legacy exclusive fullscreen mode.
+ IDXGIFactory_MakeWindowAssociation(factory, params->window,
+ DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER |
+ DXGI_MWA_NO_PRINT_SCREEN);
+
+ success = true;
+error:
+ if (!success)
+ SAFE_RELEASE(swapchain);
+ SAFE_RELEASE(factory2);
+ SAFE_RELEASE(factory);
+ SAFE_RELEASE(adapter);
+ SAFE_RELEASE(dxgi_dev);
+ return swapchain;
+}
+
+pl_swapchain pl_d3d11_create_swapchain(pl_d3d11 d3d11,
+ const struct pl_d3d11_swapchain_params *params)
+{
+ struct d3d11_ctx *ctx = PL_PRIV(d3d11);
+ pl_gpu gpu = d3d11->gpu;
+ bool success = false;
+
+ struct pl_swapchain_t *sw = pl_zalloc_obj(NULL, sw, struct priv);
+ struct priv *p = PL_PRIV(sw);
+ *sw = (struct pl_swapchain_t) {
+ .log = gpu->log,
+ .gpu = gpu,
+ };
+ *p = (struct priv) {
+ .impl = d3d11_swapchain,
+ .ctx = ctx,
+ // default to standard 8 or 10 bit RGB, unset pl_color_space
+ .csp_map = {
+ .d3d11_fmt = params->disable_10bit_sdr ?
+ DXGI_FORMAT_R8G8B8A8_UNORM :
+ (d3d11_format_supported(ctx, DXGI_FORMAT_R10G10B10A2_UNORM) ?
+ DXGI_FORMAT_R10G10B10A2_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM),
+ },
+ .disable_10bit_sdr = params->disable_10bit_sdr,
+ };
+
+ if (params->swapchain) {
+ p->swapchain = params->swapchain;
+ IDXGISwapChain_AddRef(params->swapchain);
+ } else {
+ p->swapchain = create_swapchain(ctx, params, p->csp_map.d3d11_fmt);
+ if (!p->swapchain)
+ goto error;
+ }
+
+ DXGI_SWAP_CHAIN_DESC scd = {0};
+ IDXGISwapChain_GetDesc(p->swapchain, &scd);
+ if (scd.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL ||
+ scd.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) {
+ PL_INFO(gpu, "Using flip-model presentation");
+ } else {
+ PL_INFO(gpu, "Using bitblt-model presentation");
+ }
+
+ p->csp_map.d3d11_fmt = scd.BufferDesc.Format;
+
+ update_swapchain_color_config(sw, &pl_color_space_unknown, true);
+
+ success = true;
+error:
+ if (!success) {
+ PL_FATAL(gpu, "Failed to create Direct3D 11 swapchain");
+ d3d11_sw_destroy(sw);
+ sw = NULL;
+ }
+ return sw;
+}