[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