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

felix.abecassis at gmail.com felix.abecassis at gmail.com
Fri Feb 7 19:26:18 CET 2014


From: Felix Abecassis <felix.abecassis at gmail.com>

The user can choose to apply a builtin shader using a selection list
in the options of the Direct3D vout module. A custom shader function
can also be loaded by specifying the path of the shader file.

Many changes since the latest patch proposal: we are not compiling
"shader techniques" anymore but now shader functions using the "main"
entrypoint.  All the shaders previously in pixelShader.fx are now
builtins.

Based on the code by Sasha Koruga for GSoC 2010.
---
 modules/video_output/msw/builtin_shaders.h |  151 ++++++++++++++++++
 modules/video_output/msw/common.h          |    3 +
 modules/video_output/msw/direct3d.c        |  227 +++++++++++++++++++++++++++-
 3 files changed, 379 insertions(+), 2 deletions(-)
 create mode 100644 modules/video_output/msw/builtin_shaders.h

diff --git a/modules/video_output/msw/builtin_shaders.h b/modules/video_output/msw/builtin_shaders.h
new file mode 100644
index 0000000..dbafe5d
--- /dev/null
+++ b/modules/video_output/msw/builtin_shaders.h
@@ -0,0 +1,151 @@
+/*****************************************************************************
+ * builtin_shaders.h: Builtin HLSL shader functions.
+ *****************************************************************************
+ * Copyright (C) 2014 the VideoLAN team
+ *
+ * Authors: 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *****************************************************************************/
+
+static const char shader_disabled_source[] =
+    "sampler2D screen;\n"
+    "float4 main(float2 screenCoords : TEXCOORD0) : COLOR\n"
+    "{\n"
+    "    return saturate(tex2D(screen, screenCoords.xy));\n"
+    "}\n";
+
+static const char shader_invert_source[] =
+    "sampler2D screen;\n"
+    "float4 main(float2 screenCoords : TEXCOORD0) : COLOR\n"
+    "{\n"
+    "    float4 color = tex2D(screen, screenCoords.xy);\n"
+    "    color.r = 1.0 - color.r;\n"
+    "    color.g = 1.0 - color.g;\n"
+    "    color.b = 1.0 - color.b;\n"
+    "    return color;\n"
+    "}\n";
+
+static const char shader_grayscale_source[] =
+    "sampler2D screen;\n"
+    "float4 main(float2 screenCoords : TEXCOORD0) : COLOR0\n"
+    "{\n"
+    "    float4 color = tex2D(screen, screenCoords.xy);\n"
+    "    float gray = 0.2989 * color.r + 0.5870 * color.g + 0.1140 * color.b;\n"
+    "    color.r = color.g = color.b = gray;\n"
+    "    return color;\n"
+    "}\n";
+
+static const char shader_convert601to709_source[] =
+    "sampler2D screen;\n"
+    "float4 rgb_to_yuv601(float4 RGB)\n"
+    "{\n"
+    "    float Kr = 0.299;\n"
+    "    float Kg = 0.587;\n"
+    "    float Kb = 0.114;\n"
+    "    float Y = Kr*RGB.r + Kg*RGB.g + Kb*RGB.b;\n"
+    "    float V = (RGB.r-Y)/(1-Kr);\n"
+    "    float U = (RGB.b-Y)/(1-Kb);\n"
+    "    return float4(Y,U,V,1);\n"
+    "}\n"
+
+    "float4 yuv709_to_rgb(float4 YUV)\n"
+    "{\n"
+    "    float Kr = 0.2125;\n"
+    "    float Kg = 0.7154;\n"
+    "    float Kb = 0.0721;\n"
+    "    float Y = YUV.x;\n"
+    "    float U = YUV.y;\n"
+    "    float V = YUV.z;\n"
+    "    float R = Y + V*(1-Kr);\n"
+    "    float G = Y - U*(1-Kb)*Kb/Kg - V*(1-Kr)*Kr/Kg;\n"
+    "    float B = Y + U*(1-Kb);\n"
+    "    return float4(R,G,B,1);\n"
+    "}\n"
+
+    "float4 main(float2 screenCoords : TEXCOORD0) : COLOR0\n"
+    "{\n"
+    "    float4 color = tex2D(screen, screenCoords.xy);\n"
+    "    return yuv709_to_rgb(rgb_to_yuv601(color));\n"
+    "}\n";
+
+static const char shader_gammacorrection18_source[] =
+    "sampler2D screen;\n"
+    "float4 main(float2 screenCoords : TEXCOORD0) : COLOR0\n"
+    "{\n"
+    "    float4 color = tex2D( screen, screenCoords.xy);\n"
+    "    color = pow(color,1.0/1.8);\n"
+    "    return color;\n"
+    "}\n";
+
+static const char shader_gammacorrection22_source[] =
+    "sampler2D screen;\n"
+    "float4 main(float2 screenCoords : TEXCOORD0) : COLOR0\n"
+    "{\n"
+    "    float4 color = tex2D( screen, screenCoords.xy);\n"
+    "    color = pow(color,1.0/2.2);\n"
+    "    return color;\n"
+    "}\n";
+
+static const char shader_gammacorrectionbt709_source[] =
+    "sampler2D screen;\n"
+    "float4 main(float2 screenCoords : TEXCOORD0) : COLOR0\n"
+    "{\n"
+    "    float4 color = tex2D(screen, screenCoords.xy);\n"
+    "    if(color.r > 0.018)\n"
+    "        color.r = 1.099 * pow(color.r,0.45) - 0.099;\n"
+    "    else\n"
+    "        color.r = 4.5138 * color.r;\n"
+    "    if(color.g > 0.018)\n"
+    "        color.g = 1.099 * pow(color.g,0.45) - 0.099;\n"
+    "    else\n"
+    "        color.g = 4.5138 * color.g;\n"
+    "    if(color.b > 0.018)\n"
+    "        color.b = 1.099 * pow(color.b,0.45) - 0.099;\n"
+    "    else\n"
+    "        color.b = 4.5138 * color.b;\n"
+    "    return color;\n"
+    "}\n";
+
+static const char shader_widencolorspace_source[] =
+    "sampler2D screen;\n"
+    "float4 main(float2 screenCoords : TEXCOORD0) : COLOR0\n"
+    "{\n"
+    "    float4 color = tex2D(screen, screenCoords.xy);\n"
+    "    color.r = max(color.r - 0.0627450980392157,0) * 1.164383561643836;\n"
+    "    color.g = max(color.g - 0.0627450980392157,0) * 1.164383561643836;\n"
+    "    color.b = max(color.b - 0.0627450980392157,0) * 1.164383561643836;\n"
+    "    return saturate(color);\n"
+    "}\n";
+
+typedef struct
+{
+    const char *name;
+    const char *code;
+} builtin_shader_t;
+
+static const builtin_shader_t builtin_shaders[] =
+{
+    { "Disabled", shader_disabled_source },
+    { "Invert", shader_invert_source },
+    { "Grayscale", shader_grayscale_source },
+    { "Convert601to709", shader_convert601to709_source },
+    { "GammaCorrection18", shader_gammacorrection18_source },
+    { "GammaCorrection22", shader_gammacorrection22_source },
+    { "GammaCorrectionBT709", shader_gammacorrectionbt709_source },
+    { "WidenColorSpace", shader_widencolorspace_source },
+};
+#define BUILTIN_SHADERS_COUNT (sizeof(builtin_shaders)/sizeof(builtin_shaders[0]))
diff --git a/modules/video_output/msw/common.h b/modules/video_output/msw/common.h
index 122d80a..5ddf85e 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 */
+    IDirect3DPixelShader9*  d3dx_shader;
     LPDIRECT3D9             d3dobj;
     D3DCAPS9                d3dcaps;
     LPDIRECT3DDEVICE9       d3ddev;
diff --git a/modules/video_output/msw/direct3d.c b/modules/video_output/msw/direct3d.c
index e2d7387..79e8666 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,11 +42,13 @@
 #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>
 
 #include "common.h"
+#include "builtin_shaders.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -59,8 +63,19 @@ 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_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 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 +85,10 @@ 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)
+    add_loadfile("direct3d-shader-file", NULL, PIXEL_SHADER_FILE_TEXT, PIXEL_SHADER_FILE_LONGTEXT, false)
+
     set_capability("vout display", 240)
     add_shortcut("direct3d")
     set_callbacks(Open, Close)
@@ -486,6 +505,21 @@ static void Manage (vout_display_t *vd)
     }
 }
 
+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;
+}
+
 /**
  * It initializes an instance of Direct3D9
  */
@@ -515,6 +549,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.");
+
     /*
     ** Get device capabilities
     */
@@ -547,9 +585,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 +763,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 +781,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 +809,7 @@ static void Direct3DDestroyResources(vout_display_t *vd)
 {
     Direct3DDestroyScene(vd);
     Direct3DDestroyPool(vd);
+    Direct3DDestroyShaders(vd);
 }
 
 /**
@@ -1131,6 +1181,132 @@ static void Direct3DDestroyScene(vout_display_t *vd)
     msg_Dbg(vd, "Direct3D scene released successfully");
 }
 
+static int Direct3DCompileShader(vout_display_t *vd, const char *shader_source, size_t source_length)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    HRESULT (WINAPI * OurD3DXCompileShader)(
+	LPCSTR pSrcData,
+	UINT srcDataLen,
+	const D3DXMACRO *pDefines,
+	LPD3DXINCLUDE pInclude,
+	LPCSTR pFunctionName,
+	LPCSTR pProfile,
+	DWORD Flags,
+	LPD3DXBUFFER *ppShader,
+	LPD3DXBUFFER *ppErrorMsgs,
+	LPD3DXCONSTANTTABLE *ppConstantTable);
+
+    OurD3DXCompileShader = (void*)GetProcAddress(sys->hd3d9x_dll, "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));
+	return VLC_EGENERIC;
+    }
+    hr = IDirect3DDevice9_CreatePixelShader(sys->d3ddev, ID3DXBuffer_GetBufferPointer(compiled_shader), &sys->d3dx_shader);
+    if (FAILED(hr)) {
+	msg_Warn(vd, "IDirect3DDevice9_CreatePixelShader error (hr=0x%lX)", hr);
+	return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+#define MAX_SHADER_FILE_SIZE 1024*1024
+
+static int Direct3DCreateShaders(vout_display_t *vd)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    if (!sys->hd3d9x_dll)
+	return VLC_EGENERIC;
+
+    /* Find which shader was selected in the list. */
+    char *selected_shader = var_InheritString(vd, "direct3d-shader");
+    if (!selected_shader)
+        return VLC_SUCCESS; /* Nothing to do */
+
+    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 = Direct3DCompileShader(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, "direct3d-shader-file");
+	if (!filepath || !*filepath)
+	{
+	    free(filepath);
+	    goto error;
+	}
+	/* Open file, find its size with fseek/ftell and read its content in a buffer. */
+	fs = fopen(filepath, "rb");
+	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 = malloc(sizeof(*shader_source_file) * length);
+	if (!shader_source_file)
+	    goto error;
+	ret = fread(shader_source_file, length, 1, fs);
+	if (ret != 1)
+	    goto error;
+	ret = Direct3DCompileShader(vd, shader_source_file, length);
+	if (ret)
+	    goto error;
+    }
+
+    free(selected_shader);
+    free(shader_source_file);
+    fclose(fs);
+
+    return VLC_SUCCESS;
+
+error:
+    Direct3DDestroyShaders(vd);
+    free(selected_shader);
+    free(shader_source_file);
+    if (fs)
+	fclose(fs);
+    return VLC_EGENERIC;
+}
+
+static void Direct3DDestroyShaders(vout_display_t *vd)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    if (sys->d3dx_shader)
+	IDirect3DPixelShader9_Release(sys->d3dx_shader);
+    sys->d3dx_shader = NULL;
+}
+
 static void Direct3DSetupVertices(CUSTOMVERTEX *vertices,
                                   const RECT src_full,
                                   const RECT src_crop,
@@ -1370,6 +1546,14 @@ static int Direct3DRenderRegion(vout_display_t *vd,
         return -1;
     }
 
+    if (sys->d3dx_shader) {
+        hr = IDirect3DDevice9_SetPixelShader(d3ddev, sys->d3dx_shader);
+        if (FAILED(hr)) {
+            msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
+            return -1;
+        }
+    }
+
     // Render the vertex buffer contents
     hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
     if (FAILED(hr)) {
@@ -1466,3 +1650,42 @@ 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 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(vlc_object_t *object, const char *name,
+                               char ***values, char ***descs)
+{
+    VLC_UNUSED(object);
+    VLC_UNUSED(name);
+
+    enum_context_t ctx = { NULL, NULL, 0 };
+
+    ListShaders(&ctx);
+
+    *values = ctx.values;
+    *descs = ctx.descs;
+    return ctx.count;
+
+}
-- 
1.7.10.4




More information about the vlc-devel mailing list