diff --git a/NEWS b/NEWS index f44067fb1c74..547ce44c097d 100644 --- a/NEWS +++ b/NEWS @@ -58,6 +58,7 @@ Video Output: * New OpenGL ES 2.0 through EGL video output module for Android * New Android native window provider module * Direct rendering for MediaCodec Android hardware acceleration + * Support for loading HLSL shaders in Direct3D video output Video Filter: * New Oldmovie effect filter diff --git a/modules/video_output/Modules.am b/modules/video_output/Modules.am index ae154fbefb32..a58c7bc205c9 100644 --- a/modules/video_output/Modules.am +++ b/modules/video_output/Modules.am @@ -128,7 +128,7 @@ vout_LTLIBRARIES += $(LTLIBdirect2d) EXTRA_LTLIBRARIES += libdirect2d_plugin.la libdirect3d_plugin_la_SOURCES = msw/direct3d.c \ - msw/common.c msw/common.h msw/events.c msw/events.h + msw/common.c msw/common.h msw/events.c msw/events.h msw/builtin_shaders.h libdirect3d_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) \ -DMODULE_NAME_IS_direct3d libdirect3d_plugin_la_LIBADD = -lgdi32 -lole32 -luuid diff --git a/modules/video_output/msw/builtin_shaders.h b/modules/video_output/msw/builtin_shaders.h new file mode 100644 index 000000000000..3a87ce279c2b --- /dev/null +++ b/modules/video_output/msw/builtin_shaders.h @@ -0,0 +1,150 @@ +/***************************************************************************** + * builtin_shaders.h: Builtin HLSL shader functions. + ***************************************************************************** + * Copyright (C) 2014 the VideoLAN team + * + * Authors: Sasha Koruga , + * Felix Abecassis + * 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. + *****************************************************************************/ + +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 122d80a2192e..5ddf85e0a7ad 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 +# include #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 e2d7387eb90e..6b33bf43f180 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 + * Authors: Damien Fouilleul , + * Sasha Koruga , + * Felix Abecassis * * 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 #include #include +#include /* ToT function */ #include #include #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,139 @@ 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 +1553,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 +1657,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; + +}