Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/video_output/win32/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 64 kB image not shown  

Quelle  direct3d9.c   Sprache: C

 

/*****************************************************************************
 * direct3d9.c: Windows Direct3D9 video output module
 *****************************************************************************
 * Copyright (C) 2006-2014 VLC authors and VideoLAN
 *
 * Authors: Martell Malone <martellmalone@gmail.com>,
 *          Damien Fouilleul <damienf@videolan.org>,
 *          Sasha Koruga <skoruga@gmail.com>,
 *          Felix Abecassis <felix.abecassis@gmail.com>
 *
 * 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.
 *****************************************************************************/


/*****************************************************************************
 * Preamble:
 *
 * This plugin will use YUV surface if supported, using YUV will result in
 * the best video quality (hardware filtering when rescaling the picture)
 * and the fastest display as it requires less processing.
 *
 * If YUV overlay is not supported this plugin will use RGB offscreen video
 * surfaces that will be blitted onto the primary surface (display) to
 * effectively display the pictures.
 *
 *****************************************************************************/

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

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout_display.h>
#include <vlc_fs.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 <windows.h>
#include <d3d9.h>
#ifdef HAVE_D3DX9EFFECT_H
#include <d3dx9effect.h>
#endif
#include "../../video_chroma/d3d9_fmt.h"
#include <dxvahd.h>

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

#include <assert.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

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

#define DESKTOP_LONGTEXT N_(\
    "The desktop mode allows you to display the video on the desktop.")

#define HW_BLENDING_TEXT N_("Use hardware blending support")
#define HW_BLENDING_LONGTEXT N_(\
    "Try to use hardware acceleration for subtitle/OSD blending.")
#define HW_YUV_TEXT N_("Use hardware YUV to RGB conversions")
#define HW_YUV_LONGTEXT N_(\
    "Try to use hardware acceleration for YUV to RGB conversions. " \
    "This option doesn't have any effect when using overlays.")

#define PIXEL_SHADER_TEXT N_("Pixel Shader")
#define PIXEL_SHADER_LONGTEXT N_(\
        "Choose a pixel shader to apply.")
#define PIXEL_SHADER_FILE_TEXT N_("Path to HLSL file")
#define PIXEL_SHADER_FILE_LONGTEXT N_("Path to an HLSL file containing a single pixel shader.")
/* The latest option in the selection list: used for loading a shader file. */
#define SELECTED_SHADER_FILE N_("HLSL File")

#define D3D9_HELP N_("Recommended video output for Windows Vista and later versions")

vlc_module_begin ()
    set_shortname("Direct3D9")
    set_description(N_("Direct3D9 video output"))
    set_help(D3D9_HELP)
    set_subcategory(SUBCAT_VIDEO_VOUT)

    add_bool("direct3d9-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT)
    add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT)

    add_string("direct3d9-shader", "", PIXEL_SHADER_TEXT, PIXEL_SHADER_LONGTEXT)
    add_loadfile("direct3d9-shader-file", NULL,
                 PIXEL_SHADER_FILE_TEXT, PIXEL_SHADER_FILE_LONGTEXT)

    add_bool("direct3d9-dxvahd", true, DXVAHD_TEXT, NULL)

    add_shortcut("direct3d9", "direct3d")
    set_callback_display(Open, 280)
vlc_module_end ()

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/

static const vlc_fourcc_t d3d_subpicture_chromas[] = {
    VLC_CODEC_RGBA,
    0
};

typedef struct vout_display_sys_t
{
    struct event_thread_t    *video_wnd;
    bool                     place_changed;

    bool allow_hw_yuv;    /* Should we use hardware YUV->RGB conversions */

    // core objects
    d3d9_decoder_device_t  *d3d9_device;
    vlc_decoder_device     *dec_device; // if d3d9_decoder comes from a decoder device

    D3DFORMAT               BufferFormat;

    HINSTANCE               hxdll;      /* handle of the opened d3d9x dll */
    IDirect3DPixelShader9*  d3dx_shader;

    UINT                    texture_width;
    UINT                    texture_height;

    // scene objects
    IDirect3DTexture9       *sceneTexture;
    IDirect3DVertexBuffer9  *sceneVertexBuffer;
    D3DFORMAT               d3dregion_format;    /* Backbuffer output format */
    size_t                  d3dregion_count;    /* for subpictures */
    struct d3d_region_t     *d3dregion;         /* for subpictures */

    const d3d9_format_t     *sw_texture_fmt;  /* Rendering texture(s) format */
    IDirect3DSurface9       *dx_render;

    /* */
    bool                    reset_device;
    bool                    lost_not_ready;
    bool                    clear_scene;

    /* outside rendering */
    void *outside_opaque;
    libvlc_video_update_output_cb            updateOutputCb;
    libvlc_video_swap_cb                     swapCb;
    libvlc_video_makeCurrent_cb              startEndRenderingCb;

    /* range converter */
    struct {
        IDXVAHD_VideoProcessor *proc;
    } processor;
} vout_display_sys_t;

/* */
typedef struct
{
    FLOAT       x,y,z;      // vertex untransformed position
    FLOAT       rhw;        // eye distance
    D3DCOLOR    diffuse;    // diffuse color
    FLOAT       tu, tv;     // texture relative coordinates
} CUSTOMVERTEX;
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)

typedef struct d3d_region_t {
    D3DFORMAT          format; // for subpictures
    unsigned           width;  // for pixel shaders
    unsigned           height; // for pixel shaders
    CUSTOMVERTEX       vertex[4];
    IDirect3DTexture9  *texture;
} d3d_region_t;

/* */
static HINSTANCE Direct3D9LoadShaderLibrary(void)
{
    HINSTANCE instance = NULL;
    for (int i = 43; i > 23; --i) {
        char filename[16];
        _snprintf(filename, ARRAY_SIZE(filename), "D3dx9_%d.dll", i);
        instance = LoadLibraryExA(filename, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
        if (instance)
            break;
    }
    return instance;
}

/**
 * Compute the vertex ordering needed to rotate the video. Without
 * rotation, the vertices of the rectangle are defined in a clockwise
 * order. This function computes a remapping of the coordinates to
 * implement the rotation, given fixed texture coordinates.
 * The unrotated order is the following:
 * 0--1
 * |  |
 * 3--2
 * For a 180 degrees rotation it should like this:
 * 2--3
 * |  |
 * 1--0
 * Vertex 0 should be assigned coordinates at index 2 from the
 * unrotated order and so on, thus yielding order: 2 3 0 1.
 */

static void orientationVertexOrder(video_orientation_t orientation, int vertex_order[static 4])
{
    switch (orientation) {
        case ORIENT_ROTATED_90:      /* ORIENT_RIGHT_TOP */
            vertex_order[0] = 1;
            vertex_order[1] = 2;
            vertex_order[2] = 3;
            vertex_order[3] = 0;
            break;
        case ORIENT_ROTATED_270:     /* ORIENT_LEFT_BOTTOM */
            vertex_order[0] = 3;
            vertex_order[1] = 0;
            vertex_order[2] = 1;
            vertex_order[3] = 2;
            break;
        case ORIENT_ROTATED_180:     /* ORIENT_BOTTOM_RIGHT */
            vertex_order[0] = 2;
            vertex_order[1] = 3;
            vertex_order[2] = 0;
            vertex_order[3] = 1;
            break;
        case ORIENT_TRANSPOSED:      /* ORIENT_LEFT_TOP */
            vertex_order[0] = 0;
            vertex_order[1] = 3;
            vertex_order[2] = 2;
            vertex_order[3] = 1;
            break;
        case ORIENT_HFLIPPED:        /* ORIENT_TOP_RIGHT */
            vertex_order[0] = 1;
            vertex_order[1] = 0;
            vertex_order[2] = 3;
            vertex_order[3] = 2;
            break;
        case ORIENT_VFLIPPED:        /* ORIENT_BOTTOM_LEFT */
            vertex_order[0] = 3;
            vertex_order[1] = 2;
            vertex_order[2] = 1;
            vertex_order[3] = 0;
            break;
        case ORIENT_ANTI_TRANSPOSED: /* ORIENT_RIGHT_BOTTOM */
            vertex_order[0] = 2;
            vertex_order[1] = 1;
            vertex_order[2] = 0;
            vertex_order[3] = 3;
            break;
       default:
            vertex_order[0] = 0;
            vertex_order[1] = 1;
            vertex_order[2] = 2;
            vertex_order[3] = 3;
            break;
    }
}

static void  Direct3D9SetupVertices(CUSTOMVERTEX *vertices,
                                  const RECT *full_texture, const video_format_t *src_fmt,
                                  const vout_display_place_t *place_in_display,
                                  int alpha,
                                  video_orientation_t orientation)
{
    RECT rect_in_display = {
        .left   = place_in_display->x,
        .right  = place_in_display->x + place_in_display->width,
        .top    = place_in_display->y,
        .bottom = place_in_display->y + place_in_display->height,
    };

    RECT visible_texture = {
        .left   = src_fmt->i_x_offset,
        .right  = src_fmt->i_x_offset + src_fmt->i_visible_width,
        .top    = src_fmt->i_y_offset,
        .bottom = src_fmt->i_y_offset + src_fmt->i_visible_height,
    };

    /* Vertices of the dst rectangle in the unrotated (clockwise) order. */
    const int vertices_coords[4][2] = {
        { rect_in_display.left,  rect_in_display.top    },
        { rect_in_display.right, rect_in_display.top    },
        { rect_in_display.right, rect_in_display.bottom },
        { rect_in_display.left,  rect_in_display.bottom },
    };

    /* Compute index remapping necessary to implement the rotation. */
    int vertex_order[4];
    orientationVertexOrder(orientation, vertex_order);

    for (int i = 0; i < 4; ++i) {
        vertices[i].x  = vertices_coords[vertex_order[i]][0];
        vertices[i].y  = vertices_coords[vertex_order[i]][1];
    }

    float texture_right  = (float)visible_texture.right / (float)full_texture->right;
    float texture_left   = (float)visible_texture.left  / (float)full_texture->right;
    float texture_top    = (float)visible_texture.top    / (float)full_texture->bottom;
    float texture_bottom = (float)visible_texture.bottom / (float)full_texture->bottom;

    vertices[0].tu = texture_left;
    vertices[0].tv = texture_top;

    vertices[1].tu = texture_right;
    vertices[1].tv = texture_top;

    vertices[2].tu = texture_right;
    vertices[2].tv = texture_bottom;

    vertices[3].tu = texture_left;
    vertices[3].tv = texture_bottom;

    for (int i = 0; i < 4; i++) {
        /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
        /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
        vertices[i].x -= 0.5;
        vertices[i].y -= 0.5;

        vertices[i].z       = 0.0f;
        vertices[i].rhw     = 1.0f;
        vertices[i].diffuse = D3DCOLOR_ARGB(alpha, 255, 255, 255);
    }
}

/**
 * It copies picture surface into a texture and setup the associated d3d_region_t.
 */

static int Direct3D9ImportPicture(vout_display_t *vd,
                                 d3d_region_t *region,
                                 IDirect3DSurface9 *source)
{
    vout_display_sys_t *sys = vd->sys;
    HRESULT hr;

    if (!source) {
        msg_Dbg(vd, "no surface to render?");
        return VLC_EGENERIC;
    }

    /* retrieve texture top-level surface */
    IDirect3DSurface9 *destination;
    hr = IDirect3DTexture9_GetSurfaceLevel(sys->sceneTexture, 0, &destination);
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed IDirect3DTexture9_GetSurfaceLevel: 0x%lX", hr);
        return VLC_EGENERIC;
    }

    /* Copy picture surface into texture surface
        * color space conversion happen here */

    RECT source_visible_rect = {
        .left   = vd->source->i_x_offset,
        .right  = vd->source->i_x_offset + vd->source->i_visible_width,
        .top    = vd->source->i_y_offset,
        .bottom = vd->source->i_y_offset + vd->source->i_visible_height,
    };
    RECT texture_visible_rect = {
        .left   = 0,
        .right  = sys->texture_width,
        .top    = 0,
        .bottom = sys->texture_height,
    };
    // On nVidia & AMD, StretchRect will fail if the visible size isn't even.
    // When copying the entire buffer, the margin end up being blended in the actual picture
    // on nVidia (regardless of even/odd dimensions)
    if (source_visible_rect.right & 1)
    {
        source_visible_rect.right++;
    }
    if (source_visible_rect.bottom & 1)
    {
        source_visible_rect.bottom++;
    }

    if (sys->processor.proc)
    {
        DXVAHD_STREAM_DATA inputStream = { 0 };
        inputStream.Enable = TRUE;
        inputStream.pInputSurface = source;
        hr = IDXVAHD_VideoProcessor_VideoProcessBltHD( sys->processor.proc, destination, 0, 1, &inputStream&nbsp;);
        if (FAILED(hr)) {
            D3DSURFACE_DESC srcDesc, dstDesc;
            IDirect3DSurface9_GetDesc(source, &srcDesc);
            IDirect3DSurface9_GetDesc(destination, &dstDesc);

            msg_Dbg(vd, "Failed VideoProcessBltHD src:%4.4s (%d) dst:%4.4s (%d) (hr=0x%lX)",
                    (const char*)&srcDesc.Format, srcDesc.Format,
                    (const char*)&dstDesc.Format, dstDesc.Format, hr);
        }
    }
    else
    {
        hr = IDirect3DDevice9_StretchRect(sys->d3d9_device->d3ddev.dev, source, &source_visible_rect,
                                        destination, &texture_visible_rect,
                                        D3DTEXF_NONE);
        if (FAILED(hr)) {
            msg_Dbg(vd, "Failed StretchRect: source 0x%p. (hr=0x%lX)",
                    (LPVOID)source, hr);
        }
    }
    IDirect3DSurface9_Release(destination);
    if (FAILED(hr))
        return VLC_EGENERIC;

    /* */
    region->texture = sys->sceneTexture;
    Direct3D9SetupVertices(region->vertex, &texture_visible_rect, vd->source,
                           vd->place, 255, vd->source->orientation);
    return VLC_SUCCESS;
}

static void Direct3D9DeleteRegions(size_t count, d3d_region_t *region)
{
    for (size_t i = 0; i < count; i++) {
        if (region[i].texture)
            IDirect3DTexture9_Release(region[i].texture);
    }
    free(region);
}

/**
 * It releases the scene resources.
 */

static void Direct3D9DestroyScene(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;

    Direct3D9DeleteRegions(sys->d3dregion_count, sys->d3dregion);
    sys->d3dregion_count = 0;
    sys->d3dregion       = NULL;

    if (sys->sceneVertexBuffer)
    {
        IDirect3DVertexBuffer9_Release(sys->sceneVertexBuffer);
        sys->sceneVertexBuffer = NULL;
    }

    if (sys->sceneTexture)
    {
        IDirect3DTexture9_Release(sys->sceneTexture);
        sys->sceneTexture = NULL;
    }

    msg_Dbg(vd, "Direct3D9 scene released successfully");
}

static void Direct3D9DestroyShaders(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;

    if (sys->d3dx_shader)
    {
        IDirect3DPixelShader9_Release(sys->d3dx_shader);
        sys->d3dx_shader = NULL;
    }
}

/**
 * It destroys the picture and scene resources.
 */

static void Direct3D9DestroyResources(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;

    Direct3D9DestroyScene(vd);
    if (sys->dx_render)
    {
        IDirect3DSurface9_Release(sys->dx_render);
        sys->dx_render = NULL;
    }
    Direct3D9DestroyShaders(vd);
}

static int UpdateOutput(vout_display_t *vd, const video_format_t *fmt,
                        libvlc_video_output_cfg_t *out)
{
    vout_display_sys_t *sys = vd->sys;
    libvlc_video_render_cfg_t cfg;
    cfg.width  = vd->cfg->display.width;
    cfg.height = vd->cfg->display.height;

    switch (fmt->i_chroma)
    {
    case VLC_CODEC_D3D9_OPAQUE:
        cfg.bitdepth = 8;
        break;
    case VLC_CODEC_D3D9_OPAQUE_10B:
        cfg.bitdepth = 10;
        break;
    default:
        {
            const vlc_chroma_description_t *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 = fmt->color_range == COLOR_RANGE_FULL;
    cfg.primaries  = (libvlc_video_color_primaries_t) fmt->primaries;
    cfg.colorspace = (libvlc_video_color_space_t) fmt->space;
    cfg.transfer   = (libvlc_video_transfer_func_t) fmt->transfer;

    cfg.device = sys->d3d9_device->d3ddev.dev;

    libvlc_video_output_cfg_t local_out;
    if (out == NULL)
        out = &local_out;
    if (!sys->updateOutputCb( sys->outside_opaque, &cfg, out ))
    {
        msg_Err(vd, "Failed to set the external render size");
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

/**
 * It allocates and initializes the resources needed to render the scene.
 */

static int Direct3D9CreateScene(vout_display_t *vd, const video_format_t *fmt)
{
    vout_display_sys_t *sys = vd->sys;
    IDirect3DDevice9        *d3ddev = sys->d3d9_device->d3ddev.dev;
    HRESULT hr;

    sys->texture_width  = fmt->i_visible_width;
    sys->texture_height = fmt->i_visible_height;
    // On nVidia & AMD, StretchRect will fail if the visible size isn't even.
    // When copying the entire buffer, the margin end up being blended in the actual picture
    // on nVidia (regardless of even/odd dimensions)
    if (sys->texture_height & 1) sys->texture_height++;
    if (sys->texture_width  & 1) sys->texture_width++;

    /*
     * Create a texture for use when rendering a scene
     * for performance reason, texture format is identical to backbuffer
     * which would usually be a RGB format
     */

    hr = IDirect3DDevice9_CreateTexture(d3ddev,
                                        sys->texture_width,
                                        sys->texture_height,
                                        1,
                                        D3DUSAGE_RENDERTARGET,
                                        sys->BufferFormat,
                                        D3DPOOL_DEFAULT,
                                        &sys->sceneTexture,
                                        NULL);
    if (FAILED(hr)) {
        msg_Err(vd, "Failed to create texture. (hr=0x%lX)", hr);
        return VLC_EGENERIC;
    }

#ifndef NDEBUG
    msg_Dbg(vd, "Direct3D created texture: %ix%i", sys->texture_width, sys->texture_height);
#endif

    /*
    ** Create a vertex buffer for use when rendering scene
    */

    hr = IDirect3DDevice9_CreateVertexBuffer(d3ddev,
                                             sizeof(CUSTOMVERTEX)*4,
                                             D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
                                             D3DFVF_CUSTOMVERTEX,
                                             D3DPOOL_DEFAULT,
                                             &sys->sceneVertexBuffer,
                                             NULL);
    if (FAILED(hr)) {
        msg_Err(vd, "Failed to create vertex buffer. (hr=0x%lX)", hr);
        IDirect3DTexture9_Release(sys->sceneTexture);
        sys->sceneTexture = NULL;
        return VLC_EGENERIC;
    }

    // we use FVF instead of vertex shader
    hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed SetFVF: 0x%lX", hr);
        return -1;
    }

    /* */
    sys->d3dregion_count = 0;
    sys->d3dregion       = NULL;

    sys->clear_scene = true;

    // Texture coordinates outside the range [0.0, 1.0] are set
    // to the texture color at 0.0 or 1.0, respectively.
    IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);

    // Set linear filtering quality
    if (sys->d3d9_device->d3ddev.caps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) {
        //msg_Dbg(vd, "Using D3DTEXF_LINEAR for minification");
        IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    } else {
        //msg_Dbg(vd, "Using D3DTEXF_POINT for minification");
        IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
    }
    if (sys->d3d9_device->d3ddev.caps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) {
        //msg_Dbg(vd, "Using D3DTEXF_LINEAR for magnification");
        IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
    } else {
        //msg_Dbg(vd, "Using D3DTEXF_POINT for magnification");
        IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
    }

    // set maximum ambient light
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));

    // Turn off culling
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);

    // Turn off the zbuffer
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);

    // Turn off lights
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_LIGHTING, FALSE);

    // Enable dithering
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DITHERENABLE, TRUE);

    // disable stencil
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_STENCILENABLE, FALSE);

    // manage blending
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
    IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);

    if (sys->d3d9_device->d3ddev.caps.AlphaCmpCaps & D3DPCMPCAPS_GREATER) {
        IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
        IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAREF, 0x00);
        IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
    }

    // Set texture states
    IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLOROP,D3DTOP_SELECTARG1);
    IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);

    IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
    IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
    IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);

    msg_Dbg(vd, "Direct3D9 scene created successfully");

    return VLC_SUCCESS;
}

#ifdef HAVE_D3DX9EFFECT_H
static int Direct3D9CompileShader(vout_display_t *vd, const char *shader_source, size_t source_length)
{
    vout_display_sys_t *sys = vd->sys;

    typedef HRESULT (WINAPI * OurD3DXCompileShader_ptr)(
            LPCSTR pSrcData,
            UINT srcDataLen,
            const D3DXMACRO *pDefines,
            LPD3DXINCLUDE pInclude,
            LPCSTR pFunctionName,
            LPCSTR pProfile,
            DWORD Flags,
            LPD3DXBUFFER *ppShader,
            LPD3DXBUFFER *ppErrorMsgs,
            LPD3DXCONSTANTTABLE *ppConstantTable);

    OurD3DXCompileShader_ptr OurD3DXCompileShader;
    OurD3DXCompileShader = (OurD3DXCompileShader_ptr)GetProcAddress(sys->hxdll, "D3DXCompileShader");
    if (!OurD3DXCompileShader) {
        msg_Warn(vd, "Cannot locate reference to D3DXCompileShader; pixel shading will be disabled");
        return VLC_EGENERIC;
    }

    LPD3DXBUFFER error_msgs = NULL;
    LPD3DXBUFFER compiled_shader = NULL;

    DWORD shader_flags = 0;
    HRESULT hr = OurD3DXCompileShader(shader_source, source_length, NULL, NULL,
                "main", "ps_3_0", shader_flags, &compiled_shader, &error_msgs, NULL);

    if (FAILED(hr)) {
        msg_Warn(vd, "D3DXCompileShader Error (hr=0x%lX)", hr);
        if (error_msgs) {
            msg_Warn(vd, "HLSL Compilation Error: %s", (char*)ID3DXBuffer_GetBufferPointer(error_msgs));
            ID3DXBuffer_Release(error_msgs);
    }
        return VLC_EGENERIC;
    }

    hr = IDirect3DDevice9_CreatePixelShader(sys->d3d9_device->d3ddev.dev,
            ID3DXBuffer_GetBufferPointer(compiled_shader),
            &sys->d3dx_shader);

    if (compiled_shader)
        ID3DXBuffer_Release(compiled_shader);
    if (error_msgs)
        ID3DXBuffer_Release(error_msgs);

    if (FAILED(hr)) {
        msg_Warn(vd, "IDirect3DDevice9_CreatePixelShader error (hr=0x%lX)", hr);
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}
#endif

#define MAX_SHADER_FILE_SIZE  (1024*1024)

static int Direct3D9CreateShaders(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;

    if (!sys->hxdll)
        return VLC_EGENERIC;

    /* Find which shader was selected in the list. */
    char *selected_shader = var_InheritString(vd, "direct3d9-shader");
    if (!selected_shader)
        return VLC_SUCCESS; /* Nothing to do */

#ifdef HAVE_D3DX9EFFECT_H
    const char *shader_source_builtin = NULL;
    char *shader_source_file = NULL;
    FILE *fs = NULL;

    for (size_t i = 0; i < BUILTIN_SHADERS_COUNT; ++i) {
        if (!strcmp(selected_shader, builtin_shaders[i].name)) {
            shader_source_builtin = builtin_shaders[i].code;
            break;
        }
    }

    if (shader_source_builtin) {
        /* A builtin shader was selected. */
        int err = Direct3D9CompileShader(vd, shader_source_builtin, strlen(shader_source_builtin));
        if (err)
            goto error;
    } else {
        if (strcmp(selected_shader, SELECTED_SHADER_FILE))
            goto error; /* Unrecognized entry in the list. */
        /* The source code of the shader needs to be read from a file. */
        char *filepath = var_InheritString(vd, "direct3d9-shader-file");
        if (!filepath)
            goto error;
        /* Open file, find its size with fseek/ftell and read its content in a buffer. */
        fs = vlc_fopen(filepath, "rb");
        free(filepath);
        if (!fs)
            goto error;
        int ret = fseek(fs, 0, SEEK_END);
        if (ret == -1)
            goto error;
        long length = ftell(fs);
        if (length == -1 || length >= MAX_SHADER_FILE_SIZE)
            goto error;
        rewind(fs);
        shader_source_file = vlc_alloc(length, sizeof(*shader_source_file));
        if (!shader_source_file)
            goto error;
        ret = fread(shader_source_file, length, 1, fs);
        if (ret != 1)
            goto error;
        ret = Direct3D9CompileShader(vd, shader_source_file, length);
        if (ret)
            goto error;
    }

    free(selected_shader);
    free(shader_source_file);
    fclose(fs);

    return VLC_SUCCESS;

error:
    free(shader_source_file);
    if (fs)
        fclose(fs);
#endif // HAVE_D3DX9EFFECT_H
    Direct3D9DestroyShaders(vd);
    free(selected_shader);
    return VLC_EGENERIC;
}

/**
 * It creates the picture and scene resources.
 */

static int Direct3D9CreateResources(vout_display_t *vd, const video_format_t *fmt)
{
    vout_display_sys_t *sys = vd->sys;

    if (Direct3D9CreateScene(vd, fmt)) {
        msg_Err(vd, "Direct3D scene initialization failed !");
        return VLC_EGENERIC;
    }
    if (Direct3D9CreateShaders(vd)) {
        /* Failing to initialize shaders is not fatal. */
        msg_Warn(vd, "Direct3D shaders initialization failed !");
    }

    sys->d3dregion_format = D3DFMT_UNKNOWN;
    for (int i = 0; i < 2; i++) {
        D3DFORMAT dfmt = i == 0 ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8;
        if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(sys->d3d9_device->hd3d.obj,
                                                   sys->d3d9_device->d3ddev.adapterId,
                                                   D3DDEVTYPE_HAL,
                                                   sys->BufferFormat,
                                                   D3DUSAGE_DYNAMIC,
                                                   D3DRTYPE_TEXTURE,
                                                   dfmt))) {
            sys->d3dregion_format = dfmt;
            break;
        }
    }

    if( !is_d3d9_opaque( fmt->i_chroma ) )
    {
        HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(sys->d3d9_device->d3ddev.dev,
                                                          fmt->i_width,
                                                          fmt->i_height,
                                                          sys->sw_texture_fmt->format,
                                                          D3DPOOL_DEFAULT,
                                                          &sys->dx_render,
                                                          NULL);
        if (FAILED(hr)) {
           msg_Err(vd, "Failed to allocate offscreen surface (hr=0x%lX)", hr);
           return VLC_EGENERIC;
        }
    }

    return VLC_SUCCESS;
}

/**
 * It reset the Direct3D9 device and its resources.
 */

static int Direct3D9Reset(vout_display_t *vd, const video_format_t *fmtp)
{
    vout_display_sys_t *sys = vd->sys;

    int res = D3D9_ResetDevice( VLC_OBJECT(vd), sys->d3d9_device );
    if (res != VLC_SUCCESS)
        return res;

    /* release all D3D objects */
    Direct3D9DestroyResources(vd);

    if (UpdateOutput(vd, fmtp, NULL) != VLC_SUCCESS)
        return VLC_EGENERIC;

    /* re-create them */
    if (Direct3D9CreateResources(vd, fmtp)) {
        msg_Dbg(vd, "Direct3D9CreateResources failed !");
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

static void Direct3D9ImportSubpicture(vout_display_t *vd,
                                     size_t *count_ptr, d3d_region_t **region,
                                     const vlc_render_subpicture *subpicture)
{
    vout_display_sys_t *sys = vd->sys;

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

    *count_ptr = count;
    *region    = calloc(count, sizeof(**region));
    if (*region == NULL) {
        *count_ptr = 0;
        return;
    }

    int i = 0;
    vlc_vector_foreach(r, &subpicture->regions) {
        d3d_region_t *d3dr = &(*region)[i];
        HRESULT hr;

        d3dr->texture = NULL;
        for (size_t j = 0; j < sys->d3dregion_count; j++) {
            d3d_region_t *cache = &sys->d3dregion[j];
            if (cache->texture &&
                cache->format == sys->d3dregion_format &&
                cache->width  == r->p_picture->format.i_width &&
                cache->height == r->p_picture->format.i_height) {
                *d3dr = *cache;
                memset(cache, 0, sizeof(*cache));
                sys->clear_scene = true;
                break;
            }
        }
        if (!d3dr->texture) {
            d3dr->format = sys->d3dregion_format;
            d3dr->width  = r->p_picture->format.i_width;
            d3dr->height = r->p_picture->format.i_height;
            hr = IDirect3DDevice9_CreateTexture(sys->d3d9_device->d3ddev.dev,
                                                d3dr->width, d3dr->height,
                                                1,
                                                D3DUSAGE_DYNAMIC,
                                                d3dr->format,
                                                D3DPOOL_DEFAULT,
                                                &d3dr->texture,
                                                NULL);
            if (FAILED(hr)) {
                d3dr->texture = NULL;
                msg_Err(vd, "Failed to create %dx%d texture for OSD (hr=0x%lX)",
                        d3dr->width, d3dr->height, hr);
                i++;
                continue;
            }
#ifndef NDEBUG
            msg_Dbg(vd, "Created %dx%d texture for OSD",
                    r->p_picture->format.i_width, r->p_picture->format.i_height);
#endif
        }

        D3DLOCKED_RECT lock;
        hr = IDirect3DTexture9_LockRect(d3dr->texture, 0, &lock, NULL, 0);
        if (SUCCEEDED(hr)) {
            uint8_t  *dst_data   = lock.pBits;
            int       dst_pitch  = lock.Pitch;
            uint8_t  *src_data   = r->p_picture->p->p_pixels;
            int       src_pitch  = r->p_picture->p->i_pitch;

            if (d3dr->format == D3DFMT_A8B8G8R8 /*|| d3dr->format == D3DFMT_A8R8G8B8*/) {
                if (dst_pitch == r->p_picture->p->i_pitch) {
                    memcpy(dst_data, src_data, r->p_picture->format.i_height * dst_pitch);
                } else {
                    int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_pitch);
                    for (unsigned y = 0; y < r->p_picture->format.i_height; y++) {
                        memcpy(&dst_data[y * dst_pitch], &src_data[y * src_pitch], copy_pitch);
                    }
                }
            } else {
                int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_pitch);
                for (unsigned y = 0; y < r->p_picture->format.i_height; y++) {
                    for (int x = 0; x < copy_pitch; x += 4) {
                        dst_data[y * dst_pitch + x + 0] = src_data[y * src_pitch + x + 2];
                        dst_data[y * dst_pitch + x + 1] = src_data[y * src_pitch + x + 1];
                        dst_data[y * dst_pitch + x + 2] = src_data[y * src_pitch + x + 0];
                        dst_data[y * dst_pitch + x + 3] = src_data[y * src_pitch + x + 3];
                    }
                }
            }
            hr = IDirect3DTexture9_UnlockRect(d3dr->texture, 0);
            if (FAILED(hr))
                msg_Err(vd, "Failed to unlock the texture");
        } else {
            msg_Err(vd, "Failed to lock the texture");
        }

        /* Map the subpicture to sys->sys.sys.place */
        RECT texture_rect;
        texture_rect.left   = 0;
        texture_rect.right  = r->p_picture->format.i_width;
        texture_rect.top    = 0;
        texture_rect.bottom = r->p_picture->format.i_height;

        Direct3D9SetupVertices(d3dr->vertex, &texture_rect, &r->p_picture->format,
                              &r->place, r->i_alpha, ORIENT_NORMAL);
        i++;
    }
}

static int Direct3D9RenderRegion(vout_display_t *vd,
                                const d3d_region_t *region,
                                bool use_pixel_shader)
{
    vout_display_sys_t *sys = vd->sys;

    IDirect3DDevice9 *d3ddev = sys->d3d9_device->d3ddev.dev;

    HRESULT hr;

    /* Import vertices */
    void *vertex;
    hr = IDirect3DVertexBuffer9_Lock(sys->sceneVertexBuffer, 0, 0, &vertex, D3DLOCK_DISCARD);
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed IDirect3DVertexBuffer9_Lock: 0x%lX", hr);
        return -1;
    }
    memcpy(vertex, region->vertex, sizeof(region->vertex));
    hr = IDirect3DVertexBuffer9_Unlock(sys->sceneVertexBuffer);
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed IDirect3DVertexBuffer9_Unlock: 0x%lX", hr);
        return -1;
    }

    // Render the vertex buffer contents
    hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, sys->sceneVertexBuffer, 0, sizeof(CUSTOMVERTEX));
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed SetStreamSource: 0x%lX", hr);
        return -1;
    }

    // Setup our texture. Using textures introduces the texture stage states,
    // which govern how textures get blended together (in the case of multiple
    // textures) and lighting information. In this case, we are modulating
    // (blending) our texture with the diffuse color of the vertices.
    hr = IDirect3DDevice9_SetTexture(d3ddev, 0, (IDirect3DBaseTexture9*)region->texture);
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed SetTexture: 0x%lX", hr);
        return -1;
    }

    if (sys->d3dx_shader) {
        if (use_pixel_shader)
        {
            hr = IDirect3DDevice9_SetPixelShader(d3ddev, sys->d3dx_shader);
            float shader_data[4] = { region->width, region->height, 0, 0 };
            hr = IDirect3DDevice9_SetPixelShaderConstantF(d3ddev, 0, shader_data, 1);
            if (FAILED(hr)) {
                msg_Dbg(vd, "Failed SetPixelShaderConstantF: 0x%lX", hr);
                return -1;
            }
        }
        else /* Disable any existing pixel shader. */
            hr = IDirect3DDevice9_SetPixelShader(d3ddev, NULL);
        if (FAILED(hr)) {
            msg_Dbg(vd, "Failed SetPixelShader: 0x%lX", hr);
            return -1;
        }
    }

    // draw rectangle
    hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed DrawPrimitive: 0x%lX", hr);
        return -1;
    }
    return 0;
}

/**
 * It renders the scene.
 *
 * This function is intented for higher end 3D cards, with pixel shader support
 * and at least 64 MiB of video RAM.
 */

static void Direct3D9RenderScene(vout_display_t *vd,
                                d3d_region_t *picture,
                                size_t subpicture_count,
                                d3d_region_t *subpicture)
{
    vout_display_sys_t *sys = vd->sys;
    IDirect3DDevice9 *d3ddev = sys->d3d9_device->d3ddev.dev;
    HRESULT hr;

    if (sys->startEndRenderingCb && !sys->startEndRenderingCb( sys->outside_opaque, true ))
        return;

    if (sys->clear_scene || sys->d3dregion_count != subpicture_count) {
        /* Clear the backbuffer and the zbuffer */
        hr = IDirect3DDevice9_Clear(d3ddev, 0, NULL, D3DCLEAR_TARGET,
                                  D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
        if (FAILED(hr)) {
            msg_Dbg(vd, "Failed Clear: 0x%lX", hr);
            return;
        }
        sys->clear_scene = false;
    }

    hr = IDirect3DDevice9_BeginScene(d3ddev);
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed BeginScene: 0x%lX", hr);
        return;
    }

    Direct3D9RenderRegion(vd, picture, true);

    if (subpicture_count)
    {
        IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
        for (size_t i = 0; i < subpicture_count; i++) {
            d3d_region_t *r = &subpicture[i];
            if (r->texture)
                Direct3D9RenderRegion(vd, r, false);
        }
        IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
    }

    hr = IDirect3DDevice9_EndScene(d3ddev);
    if (FAILED(hr))
        msg_Dbg(vd, "Failed EndScene: 0x%lX", hr);

    if (sys->startEndRenderingCb)
        sys->startEndRenderingCb( sys->outside_opaque, false );
}

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

    /* Position Change */
    if (sys->place_changed) {
#if 0 /* need that when bicubic filter is available */
        RECT rect;
        UINT width, height;

        GetClientRect(CommonVideoHWND(sys->video_wnd), &rect);
        width  = RECTWidth(rect);
        height = RECTHeight(rect);

        if (width != p_sys->pp.BackBufferWidth || height != p_sys->pp.BackBufferHeight)
        {
            msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
            // need to reset D3D device to resize back buffer
            if (VLC_SUCCESS != Direct3D9ResetDevice(vd, width, height))
                return VLC_EGENERIC;
        }
#endif
        UpdateOutput(vd, vd->fmt, NULL);

        sys->clear_scene = true;
        sys->place_changed = false;
    }

    d3d9_device_t *p_d3d9_dev = &sys->d3d9_device->d3ddev;

    /* check if device is still available */
    HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(p_d3d9_dev->dev);
    if (FAILED(hr)) {
        if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
            sys->reset_device = true;
            /* FIXME what to do here in case of failure */
            if (Direct3D9Reset(vd, vd->fmt)) {
                msg_Err(vd, "Failed to reset device");
                return;
            }
            sys->reset_device = false;
            sys->lost_not_ready = false;
        }
        if (hr == D3DERR_DEVICELOST && !sys->lost_not_ready) {
            /* Device is lost but not yet ready for reset. */
            sys->lost_not_ready = true;
        }
        return;
    }

    /* FIXME it is a bit ugly, we need the surface to be unlocked for
     * rendering.
     *  The clean way would be to release the picture (and ensure that
     * the vout doesn't keep a reference). But because of the vout
     * wrapper, we can't */

    IDirect3DSurface9 *surface;

    if ( !is_d3d9_opaque(picture->format.i_chroma) )
    {
        D3DLOCKED_RECT d3drect;
        surface = sys->dx_render;
        hr = IDirect3DSurface9_LockRect(surface, &d3drect, NULL, 0);
        if (unlikely(FAILED(hr))) {
            msg_Err(vd, "failed to lock surface");
            return;
        }

        picture_t fake_pic = *picture;
        picture_UpdatePlanes(&fake_pic, d3drect.pBits, d3drect.Pitch);

        /* The dx/d3d buffer is always allocated as YV12 */
        if (fake_pic.format.i_chroma == VLC_CODEC_I420)
            picture_SwapUV( &fake_pic );

        picture_CopyPixels(&fake_pic, picture);
        IDirect3DSurface9_UnlockRect(surface);
    }
    else
    {
        const picture_sys_d3d9_t *picsys = ActiveD3D9PictureSys(picture);
        surface = picsys->surface;
        if (picsys->surface != surface)
        {
            D3DSURFACE_DESC srcDesc, dstDesc;
            IDirect3DSurface9_GetDesc(picsys->surface, &srcDesc);
            IDirect3DSurface9_GetDesc(surface, &dstDesc);
            if ( srcDesc.Width == dstDesc.Width && srcDesc.Height == dstDesc.Height )
                surface = picsys->surface;
            else
            {
                RECT visibleSource;
                visibleSource.left = 0;
                visibleSource.top = 0;
                visibleSource.right = picture->format.i_visible_width;
                visibleSource.bottom = picture->format.i_visible_height;

                hr = IDirect3DDevice9_StretchRect( p_d3d9_dev->dev, picsys->surface, &visibleSource, surface, &visibleSource, D3DTEXF_NONE);
                if (FAILED(hr)) {
                    msg_Err(vd, "Failed to copy the hw surface to the decoder surface (hr=0x%lX)", hr );
                }
            }
        }
    }

    d3d_region_t picture_region;
    if (!Direct3D9ImportPicture(vd, &picture_region, surface)) {
        picture_region.width = picture->format.i_visible_width;
        picture_region.height = picture->format.i_visible_height;
        size_t subpicture_region_count     = 0;
        d3d_region_t *subpicture_region = NULL;
        if (subpicture)
            Direct3D9ImportSubpicture(vd, &subpicture_region_count, &subpicture_region,
                                     subpicture);

        Direct3D9RenderScene(vd, &picture_region,
                            subpicture_region_count, subpicture_region);

        Direct3D9DeleteRegions(sys->d3dregion_count, sys->d3dregion);
        sys->d3dregion_count = subpicture_region_count;
        sys->d3dregion       = subpicture_region;
    }
}

static void Swap(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
    const d3d9_device_t *p_d3d9_dev = &sys->d3d9_device->d3ddev;

    // Present the back buffer contents to the display
    // No stretching should happen here !
    RECT src = {
        .left   = 0,
        .right  = vd->cfg->display.width,
        .top    = 0,
        .bottom = vd->cfg->display.height
    };

    HRESULT hr;
    if (sys->d3d9_device->hd3d.use_ex) {
        hr = IDirect3DDevice9Ex_PresentEx(p_d3d9_dev->devex, &src, &src, CommonVideoHWND(sys->video_wnd), NULL, 0);
    } else {
        hr = IDirect3DDevice9_Present(p_d3d9_dev->dev, &src, &src, CommonVideoHWND(sys->video_wnd), NULL);
    }
    if (FAILED(hr)) {
        msg_Dbg(vd, "Failed Present: 0x%lX", hr);
    }
}

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

    if (sys->lost_not_ready)
        return;

    sys->swapCb( sys->outside_opaque );
}

/**
 * It releases an instance of Direct3D9
 */

static void Direct3D9Destroy(vout_display_sys_t *sys)
{
    if (sys->processor.proc)
    {
        IDXVAHD_VideoProcessor_Release(sys->processor.proc);
    }
    if (sys->dec_device)
        vlc_decoder_device_Release(sys->dec_device);
    else if (sys->d3d9_device)
    {
        D3D9_ReleaseDevice(sys->d3d9_device);
    }
    if (sys->hxdll)
    {
        FreeLibrary(sys->hxdll);
        sys->hxdll = NULL;
    }
}

/**
 * It tests if the conversion from src to dst is supported.
 */

static int Direct3D9CheckConversion(vout_display_t *vd, D3DFORMAT src)
{
    vout_display_sys_t *sys = vd->sys;
    IDirect3D9 *d3dobj = sys->d3d9_device->hd3d.obj;
    D3DFORMAT dst = sys->BufferFormat;
    HRESULT hr;

    /* test whether device can create a surface of that format */
    hr = IDirect3D9_CheckDeviceFormat(d3dobj, sys->d3d9_device->d3ddev.adapterId,
                                      D3DDEVTYPE_HAL, dst, 0,
                                      D3DRTYPE_SURFACE, src);
    if (!SUCCEEDED(hr)) {
        if (D3DERR_NOTAVAILABLE != hr)
            msg_Err(vd, "Could not query adapter supported formats. (hr=0x%lX)", hr);
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

static const d3d9_format_t d3d_formats[] = {
    /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
    { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_YV12 },
    { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_I420 },
    { "NV12",       MAKEFOURCC('N','V','1','2'),    VLC_CODEC_NV12 },
    { "DXA9",       MAKEFOURCC('N','V','1','2'),    VLC_CODEC_D3D9_OPAQUE },
    { "DXA9_422",   MAKEFOURCC('Y','U','Y','2'),    VLC_CODEC_D3D9_OPAQUE },
    { "DXA9_444",   MAKEFOURCC('A','Y','U','V'),    VLC_CODEC_D3D9_OPAQUE },
    { "DXA9_10",    MAKEFOURCC('P','0','1','0'),    VLC_CODEC_D3D9_OPAQUE_10B },
    { "DXA9_10_422", MAKEFOURCC('Y','2','1','0'),   VLC_CODEC_D3D9_OPAQUE_10B },
    { "DXA9_10_444", MAKEFOURCC('Y','4','1','0'),   VLC_CODEC_D3D9_OPAQUE_10B },
    { "UYVY",       D3DFMT_UYVY,    VLC_CODEC_UYVY },
    { "YUY2",       D3DFMT_YUY2,    VLC_CODEC_YUYV },
    { "X8R8G8B8",   D3DFMT_X8R8G8B8,VLC_CODEC_BGRX },
    { "A8R8G8B8",   D3DFMT_A8R8G8B8,VLC_CODEC_BGRA },
    { "R8G8B8",     D3DFMT_R8G8B8,  VLC_CODEC_BGR24 },
    { "R5G6B5",     D3DFMT_R5G6B5,  VLC_CODEC_RGB565LE },
    { "X1R5G5B5",   D3DFMT_X1R5G5B5,VLC_CODEC_RGB555LE },

    { NULL, 0, 0 }
};

static const d3d9_format_t *FindBufferFormat(D3DFORMAT fmt)
{
    for (unsigned j = 0; d3d_formats[j].name; j++) {
        const d3d9_format_t *format = &d3d_formats[j];

        if (format->format != fmt)
            continue;

        return format;
    }
    return NULL;
}

/**
 * It returns the format (closest to chroma) that can be converted to target */

static const d3d9_format_t *Direct3DFindFormat(vout_display_t *vd, const video_format_t *fmt, vlc_video_context *vctx)
{
    vout_display_sys_t *sys = vd->sys;
    bool hardware_scale_ok = !(fmt->i_visible_width & 1) && !(fmt->i_visible_height & 1);
    if( !hardware_scale_ok )
        msg_Warn( vd, "Disabling hardware chroma conversion due to odd dimensions" );

    for (unsigned pass = 0; pass < 2; pass++) {
        const vlc_fourcc_t *list = NULL;
        vlc_fourcc_t *fallback_list = NULL;
        const vlc_fourcc_t dxva_chroma[] = {fmt->i_chroma, 0};
        D3DFORMAT decoder_format = D3DFMT_UNKNOWN;

        if (pass == 0 && is_d3d9_opaque(fmt->i_chroma))
        {
            d3d9_video_context_t *vctx_sys = GetD3D9ContextPrivate(vctx);
            assert(vctx_sys != NULL);
            list = dxva_chroma;
            decoder_format = vctx_sys->format;
            msg_Dbg(vd, "favor decoder format: %4.4s (%d)", (const char*)&decoder_format, decoder_format);
        }
        else if (pass == 0 && hardware_scale_ok && sys->allow_hw_yuv && vlc_fourcc_IsYUV(fmt->i_chroma))
            list = fallback_list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
        else if (pass == 1)
            list = fallback_list = vlc_fourcc_GetRGBFallback(fmt->i_chroma);

        if (list == NULL)
            continue;

        for (unsigned i = 0; list[i] != 0; i++) {
            for (unsigned j = 0; d3d_formats[j].name; j++) {
                const d3d9_format_t *format = &d3d_formats[j];

                if (format->vlc_chroma != list[i])
                    continue;
                if (decoder_format != D3DFMT_UNKNOWN && format->format != decoder_format)
                    continue; // not the Hardware format we prefer

                msg_Dbg(vd, "trying surface pixel format: %s", format->name);
                if (!Direct3D9CheckConversion(vd, format->format))
                {
                    free(fallback_list);
                    return format;
                }
            }
        }
        free(fallback_list);
    }
    return NULL;
}

static void SetupProcessorInput(vout_display_t *vd, const video_format_t *fmt, const d3d9_format_t *d3dfmt)
{
    vout_display_sys_t *sys = vd->sys;
    DXVAHD_STREAM_STATE_D3DFORMAT_DATA d3dformat = { d3dfmt->format };
    IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_D3DFORMAT, sizeof(d3dformat), &d3dformat );

    DXVAHD_STREAM_STATE_FRAME_FORMAT_DATA frame_format = { DXVAHD_FRAME_FORMAT_PROGRESSIVE };
    IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_FRAME_FORMAT, sizeof(frame_format), &frame_format );

    DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE_DATA colorspace = {
        .Type = 0, // video, not graphics
        .RGB_Range = fmt->color_range == COLOR_RANGE_FULL ? 0 : 1,
        .YCbCr_xvYCC = fmt->color_range == COLOR_RANGE_FULL ? 1 : 0,
        .YCbCr_Matrix = fmt->space == COLOR_SPACE_BT601 ? 0 : 1,
    };
    IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE, sizeof(colorspace), &colorspace );

    DXVAHD_STREAM_STATE_SOURCE_RECT_DATA srcRect;
    srcRect.Enable = TRUE;
    srcRect.SourceRect = (RECT) {
        .left   = vd->source->i_x_offset,
        .right  = vd->source->i_x_offset + vd->source->i_visible_width,
        .top    = vd->source->i_y_offset,
        .bottom = vd->source->i_y_offset + vd->source->i_visible_height,
    };
    IDXVAHD_VideoProcessor_SetVideoProcessStreamState( sys->processor.proc, 0, DXVAHD_STREAM_STATE_SOURCE_RECT, sizeof(srcRect), &srcRect );

    DXVAHD_BLT_STATE_TARGET_RECT_DATA dstRect;
    dstRect.Enable = TRUE;
    dstRect.TargetRect = (RECT) {
        .left   = 0,
        .right  = vd->source->i_visible_width,
        .top    = 0,
        .bottom = vd->source->i_visible_height,
    };
    IDXVAHD_VideoProcessor_SetVideoProcessBltState( sys->processor.proc, DXVAHD_BLT_STATE_TARGET_RECT, sizeof(dstRect), &dstRect);
}

static void GetFrameRate(DXVAHD_RATIONAL *r, const video_format_t *fmt)
{
    if (fmt->i_frame_rate && fmt->i_frame_rate_base)
    {
        r->Numerator   = fmt->i_frame_rate;
        r->Denominator = fmt->i_frame_rate_base;
    }
    else
    {
        r->Numerator   = 0;
        r->Denominator = 0;
    }
}

static int InitRangeProcessor(vout_display_t *vd, const d3d9_format_t *d3dfmt,
                              const libvlc_video_output_cfg_t *render_out)
{
    vout_display_sys_t *sys = vd->sys;

    HRESULT hr;

    D3DFORMAT *formatsList = NULL;
    DXVAHD_VPCAPS *capsList = NULL;
    IDXVAHD_Device *hd_device = NULL;

    DXVAHD_CONTENT_DESC desc;
    desc.InputFrameFormat = DXVAHD_FRAME_FORMAT_PROGRESSIVE;
    GetFrameRate( &desc.InputFrameRate, vd->source );
    desc.InputWidth       = vd->source->i_visible_width;
    desc.InputHeight      = vd->source->i_visible_height;
    desc.OutputFrameRate  = desc.InputFrameRate;
    desc.OutputWidth      = vd->source->i_visible_width;
    desc.OutputHeight     = vd->source->i_visible_height;

    hr = DXVAHD_CreateDevice(sys->d3d9_device->d3ddev.devex, &desc, DXVAHD_DEVICE_USAGE_PLAYBACK_NORMAL, NULL, &hd_device);
    if (FAILED(hr))
    {
        msg_Dbg(vd, "Failed to create the device (error 0x%lX)", hr);
        goto error;
    }

    DXVAHD_VPDEVCAPS devcaps = { 0 };
    hr = IDXVAHD_Device_GetVideoProcessorDeviceCaps( hd_device, &devcaps );
    if (unlikely(FAILED(hr)))
    {
        msg_Err(vd, "Failed to get the device capabilities (error 0x%lX)", hr);
        goto error;
    }
    if (devcaps.VideoProcessorCount == 0)
    {
        msg_Warn(vd, "No good video processor found for range conversion");
        goto error;
    }

    formatsList = vlc_alloc(devcaps.InputFormatCount, sizeof(*formatsList));
    if (unlikely(formatsList == NULL))
    {
        msg_Dbg(vd, "Failed to allocate %u input formats", devcaps.InputFormatCount);
        goto error;
    }

    hr = IDXVAHD_Device_GetVideoProcessorInputFormats( hd_device, devcaps.InputFormatCount, formatsList);
    UINT i;
    for (i=0; i<devcaps.InputFormatCount; i++)
    {
        if (formatsList[i] == d3dfmt->format)
            break;
    }
    if (i == devcaps.InputFormatCount)
    {
        msg_Warn(vd, "Input format %s not supported for range conversion", d3dfmt->name);
        goto error;
    }

    free(formatsList);
    formatsList = vlc_alloc(devcaps.OutputFormatCount, sizeof(*formatsList));
    if (unlikely(formatsList == NULL))
    {
        msg_Dbg(vd, "Failed to allocate %u output formats", devcaps.OutputFormatCount);
        goto error;
    }

    hr = IDXVAHD_Device_GetVideoProcessorOutputFormats( hd_device, devcaps.OutputFormatCount, formatsList);
    for (i=0; i<devcaps.OutputFormatCount; i++)
    {
        if (formatsList[i] == sys->BufferFormat)
            break;
    }
    if (i == devcaps.OutputFormatCount)
    {
        msg_Warn(vd, "Output format %d not supported for range conversion", sys->BufferFormat);
        goto error;
    }

    capsList = vlc_alloc(devcaps.VideoProcessorCount, sizeof(*capsList));
    if (unlikely(capsList == NULL))
    {
        msg_Dbg(vd, "Failed to allocate %u video processors", devcaps.VideoProcessorCount);
        goto error;
    }
    hr = IDXVAHD_Device_GetVideoProcessorCaps( hd_device, devcaps.VideoProcessorCount, capsList);
    if (FAILED(hr))
    {
        msg_Dbg(vd, "Failed to get the processor caps (error 0x%lX)", hr);
        goto error;
    }

    hr = IDXVAHD_Device_CreateVideoProcessor( hd_device, &capsList->VPGuid, &sys->processor.proc );
    if (FAILED(hr))
    {
        msg_Dbg(vd, "Failed to create the processor (error 0x%lX)", hr);
        goto error;
    }

    free(capsList);
    free(formatsList);
    IDXVAHD_Device_Release( hd_device );

    SetupProcessorInput(vd, vd->source, d3dfmt);

    DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE_DATA colorspace = {
        .Usage = 0, // playback
        .RGB_Range = render_out->full_range ? 0 : 1,
        .YCbCr_xvYCC = render_out->full_range ? 1 : 0,
        .YCbCr_Matrix = render_out->colorspace == libvlc_video_colorspace_BT601 ? 0 : 1,
    };
    hr = IDXVAHD_VideoProcessor_SetVideoProcessBltState( sys->processor.proc, DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE, sizeof(colorspace), &colorspace);

    return VLC_SUCCESS;

error:
    free(capsList);
    free(formatsList);
    if (hd_device)
        IDXVAHD_Device_Release(hd_device);
    return VLC_EGENERIC;
}

/**
 * It creates a Direct3D9 device and the associated resources.
 */

static int Direct3D9Open(vout_display_t *vd, video_format_t *fmt, vlc_video_context *vctx)
{
    vout_display_sys_t *sys = vd->sys;

    libvlc_video_output_cfg_t render_out;
    if (UpdateOutput(vd, vd->source, &render_out) != VLC_SUCCESS)
        return VLC_EGENERIC;

    sys->BufferFormat = render_out.u.d3d9_format;
    const d3d9_format_t *dst_format = FindBufferFormat(sys->BufferFormat);
    if (unlikely(!dst_format))
        msg_Warn(vd, "Unknown back buffer format 0x%X", sys->BufferFormat);

    /* Find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
     * the requested chroma which is usable by the hardware in an offscreen surface, as they
     * typically support more formats than textures */

    const d3d9_format_t *d3dfmt = Direct3DFindFormat(vd, vd->source, vctx);
    if (!d3dfmt) {
        msg_Err(vd, "unsupported source pixel format %4.4s", (const char*)&vd->source->i_chroma);
        goto error;
    }
    msg_Dbg(vd, "found input surface format %s for source %4.4s", d3dfmt->name, (const char *)&vd->source->i_chroma);

    bool force_dxva_hd = var_InheritBool(vd, "direct3d9-dxvahd");
    if (force_dxva_hd || (dst_format && vd->source->color_range != COLOR_RANGE_FULL &&
                          sys->d3d9_device->d3ddev.identifier.VendorId == GPU_MANUFACTURER_NVIDIA &&
                          !vlc_fourcc_IsYUV(dst_format->vlc_chroma) && vlc_fourcc_IsYUV(d3dfmt->vlc_chroma)))
    {
        // NVIDIA bug, YUV to RGB internal conversion in StretchRect always converts from limited to limited range
        msg_Dbg(vd, "init DXVA-HD processor from %s to %s", d3dfmt->name, dst_format?dst_format->name:"unknown");
        int err = InitRangeProcessor( vd, d3dfmt, &render_out );
        if (err != VLC_SUCCESS)
            force_dxva_hd = false;
    }
    if (!force_dxva_hd)
    {
        // test whether device can perform color-conversion from that format to target format
        HRESULT hr = IDirect3D9_CheckDeviceFormatConversion(sys->d3d9_device->hd3d.obj,
                                                    sys->d3d9_device->d3ddev.adapterId,
                                                    D3DDEVTYPE_HAL,
                                                    d3dfmt->format, sys->BufferFormat);
        if (FAILED(hr))
        {
            msg_Dbg(vd, "Unsupported conversion from %s to %s", d3dfmt->name, dst_format?dst_format->name:"unknown" );
            goto error;
        }
        msg_Dbg(vd, "using StrecthRect from %s to %s", d3dfmt->name, dst_format?dst_format->name:"unknown" );
    }

    /* */
    *fmt = *vd->source;
    fmt->i_chroma = d3dfmt->vlc_chroma;
    sys->sw_texture_fmt = d3dfmt;

    if (Direct3D9CreateResources(vd, fmt)) {
        msg_Err(vd, "Failed to allocate resources");
        goto error;
    }

    msg_Dbg(vd, "Direct3D9 display adapter successfully initialized");
    return VLC_SUCCESS;

error:
    return VLC_EGENERIC;
}

/**
 * It releases the Direct3D9 device and its resources.
 */

static void Direct3D9Close(vout_display_t *vd)
{
    Direct3D9DestroyResources(vd);
}

static int SetDisplaySize(vout_display_t *vd, unsigned width, unsigned height)
{
    VLC_UNUSED(width); VLC_UNUSED(height);
    vout_display_sys_t *sys = vd->sys;
    CommonDisplaySizeChanged(sys->video_wnd);
    return VLC_SUCCESS;
}

static int PlacementChanged(vout_display_t *vd, const vout_display_place_t *place)
{
    vout_display_sys_t *sys = vd->sys;
    VLC_UNUSED(place);
    sys->place_changed = true;
    return VLC_SUCCESS;
}

typedef struct
{
    char **values;
    char **descs;
    size_t count;
} enum_context_t;

static void ListShaders(enum_context_t *ctx)
{
    size_t num_shaders = BUILTIN_SHADERS_COUNT;
    ctx->values = xrealloc(ctx->values, (ctx->count + num_shaders + 1) * sizeof(char *));
    ctx->descs = xrealloc(ctx->descs, (ctx->count + num_shaders + 1) * sizeof(char *));
    for (size_t i = 0; i < num_shaders; ++i) {
        ctx->values[ctx->count] = strdup(builtin_shaders[i].name);
        ctx->descs[ctx->count] = strdup(builtin_shaders[i].name);
        ctx->count++;
    }
    ctx->values[ctx->count] = strdup(SELECTED_SHADER_FILE);
    ctx->descs[ctx->count] = strdup(SELECTED_SHADER_FILE);
    ctx->count++;
}

/* Populate the list of available shader techniques in the options */
static int FindShadersCallback(const char *name, char ***values, char ***descs)
{
    VLC_UNUSED(name);

    enum_context_t ctx = { NULL, NULL, 0 };

    ListShaders(&ctx);

    *values = ctx.values;
    *descs = ctx.descs;
    return ctx.count;

}

VLC_CONFIG_STRING_ENUM(FindShadersCallback)

static bool LocalSwapchainUpdateOutput( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out )
{
    VLC_UNUSED(cfg);
    vout_display_t *vd = opaque;
    vout_display_sys_t *sys = vd->sys;

    D3DDISPLAYMODE d3ddm;
    HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3d9_device->hd3d.obj, sys->d3d9_device->d3ddev.adapterId, &d3ddm);
    if (unlikely(FAILED(hr)))
        return false;

    out->u.d3d9_format = d3ddm.Format;
    out->full_range  = true;
    out->colorspace  = libvlc_video_colorspace_BT709;
    out->primaries   = libvlc_video_primaries_BT709;
    out->transfer    = libvlc_video_transfer_func_SRGB;
    out->orientation = libvlc_video_orient_top_left;

    return true;
}

static void LocalSwapchainSwap( void *opaque )
{
    vout_display_t *vd = opaque;
    Swap( vd );
}

static const struct vlc_display_operations ops = {
    .close = Close,
    .prepare = Prepare,
    .display = Display,
    .set_display_size = SetDisplaySize,
    .video_place_changed = PlacementChanged,
};

/**
 * It creates a Direct3D vout display.
 */

static int Open(vout_display_t *vd,
                video_format_t *fmtp, vlc_video_context *context)
{
    vout_display_sys_t *sys;

    if ( !vd->obj.force && vd->source->projection_mode != PROJECTION_MODE_RECTANGULAR)
        return VLC_EGENERIC; /* let a module who can handle it do it */

    if ( !vd->obj.force && vd->source->mastering.max_luminance != 0)
        return VLC_EGENERIC; /* let a module who can handle it do it */

    /* Allocate structure */
    vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
    if (!sys)
        return VLC_ENOMEM;

    sys->place_changed = false;
    sys->video_wnd = NULL;

    sys->outside_opaque = var_InheritAddress( vd, "vout-cb-opaque" );
    sys->updateOutputCb      = var_InheritAddress( vd, "vout-cb-update-output" );
    sys->swapCb              = var_InheritAddress( vd, "vout-cb-swap" );
    sys->startEndRenderingCb = var_InheritAddress( vd, "vout-cb-make-current" );

    if ( sys->swapCb == NULL || sys->startEndRenderingCb == NULL || sys->updateOutputCb == NULL )
    {
        /* use our own callbacks, since there isn't any external ones */
        if (CommonWindowInit(vd, &sys->video_wnd, false))
            goto error;

        sys->outside_opaque = vd;
        sys->updateOutputCb      = LocalSwapchainUpdateOutput;
        sys->swapCb              = LocalSwapchainSwap;
        sys->startEndRenderingCb = NULL;
    }

    sys->dec_device = context ? vlc_video_context_HoldDevice(context) : NULL;
    sys->d3d9_device = GetD3D9OpaqueDevice(sys->dec_device);
    if ( sys->d3d9_device == NULL )
    {
        if (sys->dec_device)
        {
            vlc_decoder_device_Release(sys->dec_device);
            sys->dec_device = NULL;
        }

        sys->d3d9_device = D3D9_CreateDevice( vd );
        if (sys->d3d9_device == NULL) {
            msg_Err( vd, "D3D9 Creation failed!" );
            goto error;
        }
    }


    if ( vd->source->i_visible_width  > sys->d3d9_device->d3ddev.caps.MaxTextureWidth ||
         vd->source->i_visible_height > sys->d3d9_device->d3ddev.caps.MaxTextureHeight )
    {
        msg_Err(vd, "Textures too large %ux%u max possible: %lx%lx",
                vd->source->i_visible_width, vd->source->i_visible_height,
                sys->d3d9_device->d3ddev.caps.MaxTextureWidth,
                sys->d3d9_device->d3ddev.caps.MaxTextureHeight);
        goto error;
    }

    sys->hxdll = Direct3D9LoadShaderLibrary();
    if (!sys->hxdll)
        msg_Warn(vd, "cannot load Direct3D9 Shader Library; HLSL pixel shading will be disabled.");

    sys->reset_device = false;
    sys->lost_not_ready = false;
    sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");

    /* */
    video_format_t fmt;
    if (Direct3D9Open(vd, &fmt, context)) {
        msg_Err(vd, "Direct3D9 could not be opened");
        goto error;
    }

    /* Setup vout_display now that everything is fine */
    if (var_InheritBool(vd, "direct3d9-hw-blending") &&
        sys->d3dregion_format != D3DFMT_UNKNOWN &&
        (sys->d3d9_device->d3ddev.caps.SrcBlendCaps  & D3DPBLENDCAPS_SRCALPHA) &&
        (sys->d3d9_device->d3ddev.caps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) &&
        (sys->d3d9_device->d3ddev.caps.TextureCaps   & D3DPTEXTURECAPS_ALPHA) &&
        (sys->d3d9_device->d3ddev.caps.TextureOpCaps & D3DTEXOPCAPS_SELECTARG1) &&
        (sys->d3d9_device->d3ddev.caps.TextureOpCaps & D3DTEXOPCAPS_MODULATE))
        vd->info.subpicture_chromas = d3d_subpicture_chromas;
    else
        vd->info.subpicture_chromas = NULL;

    video_format_Clean(fmtp);
    video_format_Copy(fmtp, &fmt);

    vd->ops = &ops;

    /* Change the window title bar text */
    vlc_window_SetTitle(vd->cfg->window, VOUT_TITLE " (Direct3D9 output)");

    return VLC_SUCCESS;
error:
    Direct3D9Close(vd);
    CommonWindowClean(sys->video_wnd);
    Direct3D9Destroy(sys);
    free(vd->sys);
    return VLC_EGENERIC;
}

/**
 * It destroys a Direct3D vout display.
 */

static void Close(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;

    Direct3D9Close(vd);

    CommonWindowClean(sys->video_wnd);

    Direct3D9Destroy(sys);

    free(sys);
}

Messung
CHZG
85959290

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© 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.