Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  direct3d11.cpp   Sprache: C

 

/*****************************************************************************
 * direct3d11.cpp: Windows Direct3D11 video output module
 *****************************************************************************
 * Copyright (C) 2014-2021 VLC authors and VideoLAN
 *
 * Authors: Martell Malone <martellmalone@gmail.com>
 *          Steve Lhomme <robux4@gmail.com>
 *          Alexandre Janniaux <ajanni@videolabs.io>
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout_display.h>

#include <vlc/libvlc.h>
#include <vlc/libvlc_picture.h>
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_renderer_discoverer.h>
#include <vlc/libvlc_media_player.h>

#include <cassert>
#include <math.h>
#include <new>

#include "../../video_chroma/d3d11_fmt.h"
#include "../../hw/nvdec/nvdec_fmt.h"

#include "d3d11_quad.h"
#include "d3d11_shaders.h"
#include "d3d11_scaler.h"
#include "d3d11_tonemap.h"
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#include "d3d11_swapchain.h"
#include "common.h"
#endif

#include "../../video_chroma/copy.h"

#include <dxgi1_6.h>

using Microsoft::WRL::ComPtr;

static int  OpenDisplay(vout_display_t *,
                 video_format_t *, vlc_video_context *);
static int  OpenDrawable(vout_display_t *,
                 video_format_t *, vlc_video_context *);
static void Close(vout_display_t *);

#define D3D11_HELP N_("Recommended video output for Windows 8 and later versions")
#define HW_BLENDING_TEXT N_("Use hardware blending support")
#define HW_BLENDING_LONGTEXT N_(\
    "Try to use hardware acceleration for subtitle/OSD blending.")

#define UPSCALE_MODE_TEXT N_("Video Upscaling Mode")
#define UPSCALE_MODE_LONGTEXT N_("Select the upscaling mode for video.")

static const char *const ppsz_upscale_mode[] = {
    "linear", "point", "processor", "super" };
static const char *const ppsz_upscale_mode_text[] = {
    N_("Linear Sampler"), N_("Point Sampler"), N_("Video Processor"), N_("Super Resolution") };

#define HDR_MODE_TEXT N_("HDR Output Mode")
#define HDR_MODE_LONGTEXT N_("Use HDR output even if the source is SDR.")

static const char *const ppsz_hdr_mode[] = {
    "auto", "never", "always", "generate" };
static const char *const ppsz_hdr_mode_text[] = {
    N_("Auto"), N_("Never out HDR"), N_("Always output HDR"), N_("Generate HDR from SDR") };

vlc_module_begin ()
    set_shortname("Direct3D11")
    set_description(N_("Direct3D11 video output"))
    set_help(D3D11_HELP)
    set_subcategory(SUBCAT_VIDEO_VOUT)

    add_bool("direct3d11-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT)

    add_string("d3d11-upscale-mode", "linear", UPSCALE_MODE_TEXT, UPSCALE_MODE_LONGTEXT)
        change_string_list(ppsz_upscale_mode, ppsz_upscale_mode_text)

    add_string("d3d11-hdr-mode", "auto", HDR_MODE_TEXT, HDR_MODE_LONGTEXT)
        change_string_list(ppsz_hdr_mode, ppsz_hdr_mode_text)

    add_shortcut("direct3d11")
    set_callback_display(OpenDisplay, 300)

    add_submodule()
        set_callback_display(OpenDrawable, 0)
        add_shortcut("d3d11drawable")

vlc_module_end ()

enum d3d11_upscale
{
    upscale_LinearSampler,
    upscale_PointSampler,
    upscale_VideoProcessor,
    upscale_SuperResolution,
};

enum d3d11_hdr
{
    hdr_Auto,
    hdr_Never,
    hdr_Always,
    hdr_Fake,
};

typedef struct vout_display_sys_t
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    struct event_thread_t    *video_wnd = nullptr;
#endif
    bool                     place_changed = false;

    int                      log_level = 1;

    display_info_t           display = {};

    d3d11_device_t           *d3d_dev = NULL;
    d3d11_decoder_device_t   *local_d3d_dev = NULL; // when opened without a video context
    d3d11_quad_t             picQuad = {};

    d3d11_gpu_fence          fence = {};

    bool                     use_staging_texture = false;
    picture_sys_d3d11_t      stagingSys = {};
    plane_t                  stagingPlanes[PICTURE_PLANE_MAX];

    // NV12/P010 to RGB for D3D11 < 11.1
    struct {
        ComPtr<ID3D11VideoDevice>               d3dviddev;
        ComPtr<ID3D11VideoContext>              d3dvidctx;
        ComPtr<ID3D11VideoProcessorEnumerator>  enumerator;
        ComPtr<ID3D11VideoProcessor>            processor;
        ComPtr<ID3D11VideoProcessorOutputView>  outputView;
    } old_feature;

    video_projection_mode_t  projection_mode;
    d3d11_vertex_shader_t    projectionVShader = {};
    d3d11_vertex_shader_t    flatVShader = {};

    // SPU
    vlc_fourcc_t             pSubpictureChromas[2] = {};
    d3d11_quad_t             regionQuad = {};
    int                      d3dregion_count = 0;
    picture_t                **d3dregions = nullptr;

    /* outside rendering */
    void *outside_opaque = nullptr;
    libvlc_video_update_output_cb            updateOutputCb = nullptr;
    libvlc_video_swap_cb                     swapCb = nullptr;
    libvlc_video_makeCurrent_cb              startEndRenderingCb = nullptr;
    libvlc_video_frameMetadata_cb            sendMetadataCb = nullptr;
    libvlc_video_output_select_plane_cb      selectPlaneCb = nullptr;

    // upscaling
    enum d3d11_upscale       upscaleMode = upscale_LinearSampler;
    d3d11_scaler             *scaleProc = nullptr;
    vout_display_place_t     scalePlace;

    // HDR mode
    enum d3d11_hdr           hdrMode = hdr_Auto;
    d3d11_tonemapper         *tonemapProc = nullptr;
} vout_display_sys_t;

static void Prepare(vout_display_t *, picture_t *, const vlc_render_subpicture *, vlc_tick_t);
static void Display(vout_display_t *, picture_t *);

static int  Direct3D11Open (vout_display_t *, video_format_t *, vlc_video_context *);
static void Direct3D11Close(vout_display_t *);

static int SetupOutputFormat(vout_display_t *, video_format_t *, vlc_video_context *, video_format_t *quad);
static int  Direct3D11CreateFormatResources (vout_display_t *, const video_format_t *);
static int  Direct3D11CreateGenericResources(vout_display_t *);
static void Direct3D11DestroyResources(vout_display_t *);

static void Direct3D11DeleteRegions(int, picture_t **);
static int Direct3D11MapSubpicture(vout_display_t *, int *, picture_t ***, const vlc_render_subpicture *);

static int UpdateSource(vout_display_t *, bool force_placement);
static int SetDisplaySize(vout_display_t *, unsigned width, unsigned height);

static int UpdateDisplayFormat(vout_display_t *vd, const video_format_t *fmt)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    libvlc_video_render_cfg_t cfg;

    cfg.width  = vd->cfg->display.width;
    cfg.height = vd->cfg->display.height;

    if (sys->hdrMode == hdr_Always || sys->hdrMode == hdr_Fake)
    {
        // force a fake HDR source
        // corresponds to DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
        cfg.bitdepth = 10;
        cfg.full_range = true;
        cfg.primaries  = libvlc_video_primaries_BT2020;
        cfg.colorspace = libvlc_video_colorspace_BT2020;
        cfg.transfer   = libvlc_video_transfer_func_PQ;
    }
    else if (sys->hdrMode == hdr_Never)
    {
        // corresponds to DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
        cfg.bitdepth = 8;
        cfg.full_range = true;
        cfg.primaries  = libvlc_video_primaries_BT709;
        cfg.colorspace = libvlc_video_colorspace_BT709;
        cfg.transfer   = libvlc_video_transfer_func_BT709;
    }
    else
    {
    switch (fmt->i_chroma)
    {
    case VLC_CODEC_D3D11_OPAQUE:
    case VLC_CODEC_D3D11_OPAQUE_ALPHA:
        cfg.bitdepth = 8;
        break;
    case VLC_CODEC_D3D11_OPAQUE_RGBA:
    case VLC_CODEC_D3D11_OPAQUE_BGRA:
        cfg.bitdepth = 8;
        break;
    case VLC_CODEC_RGBA10LE:
    case VLC_CODEC_D3D11_OPAQUE_10B:
        cfg.bitdepth = 10;
        break;
    default:
        {
            const auto *p_format = vlc_fourcc_GetChromaDescription(fmt->i_chroma);
            if (p_format == NULL)
            {
                cfg.bitdepth = 8;
            }
            else
            {
                cfg.bitdepth = p_format->pixel_bits == 0 ? 8 : p_format->pixel_bits /
                                                               (p_format->plane_count==1 ? p_format->pixel_size : 1);
            }
        }
        break;
    }
    cfg.full_range = sys->picQuad.quad_fmt.color_range == COLOR_RANGE_FULL ||
                     /* the YUV->RGB conversion already output full range */
                     is_d3d11_opaque(sys->picQuad.quad_fmt.i_chroma) ||
                     vlc_fourcc_IsYUV(sys->picQuad.quad_fmt.i_chroma);
    cfg.primaries  = (libvlc_video_color_primaries_t) sys->picQuad.quad_fmt.primaries;
    cfg.colorspace = (libvlc_video_color_space_t)     sys->picQuad.quad_fmt.space;
    cfg.transfer   = (libvlc_video_transfer_func_t)   sys->picQuad.quad_fmt.transfer;
    }

    libvlc_video_output_cfg_t out;
    if (!sys->updateOutputCb( sys->outside_opaque, &cfg, &out ))
    {
        msg_Err(vd, "Failed to set format %dx%d %d bits on output", cfg.width, cfg.height, cfg.bitdepth);
        return VLC_EGENERIC;
    }

    if (sys->upscaleMode == upscale_VideoProcessor || sys->upscaleMode == upscale_SuperResolution)
    {
        D3D11_UpscalerUpdate(VLC_OBJECT(vd), sys->scaleProc, sys->d3d_dev,
                             vd->source, &sys->picQuad.quad_fmt,
                             vd->cfg->display.width, vd->cfg->display.height,
                             vd->place);

        if (D3D11_UpscalerUsed(sys->scaleProc))
        {
            D3D11_UpscalerGetSize(sys->scaleProc, &sys->picQuad.quad_fmt.i_width, &sys->picQuad.quad_fmt.i_height);

            sys->picQuad.quad_fmt.i_x_offset       = 0;
            sys->picQuad.quad_fmt.i_y_offset       = 0;
            sys->picQuad.quad_fmt.i_visible_width  = sys->picQuad.quad_fmt.i_width;
            sys->picQuad.quad_fmt.i_visible_height = sys->picQuad.quad_fmt.i_height;

            sys->picQuad.generic.i_width = sys->picQuad.quad_fmt.i_width;
            sys->picQuad.generic.i_height = sys->picQuad.quad_fmt.i_height;

            vout_display_place_t before_place = sys->scalePlace;
            sys->scalePlace.x = 0;
            sys->scalePlace.y = 0;
            sys->scalePlace.width = sys->picQuad.quad_fmt.i_width;
            sys->scalePlace.height = sys->picQuad.quad_fmt.i_height;
            sys->place_changed |= !vout_display_PlaceEquals(&before_place, &sys->scalePlace);
        }
    }

    display_info_t new_display = { };

    new_display.pixelFormat = D3D11_RenderFormat((DXGI_FORMAT)out.u.dxgi_format, DXGI_FORMAT_UNKNOWN, false);
    if (unlikely(new_display.pixelFormat == NULL))
    {
        msg_Err(vd, "Could not find the output format.");
        return VLC_EGENERIC;
    }

    new_display.color     = (video_color_space_t)     out.colorspace;
    new_display.transfer  = (video_transfer_func_t)   out.transfer;
    new_display.primaries = (video_color_primaries_t) out.primaries;
    new_display.b_full_range = out.full_range;
    new_display.orientation = (video_orientation_t) out.orientation;

    /* guestimate the display peak luminance */
    switch (new_display.transfer)
    {
    case TRANSFER_FUNC_LINEAR:
    case TRANSFER_FUNC_SRGB:
        new_display.luminance_peak = DEFAULT_SRGB_BRIGHTNESS;
        break;
    case TRANSFER_FUNC_SMPTE_ST2084:
        new_display.luminance_peak = MAX_PQ_BRIGHTNESS;
        break;
    default:
        new_display.luminance_peak = DEFAULT_SRGB_BRIGHTNESS;
        break;
    }

    if ( sys->display.pixelFormat == NULL ||
         ( sys->display.pixelFormat    != new_display.pixelFormat ||
           sys->display.luminance_peak != new_display.luminance_peak ||
           sys->display.color          != new_display.color ||
           sys->display.transfer       != new_display.transfer ||
           sys->display.primaries      != new_display.primaries ||
           sys->display.b_full_range   != new_display.b_full_range ||
           sys->display.orientation    != new_display.orientation ))
    {
        sys->display = new_display;
        /* TODO release the pixel shaders if the format changed */
        if (Direct3D11CreateFormatResources(vd, fmt)) {
            msg_Err(vd, "Failed to allocate format resources");
            return VLC_EGENERIC;
        }
    }

    return VLC_SUCCESS;
}

static void UpdateSize(vout_display_t *vd)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    msg_Dbg(vd, "Detected size change %dx%d", vd->cfg->display.width,
            vd->cfg->display.height);

    UpdateDisplayFormat(vd, vd->fmt);

    const vout_display_place_t *quad_place;
    if (sys->scaleProc && D3D11_UpscalerUsed(sys->scaleProc))
        quad_place = &sys->scalePlace;
    else
        quad_place = vd->place;
    sys->picQuad.UpdateViewport( quad_place, sys->display.pixelFormat );

    d3d11_device_lock( sys->d3d_dev );

    D3D11_UpdateQuadPosition(vd, sys->d3d_dev, &sys->picQuad,
                             video_format_GetTransform(vd->source->orientation, sys->display.orientation));

    D3D11_UpdateViewpoint( vd, sys->d3d_dev, &sys->picQuad, &vd->cfg->viewpoint,
                          (float) vd->cfg->display.width / vd->cfg->display.height );

    d3d11_device_unlock( sys->d3d_dev );

    sys->place_changed = false;

#ifndef NDEBUG
    msg_Dbg( vd, "picQuad position (%.02f,%.02f) %.02fx%.02f",
             sys->picQuad.cropViewport[0].TopLeftX, sys->picQuad.cropViewport[0].TopLeftY,
             sys->picQuad.cropViewport[0].Width, sys->picQuad.cropViewport[0].Height );
#endif
}

static int SetViewpoint(vout_display_t *vd, const vlc_viewpoint_t *viewpoint)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    if ( sys->picQuad.viewpointShaderConstant.Get() )
    {
        d3d11_device_lock( sys->d3d_dev );
        D3D11_UpdateViewpoint( vd, sys->d3d_dev, &sys->picQuad, viewpoint,
                                (float) vd->cfg->display.width / vd->cfg->display.height );
        d3d11_device_unlock( sys->d3d_dev );
    }
    return VLC_SUCCESS;
}

static int UpdateStaging(vout_display_t *vd, const video_format_t *fmt)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    if (!sys->use_staging_texture)
        return VLC_SUCCESS;

    /* we need a staging texture */
    ID3D11Texture2D *textures[DXGI_MAX_SHADER_VIEW] = {0};
    video_format_t texture_fmt = *vd->source;
    texture_fmt.i_width  = sys->picQuad.generic.i_width;
    texture_fmt.i_height = sys->picQuad.generic.i_height;
    if (!is_d3d11_opaque(fmt->i_chroma))
    {
        texture_fmt.i_chroma = sys->picQuad.generic.textureFormat->fourcc;
    }

    if (AllocateTextures(vd, sys->d3d_dev, sys->picQuad.generic.textureFormat, &texture_fmt,
                            false, textures, sys->stagingPlanes))
    {
        msg_Err(vd, "Failed to allocate the staging texture");
        return VLC_EGENERIC;
    }

    if (D3D11_AllocateResourceView(vlc_object_logger(vd), sys->d3d_dev->d3ddevice, sys->picQuad.generic.textureFormat,
                                textures, 0, sys->stagingSys.renderSrc))
    {
        msg_Err(vd, "Failed to allocate the staging shader view");
        return VLC_EGENERIC;
    }

    for (unsigned plane = 0; plane < DXGI_MAX_SHADER_VIEW; plane++)
        sys->stagingSys.texture[plane] = textures[plane];

    if (sys->old_feature.processor)
    {
        HRESULT hr;
        D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outDesc;
        outDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
        outDesc.Texture2D.MipSlice = 0;

        hr = sys->old_feature.d3dviddev->CreateVideoProcessorOutputView(
                                                                textures[0],
                                                                sys->old_feature.enumerator.Get(),
                                                                &outDesc,
                                                                sys->old_feature.outputView.ReleaseAndGetAddressOf());
        if (FAILED(hr))
        {
            msg_Dbg(vd,"Failed to create processor output. (hr=0x%lX)", hr);
            return VLC_EGENERIC;
        }
    }
    return VLC_SUCCESS;
}

static enum d3d11_hdr HdrModeFromString(vlc_logger *logger, const char *psz_hdr)
{
    if (strcmp("auto", psz_hdr) == 0)
        return hdr_Auto;
    if (strcmp("never", psz_hdr) == 0)
        return hdr_Never;
    if (strcmp("always", psz_hdr) == 0)
        return hdr_Always;
    if (strcmp("generate", psz_hdr) == 0)
        return hdr_Fake;

    vlc_warning(logger, "unknown HDR mode %s, using auto mode", psz_hdr);
    return hdr_Auto;
}

static void InitTonemapProcessor(vout_display_t *vd, const video_format_t *fmt_in)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    if (sys->hdrMode != hdr_Fake)
        return;

    { // check the main display is in HDR mode
    HRESULT hr;

    ComPtr<IDXGIAdapter> adapter;
    ComPtr<IDXGIFactory2> factory;
    ComPtr<IDXGIOutput> dxgiOutput;
    ComPtr<IDXGIOutput6> dxgiOutput6;
    DXGI_OUTPUT_DESC1 desc1;

    hr = CreateDXGIFactory1(IID_GRAPHICS_PPV_ARGS(factory.GetAddressOf()));
    if (FAILED(hr))
        goto error;
    UINT adapter_index = 0;
    hr = factory->EnumAdapters(adapter_index, adapter.GetAddressOf());
    if (FAILED(hr))
        goto error;
    UINT output_index = 0;
    hr = adapter->EnumOutputs(output_index, dxgiOutput.GetAddressOf());
    if (FAILED(hr))
        goto error;
    hr = dxgiOutput.As(&dxgiOutput6);
    if (FAILED(hr))
        goto error;
    hr = dxgiOutput6->GetDesc1(&desc1);
    if (FAILED(hr))
        goto error;
    if (desc1.ColorSpace != DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)
    {
        msg_Warn(vd, "not an HDR display");
        goto error;
    }
    }

    sys->tonemapProc = D3D11_TonemapperCreate(VLC_OBJECT(vd), sys->d3d_dev, fmt_in);
    if (sys->tonemapProc != NULL)
    {
        msg_Dbg(vd, "Using tonemapper");
        return;
    }

error:
    sys->hdrMode = hdr_Auto;
    msg_Dbg(vd, "failed to create the tone mapper, using default HDR mode");
}

static int ChangeSourceProjection(vout_display_t *vd, video_projection_mode_t projection)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    sys->projection_mode = projection;
    return Direct3D11CreateFormatResources(vd, vd->source);
}

static int UpdateFormat(vout_display_t *vd, const video_format_t *fmtp, vlc_video_context *vctx)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    const d3d_format_t *old_texture_format = sys->picQuad.generic.textureFormat;
    const video_format_t old_quad_fmt = sys->picQuad.quad_fmt;

    video_format_t fmt;
    video_format_Copy(&fmt, fmtp);
    video_format_Copy(&sys->picQuad.quad_fmt, &fmt);

    int err = SetupOutputFormat(vd, &fmt, vctx, &sys->picQuad.quad_fmt);
    if (err != VLC_SUCCESS)
        goto error;

    if (fmt.i_chroma != fmtp->i_chroma)
    {
        msg_Warn(vd, "Could not update format, the display changed the "
                     "requested chroma from %4.4s to %4.4s",
                 (const char *)&fmtp->i_chroma, (const char *)&fmt.i_chroma);
        sys->picQuad.generic.textureFormat = old_texture_format;
        sys->picQuad.quad_fmt = old_quad_fmt;
        err = VLC_EGENERIC;
        goto error;
    }

    err = UpdateDisplayFormat(vd, &sys->picQuad.quad_fmt);
    if (err != VLC_SUCCESS)
        goto error;

    err = Direct3D11CreateFormatResources(vd, &fmt);
    if (err != VLC_SUCCESS)
        goto error;

    video_format_Clean(&fmt);
    return VLC_SUCCESS;

error:
    video_format_Clean(&fmt);
    return err;
}

static int PlacementChanged(vout_display_t *vd, const vout_display_place_t *place)
{
    VLC_UNUSED(place);
    return UpdateSource(vd, true);
}

static int AspectChanged(vout_display_t *vd, const video_format_t *source)
{
    VLC_UNUSED(source);
    return UpdateSource(vd, false);
}

static constexpr const auto ops = []{
    struct vlc_display_operations ops {};
    ops.close = Close;
    ops.prepare = Prepare;
    ops.display = Display;
    ops.set_display_size = SetDisplaySize;
    ops.update_format = UpdateFormat;
    ops.set_viewpoint = SetViewpoint;
    ops.change_source_projection = ChangeSourceProjection;
    ops.video_place_changed = PlacementChanged;
    ops.set_source_aspect = AspectChanged;
    ops.set_source_crop = AspectChanged;
    return ops;
}();

static int OpenGeneric(vout_display_t *vd,
                video_format_t *fmtp, vlc_video_context *context)
{
    vout_display_sys_t *sys = new (std::nothrow) vout_display_sys_t();
    if (!sys)
        return VLC_ENOMEM;
    vd->sys = sys;

    char *psz_hdr = var_InheritString(vd, "d3d11-hdr-mode");
    sys->hdrMode = HdrModeFromString(vlc_object_logger(vd), psz_hdr);
    free(psz_hdr);

    d3d11_decoder_device_t *dev_sys = NULL;

    sys->outside_opaque = var_InheritAddress( vd, "vout-cb-opaque" );
    sys->updateOutputCb      = (libvlc_video_update_output_cb)var_InheritAddress( vd, "vout-cb-update-output" );
    sys->swapCb              = (libvlc_video_swap_cb)var_InheritAddress( vd, "vout-cb-swap" );
    sys->startEndRenderingCb = (libvlc_video_makeCurrent_cb)var_InheritAddress( vd, "vout-cb-make-current" );
    sys->sendMetadataCb      = (libvlc_video_frameMetadata_cb)var_InheritAddress( vd, "vout-cb-metadata" );
    sys->selectPlaneCb       = (libvlc_video_output_select_plane_cb)var_InheritAddress( vd, "vout-cb-select-plane" );

    dev_sys = GetD3D11OpaqueContext( context );
    if ( dev_sys == NULL )
    {
        // No d3d11 device, we create one
        sys->local_d3d_dev = D3D11_CreateDevice(vd, NULL, false, vd->obj.force);
        if (sys->local_d3d_dev == NULL) {
            msg_Err(vd, "Could not Create the D3D11 device.");
            goto error;
        }
        dev_sys = sys->local_d3d_dev;
    }
    sys->d3d_dev = &dev_sys->d3d_dev;
    sys->projection_mode = vd->cfg->projection;

    InitTonemapProcessor(vd, vd->source);

    if ( sys->swapCb == NULL || sys->startEndRenderingCb == NULL || sys->updateOutputCb == NULL )
    {
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
        msg_Err(vd, "UWP apps needs to set an external rendering target");
        goto error;
#else // WINAPI_PARTITION_DESKTOP
        if (vd->cfg->window->type == VLC_WINDOW_TYPE_HWND)
        {
            if (CommonWindowInit(vd, &sys->video_wnd,
                       sys->projection_mode != PROJECTION_MODE_RECTANGULAR))
                goto error;
        }

        /* use our internal swapchain callbacks */
        dxgi_swapchain *swap = nullptr;
        if (vd->cfg->window->type == VLC_WINDOW_TYPE_DCOMP)
            swap = DXGI_CreateLocalSwapchainHandleDComp(VLC_OBJECT(vd),
                                                        vd->cfg->window->display.dcomp_device,
                                                        vd->cfg->window->handle.dcomp_visual);
        else
            swap = DXGI_CreateLocalSwapchainHandleHwnd(VLC_OBJECT(vd), CommonVideoHWND(sys->video_wnd));
        if (unlikely(swap == NULL))
            goto error;

        sys->outside_opaque = D3D11_CreateLocalSwapchain(VLC_OBJECT(vd), sys->d3d_dev, swap,
                                                         sys->hdrMode != hdr_Never && sys->hdrMode != hdr_Always);
        if (unlikely(sys->outside_opaque == NULL))
        {
            DXGI_LocalSwapchainCleanupDevice(swap);
            goto error;
        }

        sys->updateOutputCb      = D3D11_LocalSwapchainUpdateOutput;
        sys->swapCb              = D3D11_LocalSwapchainSwap;
        sys->startEndRenderingCb = D3D11_LocalSwapchainStartEndRendering;
        sys->sendMetadataCb      = D3D11_LocalSwapchainSetMetadata;
        sys->selectPlaneCb       = D3D11_LocalSwapchainSelectPlane;
#endif // WINAPI_PARTITION_DESKTOP
    }

    if (Direct3D11Open(vd, fmtp, context)) {
        msg_Err(vd, "Direct3D11 could not be opened");
        goto error;
    }

    vlc_window_SetTitle(vd->cfg->window, VOUT_TITLE " (Direct3D11 output)");
    msg_Dbg(vd, "Direct3D11 display adapter successfully initialized");

    if (var_InheritBool(vd, "direct3d11-hw-blending") &&
        sys->regionQuad.generic.textureFormat != NULL)
    {
        sys->pSubpictureChromas[0] = sys->regionQuad.generic.textureFormat->fourcc;
        sys->pSubpictureChromas[1] = 0;
        vd->info.subpicture_chromas = sys->pSubpictureChromas;
    }
    else
        vd->info.subpicture_chromas = NULL;

    vd->ops = &ops;

    msg_Dbg(vd, "Direct3D11 Open Succeeded");

    return VLC_SUCCESS;

error:
    Close(vd);
    return VLC_EGENERIC;
}

static int OpenDisplay(vout_display_t *vd,
                       video_format_t *fmtp, vlc_video_context *context)
{
    if (vd->cfg->window->type != VLC_WINDOW_TYPE_HWND &&
        vd->cfg->window->type != VLC_WINDOW_TYPE_DCOMP)
        return VLC_ENOTSUP;

    return OpenGeneric(vd, fmtp, context);
}

static int OpenDrawable(vout_display_t *vd,
                        video_format_t *fmtp, vlc_video_context *context)
{
    if (vd->cfg->window->type != VLC_WINDOW_TYPE_DUMMY)
        return VLC_ENOTSUP;

    /* TODO: other checks for callback validity should be there. */

    return OpenGeneric(vd, fmtp, context);
}

static void Close(vout_display_t *vd)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    CommonWindowClean(sys->video_wnd);
#endif
    Direct3D11Close(vd);
    delete sys;
}

static int SetDisplaySize(vout_display_t *vd, unsigned width, unsigned height)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    bool use_scaler = false;
    if (sys->upscaleMode == upscale_VideoProcessor || sys->upscaleMode == upscale_SuperResolution)
    {
        D3D11_UpscalerUpdate(VLC_OBJECT(vd), sys->scaleProc, sys->d3d_dev,
                             vd->source, &sys->picQuad.quad_fmt,
                             width, height,
                             vd->place);

        if (sys->scaleProc && D3D11_UpscalerUsed(sys->scaleProc))
        {
            D3D11_UpscalerGetSize(sys->scaleProc, &sys->picQuad.quad_fmt.i_width, &sys->picQuad.quad_fmt.i_height);

            sys->picQuad.quad_fmt.i_x_offset       = 0;
            sys->picQuad.quad_fmt.i_y_offset       = 0;
            sys->picQuad.quad_fmt.i_visible_width  = sys->picQuad.quad_fmt.i_width;
            sys->picQuad.quad_fmt.i_visible_height = sys->picQuad.quad_fmt.i_height;

            sys->picQuad.generic.i_width = sys->picQuad.quad_fmt.i_width;
            sys->picQuad.generic.i_height = sys->picQuad.quad_fmt.i_height;

            use_scaler = true;
        }
    }

    if (!use_scaler)
    {
        sys->picQuad.quad_fmt.i_sar_num        = vd->source->i_sar_num;
        sys->picQuad.quad_fmt.i_sar_den        = vd->source->i_sar_den;
        sys->picQuad.quad_fmt.i_x_offset       = vd->source->i_x_offset;
        sys->picQuad.quad_fmt.i_y_offset       = vd->source->i_y_offset;
        sys->picQuad.quad_fmt.i_visible_width  = vd->source->i_visible_width;
        sys->picQuad.quad_fmt.i_visible_height = vd->source->i_visible_height;
    }

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    CommonDisplaySizeChanged(sys->video_wnd);
#endif /* WINAPI_PARTITION_DESKTOP */

    if ( sys->place_changed )
    {
        UpdateSize(vd);
    }
    return VLC_SUCCESS;
}

static int UpdateSource(vout_display_t *vd, bool force_placement)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    bool use_scaler = false;
    if (sys->upscaleMode == upscale_VideoProcessor || sys->upscaleMode == upscale_SuperResolution)
    {
        D3D11_UpscalerUpdate(VLC_OBJECT(vd), sys->scaleProc, sys->d3d_dev,
                             vd->source, &sys->picQuad.quad_fmt,
                             vd->cfg->display.width, vd->cfg->display.height,
                             vd->place);

        if (sys->scaleProc && D3D11_UpscalerUsed(sys->scaleProc))
        {
            D3D11_UpscalerGetSize(sys->scaleProc, &sys->picQuad.quad_fmt.i_width, &sys->picQuad.quad_fmt.i_height);

            sys->picQuad.quad_fmt.i_x_offset       = 0;
            sys->picQuad.quad_fmt.i_y_offset       = 0;
            sys->picQuad.quad_fmt.i_visible_width  = sys->picQuad.quad_fmt.i_width;
            sys->picQuad.quad_fmt.i_visible_height = sys->picQuad.quad_fmt.i_height;

            sys->picQuad.generic.i_width = sys->picQuad.quad_fmt.i_width;
            sys->picQuad.generic.i_height = sys->picQuad.quad_fmt.i_height;

            use_scaler = true;
        }
    }

    if (!use_scaler)
    {
        sys->picQuad.quad_fmt.i_sar_num        = vd->source->i_sar_num;
        sys->picQuad.quad_fmt.i_sar_den        = vd->source->i_sar_den;
        sys->picQuad.quad_fmt.i_x_offset       = vd->source->i_x_offset;
        sys->picQuad.quad_fmt.i_y_offset       = vd->source->i_y_offset;
        sys->picQuad.quad_fmt.i_visible_width  = vd->source->i_visible_width;
        sys->picQuad.quad_fmt.i_visible_height = vd->source->i_visible_height;
    }

    if (force_placement)
        sys->place_changed = true;

    if (use_scaler)
    {
        vout_display_place_t before_place = sys->scalePlace;
        sys->scalePlace.x = 0;
        sys->scalePlace.y = 0;
        sys->scalePlace.width = sys->picQuad.quad_fmt.i_width;
        sys->scalePlace.height = sys->picQuad.quad_fmt.i_height;
        sys->place_changed |= !vout_display_PlaceEquals(&before_place, &sys->scalePlace);
    }

    if ( sys->place_changed )
    {
        UpdateSize(vd);
    }

    return VLC_SUCCESS;
}

static bool SelectRenderPlane(void *opaque, size_t plane, ID3D11RenderTargetView **targetView)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(opaque);
    if (!sys->selectPlaneCb)
    {
        *targetView = NULL;
        return plane == 0; // we only support one packed RGBA plane by default
    }
    return sys->selectPlaneCb(sys->outside_opaque, plane, (void*)targetView);
}

static int assert_ProcessorInput(vout_display_t *vd, picture_sys_d3d11_t *p_sys_src)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    if (!p_sys_src->processorInput)
    {
        D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inDesc{};
        inDesc.FourCC = 0;
        inDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
        inDesc.Texture2D.MipSlice = 0;
        inDesc.Texture2D.ArraySlice = p_sys_src->slice_index;

        HRESULT hr;

        hr = sys->old_feature.d3dviddev->CreateVideoProcessorInputView(
                                                             p_sys_src->resource[KNOWN_DXGI_INDEX],
                                                             sys->old_feature.enumerator.Get(),
                                                             &inDesc,
                                                             &p_sys_src->processorInput);
        if (FAILED(hr))
        {
#ifndef NDEBUG
            msg_Dbg(vd,"Failed to create processor input for slice %d. (hr=0x%lX)", p_sys_src->slice_index, hr);
#endif
            return VLC_EGENERIC;
        }
    }
    return VLC_SUCCESS;
}


static void PreparePicture(vout_display_t *vd, picture_t *picture,
                           const vlc_render_subpicture *subpicture,
                           vlc_tick_t date)
{
    VLC_UNUSED(date);
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    if (sys->picQuad.generic.textureFormat->formatTexture == DXGI_FORMAT_UNKNOWN)
    {
        D3D11_MAPPED_SUBRESOURCE mappedResource;
        int i;
        HRESULT hr;

        assert(sys->use_staging_texture);

        bool b_mapped = true;
        for (i = 0; i < picture->i_planes; i++) {
            hr = sys->d3d_dev->d3dcontext->Map(sys->stagingSys.resource[i],
                                         0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
            if( unlikely(FAILED(hr)) )
            {
                while (i-- > 0)
                    sys->d3d_dev->d3dcontext->Unmap(sys->stagingSys.resource[i], 0);
                b_mapped = false;
                break;
            }
            sys->stagingPlanes[i].i_pitch = mappedResource.RowPitch;
            sys->stagingPlanes[i].p_pixels = static_cast<uint8_t*>(mappedResource.pData);
        }

        if (b_mapped)
        {
            for (i = 0; i < picture->i_planes; i++)
                plane_CopyPixels(&sys->stagingPlanes[i], &picture->p[i]);

            for (i = 0; i < picture->i_planes; i++)
                sys->d3d_dev->d3dcontext->Unmap(sys->stagingSys.resource[i], 0);
        }
    }
    else if (!is_d3d11_opaque(picture->format.i_chroma))
    {
        D3D11_MAPPED_SUBRESOURCE mappedResource;
        HRESULT hr;

        assert(sys->use_staging_texture);

        hr = sys->d3d_dev->d3dcontext->Map(sys->stagingSys.resource[0],
                                        0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
        if( unlikely(FAILED(hr)) )
            msg_Err(vd, "Failed to map the %4.4s staging picture. (hr=0x%lX)", (const char*)&picture->format.i_chroma, hr);
        else
        {
            uint8_t *buf = static_cast<uint8_t*>(mappedResource.pData);
            for (int i = 0; i < picture->i_planes; i++)
            {
                sys->stagingPlanes[i].i_pitch = mappedResource.RowPitch;
                sys->stagingPlanes[i].p_pixels = buf;

                plane_CopyPixels(&sys->stagingPlanes[i], &picture->p[i]);

                buf += sys->stagingPlanes[i].i_pitch * sys->stagingPlanes[i].i_lines;
            }

            sys->d3d_dev->d3dcontext->Unmap(sys->stagingSys.resource[0], 0);
        }
    }
    else
    {
        picture_sys_d3d11_t *p_sys = ActiveD3D11PictureSys(picture);

        D3D11_TEXTURE2D_DESC srcDesc;
        p_sys->texture[KNOWN_DXGI_INDEX]->GetDesc(&srcDesc);

        if (sys->tonemapProc)
        {
            if (FAILED(D3D11_TonemapperProcess(VLC_OBJECT(vd), sys->tonemapProc, p_sys)))
                return;
        }
        else if (sys->scaleProc && D3D11_UpscalerUsed(sys->scaleProc))
        {
            if (D3D11_UpscalerScale(VLC_OBJECT(vd), sys->scaleProc, p_sys) != VLC_SUCCESS)
                return;
            uint32_t witdh, height;
            D3D11_UpscalerGetSize(sys->scaleProc, &witdh, &height);
            srcDesc.Width  = witdh;
            srcDesc.Height = height;
        }
        else if (sys->use_staging_texture)
        {
            if (sys->old_feature.processor)
            {
                if (assert_ProcessorInput(vd, p_sys) != VLC_SUCCESS)
                {
                    msg_Err(vd, "fail to create upscaler input");
                }
                else
                {
                    D3D11_VIDEO_PROCESSOR_STREAM stream{};
                    stream.Enable = TRUE;
                    stream.pInputSurface = p_sys->processorInput;

                    sys->old_feature.d3dvidctx->VideoProcessorBlt(sys->old_feature.processor.Get(),
                                                                  sys->old_feature.outputView.Get(),
                                                                  0, 1, &stream);
                }
            }
            else
            {
                D3D11_TEXTURE2D_DESC texDesc;
                sys->stagingSys.texture[0]->GetDesc(&texDesc);
                D3D11_BOX box;
                box.top = 0;
                box.bottom = __MIN(srcDesc.Height, texDesc.Height);
                box.left = 0;
                box.right = __MIN(srcDesc.Width, texDesc.Width);
                box.back = 1;
                sys->d3d_dev->d3dcontext->CopySubresourceRegion(
                                                        sys->stagingSys.resource[KNOWN_DXGI_INDEX],
                                                        0, 0, 0, 0,
                                                        p_sys->resource[KNOWN_DXGI_INDEX],
                                                        p_sys->slice_index, &box);
            }
        }
        else
        {
            if (srcDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE)
            {
                /* for performance reason we don't want to allocate this during
                 * display, do it preferably when creating the texture */

                assert(p_sys->renderSrc[0]!=NULL);
            }
            if ( sys->picQuad.generic.i_height != srcDesc.Height ||
                 sys->picQuad.generic.i_width  != srcDesc.Width )
            {
                /* the decoder produced different sizes than the vout, we need to
                 * adjust the vertex */

                sys->picQuad.quad_fmt.i_width  = srcDesc.Width;
                sys->picQuad.quad_fmt.i_height = srcDesc.Height;
                sys->picQuad.generic.i_width  = sys->picQuad.quad_fmt.i_width;
                sys->picQuad.generic.i_height = sys->picQuad.quad_fmt.i_height;

                UpdateSize(vd);
            }
        }
    }

    if (subpicture) {
        int subpicture_region_count    = 0;
        picture_t **subpicture_regions = NULL;
        Direct3D11MapSubpicture(vd, &subpicture_region_count, &subpicture_regions, subpicture);
        Direct3D11DeleteRegions(sys->d3dregion_count, sys->d3dregions);
        sys->d3dregion_count = subpicture_region_count;
        sys->d3dregions      = subpicture_regions;
    }

    if (picture->format.mastering.max_luminance)
    {
        D3D11_UpdateQuadLuminanceScale(vd, sys->d3d_dev, &sys->picQuad, (float)sys->display.luminance_peak / D3D_GetFormatLuminance(VLC_OBJECT(vd), &picture->format));
    }

    /* Render the quad */
    ID3D11ShaderResourceView **renderSrc;
    ID3D11ShaderResourceView *SRV[DXGI_MAX_SHADER_VIEW];
    if (sys->scaleProc && D3D11_UpscalerUsed(sys->scaleProc))
    {
        D3D11_UpscalerGetSRV(sys->scaleProc, SRV);
        renderSrc = SRV;
    }
    else if (sys->use_staging_texture)
        renderSrc = sys->stagingSys.renderSrc;
    else {
        picture_sys_d3d11_t *p_sys = ActiveD3D11PictureSys(picture);
        if (sys->tonemapProc)
            p_sys = D3D11_TonemapperGetOutput(sys->tonemapProc);
        renderSrc = p_sys->renderSrc;
    }
    D3D11_RenderQuad(sys->d3d_dev, &sys->picQuad,
                     sys->projection_mode == PROJECTION_MODE_RECTANGULAR ? &sys->flatVShader : &sys->projectionVShader,
                     renderSrc, SelectRenderPlane, sys);

    if (subpicture) {
        // draw the additional vertices
        for (int i = 0; i < sys->d3dregion_count; ++i) {
            if (sys->d3dregions[i])
            {
                d3d11_quad_t *quad = (d3d11_quad_t *) sys->d3dregions[i]->p_sys;
                D3D11_RenderQuad(sys->d3d_dev, quad, &sys->flatVShader,
                                 quad->picSys.renderSrc, SelectRenderPlane, sys);
            }
        }
    }

    if (sys->log_level >= 4)
    {
        vlc_tick_t render_start = vlc_tick_now();
        D3D11_WaitFence(sys->fence);
        msg_Dbg(vd, "waited %" PRId64 " ms for the render fence", MS_FROM_VLC_TICK(vlc_tick_now() - render_start));
    }
    else
    {
        D3D11_WaitFence(sys->fence);
    }
}

static void Prepare(vout_display_t *vd, picture_t *picture,
                    const vlc_render_subpicture *subpicture, vlc_tick_t date)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    d3d11_device_lock( sys->d3d_dev );
    if ( sys->startEndRenderingCb( sys->outside_opaque, true ))
    {
        if ( sys->sendMetadataCb && picture->format.mastering.max_luminance )
        {
            libvlc_video_frame_hdr10_metadata_t hdr10;
            hdr10.GreenPrimary[0] = picture->format.mastering.primaries[0];
            hdr10.GreenPrimary[1] = picture->format.mastering.primaries[1];
            hdr10.BluePrimary[0]  = picture->format.mastering.primaries[2];
            hdr10.BluePrimary[1]  = picture->format.mastering.primaries[3];
            hdr10.RedPrimary[0]   = picture->format.mastering.primaries[4];
            hdr10.RedPrimary[1]   = picture->format.mastering.primaries[5];
            hdr10.WhitePoint[0]   = picture->format.mastering.white_point[0];
            hdr10.WhitePoint[1]   = picture->format.mastering.white_point[1];
            hdr10.MaxMasteringLuminance = picture->format.mastering.max_luminance;
            hdr10.MinMasteringLuminance = picture->format.mastering.min_luminance;
            hdr10.MaxContentLightLevel = picture->format.lighting.MaxCLL;
            hdr10.MaxFrameAverageLightLevel = picture->format.lighting.MaxFALL;

            sys->sendMetadataCb( sys->outside_opaque, libvlc_video_metadata_frame_hdr10, &hdr10 );
        }

        PreparePicture(vd, picture, subpicture, date);

        sys->startEndRenderingCb( sys->outside_opaque, false );
    }
    d3d11_device_unlock( sys->d3d_dev );
}

static void Display(vout_display_t *vd, picture_t *picture)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    VLC_UNUSED(picture);

    d3d11_device_lock( sys->d3d_dev );
    sys->swapCb(sys->outside_opaque);
    d3d11_device_unlock( sys->d3d_dev );
}

static const d3d_format_t *GetDirectRenderingFormat(vout_display_t *vd, vlc_fourcc_t i_src_chroma)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD;
    return FindD3D11Format( vd, sys->d3d_dev, i_src_chroma, DXGI_RGB_FORMAT|DXGI_YUV_FORMAT, 0, 0, 0, 0,
                            DXGI_CHROMA_CPU, supportFlags );
}

static const d3d_format_t *GetDisplayFormatByDepth(vout_display_t *vd, uint8_t bit_depth,
                                                   uint8_t widthDenominator,
                                                   uint8_t heightDenominator,
                                                   uint8_t alpha_bits,
                                                   bool from_processor,
                                                   int rgb_yuv)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD;
    if (from_processor)
    {
        supportFlags |= D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT;
        if (alpha_bits == 0)
            alpha_bits = 1; // allow the video processor to alpha opaque alpha
    }
    const d3d_format_t *res;
    res = FindD3D11Format( vd, sys->d3d_dev, 0, rgb_yuv,
                            bit_depth, widthDenominator+1, heightDenominator+1, alpha_bits,
                            from_processor ? DXGI_CHROMA_GPU : DXGI_CHROMA_CPU,
                            supportFlags );
    if (res == nullptr)
    {
        msg_Dbg(vd, "No display format for %u-bit %u:%u%s%s%s", bit_depth, widthDenominator, heightDenominator,
                                                          rgb_yuv & DXGI_YUV_FORMAT ? " YUV" : "",
                                                          rgb_yuv & DXGI_RGB_FORMAT ? " RGB" : "",
                                                          from_processor ? " supporting video processor" : "");
    }
    return res;
}

static const d3d_format_t *GetBlendableFormat(vout_display_t *vd, vlc_fourcc_t i_src_chroma)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD | D3D11_FORMAT_SUPPORT_BLENDABLE;
    return FindD3D11Format( vd, sys->d3d_dev, i_src_chroma, DXGI_RGB_FORMAT|DXGI_YUV_FORMAT, 0, 0, 0, 8, DXGI_CHROMA_CPU, supportFlags );
}

static void InitScaleProcessor(vout_display_t *vd)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    if (sys->upscaleMode != upscale_VideoProcessor && sys->upscaleMode != upscale_SuperResolution)
        return;

    sys->scaleProc = D3D11_UpscalerCreate(VLC_OBJECT(vd), sys->d3d_dev, sys->picQuad.quad_fmt.i_chroma,
                                          sys->upscaleMode == upscale_SuperResolution, &sys->picQuad.generic.textureFormat);
    if (sys->scaleProc == NULL)
    {
        msg_Dbg(vd, "forcing linear sampler");
        sys->upscaleMode = upscale_LinearSampler;
    }

    msg_Dbg(vd, "Using %s scaler", ppsz_upscale_mode_text[sys->upscaleMode]);
}

static int Direct3D11Open(vout_display_t *vd, video_format_t *fmtp, vlc_video_context *vctx)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    video_format_t fmt;

    video_format_Copy(&fmt, vd->source);
    video_format_Copy(&sys->picQuad.quad_fmt, &fmt);
    int err = SetupOutputFormat(vd, &fmt, vctx, &sys->picQuad.quad_fmt);
    if (err != VLC_SUCCESS)
    {
        if (!is_d3d11_opaque(vd->source->i_chroma)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
            && vd->obj.force
#endif
                )
        {
            vlc_fourcc_t *list = vlc_fourcc_GetFallback(vd->source->i_chroma);
            if (list != NULL)
            {
                for (unsigned i = 0; list[i] != 0; i++) {
                    if (list[i] == vd->source->i_chroma)
                        continue;
                    fmt.i_chroma = list[i];
                    err = SetupOutputFormat(vd, &fmt, nullptr, &sys->picQuad.quad_fmt);
                    if (err == VLC_SUCCESS)
                        break;
                }
                free(list);
            }
        }
        if (err != VLC_SUCCESS)
        {
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
            if ( sys->swapCb == D3D11_LocalSwapchainSwap && sys->outside_opaque )
            {
                D3D11_LocalSwapchainCleanupDevice( sys->outside_opaque );
                sys->outside_opaque = nullptr;
            }
#endif // WINAPI_PARTITION_DESKTOP
            return err;
        }
    }

    if (!is_d3d11_opaque(fmt.i_chroma))
    {
        sys->picQuad.quad_fmt.i_chroma = sys->picQuad.generic.textureFormat->fourcc;
    }

    /* adjust the decoder sizes to have proper padding */
    if ( sys->picQuad.generic.textureFormat->heightDenominator != 1 ||
         sys->picQuad.generic.textureFormat->widthDenominator != 1 )
    {
        sys->picQuad.quad_fmt.i_width  = (sys->picQuad.quad_fmt.i_width  + 0x01) & ~0x01;
        sys->picQuad.quad_fmt.i_height = (sys->picQuad.quad_fmt.i_height + 0x01) & ~0x01;
    }
    sys->picQuad.generic.i_width  = sys->picQuad.quad_fmt.i_width;
    sys->picQuad.generic.i_height = sys->picQuad.quad_fmt.i_height;

    char *psz_upscale = var_InheritString(vd, "d3d11-upscale-mode");
    if (strcmp("linear", psz_upscale) == 0)
        sys->upscaleMode = upscale_LinearSampler;
    else if (strcmp("point", psz_upscale) == 0)
        sys->upscaleMode = upscale_PointSampler;
    else if (strcmp("processor", psz_upscale) == 0)
        sys->upscaleMode = upscale_VideoProcessor;
    else if (strcmp("super", psz_upscale) == 0)
        sys->upscaleMode = upscale_SuperResolution;
    else
    {
        msg_Warn(vd, "unknown upscale mode %s, using linear sampler", psz_upscale);
        sys->upscaleMode = upscale_LinearSampler;
    }
    free(psz_upscale);

    InitScaleProcessor(vd);

    sys->scalePlace.x = 0;
    sys->scalePlace.y = 0;
    sys->scalePlace.width = vd->cfg->display.width;
    sys->scalePlace.height = vd->cfg->display.height;
    sys->place_changed = true;

    err = UpdateDisplayFormat(vd, &sys->picQuad.quad_fmt);
    if (err != VLC_SUCCESS) {
        msg_Err(vd, "Could not update the backbuffer");
        return err;
    }

    if (Direct3D11CreateGenericResources(vd)) {
        msg_Err(vd, "Failed to allocate resources");
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
        if ( sys->swapCb == D3D11_LocalSwapchainSwap && sys->outside_opaque )
        {
            D3D11_LocalSwapchainCleanupDevice( sys->outside_opaque );
            sys->outside_opaque = nullptr;
        }
#endif // WINAPI_PARTITION_DESKTOP
        return VLC_EGENERIC;
    }

    video_format_Clean(fmtp);
    *fmtp = fmt;

    sys->log_level = var_InheritInteger(vd, "verbose");

    return VLC_SUCCESS;
}

static const d3d_format_t *SelectOutputFormat(vout_display_t *vd, const video_format_t *fmt, vlc_video_context *vctx)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    bool allow_processor = false;

    const d3d_format_t *res = nullptr;

    d3d11_video_context_t *vtcx_sys = GetD3D11ContextPrivate(vctx);
    if (vtcx_sys != NULL)
    {
        if (D3D11_DeviceSupportsFormat( sys->d3d_dev, vtcx_sys->format, D3D11_FORMAT_SUPPORT_SHADER_LOAD ))
        {
            res = D3D11_RenderFormat(vtcx_sys->format, vtcx_sys->secondary ,true);
            if (likely(res != nullptr))
                return res;
            msg_Dbg(vd, "Unsupported rendering texture format %s/%s", DxgiFormatToStr(vtcx_sys->format), DxgiFormatToStr(vtcx_sys->secondary));
        }
        else
        {
            msg_Dbg(vd, "Texture format %s not supported by shaders", DxgiFormatToStr(vtcx_sys->format));
            if (!D3D11_DeviceSupportsFormat( sys->d3d_dev, vtcx_sys->format, D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_INPUT ))
            {
                msg_Dbg(vd, "Texture format %s not supported in video processor", DxgiFormatToStr(vtcx_sys->format));
            }
            else
            {
                allow_processor = true;
            }
        }
    }
    else
    {
        // look for the requested pixel format first
        res = GetDirectRenderingFormat(vd, fmt->i_chroma);
        if (res != nullptr)
            return res;
    }

    msg_Dbg(vd, "Direct rendering not usable for %4.4s", (char*)&fmt->i_chroma);

    // look for any pixel format that we can handle with enough pixels per channel
    uint8_t bits_per_channel;
    uint8_t widthDenominator, heightDenominator;
    uint8_t alpha_bits = 0;
    vlc_fourcc_t cpu_chroma;
    if (is_d3d11_opaque(fmt->i_chroma))
        cpu_chroma = DxgiFormatFourcc(vtcx_sys->format);
    else if (is_nvdec_opaque(fmt->i_chroma))
        cpu_chroma = NVDECToVlcChroma(fmt->i_chroma);
    else
        cpu_chroma = fmt->i_chroma;

    const auto *p_format = vlc_fourcc_GetChromaDescription(cpu_chroma);
    if (unlikely(p_format == NULL || p_format->plane_count == 0))
    {
        bits_per_channel = 8;
        widthDenominator = heightDenominator = 2;
    }
    else
    {
        bits_per_channel = p_format->pixel_bits /
                            (p_format->plane_count==1 ? p_format->pixel_size : 1);
        widthDenominator = heightDenominator = 1;
        for (size_t i=0; i<p_format->plane_count; i++)
        {
            if (widthDenominator < p_format->p[i].w.den)
                widthDenominator = p_format->p[i].w.den;
            if (heightDenominator < p_format->p[i].h.den)
                heightDenominator = p_format->p[1].h.den;
        }

        switch (cpu_chroma) // FIXME get this info from the core
        {
        case VLC_CODEC_YUVA:
        case VLC_CODEC_YUV422A:
        case VLC_CODEC_YUV420A:
        case VLC_CODEC_VUYA:
        case VLC_CODEC_RGBA:
        case VLC_CODEC_ARGB:
        case VLC_CODEC_BGRA:
        case VLC_CODEC_ABGR:
        case VLC_CODEC_D3D11_OPAQUE_RGBA:
        case VLC_CODEC_D3D11_OPAQUE_BGRA:
        case VLC_CODEC_D3D11_OPAQUE_ALPHA:
            alpha_bits = 8;
            break;
        case VLC_CODEC_YUVA_444_10L:
        case VLC_CODEC_YUVA_444_10B:
            alpha_bits = 10;
            break;
        case VLC_CODEC_RGBA10LE:
            bits_per_channel = 10;
            alpha_bits = 2;
            break;
        case VLC_CODEC_YUVA_444_12L:
        case VLC_CODEC_YUVA_444_12B:
            alpha_bits = 12;
            break;
        case VLC_CODEC_RGBA64:
            alpha_bits = 16;
            break;
        }
    }

    bool is_rgb = !vlc_fourcc_IsYUV(fmt->i_chroma);
    res = GetDisplayFormatByDepth(vd, bits_per_channel,
                                  widthDenominator, heightDenominator, alpha_bits,
                                  allow_processor,
                                  is_rgb ? DXGI_RGB_FORMAT : DXGI_YUV_FORMAT);
    if (res != nullptr)
        return res;
    // check RGB instead of YUV (and vice versa)
    res = GetDisplayFormatByDepth(vd, bits_per_channel,
                                  widthDenominator, heightDenominator, alpha_bits,
                                  allow_processor,
                                  is_rgb ? DXGI_YUV_FORMAT : DXGI_RGB_FORMAT);
    if (res != nullptr)
        return res;

    // look for any pixel format that we can handle
    if (is_d3d11_opaque(fmt->i_chroma))
    {
        res = GetDisplayFormatByDepth(vd, 0, 0, 0, 0, true, DXGI_YUV_FORMAT|DXGI_RGB_FORMAT);
        if (res != nullptr)
            return res;

        if (!vd->obj.force)
            // the source is hardware decoded but can't be handled
            // let other display modules deal with it
            return nullptr;
        // fallthrough
    }
    // any format, even CPU ones
    return GetDisplayFormatByDepth(vd, 0, 0, 0, 0, false, DXGI_YUV_FORMAT|DXGI_RGB_FORMAT);
}

static HRESULT SetupInternalConverter(vout_display_t *vd, video_format_t *fmt)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    auto d3d_dev = sys->d3d_dev;

    HRESULT hr;
    hr = d3d_dev->d3ddevice->QueryInterface(IID_GRAPHICS_PPV_ARGS(&sys->old_feature.d3dviddev));
    if (unlikely(FAILED(hr)))
    {
        msg_Err(vd, "Could not Query ID3D11VideoDevice Interface. (hr=0x%lX)", hr);
        return hr;
    }

    hr = d3d_dev->d3dcontext->QueryInterface(IID_GRAPHICS_PPV_ARGS(&sys->old_feature.d3dvidctx));
    if (unlikely(FAILED(hr)))
    {
        msg_Err(vd, "Could not Query ID3D11VideoContext Interface. (hr=0x%lX)", hr);
        return hr;
    }

    D3D11_VIDEO_PROCESSOR_CONTENT_DESC processorDesc{};
    processorDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
    processorDesc.InputFrameRate = {
        fmt->i_frame_rate, fmt->i_frame_rate_base,
    };
    processorDesc.InputWidth   = fmt->i_width;
    processorDesc.InputHeight  = fmt->i_height;
    processorDesc.OutputWidth  = fmt->i_width;
    processorDesc.OutputHeight = fmt->i_height;
    processorDesc.OutputFrameRate = {
        fmt->i_frame_rate, fmt->i_frame_rate_base,
    };
    processorDesc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
    hr = sys->old_feature.d3dviddev->CreateVideoProcessorEnumerator(&processorDesc, &sys->old_feature.enumerator);
    if (FAILED(hr))
    {
        msg_Dbg(vd, "Can't get a video processor for the video (error 0x%lx).", hr);
        return hr;
    }

    hr = sys->old_feature.d3dviddev->CreateVideoProcessor(sys->old_feature.enumerator.Get(), 0,
                                                        &sys->old_feature.processor);
    if (FAILED(hr))
    {
        msg_Dbg(vd, "failed to create the processor (error 0x%lx).", hr);
        return hr;
    }

    return S_OK;
}

static int SetupOutputFormat(vout_display_t *vd, video_format_t *fmt, vlc_video_context *vctx, video_format_t *quad_fmt)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    const d3d_format_t *decoder_format = nullptr;

    if (sys->hdrMode == hdr_Fake)
    {
        // force a fake HDR source
        if (likely(D3D11_DeviceSupportsFormat( sys->d3d_dev, DXGI_FORMAT_R10G10B10A2_UNORM, D3D11_FORMAT_SUPPORT_SHADER_LOAD )))
        {
            sys->picQuad.generic.textureFormat = D3D11_RenderFormat(DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_UNKNOWN ,true);
            if (sys->picQuad.generic.textureFormat)
            {
                quad_fmt->i_chroma = sys->picQuad.generic.textureFormat->fourcc;
                // corresponds to DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
                quad_fmt->primaries          = COLOR_PRIMARIES_BT2020;
                quad_fmt->transfer           = TRANSFER_FUNC_SMPTE_ST2084;
                quad_fmt->space              = COLOR_SPACE_BT2020;
                quad_fmt->color_range        = COLOR_RANGE_FULL;
            }
        }
        else
        {
            msg_Err(vd, "Could not use R10G10B10A2 in shaders!");
            return VLC_EGENERIC;
        }
    }
    else
        sys->picQuad.generic.textureFormat = SelectOutputFormat(vd, quad_fmt, vctx);
    if ( !sys->picQuad.generic.textureFormat )
    {
       msg_Err(vd, "Could not get a suitable texture pixel format");
       return VLC_EGENERIC;
    }

    if (vctx)
    {
        d3d11_video_context_t *vtcx_sys = GetD3D11ContextPrivate(vctx);
        if (vtcx_sys && sys->picQuad.generic.textureFormat->formatTexture != vtcx_sys->format && sys->hdrMode != hdr_Fake)
        {
            HRESULT hr;
            // check the input format can be used as input of a VideoProcessor
            decoder_format = FindD3D11Format( vd, sys->d3d_dev, fmt->i_chroma, DXGI_RGB_FORMAT|DXGI_YUV_FORMAT, 0, 0, 0, 0,
                                              DXGI_CHROMA_GPU, D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_INPUT );
            hr = SetupInternalConverter(vd, fmt);
            if (FAILED(hr))
            {
                msg_Err(vd, "Failed to initialize internal converter. (hr=0x%lX)", hr);
                return VLC_EGENERIC;
            }
        }
    }

    msg_Dbg( vd, "Using pixel format %s for chroma %4.4s", sys->picQuad.generic.textureFormat->name,
                 (char *)&fmt->i_chroma );

    fmt->i_chroma = decoder_format ? decoder_format->fourcc : sys->picQuad.generic.textureFormat->fourcc;

    /* select the subpicture region pixel format */
    sys->regionQuad.generic.textureFormat = GetBlendableFormat(vd, VLC_CODEC_RGBA);
    if (!sys->regionQuad.generic.textureFormat)
        sys->regionQuad.generic.textureFormat = GetBlendableFormat(vd, VLC_CODEC_BGRA);

    return VLC_SUCCESS;
}

static void Direct3D11Close(vout_display_t *vd)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    Direct3D11DestroyResources(vd);

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    if ( sys->swapCb == D3D11_LocalSwapchainSwap && sys->outside_opaque )
    {
        D3D11_LocalSwapchainCleanupDevice( sys->outside_opaque );
        sys->outside_opaque = nullptr;
    }
#endif // WINAPI_PARTITION_DESKTOP

    if (sys->d3d_dev && sys->d3d_dev == &sys->local_d3d_dev->d3d_dev)
        D3D11_ReleaseDevice( sys->local_d3d_dev );

    msg_Dbg(vd, "Direct3D11 display adapter closed");
}

/* TODO : handle errors better
   TODO : separate out into smaller functions like createshaders */

static int Direct3D11CreateFormatResources(vout_display_t *vd, const video_format_t *fmt)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    HRESULT hr;

    if (sys->tonemapProc == NULL && !is_d3d11_opaque(fmt->i_chroma))
        // CPU source copied in the staging texture(s)
        sys->use_staging_texture = true;
    else if (sys->old_feature.d3dviddev)
        // use a staging texture to do chroma conversion
        sys->use_staging_texture = true;
    else
        sys->use_staging_texture = false;

    d3d_shader_blob pPSBlob[DXGI_MAX_RENDER_TARGET] = { };
    hr = D3D11_CompilePixelShaderBlob(vd, sys->d3d_dev,
                                  &sys->display, fmt->transfer,
                                  fmt->color_range == COLOR_RANGE_FULL,
                                  &sys->picQuad, pPSBlob);
    if (FAILED(hr))
    {
        msg_Err(vd, "Failed to compile the pixel shader. (hr=0x%lX)", hr);
        return VLC_EGENERIC;
    }
    hr = D3D11_SetQuadPixelShader(VLC_OBJECT(vd), sys->d3d_dev, sys->upscaleMode != upscale_LinearSampler,
                                  &sys->picQuad, pPSBlob);
    if (FAILED(hr))
    {
        msg_Err(vd, "Failed to set the pixel shader. (hr=0x%lX)", hr);
        return VLC_EGENERIC;
    }

    if (D3D11_AllocateQuad(vd, sys->d3d_dev, sys->projection_mode, &sys->picQuad) != VLC_SUCCESS)
    {
        msg_Err(vd, "Could not allocate quad buffers.");
       return VLC_EGENERIC;
    }

    if (D3D11_SetupQuad( vd, sys->d3d_dev, &sys->picQuad.quad_fmt, &sys->picQuad, &sys->display) != VLC_SUCCESS)
    {
        msg_Err(vd, "Could not Create the main quad picture.");
        return VLC_EGENERIC;
    }

    if (!D3D11_UpdateQuadPosition(vd, sys->d3d_dev, &sys->picQuad,
                                  video_format_GetTransform(sys->picQuad.quad_fmt.orientation, sys->display.orientation)))
    {
        msg_Err(vd, "Could not set quad picture position.");
        return VLC_EGENERIC;
    }

    if ( sys->projection_mode == PROJECTION_MODE_EQUIRECTANGULAR ||
         sys->projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD )
        D3D11_UpdateViewpoint( vd, sys->d3d_dev, &sys->picQuad, &vd->cfg->viewpoint,
                               (float) vd->cfg->display.width / vd->cfg->display.height );

    if (is_d3d11_opaque(fmt->i_chroma)) {
        ComPtr<ID3D10Multithread> pMultithread;
        hr = sys->d3d_dev->d3ddevice->QueryInterface(IID_GRAPHICS_PPV_ARGS(&pMultithread));
        if (SUCCEEDED(hr))
            pMultithread->SetMultithreadProtected(TRUE);
    }

    return UpdateStaging(vd, fmt);
}

static int Direct3D11CreateGenericResources(vout_display_t *vd)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    HRESULT hr;

    hr = D3D11_InitFence(*sys->d3d_dev, sys->fence);
    if (SUCCEEDED(hr))
    {
        msg_Dbg(vd, "using GPU render fence");
    }

    ComPtr<ID3D11BlendState> pSpuBlendState;
    D3D11_BLEND_DESC spuBlendDesc = { };
    spuBlendDesc.RenderTarget[0].BlendEnable = TRUE;
    spuBlendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
    /* output colors */
    spuBlendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
    spuBlendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; /* keep source intact */
    spuBlendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; /* RGB colors + inverse alpha (255 is full opaque) */
    /* output alpha  */
    spuBlendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
    spuBlendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; /* keep source intact */
    spuBlendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; /* discard */

    hr = sys->d3d_dev->d3ddevice->CreateBlendState(&spuBlendDesc, &pSpuBlendState);
    if (FAILED(hr)) {
       msg_Err(vd, "Could not create SPU blend state. (hr=0x%lX)", hr);
       return VLC_EGENERIC;
    }
    sys->d3d_dev->d3dcontext->OMSetBlendState(pSpuBlendState.Get(), NULL, 0xFFFFFFFF);

    /* disable depth testing as we're only doing 2D
     * see https://msdn.microsoft.com/en-us/library/windows/desktop/bb205074%28v=vs.85%29.aspx
     * see http://rastertek.com/dx11tut11.html
    */

    D3D11_DEPTH_STENCIL_DESC stencilDesc = { };

    ComPtr<ID3D11DepthStencilState> pDepthStencilState;
    hr = sys->d3d_dev->d3ddevice->CreateDepthStencilState(&stencilDesc, &pDepthStencilState );
    if (SUCCEEDED(hr))
        sys->d3d_dev->d3dcontext->OMSetDepthStencilState(pDepthStencilState.Get(), 0);

    if (sys->regionQuad.generic.textureFormat != NULL)
    {
        d3d_shader_blob pPSBlob[DXGI_MAX_RENDER_TARGET] = { };
        hr = D3D11_CompilePixelShaderBlob(vd, sys->d3d_dev,
                                      &sys->display, TRANSFER_FUNC_SRGB, true,
                                      &sys->regionQuad, pPSBlob);
        if (FAILED(hr))
        {
            msg_Err(vd, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr);
            return VLC_EGENERIC;
        }
        hr = D3D11_SetQuadPixelShader(VLC_OBJECT(vd), sys->d3d_dev, true,
                                      &sys->regionQuad, pPSBlob);
        if (FAILED(hr))
        {
            msg_Err(vd, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr);
            return VLC_EGENERIC;
        }
    }

    d3d_shader_blob VSBlob = { };
    hr = D3D11_CompileVertexShaderBlob(VLC_OBJECT(vd), sys->d3d_dev, true, &VSBlob);
    if(FAILED(hr)) {
      msg_Err(vd, "Failed to compile the flat vertex shader. (hr=0x%lX)", hr);
      return VLC_EGENERIC;
    }
    hr = D3D11_CreateVertexShader(vd, &VSBlob, sys->d3d_dev, &sys->flatVShader);
    if(FAILED(hr)) {
      msg_Err(vd, "Failed to create the vertex input layout. (hr=0x%lX)", hr);
      return VLC_EGENERIC;
    }


    hr = D3D11_CompileVertexShaderBlob(VLC_OBJECT(vd), sys->d3d_dev, false, &VSBlob);
    if(FAILED(hr)) {
      msg_Err(vd, "Failed to compile the 360 vertex shader. (hr=0x%lX)", hr);
      return VLC_EGENERIC;
    }
    hr = D3D11_CreateVertexShader(vd, &VSBlob, sys->d3d_dev, &sys->projectionVShader);
    if(FAILED(hr)) {
      msg_Err(vd, "Failed to create the projection vertex shader. (hr=0x%lX)", hr);
      return VLC_EGENERIC;
    }

    const vout_display_place_t *quad_place;
    if (sys->scaleProc && D3D11_UpscalerUsed(sys->scaleProc))
        quad_place = &sys->scalePlace;
    else
        quad_place = vd->place;
    sys->picQuad.UpdateViewport( quad_place, sys->display.pixelFormat );

#ifndef NDEBUG
    msg_Dbg( vd, "picQuad position (%.02f,%.02f) %.02fx%.02f",
             sys->picQuad.cropViewport[0].TopLeftX, sys->picQuad.cropViewport[0].TopLeftY,
             sys->picQuad.cropViewport[0].Width, sys->picQuad.cropViewport[0].Height );
#endif

    D3D11_UpdateViewpoint( vd, sys->d3d_dev, &sys->picQuad, &vd->cfg->viewpoint,
                          (float) vd->cfg->display.width / vd->cfg->display.height );

    msg_Dbg(vd, "Direct3D11 resources created");
    return VLC_SUCCESS;
}

static void Direct3D11DestroyResources(vout_display_t *vd)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);

    if (sys->tonemapProc != NULL)
    {
        D3D11_TonemapperDestroy(sys->tonemapProc);
        sys->tonemapProc = NULL;
    }
    if (sys->scaleProc != nullptr)
    {
        D3D11_UpscalerDestroy(sys->scaleProc);
        sys->scaleProc = nullptr;
    }

    sys->picQuad.Reset();
    Direct3D11DeleteRegions(sys->d3dregion_count, sys->d3dregions);
    sys->d3dregion_count = 0;
    sys->regionQuad.Reset();

    ReleaseD3D11PictureSys(&sys->stagingSys);

    D3D11_ReleaseVertexShader(&sys->flatVShader);
    D3D11_ReleaseVertexShader(&sys->projectionVShader);

    D3D11_ReleaseFence(sys->fence);

    msg_Dbg(vd, "Direct3D11 resources destroyed");
}

static void Direct3D11DeleteRegions(int count, picture_t **region)
{
    for (int i = 0; i < count; ++i) {
        if (region[i]) {
            picture_Release(region[i]);
        }
    }
    free(region);
}

static void DestroyPictureQuad(picture_t *p_picture)
{
    d3d11_quad_t *quad = static_cast<d3d11_quad_t *>(p_picture->p_sys);
    delete quad;
}

static int Direct3D11MapSubpicture(vout_display_t *vd, int *subpicture_region_count,
                                   picture_t ***region, const vlc_render_subpicture *subpicture)
{
    vout_display_sys_t *sys = static_cast<vout_display_sys_t *>(vd->sys);
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    D3D11_TEXTURE2D_DESC texDesc;
    HRESULT hr;
    int err;

    if (sys->regionQuad.generic.textureFormat == NULL)
        return VLC_EGENERIC;

    const auto render_orientation = video_format_GetTransform(ORIENT_NORMAL, sys->display.orientation);

    size_t count = subpicture->regions.size;
    const struct subpicture_region_rendered *r;

    *region = static_cast<picture_t**>(calloc(count, sizeof(picture_t *)));
    if (unlikely(*region==NULL))
        return VLC_ENOMEM;
    *subpicture_region_count = count;

    int i = 0;
    vlc_vector_foreach(r, &subpicture->regions) {
        if (!r->place.width || !r->place.height)
        {
            i++;
            continue; // won't render anything, keep the cache for later
        }

        for (int j = 0; j < sys->d3dregion_count; j++) {
            picture_t *cache = sys->d3dregions[j];
            d3d11_quad_t *cache_quad = cache ? static_cast<d3d11_quad_t *>(cache->p_sys) : nullptr;
            if (cache_quad != nullptr && cache_quad->picSys.texture[KNOWN_DXGI_INDEX]) {
                cache_quad->picSys.texture[KNOWN_DXGI_INDEX]->GetDesc(&texDesc );
                if (texDesc.Format == sys->regionQuad.generic.textureFormat->formatTexture &&
                    texDesc.Width  == r->p_picture->format.i_width &&
                    texDesc.Height == r->p_picture->format.i_height) {
                    (*region)[i] = cache;
                    memset(&sys->d3dregions[j], 0, sizeof(cache)); // do not reuse this cached value a second time
                    break;
                }
            }
        }

        picture_t *quad_picture = (*region)[i];
        d3d11_quad_t *quad;
        if (quad_picture != NULL)
        {
            quad = static_cast<d3d11_quad_t*>(quad_picture->p_sys);

            video_format_Clean(&quad->quad_fmt);
            video_format_Copy(&quad->quad_fmt, &r->p_picture->format);
        }
        else
        {
            d3d11_quad_t *d3dquad = new (std::nothrow) d3d11_quad_t;
            if (unlikely(d3dquad==NULL)) {
                i++;
                continue;
            }
            quad = d3dquad;
            if (AllocateTextures(vd, sys->d3d_dev, sys->regionQuad.generic.textureFormat, &r->p_picture->format,
                                 false, d3dquad->picSys.texture, NULL)) {
                msg_Err(vd, "Failed to allocate %dx%d texture for OSD",
                        r->p_picture->format.i_width, r->p_picture->format.i_height);
                for (int j=0; j<DXGI_MAX_SHADER_VIEW; j++)
                    if (d3dquad->picSys.texture[j])
                        d3dquad->picSys.texture[j]->Release();
                delete d3dquad;
                i++;
                continue;
            }

            if (D3D11_AllocateResourceView(vlc_object_logger(vd), sys->d3d_dev->d3ddevice, sys->regionQuad.generic.textureFormat,
                                           d3dquad->picSys.texture, 0,
                                           d3dquad->picSys.renderSrc)) {
                msg_Err(vd, "Failed to create %dx%d shader view for OSD",
                        r->p_picture->format.i_width, r->p_picture->format.i_height);
                delete d3dquad;
                i++;
                continue;
            }
            video_format_Copy(&d3dquad->quad_fmt, &r->p_picture->format);
            d3dquad->generic.i_width  = r->p_picture->format.i_width;
            d3dquad->generic.i_height = r->p_picture->format.i_height;

            d3dquad->generic.textureFormat = sys->regionQuad.generic.textureFormat;
            err = D3D11_AllocateQuad(vd, sys->d3d_dev, PROJECTION_MODE_RECTANGULAR, d3dquad);
            if (err != VLC_SUCCESS)
            {
                msg_Err(vd, "Failed to allocate %dx%d quad for OSD",
                             r->p_picture->format.i_width, r->p_picture->format.i_height);
                delete d3dquad;
                i++;
                continue;
            }

            err = D3D11_SetupQuad( vd, sys->d3d_dev, &r->p_picture->format, d3dquad, &sys->display );
            if (err != VLC_SUCCESS) {
                msg_Err(vd, "Failed to setup %dx%d quad for OSD",
                        r->p_picture->format.i_width, r->p_picture->format.i_height);
                delete d3dquad;
                i++;
                continue;
            }
            const auto picres = [](picture_sys_d3d11_t * p_sys){
                picture_resource_t res = {};
                res.p_sys      = p_sys;
                res.pf_destroy = DestroyPictureQuad;
                return res;
            }((picture_sys_d3d11_t *) d3dquad);
            video_format_t no_crop = r->p_picture->format;
            no_crop.i_x_offset = no_crop.i_y_offset = 0;
            no_crop.i_visible_width = no_crop.i_width;
            no_crop.i_visible_height = no_crop.i_height;
            (*region)[i] = picture_NewFromResource(&no_crop, &picres);
            if ((*region)[i] == NULL) {
                msg_Err(vd, "Failed to create %dx%d picture for OSD",
                        r->p_picture->format.i_width, r->p_picture->format.i_height);
                d3dquad->Reset();
                i++;
                continue;
            }
            quad_picture = (*region)[i];
            for (size_t j=0; j<ARRAY_SIZE(sys->regionQuad.d3dpixelShader); j++)
            {
                /* TODO use something more accurate if we have different formats */
                if (sys->regionQuad.d3dpixelShader[j])
                {
                    d3dquad->d3dpixelShader[j] = sys->regionQuad.d3dpixelShader[j];
                }
            }
        }

        hr = sys->d3d_dev->d3dcontext->Map(quad->picSys.resource[KNOWN_DXGI_INDEX], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
        if( SUCCEEDED(hr) ) {
            picture_UpdatePlanes(quad_picture, static_cast<uint8_t*>(mappedResource.pData), mappedResource.RowPitch);

            picture_CopyPixels(quad_picture, r->p_picture);

            sys->d3d_dev->d3dcontext->Unmap(quad->picSys.resource[KNOWN_DXGI_INDEX], 0);
        } else {
            msg_Err(vd, "Failed to map the SPU texture (hr=0x%lX)", hr );
            picture_Release(quad_picture);
            if ((*region)[i] == quad_picture)
                (*region)[i] = NULL;
            i++;
            continue;
        }

        vout_display_place_t render_place = r->place;
        switch (render_orientation)
        {
        case TRANSFORM_R180: /* 180° */
            render_place.y = vd->cfg->display.height - render_place.y - render_place.height;
            render_place.x = vd->cfg->display.width - render_place.x - render_place.width;
            break;
        case TRANSFORM_VFLIP:
            render_place.y = vd->cfg->display.height - render_place.y - render_place.height;
            break;
        case TRANSFORM_HFLIP:
            render_place.x = vd->cfg->display.width - render_place.x - render_place.width;
            break;
        case TRANSFORM_ANTI_TRANSPOSE:
        case TRANSFORM_TRANSPOSE:
        case TRANSFORM_R90: /* 90° anti clockwise */
        case TRANSFORM_R270: /* 90° clockwise */
        case TRANSFORM_IDENTITY:
        default:
            break;
        }

        D3D11_UpdateQuadPosition(vd, sys->d3d_dev, quad,
            render_orientation);

        quad->UpdateViewport( &render_place, sys->display.pixelFormat );

        D3D11_UpdateQuadOpacity(vd, sys->d3d_dev, quad, r->i_alpha / 255.0f );
        i++;
    }
    return VLC_SUCCESS;
}

Messung
CHZG
83969490

¤ Dauer der Verarbeitung: 0.37 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104852
#Domains=417136