[vlc-devel] [PATCH] Direct3D: implement HLSL pixel shading in the rendering pipeline

Rafaël Carré funman at videolan.org
Tue Jan 28 09:40:04 CET 2014


Hello,

On 01/24/14 19:44, Felix Abecassis wrote:
> The user can choose which shader to apply using a selection list in
> the options of the Direct3D vout module. The list of shader techniques
> is populated by reading the file "pixelShader.fx" from the user data
> directory. New shaders can be added easily by extending this file.
> 
> Based on the code by Sasha Koruga for GSoC 2010.
> ---
>  modules/video_output/msw/common.h       |   24 +++
>  modules/video_output/msw/direct3d.c     |  289 ++++++++++++++++++++++++++++---
>  modules/video_output/msw/pixelShader.fx |  181 +++++++++++++++++++
>  3 files changed, 468 insertions(+), 26 deletions(-)
>  create mode 100644 modules/video_output/msw/pixelShader.fx
> 
> diff --git a/modules/video_output/msw/common.h b/modules/video_output/msw/common.h
> index 122d80a..50d4461 100644
> --- a/modules/video_output/msw/common.h
> +++ b/modules/video_output/msw/common.h
> @@ -27,6 +27,7 @@
>  #endif
>  #ifdef MODULE_NAME_IS_direct3d
>  # include <d3d9.h>
> +# include <d3dx9effect.h>
>  #endif
>  #ifdef MODULE_NAME_IS_glwin32
>  # include "../opengl.h"
> @@ -147,6 +148,8 @@ struct vout_display_sys_t
>  
>      // core objects
>      HINSTANCE               hd3d9_dll;       /* handle of the opened d3d9 dll */
> +    HINSTANCE               hd3d9x_dll;      /* handle of the opened d3d9x dll */

Why not linking directly to the DLL ?

http://msdn.microsoft.com/en-us/library/windows/desktop/bb172638(v=vs.85).aspx

D3DX_SDK_VERSION is 36 in the headers afaiu we should link to d3dx9_36.dll

BTW I notice that these 2 members (and those below) are not common to all
msw vout modules, they should be moved to direct3d.c

> +    ID3DXEffect*            d3dx_shader;
>      LPDIRECT3D9             d3dobj;
>      D3DCAPS9                d3dcaps;
>      LPDIRECT3DDEVICE9       d3ddev;
> @@ -213,3 +216,24 @@ void AlignRect(RECT *, int align_boundary, int align_size);
>  #define DX_POSITION_CHANGE 0x1000
>  #define DX_WALLPAPER_CHANGE 0x2000
>  #define DX_DESKTOP_CHANGE 0x4000
> +
> +/*****************************************************************************
> + * Functions missing from the mingw headers
> + *****************************************************************************/
> +/* If one prototype is missing, assume the others are too. */
> +#ifndef ID3DXEffect_Begin
> +# define ID3DXEffect_Begin(p, a, b)      (p)->lpVtbl->Begin(p, a, b)
> +# define ID3DXEffect_End(p)              (p)->lpVtbl->End(p)
> +# define ID3DXEffect_BeginPass(p, a)     (p)->lpVtbl->BeginPass(p, a)
> +# define ID3DXEffect_EndPass(p)          (p)->lpVtbl->EndPass(p)
> +# define ID3DXEffect_GetDesc(p, a)       (p)->lpVtbl->GetDesc(p, a)
> +# define ID3DXEffect_GetTechnique(p, a)  (p)->lpVtbl->GetTechnique(p, a)
> +# define ID3DXEffect_GetTechniqueByName(p, a)  (p)->lpVtbl->GetTechniqueByName(p, a)
> +# define ID3DXEffect_SetTechnique(p, a)  (p)->lpVtbl->SetTechnique(p, a)
> +# define ID3DXEffect_GetTechniqueDesc(p, a, b)  (p)->lpVtbl->GetTechniqueDesc(p, a, b)
> +# define ID3DXEffect_Release(p)          (p)->lpVtbl->Release(p)
> +#endif
> +
> +#ifndef ID3DXEffectCompiler_CompileEffect
> +# define ID3DXEffectCompiler_CompileEffect(p, a, b, c) (p)->lpVtbl->CompileEffect(p, a, b, c)
> +#endif

Do you want to add these to mingw-w64 ?

It is very easy and I will gladly guide you through the process.

In fact it might be as easy as pointing it out to jacek (caban) who's
working
on DirectX in wine (among other things).

> diff --git a/modules/video_output/msw/direct3d.c b/modules/video_output/msw/direct3d.c
> index e2d7387..8e11dcd 100644
> --- a/modules/video_output/msw/direct3d.c
> +++ b/modules/video_output/msw/direct3d.c
> @@ -1,10 +1,12 @@
>  /*****************************************************************************
>   * direct3d.c: Windows Direct3D video output module
>   *****************************************************************************
> - * Copyright (C) 2006-2009 VLC authors and VideoLAN
> + * Copyright (C) 2006-2014 VLC authors and VideoLAN
>   *$Id$
>   *
> - * Authors: Damien Fouilleul <damienf at videolan.org>
> + * Authors: Damien Fouilleul <damienf at videolan.org>,
> + *          Sasha Koruga <skoruga at gmail.com>,
> + *          Felix Abecassis <felix.abecassis at 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
> @@ -40,6 +42,7 @@
>  #include <vlc_common.h>
>  #include <vlc_plugin.h>
>  #include <vlc_vout_display.h>
> +#include <vlc_charset.h> /* ToT function */
>  
>  #include <windows.h>
>  #include <d3d9.h>
> @@ -59,8 +62,17 @@ static void Close(vlc_object_t *);
>  #define HW_BLENDING_LONGTEXT N_(\
>      "Try to use hardware acceleration for subtitle/OSD blending.")
>  
> +#define PIXEL_SHADER_TEXT N_("Pixel Shader")
> +#define PIXEL_SHADER_LONGTEXT N_(\
> +        "Choose a pixel shader to apply.")
> +#define PIXEL_SHADER_PATH config_GetUserDir(VLC_DATA_DIR)
> +#define PIXEL_SHADER_FILENAME "pixelShader.fx"
> +
>  #define D3D_HELP N_("Recommended video output for Windows Vista and later versions")
>  
> +static int FindShadersCallback(vlc_object_t *, const char *,
> +                               char ***, char ***);
> +
>  vlc_module_begin ()
>      set_shortname("Direct3D")
>      set_description(N_("Direct3D video output"))
> @@ -70,6 +82,9 @@ vlc_module_begin ()
>  
>      add_bool("direct3d-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
>  
> +    add_string("direct3d-shader", "", PIXEL_SHADER_TEXT, PIXEL_SHADER_LONGTEXT, true)
> +        change_string_cb(FindShadersCallback)
> +
>      set_capability("vout display", 240)
>      add_shortcut("direct3d")
>      set_callbacks(Open, Close)
> @@ -133,6 +148,9 @@ static void Direct3DRenderScene(vout_display_t *vd, d3d_region_t *, int, d3d_reg
>  /* */
>  static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
>  
> +static HINSTANCE Direct3DLoadShaderLibrary(void);
> +static char* Direct3DGetShaderFile(void);

Can you reorganize the order of functions in the file to not need these?

>  /**
>   * It creates a Direct3D vout display.
>   */
> @@ -515,6 +533,10 @@ static int Direct3DCreate(vout_display_t *vd)
>      }
>      sys->d3dobj = d3dobj;
>  
> +    sys->hd3d9x_dll = Direct3DLoadShaderLibrary();
> +    if (!sys->hd3d9x_dll)
> +        msg_Warn(vd, "cannot load Direct3D Shader Library; HLSL pixel shading will be disabled.");

Is d3dx9_x.dll only a 'shader library' ?

> +
>      /*
>      ** Get device capabilities
>      */
> @@ -547,9 +569,12 @@ static void Direct3DDestroy(vout_display_t *vd)
>         IDirect3D9_Release(sys->d3dobj);
>      if (sys->hd3d9_dll)
>          FreeLibrary(sys->hd3d9_dll);
> +    if (sys->hd3d9x_dll)
> +        FreeLibrary(sys->hd3d9x_dll);
>  
>      sys->d3dobj = NULL;
>      sys->hd3d9_dll = NULL;
> +    sys->hd3d9x_dll = NULL;
>  }
>  
>  
> @@ -722,6 +747,9 @@ static void Direct3DDestroyPool(vout_display_t *vd);
>  static int  Direct3DCreateScene(vout_display_t *vd, const video_format_t *fmt);
>  static void Direct3DDestroyScene(vout_display_t *vd);
>  
> +static int  Direct3DCreateShaders(vout_display_t *vd);
> +static void Direct3DDestroyShaders(vout_display_t *vd);
> +
>  /**
>   * It creates the picture and scene resources.
>   */
> @@ -737,6 +765,11 @@ static int Direct3DCreateResources(vout_display_t *vd, video_format_t *fmt)
>          msg_Err(vd, "Direct3D scene initialization failed !");
>          return VLC_EGENERIC;
>      }
> +    if (Direct3DCreateShaders(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 fmt = i == 0 ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8;
> @@ -760,6 +793,7 @@ static void Direct3DDestroyResources(vout_display_t *vd)
>  {
>      Direct3DDestroyScene(vd);
>      Direct3DDestroyPool(vd);
> +    Direct3DDestroyShaders(vd);
>  }
>  
>  /**
> @@ -1131,6 +1165,79 @@ static void Direct3DDestroyScene(vout_display_t *vd)
>      msg_Dbg(vd, "Direct3D scene released successfully");
>  }
>  
> +static int Direct3DCreateShaders(vout_display_t *vd)
> +{
> +    vout_display_sys_t *sys = vd->sys;
> +
> +    /* Find which shader was enabled. */
> +    char *selected_shader = var_InheritString(vd, "direct3d-shader");
> +    if (!selected_shader)
> +        return VLC_SUCCESS; /* Nothing to do */
> +
> +    if (!sys->hd3d9x_dll)
> +        goto error;
> +
> +    HRESULT (WINAPI * OurD3DXCreateEffectFromFile)(
> +        LPDIRECT3DDEVICE9               pDevice,
> +        LPCSTR                          pSrcFile,
> +        CONST D3DXMACRO*                pDefines,
> +        LPD3DXINCLUDE                   pInclude,
> +        DWORD                           Flags,
> +        LPD3DXEFFECTPOOL                pPool,
> +        LPD3DXEFFECT*                   ppEffect,
> +        LPD3DXBUFFER*                   ppCompilationErrors);
> +
> +    OurD3DXCreateEffectFromFile = (void*)GetProcAddress(sys->hd3d9x_dll, "D3DXCreateEffectFromFileA");
> +    if (!OurD3DXCreateEffectFromFile) {
> +        msg_Warn(vd, "Cannot locate reference to D3DXCreateEffectFromFile; pixel shading will be disabled");
> +        goto error;
> +    }
> +
> +    LPD3DXBUFFER buffer = NULL;
> +    char *shader_file = Direct3DGetShaderFile();

memleak

> +    if (!shader_file)
> +        goto error;
> +    DWORD shader_flags = D3DXFX_NOT_CLONEABLE;
> +    HRESULT hr = OurD3DXCreateEffectFromFile(sys->d3ddev, shader_file, NULL, NULL,
> +                                             shader_flags, NULL, &sys->d3dx_shader, &buffer);
> +    if (FAILED(hr)) {
> +        msg_Warn(vd, "D3DXCreateEffectFromFile Error (hr=0x%lX) -- pixel shader file probably missing.", hr);

msg_Err ?

> +        if (buffer)
> +            msg_Warn(vd, "HLSL Compilation Error: %s", (char*)ID3DXBuffer_GetBufferPointer(buffer));
> +        goto error;
> +    }
> +
> +    D3DXHANDLE technique = ID3DXEffect_GetTechniqueByName(sys->d3dx_shader, selected_shader);
> +    if (!technique) {
> +        msg_Warn(vd, "Could not find requested technique from shader file.");
> +        goto error;
> +    }
> +    hr = ID3DXEffect_SetTechnique(sys->d3dx_shader, technique);
> +    if (FAILED(hr)) {
> +        msg_Warn(vd, "Could not set requested shader technique.");
> +        goto error;
> +    }
> +
> +    free(selected_shader);
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    if (sys->d3dx_shader)
> +        Direct3DDestroyShaders(vd);
> +    free(selected_shader);
> +    return VLC_EGENERIC;
> +}
> +
> +static void Direct3DDestroyShaders(vout_display_t *vd)
> +{
> +    vout_display_sys_t *sys = vd->sys;
> +
> +    if (sys->d3dx_shader)
> +        ID3DXEffect_Release(sys->d3dx_shader);
> +    sys->d3dx_shader = NULL;

nit picking: no need to set sys->d3dx_shader if it's already NULL

> +}
> +
>  static void Direct3DSetupVertices(CUSTOMVERTEX *vertices,
>                                    const RECT src_full,
>                                    const RECT src_crop,
> @@ -1360,35 +1467,69 @@ static int Direct3DRenderRegion(vout_display_t *vd,
>          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, (LPDIRECT3DBASETEXTURE9)d3dtex);
> -    if (FAILED(hr)) {
> -        msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
> -        return -1;
> +    unsigned int shaderPasses;
> +    if (sys->d3dx_shader)
> +    {
> +        hr = ID3DXEffect_Begin(sys->d3dx_shader, &shaderPasses, 0);
> +        if (FAILED(hr))
> +           msg_Err(vd, "Begin failed (hr=0x%lX)", hr);

What's shaderPasses value here?

>      }
> +    else
> +        shaderPasses = 1;
>  
> -    // Render the vertex buffer contents
> -    hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
> -    if (FAILED(hr)) {
> -        msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
> -        return -1;
> -    }
> +    for (UINT uPass = 0; uPass < shaderPasses; ++uPass) {
> +        if (sys->d3dx_shader) {
> +            hr = ID3DXEffect_BeginPass(sys->d3dx_shader, uPass);
> +            if (FAILED(hr))
> +                msg_Err(vd, "BeginPass failed (hr=0x%lX)", hr);
> +        }
>  
> -    // we use FVF instead of vertex shader
> -    hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
> -    if (FAILED(hr)) {
> -        msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, 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, (LPDIRECT3DBASETEXTURE9)d3dtex);
> +        if (FAILED(hr)) {
> +            msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
> +            IDirect3DDevice9_EndScene(d3ddev);
> +            return -1;
> +        }
> +
> +        // Render the vertex buffer contents
> +        hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
> +        if (FAILED(hr)) {
> +            msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
> +            IDirect3DDevice9_EndScene(d3ddev);
> +            return -1;
> +        }
> +
> +        // we use FVF instead of vertex shader
> +        hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
> +        if (FAILED(hr)) {
> +            msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
> +            IDirect3DDevice9_EndScene(d3ddev);
> +            return -1;
> +        }
> +
> +        // draw rectangle
> +        hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
> +        if (FAILED(hr)) {
> +            msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
> +            IDirect3DDevice9_EndScene(d3ddev);
> +            return -1;
> +        }
> +
> +        if (sys->d3dx_shader) {
> +            hr = ID3DXEffect_EndPass(sys->d3dx_shader);
> +            if (FAILED(hr))
> +                msg_Err(vd, "EndPass failed (hr=0x%lX)", hr);
> +        }
>      }
>  
> -    // draw rectangle
> -    hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
> -    if (FAILED(hr)) {
> -        msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
> -        return -1;
> +    if (sys->d3dx_shader) {
> +        hr = ID3DXEffect_End(sys->d3dx_shader);
> +        if (FAILED(hr))
> +           msg_Err(vd, "End failed (hr=0x%lX)", hr);
>      }
>      return 0;
>  }
> @@ -1466,3 +1607,99 @@ static int DesktopCallback(vlc_object_t *object, char const *psz_cmd,
>      vlc_mutex_unlock(&sys->lock);
>      return VLC_SUCCESS;
>  }
> +
> +typedef struct
> +{
> +    char **values;
> +    char **descs;
> +    size_t count;
> +} enum_context_t;
> +
> +static void ListTechniquesFromFile(enum_context_t *ctx)
> +{
> +    HINSTANCE hd3d9x_dll = Direct3DLoadShaderLibrary();
> +    if (!hd3d9x_dll)
> +        goto end;
> +
> +    HRESULT (WINAPI * OurD3DXCreateEffectCompilerFromFile)(
> +        LPCSTR pSrcFile,
> +        const D3DXMACRO *pDefines,
> +        LPD3DXINCLUDE pInclude,
> +        DWORD Flags,
> +        LPD3DXEFFECTCOMPILER *ppEffectCompiler,
> +        LPD3DXBUFFER *ppParseErrors);
> +    /* We use CreateEffectCompilerFromFile since it does not creating a D3D device. */
> +    OurD3DXCreateEffectCompilerFromFile = (void*)GetProcAddress(hd3d9x_dll, "D3DXCreateEffectCompilerFromFileA");
> +    if (!OurD3DXCreateEffectCompilerFromFile)
> +        goto end;
> +
> +    char* shader_file = Direct3DGetShaderFile();
> +    if (!shader_file)
> +        goto end;
> +    DWORD shader_flags = D3DXFX_NOT_CLONEABLE;
> +    LPD3DXEFFECTCOMPILER d3dx_effect;
> +    HRESULT hr = OurD3DXCreateEffectCompilerFromFile(shader_file, NULL, NULL,
> +                                                     shader_flags, &d3dx_effect, NULL);
> +    if (FAILED(hr))
> +        goto end;
> +
> +    D3DXEFFECT_DESC d3ddesc;
> +    hr = ID3DXEffect_GetDesc(d3dx_effect, &d3ddesc);
> +    if (FAILED(hr))
> +        goto end;
> +
> +    int num_techniques = d3ddesc.Techniques;
> +    ctx->values = xrealloc(ctx->values, (ctx->count + num_techniques) * sizeof(char *));
> +    ctx->descs = xrealloc(ctx->descs, (ctx->count + num_techniques) * sizeof(char *));
> +    for (UINT i = 0; i < d3ddesc.Techniques; ++i) {
> +        D3DXHANDLE hTechnique = ID3DXEffect_GetTechnique(d3dx_effect, i);
> +        D3DXTECHNIQUE_DESC d3ddesc;
> +        ID3DXEffect_GetTechniqueDesc(d3dx_effect, hTechnique, &d3ddesc);
> +        ctx->values[ctx->count] = strdup(d3ddesc.Name);
> +        ctx->descs[ctx->count] = strdup(d3ddesc.Name);
> +        ctx->count++;
> +    }
> +
> +end:
> +    if (hd3d9x_dll)

hd3d9x_dll is always non NULL

> +        FreeLibrary(hd3d9x_dll);
> +}
> +
> +/* Populate the list of available shader techniques in the options */
> +static int FindShadersCallback(vlc_object_t *object, const char *name,
> +                               char ***values, char ***descs)
> +{
> +    VLC_UNUSED(object);
> +    VLC_UNUSED(name);
> +
> +    enum_context_t ctx = { NULL, NULL, 0 };
> +
> +    ListTechniquesFromFile(&ctx);
> +
> +    *values = ctx.values;
> +    *descs = ctx.descs;
> +    return ctx.count;
> +}
> +
> +static HINSTANCE Direct3DLoadShaderLibrary()

(void)?

> +{
> +    HINSTANCE instance = NULL;
> +    for (int i = 43; i > 23; --i) {
> +        char *filename = NULL;
> +        if (asprintf(&filename, "D3dx9_%d.dll", i) == -1)
> +            continue;
> +        instance = LoadLibrary(ToT(filename));
> +        free(filename);
> +        if (instance)
> +            break;
> +    }
> +    return instance;
> +}
> +
> +static char* Direct3DGetShaderFile()
> +{
> +    char* filename = NULL;
> +    if (asprintf(&filename, "%s\\%s", PIXEL_SHADER_PATH, PIXEL_SHADER_FILENAME) == -1)
> +        return NULL;
> +    return filename;
> +}



More information about the vlc-devel mailing list