diff options
Diffstat (limited to 'libvo')
-rw-r--r-- | libvo/cocoa_common.h | 3 | ||||
-rw-r--r-- | libvo/cocoa_common.m | 47 | ||||
-rw-r--r-- | libvo/d3d_shader_yuv.h | 142 | ||||
-rw-r--r-- | libvo/d3d_shader_yuv.hlsl | 44 | ||||
-rw-r--r-- | libvo/d3d_shader_yuv_2ch.h | 170 | ||||
-rw-r--r-- | libvo/eosd_packer.c | 254 | ||||
-rw-r--r-- | libvo/eosd_packer.h | 71 | ||||
-rw-r--r-- | libvo/filter_kernels.c | 279 | ||||
-rw-r--r-- | libvo/filter_kernels.h | 45 | ||||
-rw-r--r-- | libvo/gl_common.c | 845 | ||||
-rw-r--r-- | libvo/gl_common.h | 342 | ||||
-rw-r--r-- | libvo/gl_header_fixes.h | 231 | ||||
-rw-r--r-- | libvo/mga_template.c | 2 | ||||
-rw-r--r-- | libvo/video_out.c | 16 | ||||
-rw-r--r-- | libvo/vo_direct3d.c | 2137 | ||||
-rw-r--r-- | libvo/vo_directfb2.c | 14 | ||||
-rw-r--r-- | libvo/vo_directx.c | 1742 | ||||
-rw-r--r-- | libvo/vo_gl.c | 326 | ||||
-rw-r--r-- | libvo/vo_gl3.c | 2418 | ||||
-rw-r--r-- | libvo/vo_gl3_shaders.glsl | 316 | ||||
-rw-r--r-- | libvo/vo_svga.c | 4 | ||||
-rw-r--r-- | libvo/vo_vdpau.c | 255 | ||||
-rw-r--r-- | libvo/w32_common.c | 355 | ||||
-rw-r--r-- | libvo/x11_common.c | 3 |
24 files changed, 7702 insertions, 2359 deletions
diff --git a/libvo/cocoa_common.h b/libvo/cocoa_common.h index 16f9a2f1f9..d47ac51500 100644 --- a/libvo/cocoa_common.h +++ b/libvo/cocoa_common.h @@ -29,7 +29,8 @@ void vo_cocoa_update_xinerama_info(struct vo *vo); int vo_cocoa_change_attributes(struct vo *vo); int vo_cocoa_create_window(struct vo *vo, uint32_t d_width, - uint32_t d_height, uint32_t flags); + uint32_t d_height, uint32_t flags, + int gl3profile); void vo_cocoa_swap_buffers(void); int vo_cocoa_check_events(struct vo *vo); diff --git a/libvo/cocoa_common.m b/libvo/cocoa_common.m index f5ad7a0943..e8ef278b1e 100644 --- a/libvo/cocoa_common.m +++ b/libvo/cocoa_common.m @@ -20,7 +20,7 @@ #import <Cocoa/Cocoa.h> #import <OpenGL/OpenGL.h> #import <QuartzCore/QuartzCore.h> -#import <CoreServices/CoreServices.h> // for CGDisplayHideCursor +#import <CoreServices/CoreServices.h> // for CGDisplayHideCursor and Gestalt #include "cocoa_common.h" #include "options.h" @@ -35,6 +35,18 @@ #include "osx_common.h" #include "mp_msg.h" +#ifndef NSOpenGLPFAOpenGLProfile +#define NSOpenGLPFAOpenGLProfile 99 +#endif + +#ifndef NSOpenGLProfileVersionLegacy +#define NSOpenGLProfileVersionLegacy 0x1000 +#endif + +#ifndef NSOpenGLProfileVersion3_2Core +#define NSOpenGLProfileVersion3_2Core 0x3200 +#endif + #define NSLeftAlternateKeyMask (0x000020 | NSAlternateKeyMask) #define NSRightAlternateKeyMask (0x000040 | NSAlternateKeyMask) @@ -94,6 +106,8 @@ void resize_window(struct vo *vo); void vo_cocoa_display_cursor(int requested_state); void create_menu(void); +bool is_lion_or_better(void); + struct vo_cocoa_state *vo_cocoa_init_state(void) { struct vo_cocoa_state *s = talloc_ptrtype(NULL, s); @@ -192,7 +206,8 @@ void vo_cocoa_ontop(struct vo *vo) } int vo_cocoa_create_window(struct vo *vo, uint32_t d_width, - uint32_t d_height, uint32_t flags) + uint32_t d_height, uint32_t flags, + int gl3profile) { struct MPOpts *opts = vo->opts; if (s->current_video_size.width > 0 || s->current_video_size.height > 0) @@ -206,13 +221,18 @@ int vo_cocoa_create_window(struct vo *vo, uint32_t d_width, GLMPlayerOpenGLView *glView = [[GLMPlayerOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; - NSOpenGLPixelFormatAttribute attrs[] = { - NSOpenGLPFADoubleBuffer, // double buffered - NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16, // 16 bit depth buffer - (NSOpenGLPixelFormatAttribute)0 - }; + int i = 0; + NSOpenGLPixelFormatAttribute attr[32]; + if (is_lion_or_better()) { + attr[i++] = NSOpenGLPFAOpenGLProfile; + attr[i++] = (gl3profile ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy); + } + attr[i++] = NSOpenGLPFADoubleBuffer; // double buffered + attr[i++] = NSOpenGLPFADepthSize; + attr[i++] = (NSOpenGLPixelFormatAttribute)16; // 16 bit depth buffer + attr[i] = (NSOpenGLPixelFormatAttribute)0; - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; s->glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; create_menu(); @@ -368,6 +388,17 @@ void create_menu() [menuItem release]; } +bool is_lion_or_better(void) +{ + SInt32 major, minor; + Gestalt(gestaltSystemVersionMajor, &major); + Gestalt(gestaltSystemVersionMinor, &minor); + if(major >= 10 && minor >= 7) + return YES; + else + return NO; +} + @implementation GLMPlayerWindow - (void) windowDidResize:(NSNotification *) notification diff --git a/libvo/d3d_shader_yuv.h b/libvo/d3d_shader_yuv.h new file mode 100644 index 0000000000..49ef753b4c --- /dev/null +++ b/libvo/d3d_shader_yuv.h @@ -0,0 +1,142 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022 +// +// fxc /Tps_2_0 /Fhz:\tmp\mplayer\libvo\d3d_shader_yuv.h +// z:\tmp\mplayer\libvo\d3d_shader_yuv.hlsl /Vnd3d_shader_yuv +// +// +// Parameters: +// +// float4x4 colormatrix; +// sampler2D tex0; +// sampler2D tex1; +// sampler2D tex2; +// +// +// Registers: +// +// Name Reg Size +// ------------ ----- ---- +// colormatrix c0 4 +// tex0 s0 1 +// tex1 s1 1 +// tex2 s2 1 +// + + ps_2_0 + def c4, 1, 0, 0, 0 + dcl t0.xy + dcl t1.xy + dcl t2.xy + dcl_2d s0 + dcl_2d s1 + dcl_2d s2 + texld r0, t0, s0 + texld r1, t1, s1 + texld r2, t2, s2 + mov r0.y, r1.x + mov r0.z, r2.x + mov r0.w, c4.x + dp4 r1.x, r0, c0 + dp4 r1.y, r0, c1 + dp4 r1.z, r0, c2 + dp4 r1.w, r0, c3 + mov oC0, r1 + +// approximately 11 instruction slots used (3 texture, 8 arithmetic) +#endif + +const BYTE d3d_shader_yuv[] = +{ + 0, 2, 255, 255, 254, 255, + 67, 0, 67, 84, 65, 66, + 28, 0, 0, 0, 215, 0, + 0, 0, 0, 2, 255, 255, + 4, 0, 0, 0, 28, 0, + 0, 0, 0, 1, 0, 0, + 208, 0, 0, 0, 108, 0, + 0, 0, 2, 0, 0, 0, + 4, 0, 2, 0, 120, 0, + 0, 0, 0, 0, 0, 0, + 136, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 2, 0, + 144, 0, 0, 0, 0, 0, + 0, 0, 160, 0, 0, 0, + 3, 0, 1, 0, 1, 0, + 6, 0, 168, 0, 0, 0, + 0, 0, 0, 0, 184, 0, + 0, 0, 3, 0, 2, 0, + 1, 0, 10, 0, 192, 0, + 0, 0, 0, 0, 0, 0, + 99, 111, 108, 111, 114, 109, + 97, 116, 114, 105, 120, 0, + 3, 0, 3, 0, 4, 0, + 4, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 116, 101, + 120, 48, 0, 171, 171, 171, + 4, 0, 12, 0, 1, 0, + 1, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 116, 101, + 120, 49, 0, 171, 171, 171, + 4, 0, 12, 0, 1, 0, + 1, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 116, 101, + 120, 50, 0, 171, 171, 171, + 4, 0, 12, 0, 1, 0, + 1, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 112, 115, + 95, 50, 95, 48, 0, 77, + 105, 99, 114, 111, 115, 111, + 102, 116, 32, 40, 82, 41, + 32, 72, 76, 83, 76, 32, + 83, 104, 97, 100, 101, 114, + 32, 67, 111, 109, 112, 105, + 108, 101, 114, 32, 57, 46, + 50, 55, 46, 57, 53, 50, + 46, 51, 48, 50, 50, 0, + 81, 0, 0, 5, 4, 0, + 15, 160, 0, 0, 128, 63, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 31, 0, 0, 2, 0, 0, + 0, 128, 0, 0, 3, 176, + 31, 0, 0, 2, 0, 0, + 0, 128, 1, 0, 3, 176, + 31, 0, 0, 2, 0, 0, + 0, 128, 2, 0, 3, 176, + 31, 0, 0, 2, 0, 0, + 0, 144, 0, 8, 15, 160, + 31, 0, 0, 2, 0, 0, + 0, 144, 1, 8, 15, 160, + 31, 0, 0, 2, 0, 0, + 0, 144, 2, 8, 15, 160, + 66, 0, 0, 3, 0, 0, + 15, 128, 0, 0, 228, 176, + 0, 8, 228, 160, 66, 0, + 0, 3, 1, 0, 15, 128, + 1, 0, 228, 176, 1, 8, + 228, 160, 66, 0, 0, 3, + 2, 0, 15, 128, 2, 0, + 228, 176, 2, 8, 228, 160, + 1, 0, 0, 2, 0, 0, + 2, 128, 1, 0, 0, 128, + 1, 0, 0, 2, 0, 0, + 4, 128, 2, 0, 0, 128, + 1, 0, 0, 2, 0, 0, + 8, 128, 4, 0, 0, 160, + 9, 0, 0, 3, 1, 0, + 1, 128, 0, 0, 228, 128, + 0, 0, 228, 160, 9, 0, + 0, 3, 1, 0, 2, 128, + 0, 0, 228, 128, 1, 0, + 228, 160, 9, 0, 0, 3, + 1, 0, 4, 128, 0, 0, + 228, 128, 2, 0, 228, 160, + 9, 0, 0, 3, 1, 0, + 8, 128, 0, 0, 228, 128, + 3, 0, 228, 160, 1, 0, + 0, 2, 0, 8, 15, 128, + 1, 0, 228, 128, 255, 255, + 0, 0 +}; diff --git a/libvo/d3d_shader_yuv.hlsl b/libvo/d3d_shader_yuv.hlsl new file mode 100644 index 0000000000..b17e257210 --- /dev/null +++ b/libvo/d3d_shader_yuv.hlsl @@ -0,0 +1,44 @@ +// Compile with: +// fxc.exe /Tps_2_0 /Fhd3d_shader_yuv.h d3d_shader_yuv.hlsl /Vnd3d_shader_yuv +// fxc.exe /Tps_2_0 /Fhd3d_shader_yuv_2ch.h d3d_shader_yuv.hlsl /Vnd3d_shader_yuv_2ch /DUSE_2CH=1 + +// Be careful with this shader. You can't use constant slots, since we don't +// load the shader with D3DX. All uniform variables are mapped to hardcoded +// constant slots. + +sampler2D tex0 : register(s0); +sampler2D tex1 : register(s1); +sampler2D tex2 : register(s2); + +uniform float4x4 colormatrix : register(c0); +uniform float2 depth : register(c5); + +#ifdef USE_2CH + +float1 sample(sampler2D tex, float2 t) +{ + // Sample from A8L8 format as if we sampled a single value from L16. + // We compute the 2 channel values back into one. + return dot(tex2D(tex, t).xw, depth); +} + +#else + +float1 sample(sampler2D tex, float2 t) +{ + return tex2D(tex, t).x; +} + +#endif + +float4 main(float2 t0 : TEXCOORD0, + float2 t1 : TEXCOORD1, + float2 t2 : TEXCOORD2) + : COLOR +{ + float4 c = float4(sample(tex0, t0), + sample(tex1, t1), + sample(tex2, t2), + 1); + return mul(c, colormatrix); +} diff --git a/libvo/d3d_shader_yuv_2ch.h b/libvo/d3d_shader_yuv_2ch.h new file mode 100644 index 0000000000..45dcc73992 --- /dev/null +++ b/libvo/d3d_shader_yuv_2ch.h @@ -0,0 +1,170 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022 +// +// fxc /Tps_2_0 /Fhz:\tmp\mplayer\libvo\d3d_shader_yuv_2ch.h +// z:\tmp\mplayer\libvo\d3d_shader_yuv.hlsl /Vnd3d_shader_yuv_2ch +// /DUSE_2CH=1 +// +// +// Parameters: +// +// float4x4 colormatrix; +// float2 depth; +// sampler2D tex0; +// sampler2D tex1; +// sampler2D tex2; +// +// +// Registers: +// +// Name Reg Size +// ------------ ----- ---- +// colormatrix c0 4 +// depth c5 1 +// tex0 s0 1 +// tex1 s1 1 +// tex2 s2 1 +// + + ps_2_0 + def c4, 1, 0, 0, 0 + dcl t0.xy + dcl t1.xy + dcl t2.xy + dcl_2d s0 + dcl_2d s1 + dcl_2d s2 + texld r0, t0, s0 + texld r1, t1, s1 + texld r2, t2, s2 + mul r0.x, r0.x, c5.x + mad r0.x, r0.w, c5.y, r0.x + mul r1.x, r1.x, c5.x + mad r0.y, r1.w, c5.y, r1.x + mul r1.x, r2.x, c5.x + mad r0.z, r2.w, c5.y, r1.x + mov r0.w, c4.x + dp4 r1.x, r0, c0 + dp4 r1.y, r0, c1 + dp4 r1.z, r0, c2 + dp4 r1.w, r0, c3 + mov oC0, r1 + +// approximately 15 instruction slots used (3 texture, 12 arithmetic) +#endif + +const BYTE d3d_shader_yuv_2ch[] = +{ + 0, 2, 255, 255, 254, 255, + 78, 0, 67, 84, 65, 66, + 28, 0, 0, 0, 3, 1, + 0, 0, 0, 2, 255, 255, + 5, 0, 0, 0, 28, 0, + 0, 0, 0, 1, 0, 0, + 252, 0, 0, 0, 128, 0, + 0, 0, 2, 0, 0, 0, + 4, 0, 2, 0, 140, 0, + 0, 0, 0, 0, 0, 0, + 156, 0, 0, 0, 2, 0, + 5, 0, 1, 0, 22, 0, + 164, 0, 0, 0, 0, 0, + 0, 0, 180, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 2, 0, 188, 0, 0, 0, + 0, 0, 0, 0, 204, 0, + 0, 0, 3, 0, 1, 0, + 1, 0, 6, 0, 212, 0, + 0, 0, 0, 0, 0, 0, + 228, 0, 0, 0, 3, 0, + 2, 0, 1, 0, 10, 0, + 236, 0, 0, 0, 0, 0, + 0, 0, 99, 111, 108, 111, + 114, 109, 97, 116, 114, 105, + 120, 0, 3, 0, 3, 0, + 4, 0, 4, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 100, 101, 112, 116, 104, 0, + 171, 171, 1, 0, 3, 0, + 1, 0, 2, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 116, 101, 120, 48, 0, 171, + 171, 171, 4, 0, 12, 0, + 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 116, 101, 120, 49, 0, 171, + 171, 171, 4, 0, 12, 0, + 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 116, 101, 120, 50, 0, 171, + 171, 171, 4, 0, 12, 0, + 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 112, 115, 95, 50, 95, 48, + 0, 77, 105, 99, 114, 111, + 115, 111, 102, 116, 32, 40, + 82, 41, 32, 72, 76, 83, + 76, 32, 83, 104, 97, 100, + 101, 114, 32, 67, 111, 109, + 112, 105, 108, 101, 114, 32, + 57, 46, 50, 55, 46, 57, + 53, 50, 46, 51, 48, 50, + 50, 0, 81, 0, 0, 5, + 4, 0, 15, 160, 0, 0, + 128, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 2, + 0, 0, 0, 128, 0, 0, + 3, 176, 31, 0, 0, 2, + 0, 0, 0, 128, 1, 0, + 3, 176, 31, 0, 0, 2, + 0, 0, 0, 128, 2, 0, + 3, 176, 31, 0, 0, 2, + 0, 0, 0, 144, 0, 8, + 15, 160, 31, 0, 0, 2, + 0, 0, 0, 144, 1, 8, + 15, 160, 31, 0, 0, 2, + 0, 0, 0, 144, 2, 8, + 15, 160, 66, 0, 0, 3, + 0, 0, 15, 128, 0, 0, + 228, 176, 0, 8, 228, 160, + 66, 0, 0, 3, 1, 0, + 15, 128, 1, 0, 228, 176, + 1, 8, 228, 160, 66, 0, + 0, 3, 2, 0, 15, 128, + 2, 0, 228, 176, 2, 8, + 228, 160, 5, 0, 0, 3, + 0, 0, 1, 128, 0, 0, + 0, 128, 5, 0, 0, 160, + 4, 0, 0, 4, 0, 0, + 1, 128, 0, 0, 255, 128, + 5, 0, 85, 160, 0, 0, + 0, 128, 5, 0, 0, 3, + 1, 0, 1, 128, 1, 0, + 0, 128, 5, 0, 0, 160, + 4, 0, 0, 4, 0, 0, + 2, 128, 1, 0, 255, 128, + 5, 0, 85, 160, 1, 0, + 0, 128, 5, 0, 0, 3, + 1, 0, 1, 128, 2, 0, + 0, 128, 5, 0, 0, 160, + 4, 0, 0, 4, 0, 0, + 4, 128, 2, 0, 255, 128, + 5, 0, 85, 160, 1, 0, + 0, 128, 1, 0, 0, 2, + 0, 0, 8, 128, 4, 0, + 0, 160, 9, 0, 0, 3, + 1, 0, 1, 128, 0, 0, + 228, 128, 0, 0, 228, 160, + 9, 0, 0, 3, 1, 0, + 2, 128, 0, 0, 228, 128, + 1, 0, 228, 160, 9, 0, + 0, 3, 1, 0, 4, 128, + 0, 0, 228, 128, 2, 0, + 228, 160, 9, 0, 0, 3, + 1, 0, 8, 128, 0, 0, + 228, 128, 3, 0, 228, 160, + 1, 0, 0, 2, 0, 8, + 15, 128, 1, 0, 228, 128, + 255, 255, 0, 0 +}; diff --git a/libvo/eosd_packer.c b/libvo/eosd_packer.c new file mode 100644 index 0000000000..103648b7c4 --- /dev/null +++ b/libvo/eosd_packer.c @@ -0,0 +1,254 @@ +/* + * Common code for packing EOSD images into larger surfaces. + * + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <libavutil/common.h> +#include "talloc.h" +#include "mp_msg.h" +#include "eosd_packer.h" + +// Initial size of EOSD surface in pixels (x*x) +#define EOSD_SURFACE_INITIAL_SIZE 256 + +// Allocate an eosd_packer, which can be used to layout and cache the list of +// EOSD images contained in a mp_eosd_images_t into a flat surface. +// It can be free'd with talloc_free(). +// Don't forget to call eosd_init() before using it. +struct eosd_packer *eosd_packer_create(void *talloc_ctx) { + return talloc_zero(talloc_ctx, struct eosd_packer); +} + +// Call this when you need to completely reinitialize the EOSD state, e.g. when +// when your EOSD surface was deleted. +// max_width and max_height are the maximum surface sizes that should be +// allowed. +void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width, + uint32_t max_height) +{ + state->max_surface_width = max_width; + state->max_surface_height = max_height; + state->surface.w = 0; + state->surface.h = 0; + state->targets_count = 0; +} + +#define HEIGHT_SORT_BITS 4 +static int size_index(struct eosd_target *r) +{ + unsigned int h = r->source.y1; + int n = av_log2_16bit(h); + return (n << HEIGHT_SORT_BITS) + + (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1); +} + +/* Pack the given rectangles into an area of size w * h. + * The size of each rectangle is read from .source.x1/.source.y1. + * The height of each rectangle must be at least 1 and less than 65536. + * The .source rectangle is then set corresponding to the packed position. + * 'scratch' must point to work memory for num_rects+16 ints. + * Return 0 on success, -1 if the rectangles did not fit in w*h. + * + * The rectangles are placed in rows in order approximately sorted by + * height (the approximate sorting is simpler than a full one would be, + * and allows the algorithm to work in linear time). Additionally, to + * reduce wasted space when there are a few tall rectangles, empty + * lower-right parts of rows are filled recursively when the size of + * rectangles in the row drops past a power-of-two threshold. So if a + * row starts with rectangles of size 3x50, 10x40 and 5x20 then the + * free rectangle with corners (13, 20)-(w, 50) is filled recursively. + */ +static int pack_rectangles(struct eosd_target *rects, int num_rects, + int w, int h, int *scratch) +{ + int bins[16 << HEIGHT_SORT_BITS]; + int sizes[16 << HEIGHT_SORT_BITS] = {}; + for (int i = 0; i < num_rects; i++) + sizes[size_index(rects + i)]++; + int idx = 0; + for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) { + for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) { + bins[i + j] = idx; + idx += sizes[i + j]; + } + scratch[idx++] = -1; + } + for (int i = 0; i < num_rects; i++) + scratch[bins[size_index(rects + i)]++] = i; + for (int i = 0; i < 16; i++) + bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS]; + struct { + int size, x, bottom; + } stack[16] = {{15, 0, h}}, s = {}; + int stackpos = 1; + int y; + while (stackpos) { + y = s.bottom; + s = stack[--stackpos]; + s.size++; + while (s.size--) { + int maxy = -1; + int obj; + while ((obj = scratch[bins[s.size]]) >= 0) { + int bottom = y + rects[obj].source.y1; + if (bottom > s.bottom) + break; + int right = s.x + rects[obj].source.x1; + if (right > w) + break; + bins[s.size]++; + rects[obj].source.x0 = s.x; + rects[obj].source.x1 += s.x; + rects[obj].source.y0 = y; + rects[obj].source.y1 += y; + num_rects--; + if (maxy <= 0) + stack[stackpos++] = s; + s.x = right; + maxy = FFMAX(maxy, bottom); + } + if (maxy > 0) + s.bottom = maxy; + } + } + return num_rects ? -1 : 0; +} + +// padding to reduce interpolation artifacts when doing scaling & filtering +#define EOSD_PADDING 0 + +// Release all previous images, and packs the images in imgs into state. The +// caller must check the change variables: +// *out_need_reposition == true: sub-image positions changed +// *out_need_upload == true: upload all sub-images again +// *out_need_reallocate == true: resize the EOSD texture to state->surface.w/h +// Logical implications: need_reallocate => need_upload => need_reposition +void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs, + bool *out_need_reposition, bool *out_need_upload, + bool *out_need_reallocate) +{ + int i; + ASS_Image *img = imgs->imgs; + ASS_Image *p; + struct eosd_surface *sfc = &state->surface; + + *out_need_reposition = false; + *out_need_upload = false; + *out_need_reallocate = false; + + int change_state = imgs->changed; + + // eosd_reinit() was probably called, force full reupload. + if (state->targets_count == 0 && img) + change_state = 2; + + if (change_state == 0) + return; // Nothing changed, no need to redraw + + state->targets_count = 0; + + *out_need_reposition = true; + + if (!img) + return; // There's nothing to render! + + if (change_state == 1) + goto eosd_skip_upload; + + *out_need_upload = true; + while (1) { + for (p = img, i = 0; p; p = p->next) { + if (p->w <= 0 || p->h <= 0) + continue; + // Allocate new space for surface/target arrays + if (i >= state->targets_size) { + state->targets_size = FFMAX(state->targets_size * 2, 512); + state->targets = + talloc_realloc_size(state, state->targets, + state->targets_size + * sizeof(*state->targets)); + state->scratch = + talloc_realloc_size(state, state->scratch, + (state->targets_size + 16) + * sizeof(*state->scratch)); + } + state->targets[i].source.x1 = p->w + EOSD_PADDING; + state->targets[i].source.y1 = p->h + EOSD_PADDING; + i++; + } + if (pack_rectangles(state->targets, i, sfc->w, sfc->h, + state->scratch) >= 0) + break; + int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE), + state->max_surface_width); + int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE), + state->max_surface_height); + if (w == sfc->w && h == sfc->h) { + mp_msg(MSGT_VO, MSGL_ERR, "[eosd] EOSD bitmaps do not fit on " + "a surface with the maximum supported size\n"); + return; + } + sfc->w = w; + sfc->h = h; + *out_need_reallocate = true; + } + if (*out_need_reallocate) { + mp_msg(MSGT_VO, MSGL_V, "[eosd] Allocate a %dx%d surface for " + "EOSD bitmaps.\n", sfc->w, sfc->h); + } + +eosd_skip_upload: + for (p = img; p; p = p->next) { + if (p->w <= 0 || p->h <= 0) + continue; + struct eosd_target *target = &state->targets[state->targets_count]; + target->source.x1 -= EOSD_PADDING; + target->source.y1 -= EOSD_PADDING; + target->dest.x0 = p->dst_x; + target->dest.y0 = p->dst_y; + target->dest.x1 = p->w + p->dst_x; + target->dest.y1 = p->h + p->dst_y; + target->color = p->color; + target->ass_img = p; + state->targets_count++; + } +} + +// Calculate the bounding box of all sub-rectangles in the EOSD surface that +// will be used for EOSD rendering. +// If the bounding box is empty, return false. +bool eosd_packer_calculate_source_bb(struct eosd_packer *state, + struct eosd_rect *out_bb) +{ + struct eosd_rect bb = { state->surface.w, state->surface.h, 0, 0 }; + + for (int n = 0; n < state->targets_count; n++) { + struct eosd_rect s = state->targets[n].source; + bb.x0 = FFMIN(bb.x0, s.x0); + bb.y0 = FFMIN(bb.y0, s.y0); + bb.x1 = FFMAX(bb.x1, s.x1); + bb.y1 = FFMAX(bb.y1, s.y1); + } + + // avoid degenerate bounding box if empty + bb.x0 = FFMIN(bb.x0, bb.x1); + bb.y0 = FFMIN(bb.y0, bb.y1); + + *out_bb = bb; + return state->targets_count > 0; +} diff --git a/libvo/eosd_packer.h b/libvo/eosd_packer.h new file mode 100644 index 0000000000..e207a4a2dd --- /dev/null +++ b/libvo/eosd_packer.h @@ -0,0 +1,71 @@ +/* + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_EOSD_PACKER_H +#define MPLAYER_EOSD_PACKER_H + +#include <inttypes.h> +#include <stdbool.h> + +#include "sub/ass_mp.h" + +// Pool of surfaces +struct eosd_surface { + //void *native_surface; + int w; + int h; +}; + +struct eosd_rect { + int x0, y0, x1, y1; +}; + +// List of surfaces to be rendered +struct eosd_target { + struct eosd_rect source; // position in EOSD surface + struct eosd_rect dest; // position on screen + uint32_t color; // libass-style color of the image + // NOTE: This must not be accessed after you return from your VO's + // VOCTRL_DRAW_EOSD call - libass will free or reuse the associated + // memory. Feel free to set this to NULL to make erroneous accesses to + // this member fail early. + ASS_Image *ass_img; +}; + +struct eosd_packer { + struct eosd_surface surface; + struct eosd_target *targets; + int targets_count; // number of valid elements in targets + int targets_size; // number of allocated elements in targets + + uint32_t max_surface_width; + uint32_t max_surface_height; + + int *scratch; +}; + +struct eosd_packer *eosd_packer_create(void *talloc_ctx); +void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width, + uint32_t max_height); +void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs, + bool *out_need_reposition, bool *out_need_upload, + bool *out_need_reallocate); +bool eosd_packer_calculate_source_bb(struct eosd_packer *state, + struct eosd_rect *out_bb); + +#endif /* MPLAYER_EOSD_PACKER_H */ diff --git a/libvo/filter_kernels.c b/libvo/filter_kernels.c new file mode 100644 index 0000000000..2c2f56ee51 --- /dev/null +++ b/libvo/filter_kernels.c @@ -0,0 +1,279 @@ +/* + * This file is part of mplayer2. + * + * Most code for computing the weights is taken from Anti-Grain Geometry (AGG) + * (licensed under GPL 2 or later), with modifications. + * Copyright (C) 2002-2006 Maxim Shemanarev + * http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h?view=markup + * + * Also see glumpy (BSD licensed), contains the same code in Python: + * http://code.google.com/p/glumpy/source/browse/glumpy/image/filter.py + * + * Also see: Paul Heckbert's "zoom" + * + * Also see XBMC: ConvolutionKernels.cpp etc. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stddef.h> +#include <string.h> +#include <math.h> +#include <assert.h> + +#include "filter_kernels.h" + +// NOTE: all filters are separable, symmetric, and are intended for use with +// a lookup table/texture. + +const struct filter_kernel *mp_find_filter_kernel(const char *name) +{ + for (const struct filter_kernel *k = mp_filter_kernels; k->name; k++) { + if (strcmp(k->name, name) == 0) + return k; + } + return NULL; +} + +// sizes = sorted list of available filter sizes, terminated with size 0 +// inv_scale = source_size / dest_size +bool mp_init_filter(struct filter_kernel *filter, const int *sizes, + double inv_scale) +{ + // only downscaling requires widening the filter + filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0; + double support = filter->radius * filter->inv_scale; + int size = ceil(2.0 * support); + // round up to smallest available size that's still large enough + if (size < sizes[0]) + size = sizes[0]; + const int *cursize = sizes; + while (size > *cursize && *cursize) + cursize++; + if (*cursize) { + filter->size = *cursize; + return true; + } else { + // The filter doesn't fit - instead of failing completely, use the + // largest filter available. This is incorrect, but better than refusing + // to do anything. + filter->size = cursize[-1]; + filter->inv_scale = filter->size / 2.0 / filter->radius; + return false; + } +} + +// Calculate the 1D filtering kernel for N sample points. +// N = number of samples, which is filter->size +// The weights will be stored in out_w[0] to out_w[N - 1] +// f = x0 - abs(x0), subpixel position in the range [0,1) or [0,1]. +void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w) +{ + assert(filter->size > 0); + double sum = 0; + for (int n = 0; n < filter->size; n++) { + double x = f - (n - filter->size / 2 + 1); + double w = filter->weight(filter, fabs(x) / filter->inv_scale); + out_w[n] = w; + sum += w; + } + //normalize + for (int n = 0; n < filter->size; n++) + out_w[n] /= sum; +} + +// Fill the given array with weights for the range [0.0, 1.0]. The array is +// interpreted as rectangular array of count * filter->size items. +void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array) +{ + for (int n = 0; n < count; n++) { + mp_compute_weights(filter, n / (double)(count - 1), + out_array + filter->size * n); + } +} + +typedef struct filter_kernel kernel; + +static double bilinear(kernel *k, double x) +{ + return 1.0 - x; +} + +static double hanning(kernel *k, double x) +{ + return 0.5 + 0.5 * cos(M_PI * x); +} + +static double hamming(kernel *k, double x) +{ + return 0.54 + 0.46 * cos(M_PI * x); +} + +static double hermite(kernel *k, double x) +{ + return (2.0 * x - 3.0) * x * x + 1.0; +} + +static double quadric(kernel *k, double x) +{ + // NOTE: glumpy uses 0.75, AGG uses 0.5 + if (x < 0.5) + return 0.75 - x * x; + if (x < 1.5) + return 0.5 * (x - 1.5) * (x - 1.5); + return 0; +} + +static double bc_pow3(double x) +{ + return (x <= 0) ? 0 : x * x * x; +} + +static double bicubic(kernel *k, double x) +{ + return (1.0/6.0) * ( bc_pow3(x + 2) + - 4 * bc_pow3(x + 1) + + 6 * bc_pow3(x) + - 4 * bc_pow3(x - 1)); +} + +static double bessel_i0(double epsilon, double x) +{ + double sum = 1; + double y = x * x / 4; + double t = y; + for (int i = 2; t > epsilon; i++) { + sum += t; + t *= y / (i * i); + } + return sum; +} + +static double kaiser(kernel *k, double x) +{ + double a = k->params[0]; + double b = k->params[1]; + double epsilon = 1e-12; + double i0a = 1 / bessel_i0(epsilon, b); + return bessel_i0(epsilon, a * sqrt(1 - x * x)) * i0a; +} + +static double catmull_rom(kernel *k, double x) +{ + if (x < 1.0) + return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0)); + if (x < 2.0) + return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x))); + return 0; +} + +// Mitchell-Netravali +static double mitchell(kernel *k, double x) +{ + double b = k->params[0]; + double c = k->params[1]; + double + p0 = (6.0 - 2.0 * b) / 6.0, + p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0, + p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0, + q0 = (8.0 * b + 24.0 * c) / 6.0, + q1 = (-12.0 * b - 48.0 * c) / 6.0, + q2 = (6.0 * b + 30.0 * c) / 6.0, + q3 = (-b - 6.0 * c) / 6.0; + if (x < 1.0) + return p0 + x * x * (p2 + x * p3); + if (x < 2.0) + return q0 + x * (q1 + x * (q2 + x * q3)); + return 0; +} + +static double spline16(kernel *k, double x) +{ + if (x < 1.0) + return ((x - 9.0/5.0 ) * x - 1.0/5.0 ) * x + 1.0; + return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0 ) * (x-1); +} + +static double spline36(kernel *k, double x) +{ + if(x < 1.0) + return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0; + if(x < 2.0) + return ((-6.0/11.0 * (x - 1) + 270.0/209.0) * (x - 1) - 156.0/209.0) + * (x - 1); + return ((1.0/11.0 * (x - 2) - 45.0/209.0) * (x - 2) + 26.0/209.0) + * (x - 2); +} + +static double gaussian(kernel *k, double x) +{ + return exp(-2.0 * x * x) * sqrt(2.0 / M_PI); +} + +static double sinc(kernel *k, double x) +{ + if (x == 0.0) + return 1.0; + double pix = M_PI * x; + return sin(pix) / pix; +} + +static double lanczos(kernel *k, double x) +{ + double radius = k->size / 2; + if (x < -radius || x > radius) + return 0; + if (x == 0) + return 1; + double pix = M_PI * x; + return radius * sin(pix) * sin(pix / radius) / (pix * pix); +} + +static double blackman(kernel *k, double x) +{ + double radius = k->size / 2; + if (x == 0.0) + return 1.0; + if (x > radius) + return 0.0; + x *= M_PI; + double xr = x / radius; + return (sin(x) / x) * (0.42 + 0.5 * cos(xr) + 0.08 * cos(2 * xr)); +} + +const struct filter_kernel mp_filter_kernels[] = { + {"bilinear_slow", 1, bilinear}, + {"hanning", 1, hanning}, + {"hamming", 1, hamming}, + {"hermite", 1, hermite}, + {"quadric", 1.5, quadric}, + {"bicubic", 2, bicubic}, + {"kaiser", 1, kaiser, .params = {6.33, 6.33} }, + {"catmull_rom", 2, catmull_rom}, + {"mitchell", 2, mitchell, .params = {1.0/3.0, 1.0/3.0} }, + {"spline16", 2, spline16}, + {"spline36", 3, spline36}, + {"gaussian", 2, gaussian}, + {"sinc2", 2, sinc}, + {"sinc3", 3, sinc}, + {"sinc4", 4, sinc}, + {"lanczos2", 2, lanczos}, + {"lanczos3", 3, lanczos}, + {"lanczos4", 4, lanczos}, + {"blackman2", 2, blackman}, + {"blackman3", 3, blackman}, + {"blackman4", 4, blackman}, + {0} +}; diff --git a/libvo/filter_kernels.h b/libvo/filter_kernels.h new file mode 100644 index 0000000000..46a392c40a --- /dev/null +++ b/libvo/filter_kernels.h @@ -0,0 +1,45 @@ +/* + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_FILTER_KERNELS_H +#define MPLAYER_FILTER_KERNELS_H + +#include <stdbool.h> + +struct filter_kernel { + const char *name; + double radius; + double (*weight)(struct filter_kernel *kernel, double x); + + // The filter params can be changed at runtime. Only used by some filters. + float params[2]; + // The following values are set by mp_init_filter() at runtime. + // Number of coefficients; equals the rounded up radius multiplied with 2. + int size; + double inv_scale; +}; + +extern const struct filter_kernel mp_filter_kernels[]; + +const struct filter_kernel *mp_find_filter_kernel(const char *name); +bool mp_init_filter(struct filter_kernel *filter, const int *sizes, + double scale); +void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w); +void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array); + +#endif /* MPLAYER_FILTER_KERNELS_H */ diff --git a/libvo/gl_common.c b/libvo/gl_common.c index 9245811911..414e52dbd2 100644 --- a/libvo/gl_common.c +++ b/libvo/gl_common.c @@ -37,6 +37,7 @@ #include <stdio.h> #include <string.h> #include <ctype.h> +#include <stdbool.h> #include <math.h> #include "talloc.h" #include "gl_common.h" @@ -48,6 +49,31 @@ //! \defgroup glgeneral OpenGL general helper functions +// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL) +static const char *gl_error_to_string(GLenum error) +{ + switch (error) { + case GL_INVALID_ENUM: return "INVALID_ENUM"; + case GL_INVALID_VALUE: return "INVALID_VALUE"; + case GL_INVALID_OPERATION: return "INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY"; + default: return "unknown"; + } +} + +void glCheckError(GL *gl, const char *info) +{ + for (;;) { + GLenum error = gl->GetError(); + if (error == GL_NO_ERROR) + break; + mp_msg(MSGT_VO, MSGL_ERR, "[gl] %s: OpenGL error %s.\n", info, + gl_error_to_string(error)); + } +} + //! \defgroup glcontext OpenGL context management helper functions //! \defgroup gltexture OpenGL texture handling helper functions @@ -74,62 +100,6 @@ void glAdjustAlignment(GL *gl, int stride) gl->PixelStorei(GL_PACK_ALIGNMENT, gl_alignment); } -struct gl_name_map_struct { - GLint value; - const char *name; -}; - -#undef MAP -#define MAP(a) {a, # a} -//! mapping table for the glValName function -static const struct gl_name_map_struct gl_name_map[] = { - // internal format - MAP(GL_R3_G3_B2), MAP(GL_RGB4), MAP(GL_RGB5), MAP(GL_RGB8), - MAP(GL_RGB10), MAP(GL_RGB12), MAP(GL_RGB16), MAP(GL_RGBA2), - MAP(GL_RGBA4), MAP(GL_RGB5_A1), MAP(GL_RGBA8), MAP(GL_RGB10_A2), - MAP(GL_RGBA12), MAP(GL_RGBA16), MAP(GL_LUMINANCE8), MAP(GL_LUMINANCE16), - MAP(GL_R16), - - // format - MAP(GL_RGB), MAP(GL_RGBA), MAP(GL_RED), MAP(GL_GREEN), MAP(GL_BLUE), - MAP(GL_ALPHA), MAP(GL_LUMINANCE), MAP(GL_LUMINANCE_ALPHA), - MAP(GL_COLOR_INDEX), - // rest 1.2 only - MAP(GL_BGR), MAP(GL_BGRA), - - //type - MAP(GL_BYTE), MAP(GL_UNSIGNED_BYTE), MAP(GL_SHORT), MAP(GL_UNSIGNED_SHORT), - MAP(GL_INT), MAP(GL_UNSIGNED_INT), MAP(GL_FLOAT), MAP(GL_DOUBLE), - MAP(GL_2_BYTES), MAP(GL_3_BYTES), MAP(GL_4_BYTES), - // rest 1.2 only - MAP(GL_UNSIGNED_BYTE_3_3_2), MAP(GL_UNSIGNED_BYTE_2_3_3_REV), - MAP(GL_UNSIGNED_SHORT_5_6_5), MAP(GL_UNSIGNED_SHORT_5_6_5_REV), - MAP(GL_UNSIGNED_SHORT_4_4_4_4), MAP(GL_UNSIGNED_SHORT_4_4_4_4_REV), - MAP(GL_UNSIGNED_SHORT_5_5_5_1), MAP(GL_UNSIGNED_SHORT_1_5_5_5_REV), - MAP(GL_UNSIGNED_INT_8_8_8_8), MAP(GL_UNSIGNED_INT_8_8_8_8_REV), - MAP(GL_UNSIGNED_INT_10_10_10_2), MAP(GL_UNSIGNED_INT_2_10_10_10_REV), - {0, 0} -}; -#undef MAP - -/** - * \brief return the name of an OpenGL constant - * \param value the constant - * \return name of the constant or "Unknown format!" - * \ingroup glgeneral - */ -const char *glValName(GLint value) -{ - int i = 0; - - while (gl_name_map[i].name) { - if (gl_name_map[i].value == value) - return gl_name_map[i].name; - i++; - } - return "Unknown format!"; -} - //! always return this format as internal texture format in glFindFormat #define TEXTUREFORMAT_ALWAYS GL_RGB8 #undef TEXTUREFORMAT_ALWAYS @@ -218,8 +188,8 @@ int glFindFormat(uint32_t fmt, int have_texture_rg, int *bpp, GLint *gl_texfmt, // we do not support palettized formats, although the format the // swscale produces works case IMGFMT_RGB8: - gl_format = GL_RGB; - gl_type = GL_UNSIGNED_BYTE_2_3_3_REV; + *gl_format = GL_RGB; + *gl_type = GL_UNSIGNED_BYTE_2_3_3_REV; break; #endif case IMGFMT_RGB15: @@ -232,12 +202,12 @@ int glFindFormat(uint32_t fmt, int have_texture_rg, int *bpp, GLint *gl_texfmt, break; #if 0 case IMGFMT_BGR8: - // special case as red and blue have a differen number of bits. + // special case as red and blue have a different number of bits. // GL_BGR and GL_UNSIGNED_BYTE_3_3_2 isn't supported at least // by nVidia drivers, and in addition would give more bits to // blue than to red, which isn't wanted - gl_format = GL_RGB; - gl_type = GL_UNSIGNED_BYTE_3_3_2; + *gl_format = GL_RGB; + *gl_type = GL_UNSIGNED_BYTE_3_3_2; break; #endif case IMGFMT_BGR15: @@ -295,45 +265,28 @@ typedef struct { const char *extstr; const char *funcnames[7]; void *fallback; + bool is_gl3; } extfunc_desc_t; #define DEF_FUNC_DESC(name) \ - {offsetof(GL, name), NULL, {"gl" # name, NULL}, gl ## name} + {offsetof(GL, name), NULL, {"gl" # name}, gl ## name} #define DEF_EXT_FUNCS(...) __VA_ARGS__ #define DEF_EXT_DESC(name, ext, funcnames) \ {offsetof(GL, name), ext, {DEF_EXT_FUNCS funcnames}} +// These are mostly handled the same, but needed because at least the MESA +// headers don't define any function prototypes for these. +#define DEF_GL3_DESC(name) \ + {offsetof(GL, name), NULL, {"gl" # name}, NULL, .is_gl3 = true} static const extfunc_desc_t extfuncs[] = { // these aren't extension functions but we query them anyway to allow // different "backends" with one binary - DEF_FUNC_DESC(Begin), - DEF_FUNC_DESC(End), DEF_FUNC_DESC(Viewport), - DEF_FUNC_DESC(MatrixMode), - DEF_FUNC_DESC(LoadIdentity), - DEF_FUNC_DESC(Translated), - DEF_FUNC_DESC(Scaled), - DEF_FUNC_DESC(Ortho), - DEF_FUNC_DESC(Frustum), - DEF_FUNC_DESC(PushMatrix), - DEF_FUNC_DESC(PopMatrix), DEF_FUNC_DESC(Clear), - DEF_FUNC_DESC(GenLists), - DEF_FUNC_DESC(DeleteLists), - DEF_FUNC_DESC(NewList), - DEF_FUNC_DESC(EndList), - DEF_FUNC_DESC(CallList), - DEF_FUNC_DESC(CallLists), DEF_FUNC_DESC(GenTextures), DEF_FUNC_DESC(DeleteTextures), - DEF_FUNC_DESC(TexEnvf), DEF_FUNC_DESC(TexEnvi), - DEF_FUNC_DESC(Color4ub), - DEF_FUNC_DESC(Color3f), - DEF_FUNC_DESC(Color4f), DEF_FUNC_DESC(ClearColor), - DEF_FUNC_DESC(ClearDepth), - DEF_FUNC_DESC(DepthFunc), DEF_FUNC_DESC(Enable), DEF_FUNC_DESC(Disable), DEF_FUNC_DESC(DrawBuffer), @@ -349,18 +302,42 @@ static const extfunc_desc_t extfuncs[] = { DEF_FUNC_DESC(TexParameteri), DEF_FUNC_DESC(TexParameterf), DEF_FUNC_DESC(TexParameterfv), - DEF_FUNC_DESC(TexCoord2f), - DEF_FUNC_DESC(Vertex2f), - DEF_FUNC_DESC(Vertex3f), - DEF_FUNC_DESC(Normal3f), - DEF_FUNC_DESC(Lightfv), - DEF_FUNC_DESC(ColorMaterial), - DEF_FUNC_DESC(ShadeModel), DEF_FUNC_DESC(GetIntegerv), + DEF_FUNC_DESC(GetBooleanv), DEF_FUNC_DESC(ColorMask), DEF_FUNC_DESC(ReadPixels), DEF_FUNC_DESC(ReadBuffer), + DEF_FUNC_DESC(DrawArrays), + DEF_FUNC_DESC(GetString), + DEF_FUNC_DESC(GetError), + // legacy GL functions (1.x - 2.x) + DEF_FUNC_DESC(Begin), + DEF_FUNC_DESC(End), + DEF_FUNC_DESC(MatrixMode), + DEF_FUNC_DESC(LoadIdentity), + DEF_FUNC_DESC(Translated), + DEF_FUNC_DESC(Scaled), + DEF_FUNC_DESC(Ortho), + DEF_FUNC_DESC(PushMatrix), + DEF_FUNC_DESC(PopMatrix), + DEF_FUNC_DESC(GenLists), + DEF_FUNC_DESC(DeleteLists), + DEF_FUNC_DESC(NewList), + DEF_FUNC_DESC(EndList), + DEF_FUNC_DESC(CallList), + DEF_FUNC_DESC(CallLists), + DEF_FUNC_DESC(Color4ub), + DEF_FUNC_DESC(Color4f), + DEF_FUNC_DESC(TexCoord2f), + DEF_FUNC_DESC(Vertex2f), + DEF_FUNC_DESC(VertexPointer), + DEF_FUNC_DESC(ColorPointer), + DEF_FUNC_DESC(TexCoordPointer), + DEF_FUNC_DESC(EnableClientState), + DEF_FUNC_DESC(DisableClientState), + + // OpenGL extension functions DEF_EXT_DESC(GenBuffers, NULL, ("glGenBuffers", "glGenBuffersARB")), DEF_EXT_DESC(DeleteBuffers, NULL, @@ -373,18 +350,6 @@ static const extfunc_desc_t extfuncs[] = { ("glUnmapBuffer", "glUnmapBufferARB")), DEF_EXT_DESC(BufferData, NULL, ("glBufferData", "glBufferDataARB")), - DEF_EXT_DESC(BeginFragmentShader, "ATI_fragment_shader", - ("glBeginFragmentShaderATI")), - DEF_EXT_DESC(EndFragmentShader, "ATI_fragment_shader", - ("glEndFragmentShaderATI")), - DEF_EXT_DESC(SampleMap, "ATI_fragment_shader", - ("glSampleMapATI")), - DEF_EXT_DESC(ColorFragmentOp2, "ATI_fragment_shader", - ("glColorFragmentOp2ATI")), - DEF_EXT_DESC(ColorFragmentOp3, "ATI_fragment_shader", - ("glColorFragmentOp3ATI")), - DEF_EXT_DESC(SetFragmentShaderConstant, "ATI_fragment_shader", - ("glSetFragmentShaderConstantATI")), DEF_EXT_DESC(ActiveTexture, NULL, ("glActiveTexture", "glActiveTextureARB")), DEF_EXT_DESC(BindTexture, NULL, @@ -399,7 +364,7 @@ static const extfunc_desc_t extfuncs[] = { ("glBindProgramARB")), DEF_EXT_DESC(ProgramString, "_program", ("glProgramStringARB")), - DEF_EXT_DESC(GetProgramiv, "_program", + DEF_EXT_DESC(GetProgramivARB, "_program", ("glGetProgramivARB")), DEF_EXT_DESC(ProgramEnvParameter4f, "_program", ("glProgramEnvParameter4fARB")), @@ -408,6 +373,64 @@ static const extfunc_desc_t extfuncs[] = { "wglSwapInterval", "wglSwapIntervalEXT")), DEF_EXT_DESC(TexImage3D, NULL, ("glTexImage3D")), + + // ancient ATI extensions + DEF_EXT_DESC(BeginFragmentShader, "ATI_fragment_shader", + ("glBeginFragmentShaderATI")), + DEF_EXT_DESC(EndFragmentShader, "ATI_fragment_shader", + ("glEndFragmentShaderATI")), + DEF_EXT_DESC(SampleMap, "ATI_fragment_shader", + ("glSampleMapATI")), + DEF_EXT_DESC(ColorFragmentOp2, "ATI_fragment_shader", + ("glColorFragmentOp2ATI")), + DEF_EXT_DESC(ColorFragmentOp3, "ATI_fragment_shader", + ("glColorFragmentOp3ATI")), + DEF_EXT_DESC(SetFragmentShaderConstant, "ATI_fragment_shader", + ("glSetFragmentShaderConstantATI")), + + // GL 3, possibly in GL 2.x as well in form of extensions + DEF_GL3_DESC(GenBuffers), + DEF_GL3_DESC(DeleteBuffers), + DEF_GL3_DESC(BindBuffer), + DEF_GL3_DESC(MapBuffer), + DEF_GL3_DESC(UnmapBuffer), + DEF_GL3_DESC(BufferData), + DEF_GL3_DESC(ActiveTexture), + DEF_GL3_DESC(BindTexture), + DEF_GL3_DESC(GenVertexArrays), + DEF_GL3_DESC(BindVertexArray), + DEF_GL3_DESC(GetAttribLocation), + DEF_GL3_DESC(EnableVertexAttribArray), + DEF_GL3_DESC(DisableVertexAttribArray), + DEF_GL3_DESC(VertexAttribPointer), + DEF_GL3_DESC(DeleteVertexArrays), + DEF_GL3_DESC(UseProgram), + DEF_GL3_DESC(GetUniformLocation), + DEF_GL3_DESC(CompileShader), + DEF_GL3_DESC(CreateProgram), + DEF_GL3_DESC(CreateShader), + DEF_GL3_DESC(ShaderSource), + DEF_GL3_DESC(LinkProgram), + DEF_GL3_DESC(AttachShader), + DEF_GL3_DESC(DeleteShader), + DEF_GL3_DESC(DeleteProgram), + DEF_GL3_DESC(GetShaderInfoLog), + DEF_GL3_DESC(GetShaderiv), + DEF_GL3_DESC(GetProgramInfoLog), + DEF_GL3_DESC(GetProgramiv), + DEF_GL3_DESC(GetStringi), + DEF_GL3_DESC(BindAttribLocation), + DEF_GL3_DESC(BindFramebuffer), + DEF_GL3_DESC(GenFramebuffers), + DEF_GL3_DESC(DeleteFramebuffers), + DEF_GL3_DESC(CheckFramebufferStatus), + DEF_GL3_DESC(FramebufferTexture2D), + DEF_GL3_DESC(Uniform1f), + DEF_GL3_DESC(Uniform3f), + DEF_GL3_DESC(Uniform1i), + DEF_GL3_DESC(UniformMatrix3fv), + DEF_GL3_DESC(UniformMatrix4x3fv), + {-1} }; @@ -417,43 +440,53 @@ static const extfunc_desc_t extfuncs[] = { * \param ext2 an extra extension string */ static void getFunctions(GL *gl, void *(*getProcAddress)(const GLubyte *), - const char *ext2) + const char *ext2, bool is_gl3) { const extfunc_desc_t *dsc; - const char *extensions; - char *allexts; + char *allexts = talloc_strdup(NULL, ext2 ? ext2 : ""); + + *gl = (GL) {0}; if (!getProcAddress) getProcAddress = (void *)getdladdr; - // special case, we need glGetString before starting to find the other functions - gl->GetString = getProcAddress("glGetString"); - if (!gl->GetString) - gl->GetString = glGetString; - - extensions = (const char *)gl->GetString(GL_EXTENSIONS); - if (!extensions) - extensions = ""; - if (!ext2) - ext2 = ""; - allexts = malloc(strlen(extensions) + strlen(ext2) + 2); - strcpy(allexts, extensions); - strcat(allexts, " "); - strcat(allexts, ext2); + if (is_gl3) { + gl->GetStringi = getProcAddress("glGetStringi"); + gl->GetIntegerv = getProcAddress("glGetIntegerv"); + + if (!(gl->GetStringi && gl->GetIntegerv)) + return; + + GLint exts; + gl->GetIntegerv(GL_NUM_EXTENSIONS, &exts); + for (int n = 0; n < exts; n++) { + allexts = talloc_asprintf_append(allexts, " %s", + gl->GetStringi(GL_EXTENSIONS, n)); + } + } else { + gl->GetString = getProcAddress("glGetString"); + if (!gl->GetString) + gl->GetString = glGetString; + const char *ext = (char*)gl->GetString(GL_EXTENSIONS); + allexts = talloc_asprintf_append(allexts, " %s", ext); + } + mp_msg(MSGT_VO, MSGL_DBG2, "OpenGL extensions string:\n%s\n", allexts); for (dsc = extfuncs; dsc->offset >= 0; dsc++) { void *ptr = NULL; - int i; if (!dsc->extstr || strstr(allexts, dsc->extstr)) { - for (i = 0; !ptr && dsc->funcnames[i]; i++) + for (int i = 0; !ptr && dsc->funcnames[i]; i++) ptr = getProcAddress((const GLubyte *)dsc->funcnames[i]); } if (!ptr) ptr = dsc->fallback; + if (!ptr && !dsc->extstr && (!dsc->is_gl3 || is_gl3)) + mp_msg(MSGT_VO, MSGL_WARN, "[gl] OpenGL function not found: %s\n", + dsc->funcnames[0]); void **funcptr = (void**)(((char*)gl) + dsc->offset); *funcptr = ptr; } - free(allexts); + talloc_free(allexts); } /** @@ -595,6 +628,9 @@ int glFmt2bpp(GLenum format, GLenum type) return 4 * component_size; case GL_RED: return component_size; + case GL_RG: + case GL_LUMINANCE_ALPHA: + return 2 * component_size; } return 0; // unknown } @@ -855,11 +891,41 @@ static void gen_spline_lookup_tex(GL *gl, GLenum unit) free(tex); } +#define NOISE_RES 2048 + +/** + * \brief creates the 1D lookup texture needed to generate pseudo-random numbers. + * \param unit texture unit to attach texture to + */ +static void gen_noise_lookup_tex(GL *gl, GLenum unit) { + GLfloat *tex = calloc(NOISE_RES, sizeof(*tex)); + uint32_t lcg = 0x79381c11; + int i; + for (i = 0; i < NOISE_RES; i++) + tex[i] = (double)i / (NOISE_RES - 1); + for (i = 0; i < NOISE_RES - 1; i++) { + int remain = NOISE_RES - i; + int idx = i + (lcg >> 16) % remain; + GLfloat tmp = tex[i]; + tex[i] = tex[idx]; + tex[idx] = tmp; + lcg = lcg * 1664525 + 1013904223; + } + gl->ActiveTexture(unit); + gl->TexImage1D(GL_TEXTURE_1D, 0, 1, NOISE_RES, 0, GL_RED, GL_FLOAT, tex); + gl->TexParameterf(GL_TEXTURE_1D, GL_TEXTURE_PRIORITY, 1.0); + gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl->ActiveTexture(GL_TEXTURE0); + free(tex); +} + #define SAMPLE(dest, coord, texture) \ "TEX textemp, " coord ", " texture ", $tex_type;\n" \ "MOV " dest ", textemp.r;\n" -static const char *bilin_filt_template = +static const char bilin_filt_template[] = SAMPLE("yuv.$out_comp","fragment.texcoord[$in_tex]","texture[$in_tex]"); #define BICUB_FILT_MAIN \ @@ -876,7 +942,7 @@ static const char *bilin_filt_template = /* x-interpolation */ \ "LRP yuv.$out_comp, parmx.b, a.bbbb, a.aaaa;\n" -static const char *bicub_filt_template_2D = +static const char bicub_filt_template_2D[] = "MAD coord.xy, fragment.texcoord[$in_tex], {$texw, $texh}, {0.5, 0.5};\n" "TEX parmx, coord.x, texture[$texs], 1D;\n" "MUL cdelta.xz, parmx.rrgg, {-$ptw, 0, $ptw, 0};\n" @@ -884,7 +950,7 @@ static const char *bicub_filt_template_2D = "MUL cdelta.yw, parmy.rrgg, {0, -$pth, 0, $pth};\n" BICUB_FILT_MAIN; -static const char *bicub_filt_template_RECT = +static const char bicub_filt_template_RECT[] = "ADD coord, fragment.texcoord[$in_tex], {0.5, 0.5};\n" "TEX parmx, coord.x, texture[$texs], 1D;\n" "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};\n" @@ -902,7 +968,7 @@ static const char *bicub_filt_template_RECT = "ADD "t ".x, "t ".xxxx, "s ";\n" \ "SUB "t ".y, "t ".yyyy, "s ";\n" -static const char *bicub_notex_filt_template_2D = +static const char bicub_notex_filt_template_2D[] = "MAD coord.xy, fragment.texcoord[$in_tex], {$texw, $texh}, {0.5, 0.5};\n" "FRC coord.xy, coord.xyxy;\n" CALCWEIGHTS("parmx", "coord.xxxx") @@ -911,7 +977,7 @@ static const char *bicub_notex_filt_template_2D = "MUL cdelta.yw, parmy.rrgg, {0, -$pth, 0, $pth};\n" BICUB_FILT_MAIN; -static const char *bicub_notex_filt_template_RECT = +static const char bicub_notex_filt_template_RECT[] = "ADD coord, fragment.texcoord[$in_tex], {0.5, 0.5};\n" "FRC coord.xy, coord.xyxy;\n" CALCWEIGHTS("parmx", "coord.xxxx") @@ -928,19 +994,19 @@ static const char *bicub_notex_filt_template_RECT = /* x-interpolation */ \ "LRP yuv.$out_comp, parmx.b, a.rrrr, b.rrrr;\n" -static const char *bicub_x_filt_template_2D = +static const char bicub_x_filt_template_2D[] = "MAD coord.x, fragment.texcoord[$in_tex], {$texw}, {0.5};\n" "TEX parmx, coord, texture[$texs], 1D;\n" "MUL cdelta.xyz, parmx.rrgg, {-$ptw, 0, $ptw};\n" BICUB_X_FILT_MAIN; -static const char *bicub_x_filt_template_RECT = +static const char bicub_x_filt_template_RECT[] = "ADD coord.x, fragment.texcoord[$in_tex], {0.5};\n" "TEX parmx, coord, texture[$texs], 1D;\n" "MUL cdelta.xyz, parmx.rrgg, {-1, 0, 1};\n" BICUB_X_FILT_MAIN; -static const char *unsharp_filt_template = +static const char unsharp_filt_template[] = "PARAM dcoord$out_comp = {$ptw_05, $pth_05, $ptw_05, -$pth_05};\n" "ADD coord, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n" "SUB coord2, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n" @@ -955,7 +1021,7 @@ static const char *unsharp_filt_template = "MAD textemp.r, b.r, {$strength}, a.r;\n" "MOV yuv.$out_comp, textemp.r;\n"; -static const char *unsharp_filt_template2 = +static const char unsharp_filt_template2[] = "PARAM dcoord$out_comp = {$ptw_12, $pth_12, $ptw_12, -$pth_12};\n" "PARAM dcoord2$out_comp = {$ptw_15, 0, 0, $pth_15};\n" "ADD coord, fragment.texcoord[$in_tex].xyxy, dcoord$out_comp;\n" @@ -980,7 +1046,7 @@ static const char *unsharp_filt_template2 = "MAD textemp.r, b.r, {$strength}, a.r;\n" "MOV yuv.$out_comp, textemp.r;\n"; -static const char *yuv_prog_template = +static const char yuv_prog_template[] = "PARAM ycoef = {$cm11, $cm21, $cm31};\n" "PARAM ucoef = {$cm12, $cm22, $cm32};\n" "PARAM vcoef = {$cm13, $cm23, $cm33};\n" @@ -988,10 +1054,9 @@ static const char *yuv_prog_template = "TEMP res;\n" "MAD res.rgb, yuv.rrrr, ycoef, offsets;\n" "MAD res.rgb, yuv.gggg, ucoef, res;\n" - "MAD result.color.rgb, yuv.bbbb, vcoef, res;\n" - "END"; + "MAD res.rgb, yuv.bbbb, vcoef, res;\n"; -static const char *yuv_pow_prog_template = +static const char yuv_pow_prog_template[] = "PARAM ycoef = {$cm11, $cm21, $cm31};\n" "PARAM ucoef = {$cm12, $cm22, $cm32};\n" "PARAM vcoef = {$cm13, $cm23, $cm33};\n" @@ -1001,12 +1066,11 @@ static const char *yuv_pow_prog_template = "MAD res.rgb, yuv.rrrr, ycoef, offsets;\n" "MAD res.rgb, yuv.gggg, ucoef, res;\n" "MAD_SAT res.rgb, yuv.bbbb, vcoef, res;\n" - "POW result.color.r, res.r, gamma.r;\n" - "POW result.color.g, res.g, gamma.g;\n" - "POW result.color.b, res.b, gamma.b;\n" - "END"; + "POW res.r, res.r, gamma.r;\n" + "POW res.g, res.g, gamma.g;\n" + "POW res.b, res.b, gamma.b;\n"; -static const char *yuv_lookup_prog_template = +static const char yuv_lookup_prog_template[] = "PARAM ycoef = {$cm11, $cm21, $cm31, 0};\n" "PARAM ucoef = {$cm12, $cm22, $cm32, 0};\n" "PARAM vcoef = {$cm13, $cm23, $cm33, 0};\n" @@ -1015,16 +1079,23 @@ static const char *yuv_lookup_prog_template = "MAD res, yuv.rrrr, ycoef, offsets;\n" "MAD res.rgb, yuv.gggg, ucoef, res;\n" "MAD res.rgb, yuv.bbbb, vcoef, res;\n" - "TEX result.color.r, res.raaa, texture[$conv_tex0], 2D;\n" + "TEX res.r, res.raaa, texture[$conv_tex0], 2D;\n" "ADD res.a, res.a, 0.25;\n" - "TEX result.color.g, res.gaaa, texture[$conv_tex0], 2D;\n" + "TEX res.g, res.gaaa, texture[$conv_tex0], 2D;\n" "ADD res.a, res.a, 0.25;\n" - "TEX result.color.b, res.baaa, texture[$conv_tex0], 2D;\n" - "END"; + "TEX res.b, res.baaa, texture[$conv_tex0], 2D;\n"; -static const char *yuv_lookup3d_prog_template = - "TEX result.color, yuv, texture[$conv_tex0], 3D;\n" - "END"; +static const char yuv_lookup3d_prog_template[] = + "TEMP res;\n" + "TEX res, yuv, texture[$conv_tex0], 3D;\n"; + +static const char noise_filt_template[] = + "MUL coord.xy, fragment.texcoord[0], {$noise_sx, $noise_sy};\n" + "TEMP rand;\n" + "TEX rand.r, coord.x, texture[$noise_filt_tex], 1D;\n" + "ADD rand.r, rand.r, coord.y;\n" + "TEX rand.r, rand.r, texture[$noise_filt_tex], 1D;\n" + "MAD res.rgb, rand.rrrr, {$noise_str, $noise_str, $noise_str}, res;\n"; /** * \brief creates and initializes helper textures needed for scaling texture read @@ -1232,12 +1303,12 @@ int loadGPUProgram(GL *gl, GLenum target, char *prog) gl->GetString(GL_PROGRAM_ERROR_STRING), &prog[err]); return 0; } - if (!gl->GetProgramiv || !mp_msg_test(MSGT_VO, MSGL_DBG2)) + if (!gl->GetProgramivARB || !mp_msg_test(MSGT_VO, MSGL_DBG2)) return 1; mp_msg(MSGT_VO, MSGL_V, "[gl] Program statistics:\n"); for (i = 0; progstats[i].name; i++) { - gl->GetProgramiv(target, progstats[i].cur, &cur); - gl->GetProgramiv(target, progstats[i].max, &max); + gl->GetProgramivARB(target, progstats[i].cur, &cur); + gl->GetProgramivARB(target, progstats[i].max, &max); mp_msg(MSGT_VO, MSGL_V, "[gl] %s: %i/%i\n", progstats[i].name, cur, max); } @@ -1269,10 +1340,12 @@ static void glSetupYUVFragprog(GL *gl, gl_conversion_params_t *params) char lum_scale_texs[1]; char chrom_scale_texs[1]; char conv_texs[1]; + char filt_texs[1] = {0}; GLint i; // this is the conversion matrix, with y, u, v factors // for red, green, blue and the constant offsets float yuv2rgb[3][4]; + int noise = params->noise_strength != 0; create_conv_textures(gl, params, &cur_texu, conv_texs); create_scaler_textures(gl, YUV_LUM_SCALER(type), &cur_texu, lum_scale_texs); if (YUV_CHROM_SCALER(type) == YUV_LUM_SCALER(type)) @@ -1280,6 +1353,12 @@ static void glSetupYUVFragprog(GL *gl, gl_conversion_params_t *params) else create_scaler_textures(gl, YUV_CHROM_SCALER(type), &cur_texu, chrom_scale_texs); + + if (noise) { + gen_noise_lookup_tex(gl, cur_texu); + filt_texs[0] = '0' + cur_texu++; + } + gl->GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &i); if (i < cur_texu) mp_msg(MSGT_VO, MSGL_ERR, @@ -1328,6 +1407,25 @@ static void glSetupYUVFragprog(GL *gl, gl_conversion_params_t *params) replace_var_float(prog, "gamma_g", (float)1.0 / params->csp_params.ggamma); replace_var_float(prog, "gamma_b", (float)1.0 / params->csp_params.bgamma); replace_var_char(prog, "conv_tex0", conv_texs[0]); + + if (noise) { + // 1.0 strength is suitable for dithering 8 to 6 bit + double str = params->noise_strength * (1.0 / 64); + double scale_x = (double)NOISE_RES / texw; + double scale_y = (double)NOISE_RES / texh; + if (rect) { + scale_x /= texw; + scale_y /= texh; + } + append_template(prog, noise_filt_template); + replace_var_float(prog, "noise_sx", scale_x); + replace_var_float(prog, "noise_sy", scale_y); + replace_var_char(prog, "noise_filt_tex", filt_texs[0]); + replace_var_float(prog, "noise_str", str); + } + + append_template(prog, "MOV result.color.rgb, res;\nEND"); + mp_msg(MSGT_VO, MSGL_DBG2, "[gl] generated fragment program:\n%s\n", yuv_prog); loadGPUProgram(gl, GL_FRAGMENT_PROGRAM, yuv_prog); @@ -1615,16 +1713,26 @@ void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h, static int create_window_cocoa(struct MPGLContext *ctx, uint32_t d_width, uint32_t d_height, uint32_t flags) { - if (vo_cocoa_create_window(ctx->vo, d_width, d_height, flags) == 0) { + if (vo_cocoa_create_window(ctx->vo, d_width, d_height, flags, 0) == 0) { return SET_WINDOW_OK; } else { return SET_WINDOW_FAILED; } } + +static int create_window_cocoa_gl3(struct MPGLContext *ctx, int gl_flags, + int gl_version, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + int rv = vo_cocoa_create_window(ctx->vo, d_width, d_height, flags, 1); + getFunctions(ctx->gl, (void *)getdladdr, NULL, true); + return rv; +} + static int setGlWindow_cocoa(MPGLContext *ctx) { vo_cocoa_change_attributes(ctx->vo); - getFunctions(ctx->gl, (void *)getdladdr, NULL); + getFunctions(ctx->gl, (void *)getdladdr, NULL, false); if (!ctx->gl->SwapInterval) ctx->gl->SwapInterval = vo_cocoa_swap_interval; return SET_WINDOW_OK; @@ -1656,8 +1764,14 @@ static void cocoa_fullscreen(struct vo *vo) #endif #ifdef CONFIG_GL_WIN32 +#include <windows.h> #include "w32_common.h" +struct w32_context { + int vinfo; + HGLRC context; +}; + static int create_window_w32(struct MPGLContext *ctx, uint32_t d_width, uint32_t d_height, uint32_t flags) { @@ -1682,11 +1796,110 @@ static void *w32gpa(const GLubyte *procName) return GetProcAddress(oglmod, procName); } +static int create_window_w32_gl3(struct MPGLContext *ctx, int gl_flags, + int gl_version, uint32_t d_width, + uint32_t d_height, uint32_t flags) { + if (!vo_w32_config(d_width, d_height, flags)) + return -1; + + struct w32_context *w32_ctx = ctx->priv; + HGLRC *context = &w32_ctx->context; + + if (*context) // reuse existing context + return 0; // not reusing it breaks gl3! + + HWND win = vo_w32_window; + HDC windc = vo_w32_get_dc(win); + HGLRC new_context = 0; + + new_context = wglCreateContext(windc); + if (!new_context) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GL context!\n"); + return -1; + } + + // set context + if (!wglMakeCurrent(windc, new_context)) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL context!\n"); + goto out; + } + + const char *(GLAPIENTRY *wglGetExtensionsStringARB)(HDC hdc) + = w32gpa((const GLubyte*)"wglGetExtensionsStringARB"); + + if (!wglGetExtensionsStringARB) + goto unsupported; + + const char *wgl_exts = wglGetExtensionsStringARB(windc); + if (!strstr(wgl_exts, "WGL_ARB_create_context")) + goto unsupported; + + HGLRC (GLAPIENTRY *wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext, + const int *attribList) + = w32gpa((const GLubyte*)"wglCreateContextAttribsARB"); + + if (!wglCreateContextAttribsARB) + goto unsupported; + + int attribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version), + WGL_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version), + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0 + }; + + *context = wglCreateContextAttribsARB(windc, 0, attribs); + if (! *context) { + // NVidia, instead of ignoring WGL_CONTEXT_FLAGS_ARB, will error out if + // it's present on pre-3.2 contexts. + // Remove it from attribs and retry the context creation. + attribs[6] = attribs[7] = 0; + *context = wglCreateContextAttribsARB(windc, 0, attribs); + } + if (! *context) { + int err = GetLastError(); + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create an OpenGL 3.x" + " context: error 0x%x\n", err); + goto out; + } + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(new_context); + + if (!wglMakeCurrent(windc, *context)) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL3 context!\n"); + wglDeleteContext(*context); + return -1; + } + + /* update function pointers */ + getFunctions(ctx->gl, w32gpa, NULL, true); + + int pfmt = GetPixelFormat(windc); + PIXELFORMATDESCRIPTOR pfd; + if (DescribePixelFormat(windc, pfmt, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { + ctx->depth_r = pfd.cRedBits; + ctx->depth_g = pfd.cGreenBits; + ctx->depth_b = pfd.cBlueBits; + } + + return 0; + +unsupported: + mp_msg(MSGT_VO, MSGL_ERR, "[gl] The current OpenGL implementation does" + " not support OpenGL 3.x \n"); +out: + wglDeleteContext(new_context); + return -1; +} + static int setGlWindow_w32(MPGLContext *ctx) { HWND win = vo_w32_window; - int *vinfo = &ctx->vinfo.w32; - HGLRC *context = &ctx->context.w32; + struct w32_context *w32_ctx = ctx->priv; + int *vinfo = &w32_ctx->vinfo; + HGLRC *context = &w32_ctx->context; int new_vinfo; HDC windc = vo_w32_get_dc(win); HGLRC new_context = 0; @@ -1733,7 +1946,8 @@ static int setGlWindow_w32(MPGLContext *ctx) wglDeleteContext(*context); *context = new_context; *vinfo = new_vinfo; - getFunctions(gl, w32gpa, NULL); + + getFunctions(ctx->gl, w32gpa, NULL, false); // and inform that reinit is neccessary res = SET_WINDOW_REINIT; @@ -1747,8 +1961,9 @@ out: static void releaseGlContext_w32(MPGLContext *ctx) { - int *vinfo = &ctx->vinfo.w32; - HGLRC *context = &ctx->context.w32; + struct w32_context *w32_ctx = ctx->priv; + int *vinfo = &w32_ctx->vinfo; + HGLRC *context = &w32_ctx->context; *vinfo = 0; if (*context) { wglMakeCurrent(0, 0); @@ -1770,10 +1985,19 @@ static void new_vo_w32_border(struct vo *vo) { vo_w32_border(); } static void new_vo_w32_fullscreen(struct vo *vo) { vo_w32_fullscreen(); } static int new_vo_w32_check_events(struct vo *vo) { return vo_w32_check_events(); } static void new_w32_update_xinerama_info(struct vo *vo) { w32_update_xinerama_info(); } +static void new_vo_w32_uninit(struct vo *vo) { vo_w32_uninit(); } #endif + #ifdef CONFIG_GL_X11 +#include <X11/Xlib.h> +#include <GL/glx.h> #include "x11_common.h" +struct glx_context { + XVisualInfo *vinfo; + GLXContext context; +}; + static int create_window_x11(struct MPGLContext *ctx, uint32_t d_width, uint32_t d_height, uint32_t flags) { @@ -1828,19 +2052,26 @@ static XVisualInfo *getWindowVisualInfo(MPGLContext *ctx, Window win) return XGetVisualInfo(ctx->vo->x11->display, VisualIDMask, &vinfo_template, &tmp); } -static void appendstr(char **dst, const char *str) +static char *get_glx_exts(MPGLContext *ctx) { - int newsize; - char *newstr; - if (!str) - return; - newsize = strlen(*dst) + 1 + strlen(str) + 1; - newstr = realloc(*dst, newsize); - if (!newstr) - return; - *dst = newstr; - strcat(*dst, " "); - strcat(*dst, str); + Display *display = ctx->vo->x11->display; + const char *(*glXExtStr)(Display *, int); + char *glxstr = talloc_strdup(NULL, ""); + + glXExtStr = getdladdr("glXQueryExtensionsString"); + if (glXExtStr) + glxstr = talloc_asprintf_append(glxstr, " %s", + glXExtStr(display, ctx->vo->x11->screen)); + glXExtStr = getdladdr("glXGetClientString"); + if (glXExtStr) + glxstr = talloc_asprintf_append(glxstr, " %s", + glXExtStr(display, GLX_EXTENSIONS)); + glXExtStr = getdladdr("glXGetServerString"); + if (glXExtStr) + glxstr = talloc_asprintf_append(glxstr, " %s", + glXExtStr(display, GLX_EXTENSIONS)); + + return glxstr; } /** @@ -1857,8 +2088,9 @@ static void appendstr(char **dst, const char *str) */ static int setGlWindow_x11(MPGLContext *ctx) { - XVisualInfo **vinfo = &ctx->vinfo.x11; - GLXContext *context = &ctx->context.x11; + struct glx_context *glx_context = ctx->priv; + XVisualInfo **vinfo = &glx_context->vinfo; + GLXContext *context = &glx_context->context; Display *display = ctx->vo->x11->display; Window win = ctx->vo->x11->window; XVisualInfo *new_vinfo; @@ -1903,8 +2135,6 @@ static int setGlWindow_x11(MPGLContext *ctx) vo_x11_update_geometry(ctx->vo, 1); if (!keep_context) { void *(*getProcAddress)(const GLubyte *); - const char *(*glXExtStr)(Display *, int); - char *glxstr = strdup(""); if (*context) glXDestroyContext(display, *context); *context = new_context; @@ -1914,25 +2144,19 @@ static int setGlWindow_x11(MPGLContext *ctx) getProcAddress = getdladdr("glXGetProcAddress"); if (!getProcAddress) getProcAddress = getdladdr("glXGetProcAddressARB"); - glXExtStr = getdladdr("glXQueryExtensionsString"); - if (glXExtStr) - appendstr(&glxstr, glXExtStr(display, DefaultScreen(display))); - glXExtStr = getdladdr("glXGetClientString"); - if (glXExtStr) - appendstr(&glxstr, glXExtStr(display, GLX_EXTENSIONS)); - glXExtStr = getdladdr("glXGetServerString"); - if (glXExtStr) - appendstr(&glxstr, glXExtStr(display, GLX_EXTENSIONS)); - - getFunctions(gl, getProcAddress, glxstr); + + char *glxstr = get_glx_exts(ctx); + + getFunctions(gl, getProcAddress, glxstr, false); if (!gl->GenPrograms && gl->GetString && getProcAddress && strstr(gl->GetString(GL_EXTENSIONS), "GL_ARB_vertex_program")) { mp_msg(MSGT_VO, MSGL_WARN, "Broken glXGetProcAddress detected, trying workaround\n"); - getFunctions(gl, NULL, glxstr); + getFunctions(gl, NULL, glxstr, false); } - free(glxstr); + + talloc_free(glxstr); // and inform that reinit is neccessary return SET_WINDOW_REINIT; @@ -1940,14 +2164,156 @@ static int setGlWindow_x11(MPGLContext *ctx) return SET_WINDOW_OK; } +// The GL3 initialization code roughly follows/copies from: +// http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX) +// but also uses some of the old code. + +static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs) +{ + int fbcount; + GLXFBConfig *fbc = glXChooseFBConfig(vo->x11->display, vo->x11->screen, + attribs, &fbcount); + if (!fbc) + return NULL; + + // The list in fbc is sorted (so that the first element is the best). + GLXFBConfig fbconfig = fbc[0]; + + XFree(fbc); + + return fbconfig; +} + +typedef GLXContext (*glXCreateContextAttribsARBProc) + (Display*, GLXFBConfig, GLXContext, Bool, const int*); + +static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, + int gl_version, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + struct vo *vo = ctx->vo; + struct glx_context *glx_ctx = ctx->priv; + + if (glx_ctx->context) { + // GL context and window already exist. + // Only update window geometry etc. + Colormap colormap = XCreateColormap(vo->x11->display, vo->x11->rootwin, + glx_ctx->vinfo->visual, AllocNone); + vo_x11_create_vo_window(vo, glx_ctx->vinfo, vo->dx, vo->dy, d_width, + d_height, flags, colormap, "gl"); + XFreeColormap(vo->x11->display, colormap); + return SET_WINDOW_OK; + } + + int glx_major, glx_minor; + + // FBConfigs were added in GLX version 1.3. + if (!glXQueryVersion(vo->x11->display, &glx_major, &glx_minor) || + (MPGL_VER(glx_major, glx_minor) < MPGL_VER(1, 3))) + { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] GLX version older than 1.3.\n"); + return SET_WINDOW_FAILED; + } + + const int glx_attribs_stereo_value_idx = 1; // index of GLX_STEREO + 1 + int glx_attribs[] = { + GLX_STEREO, False, + GLX_X_RENDERABLE, True, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, True, + None + }; + GLXFBConfig fbc = NULL; + if (flags & VOFLAG_STEREO) { + glx_attribs[glx_attribs_stereo_value_idx] = True; + fbc = select_fb_config(vo, glx_attribs); + if (!fbc) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] Could not find a stereo visual," + " 3D will probably not work!\n"); + glx_attribs[glx_attribs_stereo_value_idx] = False; + } + } + if (!fbc) + fbc = select_fb_config(vo, glx_attribs); + if (!fbc) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] no GLX support present\n"); + return SET_WINDOW_FAILED; + } + + glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_RED_SIZE, &ctx->depth_r); + glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_GREEN_SIZE, &ctx->depth_g); + glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_BLUE_SIZE, &ctx->depth_b); + + XVisualInfo *vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc); + mp_msg(MSGT_VO, MSGL_V, "[gl] GLX chose visual with ID 0x%x\n", + (int)vinfo->visualid); + Colormap colormap = XCreateColormap(vo->x11->display, vo->x11->rootwin, + vinfo->visual, AllocNone); + vo_x11_create_vo_window(vo, vinfo, vo->dx, vo->dy, d_width, d_height, + flags, colormap, "gl"); + XFreeColormap(vo->x11->display, colormap); + + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = + (glXCreateContextAttribsARBProc) + glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB"); + + char *glxstr = get_glx_exts(ctx); + bool have_ctx_ext = !!strstr(glxstr, "GLX_ARB_create_context"); + + if (!(have_ctx_ext && glXCreateContextAttribsARB)) + { + XFree(vinfo); + talloc_free(glxstr); + return SET_WINDOW_FAILED; + } + + int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version), + GLX_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version), + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB + | (gl_flags & MPGLFLAG_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), + None + }; + GLXContext context = glXCreateContextAttribsARB(vo->x11->display, fbc, 0, + True, context_attribs); + if (!context) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GLX context!\n"); + XFree(vinfo); + talloc_free(glxstr); + return SET_WINDOW_FAILED; + } + + // set context + if (!glXMakeCurrent(vo->x11->display, vo->x11->window, context)) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GLX context!\n"); + glXDestroyContext(vo->x11->display, context); + XFree(vinfo); + talloc_free(glxstr); + return SET_WINDOW_FAILED; + } + + glx_ctx->vinfo = vinfo; + glx_ctx->context = context; + + getFunctions(ctx->gl, (void *)glXGetProcAddress, glxstr, true); + + talloc_free(glxstr); + + return SET_WINDOW_REINIT; +} + /** * \brief free the VisualInfo and GLXContext of an OpenGL context. * \ingroup glcontext */ static void releaseGlContext_x11(MPGLContext *ctx) { - XVisualInfo **vinfo = &ctx->vinfo.x11; - GLXContext *context = &ctx->context.x11; + struct glx_context *glx_ctx = ctx->priv; + XVisualInfo **vinfo = &glx_ctx->vinfo; + GLXContext *context = &glx_ctx->context; Display *display = ctx->vo->x11->display; GL *gl = ctx->gl; if (*vinfo) @@ -1994,7 +2360,7 @@ static int setGlWindow_sdl(MPGLContext *ctx) if (sdl_set_mode(0, SDL_OPENGL | SDL_RESIZABLE) < 0) return SET_WINDOW_FAILED; SDL_GL_LoadLibrary(NULL); - getFunctions(ctx->gl, sdlgpa, NULL); + getFunctions(ctx->gl, sdlgpa, NULL, false); return SET_WINDOW_OK; } @@ -2017,9 +2383,40 @@ static int sdl_check_events(struct vo *vo) static void new_sdl_update_xinerama_info(struct vo *vo) { sdl_update_xinerama_info(); } static void new_vo_sdl_fullscreen(struct vo *vo) { vo_sdl_fullscreen(); } +static void new_vo_sdl_uninit(struct vo *vo) { vo_sdl_uninit(); } #endif +struct backend { + const char *name; + enum MPGLType type; +}; + +static struct backend backends[] = { + {"auto", GLTYPE_AUTO}, + {"cocoa", GLTYPE_COCOA}, + {"win", GLTYPE_W32}, + {"x11", GLTYPE_X11}, + {"sdl", GLTYPE_SDL}, + // mplayer-svn aliases (note that mplayer-svn couples these with the numeric + // values of the internal GLTYPE_* constants) + {"-1", GLTYPE_AUTO}, + { "0", GLTYPE_W32}, + { "1", GLTYPE_X11}, + { "2", GLTYPE_SDL}, + + {0} +}; + +int mpgl_find_backend(const char *name) +{ + for (const struct backend *entry = backends; entry->name; entry++) { + if (strcmp(entry->name, name) == 0) + return entry->type; + } + return -1; +} + MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) { MPGLContext *ctx; @@ -2043,6 +2440,7 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) #ifdef CONFIG_GL_COCOA case GLTYPE_COCOA: ctx->create_window = create_window_cocoa; + ctx->create_window_gl3 = create_window_cocoa_gl3; ctx->setGlWindow = setGlWindow_cocoa; ctx->releaseGlContext = releaseGlContext_cocoa; ctx->swapGlBuffers = swapGlBuffers_cocoa; @@ -2050,13 +2448,16 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) ctx->update_xinerama_info = cocoa_update_xinerama_info; ctx->fullscreen = cocoa_fullscreen; ctx->ontop = vo_cocoa_ontop; + ctx->vo_uninit = vo_cocoa_uninit; if (vo_cocoa_init(vo)) return ctx; break; #endif #ifdef CONFIG_GL_WIN32 case GLTYPE_W32: + ctx->priv = talloc_zero(ctx, struct w32_context); ctx->create_window = create_window_w32; + ctx->create_window_gl3 = create_window_w32_gl3; ctx->setGlWindow = setGlWindow_w32; ctx->releaseGlContext = releaseGlContext_w32; ctx->swapGlBuffers = swapGlBuffers_w32; @@ -2065,6 +2466,7 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) ctx->check_events = new_vo_w32_check_events; ctx->fullscreen = new_vo_w32_fullscreen; ctx->ontop = new_vo_w32_ontop; + ctx->vo_uninit = new_vo_w32_uninit; //the win32 code is hardcoded to use the deprecated vo API global_vo = vo; if (vo_w32_init()) @@ -2073,8 +2475,10 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) #endif #ifdef CONFIG_GL_X11 case GLTYPE_X11: + ctx->priv = talloc_zero(ctx, struct glx_context); ctx->create_window = create_window_x11; ctx->setGlWindow = setGlWindow_x11; + ctx->create_window_gl3 = create_window_x11_gl3; ctx->releaseGlContext = releaseGlContext_x11; ctx->swapGlBuffers = swapGlBuffers_x11; ctx->update_xinerama_info = update_xinerama_info; @@ -2082,6 +2486,7 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) ctx->check_events = vo_x11_check_events; ctx->fullscreen = vo_x11_fullscreen; ctx->ontop = vo_x11_ontop; + ctx->vo_uninit = vo_x11_uninit; if (vo_init(vo)) return ctx; break; @@ -2095,6 +2500,7 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) ctx->update_xinerama_info = new_sdl_update_xinerama_info; ctx->check_events = sdl_check_events; ctx->fullscreen = new_vo_sdl_fullscreen; + ctx->vo_uninit = new_vo_sdl_uninit; //the SDL code is hardcoded to use the deprecated vo API global_vo = vo; if (vo_sdl_init()) @@ -2106,32 +2512,45 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) return NULL; } +int create_mpglcontext(struct MPGLContext *ctx, int gl_flags, int gl_version, + uint32_t d_width, uint32_t d_height, uint32_t flags) +{ + if (gl_version < MPGL_VER(3, 0)) { + if (ctx->create_window(ctx, d_width, d_height, flags) < 0) + return SET_WINDOW_FAILED; + return ctx->setGlWindow(ctx); + } else { + if (!ctx->create_window_gl3) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] OpenGL 3.x context creation not " + "implemented.\n"); + return SET_WINDOW_FAILED; + } + return ctx->create_window_gl3(ctx, gl_flags, gl_version, d_width, + d_height, flags); + } +} + void uninit_mpglcontext(MPGLContext *ctx) { if (!ctx) return; ctx->releaseGlContext(ctx); - switch (ctx->type) { -#ifdef CONFIG_GL_COCOA - case GLTYPE_COCOA: - vo_cocoa_uninit(ctx->vo); - break; -#endif -#ifdef CONFIG_GL_WIN32 - case GLTYPE_W32: - vo_w32_uninit(); - break; -#endif -#ifdef CONFIG_GL_X11 - case GLTYPE_X11: - vo_x11_uninit(ctx->vo); - break; -#endif -#ifdef CONFIG_GL_SDL - case GLTYPE_SDL: - vo_sdl_uninit(); - break; -#endif - } + ctx->vo_uninit(ctx->vo); talloc_free(ctx); } + +void mp_log_source(int mod, int lev, const char *src) +{ + int line = 1; + if (!src) + return; + while (*src) { + const char *end = strchr(src, '\n'); + const char *next = end + 1; + if (!end) + next = end = src + strlen(src); + mp_msg(mod, lev, "[%3d] %.*s\n", line, (int)(end - src), src); + line++; + src = next; + } +} diff --git a/libvo/gl_common.h b/libvo/gl_common.h index 8e3294f441..8091886b1f 100644 --- a/libvo/gl_common.h +++ b/libvo/gl_common.h @@ -33,221 +33,16 @@ #include "video_out.h" #include "csputils.h" -#ifdef CONFIG_GL_WIN32 -#include <windows.h> -#include "w32_common.h" -#endif -#ifdef CONFIG_GL_X11 -#include <X11/Xlib.h> -#include <GL/glx.h> -#include "x11_common.h" -// This old-vo wrapper macro would conflict with the struct member -#undef update_xinerama_info -#endif #include <GL/gl.h> +#include <GL/glext.h> -// workaround for some gl.h headers -#ifndef GLAPIENTRY -#ifdef APIENTRY -#define GLAPIENTRY APIENTRY -#elif defined(CONFIG_GL_WIN32) -#define GLAPIENTRY __stdcall -#else -#define GLAPIENTRY -#endif -#endif - -/** - * \defgroup glextdefines OpenGL extension defines - * - * conditionally define all extension defines used. - * vendor specific extensions should be marked as such - * (e.g. _NV), _ARB is not used to ease readability. - * \{ - */ -#ifndef GL_TEXTURE_3D -#define GL_TEXTURE_3D 0x806F -#endif -#ifndef GL_TEXTURE_WRAP_R -#define GL_TEXTURE_WRAP_R 0x8072 -#endif -#ifndef GL_CLAMP_TO_EDGE -#define GL_CLAMP_TO_EDGE 0x812F -#endif -#ifndef GL_GENERATE_MIPMAP -#define GL_GENERATE_MIPMAP 0x8191 -#endif -#ifndef GL_TEXT_FRAGMENT_SHADER_ATI -#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 -#endif -#ifndef GL_FRAGMENT_SHADER_ATI -#define GL_FRAGMENT_SHADER_ATI 0x8920 -#endif -#ifndef GL_NUM_FRAGMENT_REGISTERS_ATI -#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E -#endif -#ifndef GL_REG_0_ATI -#define GL_REG_0_ATI 0x8921 -#endif -#ifndef GL_REG_1_ATI -#define GL_REG_1_ATI 0x8922 -#endif -#ifndef GL_REG_2_ATI -#define GL_REG_2_ATI 0x8923 -#endif -#ifndef GL_CON_0_ATI -#define GL_CON_0_ATI 0x8941 -#endif -#ifndef GL_CON_1_ATI -#define GL_CON_1_ATI 0x8942 -#endif -#ifndef GL_CON_2_ATI -#define GL_CON_2_ATI 0x8943 -#endif -#ifndef GL_CON_3_ATI -#define GL_CON_3_ATI 0x8944 -#endif -#ifndef GL_ADD_ATI -#define GL_ADD_ATI 0x8963 -#endif -#ifndef GL_MUL_ATI -#define GL_MUL_ATI 0x8964 -#endif -#ifndef GL_MAD_ATI -#define GL_MAD_ATI 0x8968 -#endif -#ifndef GL_SWIZZLE_STR_ATI -#define GL_SWIZZLE_STR_ATI 0x8976 -#endif -#ifndef GL_4X_BIT_ATI -#define GL_4X_BIT_ATI 2 -#endif -#ifndef GL_8X_BIT_ATI -#define GL_8X_BIT_ATI 4 -#endif -#ifndef GL_BIAS_BIT_ATI -#define GL_BIAS_BIT_ATI 8 -#endif -#ifndef GL_MAX_TEXTURE_UNITS -#define GL_MAX_TEXTURE_UNITS 0x84E2 -#endif -#ifndef GL_TEXTURE0 -#define GL_TEXTURE0 0x84C0 -#endif -#ifndef GL_TEXTURE1 -#define GL_TEXTURE1 0x84C1 -#endif -#ifndef GL_TEXTURE2 -#define GL_TEXTURE2 0x84C2 -#endif -#ifndef GL_TEXTURE3 -#define GL_TEXTURE3 0x84C3 -#endif -#ifndef GL_TEXTURE_RECTANGLE -#define GL_TEXTURE_RECTANGLE 0x84F5 -#endif -#ifndef GL_PIXEL_UNPACK_BUFFER -#define GL_PIXEL_UNPACK_BUFFER 0x88EC -#endif -#ifndef GL_STREAM_DRAW -#define GL_STREAM_DRAW 0x88E0 -#endif -#ifndef GL_DYNAMIC_DRAW -#define GL_DYNAMIC_DRAW 0x88E8 -#endif -#ifndef GL_WRITE_ONLY -#define GL_WRITE_ONLY 0x88B9 -#endif -#ifndef GL_BGR -#define GL_BGR 0x80E0 -#endif -#ifndef GL_BGRA -#define GL_BGRA 0x80E1 -#endif -#ifndef GL_UNSIGNED_BYTE_3_3_2 -#define GL_UNSIGNED_BYTE_3_3_2 0x8032 -#endif -#ifndef GL_UNSIGNED_BYTE_2_3_3_REV -#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 -#endif -#ifndef GL_UNSIGNED_SHORT_4_4_4_4 -#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 -#endif -#ifndef GL_UNSIGNED_SHORT_4_4_4_4_REV -#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 -#endif -#ifndef GL_UNSIGNED_SHORT_5_6_5 -#define GL_UNSIGNED_SHORT_5_6_5 0x8363 -#endif -#ifndef GL_UNSIGNED_INT_8_8_8_8 -#define GL_UNSIGNED_INT_8_8_8_8 0x8035 -#endif -#ifndef GL_UNSIGNED_INT_8_8_8_8_REV -#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 -#endif -#ifndef GL_UNSIGNED_SHORT_5_6_5_REV -#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 -#endif -#ifndef GL_UNSIGNED_INT_10_10_10_2 -#define GL_UNSIGNED_INT_10_10_10_2 0x8036 -#endif -#ifndef GL_UNSIGNED_INT_2_10_10_10_REV -#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 -#endif -#ifndef GL_UNSIGNED_SHORT_5_5_5_1 -#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 -#endif -#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV -#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 -#endif -#ifndef GL_UNSIGNED_SHORT_8_8 -#define GL_UNSIGNED_SHORT_8_8 0x85BA -#endif -#ifndef GL_UNSIGNED_SHORT_8_8_REV -#define GL_UNSIGNED_SHORT_8_8_REV 0x85BB -#endif -#ifndef GL_YCBCR_MESA -#define GL_YCBCR_MESA 0x8757 -#endif -#ifndef GL_RGB32F -#define GL_RGB32F 0x8815 -#endif -#ifndef GL_FLOAT_RGB32_NV -#define GL_FLOAT_RGB32_NV 0x8889 -#endif -#ifndef GL_LUMINANCE16 -#define GL_LUMINANCE16 0x8042 -#endif -#ifndef GL_R16 -#define GL_R16 0x822A -#endif -#ifndef GL_UNPACK_CLIENT_STORAGE_APPLE -#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 -#endif -#ifndef GL_FRAGMENT_PROGRAM -#define GL_FRAGMENT_PROGRAM 0x8804 -#endif -#ifndef GL_PROGRAM_FORMAT_ASCII -#define GL_PROGRAM_FORMAT_ASCII 0x8875 -#endif -#ifndef GL_PROGRAM_ERROR_POSITION -#define GL_PROGRAM_ERROR_POSITION 0x864B -#endif -#ifndef GL_MAX_TEXTURE_IMAGE_UNITS -#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 -#endif -#ifndef GL_PROGRAM_ERROR_STRING -#define GL_PROGRAM_ERROR_STRING 0x8874 -#endif -/** \} */ // end of glextdefines group +#include "libvo/gl_header_fixes.h" struct GL; typedef struct GL GL; void glAdjustAlignment(GL *gl, int stride); -const char *glValName(GLint value); - int glFindFormat(uint32_t format, int have_texture_rg, int *bpp, GLint *gl_texfmt, GLenum *gl_format, GLenum *gl_type); int glFmt2bpp(GLenum format, GLenum type); @@ -265,6 +60,7 @@ void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h, GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th, int sx, int sy, int rect_tex, int is_yv12, int flip); int loadGPUProgram(GL *gl, GLenum target, char *prog); +void glCheckError(GL *gl, const char *info); /** \addtogroup glconversion * \{ */ @@ -335,6 +131,7 @@ typedef struct { int chrom_texw; int chrom_texh; float filter_strength; + float noise_strength; } gl_conversion_params_t; int glAutodetectYUVConversion(GL *gl); @@ -368,39 +165,55 @@ enum MPGLType { GLTYPE_SDL, }; +enum { + MPGLFLAG_DEBUG = 1, +}; + +#define MPGL_VER(major, minor) (((major) << 16) | (minor)) +#define MPGL_VER_GET_MAJOR(ver) ((ver) >> 16) +#define MPGL_VER_GET_MINOR(ver) ((ver) & ((1 << 16) - 1)) + typedef struct MPGLContext { GL *gl; enum MPGLType type; struct vo *vo; - union { - int w32; -#ifdef CONFIG_GL_X11 - XVisualInfo *x11; -#endif - } vinfo; - union { -#ifdef CONFIG_GL_WIN32 - HGLRC w32; -#endif -#ifdef CONFIG_GL_X11 - GLXContext x11; -#endif - } context; + void *priv; + // Bit size of each component in the created framebuffer. 0 if unknown. + int depth_r, depth_g, depth_b; int (*create_window)(struct MPGLContext *ctx, uint32_t d_width, uint32_t d_height, uint32_t flags); int (*setGlWindow)(struct MPGLContext *); void (*releaseGlContext)(struct MPGLContext *); void (*swapGlBuffers)(struct MPGLContext *); - void (*update_xinerama_info)(struct vo *vo); - void (*border)(struct vo *vo); int (*check_events)(struct vo *vo); void (*fullscreen)(struct vo *vo); + void (*vo_uninit)(struct vo *vo); + // only available if GL3 context creation is supported + // gl_flags: bitfield of MPGLFLAG_* constants + // gl_version: requested OpenGL version number (use MPGL_VER()) + // return value is one of the SET_WINDOW_* constants + int (*create_window_gl3)(struct MPGLContext *ctx, int gl_flags, + int gl_version, uint32_t d_width, + uint32_t d_height, uint32_t flags); + // optional void (*ontop)(struct vo *vo); + void (*border)(struct vo *vo); + void (*update_xinerama_info)(struct vo *vo); } MPGLContext; +int mpgl_find_backend(const char *name); + MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo); void uninit_mpglcontext(MPGLContext *ctx); +// calls create_window_gl3 or create_window+setGlWindow +int create_mpglcontext(struct MPGLContext *ctx, int gl_flags, int gl_version, + uint32_t d_width, uint32_t d_height, uint32_t flags); + +// print a multi line string with line numbers (e.g. for shader sources) +// mod, lev: module and log level, as in mp_msg() +void mp_log_source(int mod, int lev, const char *src); + //function pointers loaded from the OpenGL library struct GL { void (GLAPIENTRY *Begin)(GLenum); @@ -411,7 +224,6 @@ struct GL { void (GLAPIENTRY *Translated)(double, double, double); void (GLAPIENTRY *Scaled)(double, double, double); void (GLAPIENTRY *Ortho)(double, double, double, double, double,double); - void (GLAPIENTRY *Frustum)(double, double, double, double, double, double); void (GLAPIENTRY *PushMatrix)(void); void (GLAPIENTRY *PopMatrix)(void); void (GLAPIENTRY *Clear)(GLbitfield); @@ -423,14 +235,10 @@ struct GL { void (GLAPIENTRY *CallLists)(GLsizei, GLenum, const GLvoid *); void (GLAPIENTRY *GenTextures)(GLsizei, GLuint *); void (GLAPIENTRY *DeleteTextures)(GLsizei, const GLuint *); - void (GLAPIENTRY *TexEnvf)(GLenum, GLenum, GLfloat); void (GLAPIENTRY *TexEnvi)(GLenum, GLenum, GLint); void (GLAPIENTRY *Color4ub)(GLubyte, GLubyte, GLubyte, GLubyte); - void (GLAPIENTRY *Color3f)(GLfloat, GLfloat, GLfloat); void (GLAPIENTRY *Color4f)(GLfloat, GLfloat, GLfloat, GLfloat); void (GLAPIENTRY *ClearColor)(GLclampf, GLclampf, GLclampf, GLclampf); - void (GLAPIENTRY *ClearDepth)(GLclampd); - void (GLAPIENTRY *DepthFunc)(GLenum); void (GLAPIENTRY *Enable)(GLenum); void (GLAPIENTRY *Disable)(GLenum); const GLubyte *(GLAPIENTRY * GetString)(GLenum); @@ -453,16 +261,20 @@ struct GL { void (GLAPIENTRY *TexParameterfv)(GLenum, GLenum, const GLfloat *); void (GLAPIENTRY *TexCoord2f)(GLfloat, GLfloat); void (GLAPIENTRY *Vertex2f)(GLfloat, GLfloat); - void (GLAPIENTRY *Vertex3f)(GLfloat, GLfloat, GLfloat); - void (GLAPIENTRY *Normal3f)(GLfloat, GLfloat, GLfloat); - void (GLAPIENTRY *Lightfv)(GLenum, GLenum, const GLfloat *); - void (GLAPIENTRY *ColorMaterial)(GLenum, GLenum); - void (GLAPIENTRY *ShadeModel)(GLenum); void (GLAPIENTRY *GetIntegerv)(GLenum, GLint *); + void (GLAPIENTRY *GetBooleanv)(GLenum, GLboolean *); void (GLAPIENTRY *ColorMask)(GLboolean, GLboolean, GLboolean, GLboolean); void (GLAPIENTRY *ReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *); void (GLAPIENTRY *ReadBuffer)(GLenum); + void (GLAPIENTRY *VertexPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *ColorPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *TexCoordPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *DrawArrays)(GLenum, GLint, GLsizei); + void (GLAPIENTRY *EnableClientState)(GLenum); + void (GLAPIENTRY *DisableClientState)(GLenum); + GLenum (GLAPIENTRY *GetError)(void); + // OpenGL extension functions void (GLAPIENTRY *GenBuffers)(GLsizei, GLuint *); @@ -471,15 +283,6 @@ struct GL { GLvoid * (GLAPIENTRY * MapBuffer)(GLenum, GLenum); GLboolean (GLAPIENTRY *UnmapBuffer)(GLenum); void (GLAPIENTRY *BufferData)(GLenum, intptr_t, const GLvoid *, GLenum); - void (GLAPIENTRY *BeginFragmentShader)(void); - void (GLAPIENTRY *EndFragmentShader)(void); - void (GLAPIENTRY *SampleMap)(GLuint, GLuint, GLenum); - void (GLAPIENTRY *ColorFragmentOp2)(GLenum, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint, GLuint, GLuint); - void (GLAPIENTRY *ColorFragmentOp3)(GLenum, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint); - void (GLAPIENTRY *SetFragmentShaderConstant)(GLuint, const GLfloat *); void (GLAPIENTRY *ActiveTexture)(GLenum); void (GLAPIENTRY *BindTexture)(GLenum, GLuint); void (GLAPIENTRY *MultiTexCoord2f)(GLenum, GLfloat, GLfloat); @@ -487,13 +290,66 @@ struct GL { void (GLAPIENTRY *DeletePrograms)(GLsizei, const GLuint *); void (GLAPIENTRY *BindProgram)(GLenum, GLuint); void (GLAPIENTRY *ProgramString)(GLenum, GLenum, GLsizei, const GLvoid *); - void (GLAPIENTRY *GetProgramiv)(GLenum, GLenum, GLint *); + void (GLAPIENTRY *GetProgramivARB)(GLenum, GLenum, GLint *); void (GLAPIENTRY *ProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat); int (GLAPIENTRY *SwapInterval)(int); void (GLAPIENTRY *TexImage3D)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *); + + // ancient ATI extensions + void (GLAPIENTRY *BeginFragmentShader)(void); + void (GLAPIENTRY *EndFragmentShader)(void); + void (GLAPIENTRY *SampleMap)(GLuint, GLuint, GLenum); + void (GLAPIENTRY *ColorFragmentOp2)(GLenum, GLuint, GLuint, GLuint, GLuint, + GLuint, GLuint, GLuint, GLuint, GLuint); + void (GLAPIENTRY *ColorFragmentOp3)(GLenum, GLuint, GLuint, GLuint, GLuint, + GLuint, GLuint, GLuint, GLuint, GLuint, + GLuint, GLuint, GLuint); + void (GLAPIENTRY *SetFragmentShaderConstant)(GLuint, const GLfloat *); + + + // GL 3, possibly in GL 2.x as well in form of extensions + void (GLAPIENTRY *GenVertexArrays)(GLsizei, GLuint *); + void (GLAPIENTRY *BindVertexArray)(GLuint); + GLint (GLAPIENTRY *GetAttribLocation)(GLuint, const GLchar *); + void (GLAPIENTRY *EnableVertexAttribArray)(GLuint); + void (GLAPIENTRY *DisableVertexAttribArray)(GLuint); + void (GLAPIENTRY *VertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, + GLsizei, const GLvoid *); + void (GLAPIENTRY *DeleteVertexArrays)(GLsizei, const GLuint *); + void (GLAPIENTRY *UseProgram)(GLuint); + GLint (GLAPIENTRY *GetUniformLocation)(GLuint, const GLchar *); + void (GLAPIENTRY *CompileShader)(GLuint); + GLuint (GLAPIENTRY *CreateProgram)(void); + GLuint (GLAPIENTRY *CreateShader)(GLenum); + void (GLAPIENTRY *ShaderSource)(GLuint, GLsizei, const GLchar **, + const GLint *); + void (GLAPIENTRY *LinkProgram)(GLuint); + void (GLAPIENTRY *AttachShader)(GLuint, GLuint); + void (GLAPIENTRY *DeleteShader)(GLuint); + void (GLAPIENTRY *DeleteProgram)(GLuint); + void (GLAPIENTRY *GetShaderInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *); + void (GLAPIENTRY *GetShaderiv)(GLuint, GLenum, GLint *); + void (GLAPIENTRY *GetProgramInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *); + void (GLAPIENTRY *GetProgramiv)(GLenum, GLenum, GLint *); + const GLubyte* (GLAPIENTRY *GetStringi)(GLenum, GLuint); + void (GLAPIENTRY *BindAttribLocation)(GLuint, GLuint, const GLchar *); + void (GLAPIENTRY *BindFramebuffer)(GLenum, GLuint); + void (GLAPIENTRY *GenFramebuffers)(GLsizei, GLuint *); + void (GLAPIENTRY *DeleteFramebuffers)(GLsizei, const GLuint *); + GLenum (GLAPIENTRY *CheckFramebufferStatus)(GLenum); + void (GLAPIENTRY *FramebufferTexture2D)(GLenum, GLenum, GLenum, GLuint, + GLint); + + void (GLAPIENTRY *Uniform1f)(GLint, GLfloat); + void (GLAPIENTRY *Uniform3f)(GLint, GLfloat, GLfloat, GLfloat); + void (GLAPIENTRY *Uniform1i)(GLint, GLint); + void (GLAPIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean, + const GLfloat *); + void (GLAPIENTRY *UniformMatrix4x3fv)(GLint, GLsizei, GLboolean, + const GLfloat *); }; #endif /* MPLAYER_GL_COMMON_H */ diff --git a/libvo/gl_header_fixes.h b/libvo/gl_header_fixes.h new file mode 100644 index 0000000000..c6f260b289 --- /dev/null +++ b/libvo/gl_header_fixes.h @@ -0,0 +1,231 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * You can alternatively redistribute this file 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. + */ + +// workaround for some gl.h headers +#ifndef GLAPIENTRY +#ifdef APIENTRY +#define GLAPIENTRY APIENTRY +#elif defined(CONFIG_GL_WIN32) +#define GLAPIENTRY __stdcall +#else +#define GLAPIENTRY +#endif +#endif + +/** + * \defgroup glextdefines OpenGL extension defines + * + * conditionally define all extension defines used. + * vendor specific extensions should be marked as such + * (e.g. _NV), _ARB is not used to ease readability. + * \{ + */ +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif +#ifndef GL_TEXTURE_WRAP_R +#define GL_TEXTURE_WRAP_R 0x8072 +#endif +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif +#ifndef GL_GENERATE_MIPMAP +#define GL_GENERATE_MIPMAP 0x8191 +#endif +#ifndef GL_TEXT_FRAGMENT_SHADER_ATI +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif +#ifndef GL_FRAGMENT_SHADER_ATI +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#endif +#ifndef GL_NUM_FRAGMENT_REGISTERS_ATI +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#endif +#ifndef GL_REG_0_ATI +#define GL_REG_0_ATI 0x8921 +#endif +#ifndef GL_REG_1_ATI +#define GL_REG_1_ATI 0x8922 +#endif +#ifndef GL_REG_2_ATI +#define GL_REG_2_ATI 0x8923 +#endif +#ifndef GL_CON_0_ATI +#define GL_CON_0_ATI 0x8941 +#endif +#ifndef GL_CON_1_ATI +#define GL_CON_1_ATI 0x8942 +#endif +#ifndef GL_CON_2_ATI +#define GL_CON_2_ATI 0x8943 +#endif +#ifndef GL_CON_3_ATI +#define GL_CON_3_ATI 0x8944 +#endif +#ifndef GL_ADD_ATI +#define GL_ADD_ATI 0x8963 +#endif +#ifndef GL_MUL_ATI +#define GL_MUL_ATI 0x8964 +#endif +#ifndef GL_MAD_ATI +#define GL_MAD_ATI 0x8968 +#endif +#ifndef GL_SWIZZLE_STR_ATI +#define GL_SWIZZLE_STR_ATI 0x8976 +#endif +#ifndef GL_4X_BIT_ATI +#define GL_4X_BIT_ATI 2 +#endif +#ifndef GL_8X_BIT_ATI +#define GL_8X_BIT_ATI 4 +#endif +#ifndef GL_BIAS_BIT_ATI +#define GL_BIAS_BIT_ATI 8 +#endif +#ifndef GL_MAX_TEXTURE_UNITS +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#endif +#ifndef GL_TEXTURE0 +#define GL_TEXTURE0 0x84C0 +#endif +#ifndef GL_TEXTURE1 +#define GL_TEXTURE1 0x84C1 +#endif +#ifndef GL_TEXTURE2 +#define GL_TEXTURE2 0x84C2 +#endif +#ifndef GL_TEXTURE3 +#define GL_TEXTURE3 0x84C3 +#endif +#ifndef GL_TEXTURE_RECTANGLE +#define GL_TEXTURE_RECTANGLE 0x84F5 +#endif +#ifndef GL_PIXEL_UNPACK_BUFFER +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#endif +#ifndef GL_STREAM_DRAW +#define GL_STREAM_DRAW 0x88E0 +#endif +#ifndef GL_DYNAMIC_DRAW +#define GL_DYNAMIC_DRAW 0x88E8 +#endif +#ifndef GL_WRITE_ONLY +#define GL_WRITE_ONLY 0x88B9 +#endif +#ifndef GL_BGR +#define GL_BGR 0x80E0 +#endif +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif +#ifndef GL_UNSIGNED_BYTE_3_3_2 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#endif +#ifndef GL_UNSIGNED_BYTE_2_3_3_REV +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#endif +#ifndef GL_UNSIGNED_SHORT_4_4_4_4 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif +#ifndef GL_UNSIGNED_SHORT_4_4_4_4_REV +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#endif +#ifndef GL_UNSIGNED_SHORT_5_6_5 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#endif +#ifndef GL_UNSIGNED_INT_8_8_8_8 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#endif +#ifndef GL_UNSIGNED_INT_8_8_8_8_REV +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif +#ifndef GL_UNSIGNED_SHORT_5_6_5_REV +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#endif +#ifndef GL_UNSIGNED_INT_10_10_10_2 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#endif +#ifndef GL_UNSIGNED_INT_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif +#ifndef GL_UNSIGNED_SHORT_5_5_5_1 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#endif +#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#endif +#ifndef GL_UNSIGNED_SHORT_8_8 +#define GL_UNSIGNED_SHORT_8_8 0x85BA +#endif +#ifndef GL_UNSIGNED_SHORT_8_8_REV +#define GL_UNSIGNED_SHORT_8_8_REV 0x85BB +#endif +#ifndef GL_YCBCR_MESA +#define GL_YCBCR_MESA 0x8757 +#endif +#ifndef GL_RGB32F +#define GL_RGB32F 0x8815 +#endif +#ifndef GL_FLOAT_RGB32_NV +#define GL_FLOAT_RGB32_NV 0x8889 +#endif +#ifndef GL_LUMINANCE16 +#define GL_LUMINANCE16 0x8042 +#endif +#ifndef GL_R16 +#define GL_R16 0x822A +#endif +#ifndef GL_UNPACK_CLIENT_STORAGE_APPLE +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif +#ifndef GL_FRAGMENT_PROGRAM +#define GL_FRAGMENT_PROGRAM 0x8804 +#endif +#ifndef GL_PROGRAM_FORMAT_ASCII +#define GL_PROGRAM_FORMAT_ASCII 0x8875 +#endif +#ifndef GL_PROGRAM_ERROR_POSITION +#define GL_PROGRAM_ERROR_POSITION 0x864B +#endif +#ifndef GL_MAX_TEXTURE_IMAGE_UNITS +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#endif +#ifndef GL_PROGRAM_ERROR_STRING +#define GL_PROGRAM_ERROR_STRING 0x8874 +#endif +/** \} */ // end of glextdefines group + + +#if defined(CONFIG_GL_WIN32) && !defined(WGL_CONTEXT_MAJOR_VERSION_ARB) +/* these are supposed to be defined in wingdi.h but mingw's is too old */ +/* only the bits actually used by mplayer are defined */ +/* reference: http://www.opengl.org/registry/specs/ARB/wgl_create_context.txt */ + +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#endif diff --git a/libvo/mga_template.c b/libvo/mga_template.c index 6886ecd8f7..274be76098 100644 --- a/libvo/mga_template.c +++ b/libvo/mga_template.c @@ -189,7 +189,7 @@ draw_image(mp_image_t *mpi){ if(mpi->flags&MP_IMGFLAG_PLANAR){ // copy planar: - draw_slice(mpi->planes,mpi->stride,mpi->w,mpi->h,mpi->x,mpi->y); + draw_slice(mpi->planes,mpi->stride,mpi->w,mpi->h,0,0); } else { // copy packed: mem2agpcpy_pic(vid_data, mpi->planes[0], // dst,src diff --git a/libvo/video_out.c b/libvo/video_out.c index 82fe4f8550..540fedb132 100644 --- a/libvo/video_out.c +++ b/libvo/video_out.c @@ -84,7 +84,7 @@ extern struct vo_driver video_out_vdpau; extern struct vo_driver video_out_xv; extern struct vo_driver video_out_gl_nosw; extern struct vo_driver video_out_gl; -extern struct vo_driver video_out_gl_sdl; +extern struct vo_driver video_out_gl3; extern struct vo_driver video_out_dga; extern struct vo_driver video_out_sdl; extern struct vo_driver video_out_3dfx; @@ -103,6 +103,7 @@ extern struct vo_driver video_out_caca; extern struct vo_driver video_out_mpegpes; extern struct vo_driver video_out_yuv4mpeg; extern struct vo_driver video_out_direct3d; +extern struct vo_driver video_out_direct3d_shaders; extern struct vo_driver video_out_directx; extern struct vo_driver video_out_kva; extern struct vo_driver video_out_dxr3; @@ -128,12 +129,13 @@ const struct vo_driver *video_out_drivers[] = #ifdef CONFIG_TDFX_VID &video_out_tdfx_vid, #endif -#ifdef CONFIG_DIRECTX - &video_out_directx, -#endif #ifdef CONFIG_DIRECT3D + &video_out_direct3d_shaders, &video_out_direct3d, #endif +#ifdef CONFIG_DIRECTX + &video_out_directx, +#endif #ifdef CONFIG_KVA &video_out_kva, #endif @@ -167,6 +169,9 @@ const struct vo_driver *video_out_drivers[] = #ifdef CONFIG_XV &video_out_xv, #endif +#ifdef CONFIG_GL + &video_out_gl3, +#endif #ifdef CONFIG_X11 #ifdef CONFIG_GL &video_out_gl_nosw, @@ -180,9 +185,6 @@ const struct vo_driver *video_out_drivers[] = #if (defined CONFIG_GL && !defined CONFIG_GL_COCOA) &video_out_gl, #endif -#ifdef CONFIG_GL_SDL - &video_out_gl_sdl, -#endif #ifdef CONFIG_DGA &video_out_dga, #endif diff --git a/libvo/vo_direct3d.c b/libvo/vo_direct3d.c index 2fb40d0398..cffe7f81d0 100644 --- a/libvo/vo_direct3d.c +++ b/libvo/vo_direct3d.c @@ -21,10 +21,22 @@ #include <windows.h> #include <errno.h> #include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdbool.h> +#include <assert.h> #include <d3d9.h> #include "config.h" +#include "options.h" +#include "subopt-helper.h" +#include "talloc.h" #include "video_out.h" -#include "video_out_internal.h" +#include "libmpcodecs/vfcap.h" +// for global_vo +#include "old_vo_wrapper.h" +#include "csputils.h" +#include "libmpcodecs/mp_image.h" +#include "libmpcodecs/img_format.h" #include "fastmemcpy.h" #include "mp_msg.h" #include "aspect.h" @@ -32,28 +44,100 @@ #include "libavutil/common.h" #include "sub/font_load.h" #include "sub/sub.h" +#include "eosd_packer.h" -static const vo_info_t info = -{ - "Direct3D 9 Renderer", - "direct3d", - "Georgi Petrov (gogothebee) <gogothebee@gmail.com>", - "" -}; +// shaders generated by fxc.exe from d3d_shader_yuv.hlsl +#include "d3d_shader_yuv.h" +#include "d3d_shader_yuv_2ch.h" -/* - * Link essential libvo functions: preinit, config, control, draw_frame, - * draw_slice, draw_osd, flip_page, check_events, uninit and - * the structure info. - */ -const LIBVO_EXTERN(direct3d) +// TODO: beg someone to add this (there is already IMGFMT_Y8) +// equals MAKEFOURCC('Y', '1', '6', ' ') +#define IMGFMT_Y16 0x20363159 +#define IMGFMT_A8Y8 MAKEFOURCC('A', '8', 'Y', '8') + +#define IMGFMT_IS_Y(x) ((x) == IMGFMT_Y8 || (x) == IMGFMT_Y16 || (x) == IMGFMT_A8Y8) +#define IMGFMT_Y_DEPTH(x) ((x) == IMGFMT_Y8 ? 8 : 16) + +#define DEVTYPE D3DDEVTYPE_HAL +//#define DEVTYPE D3DDEVTYPE_REF + + +#define D3DFVF_OSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1) + +typedef struct { + float x, y, z; /* Position of vertex in 3D space */ + float tu, tv; /* Texture coordinates */ +} vertex_osd; + +#define D3DFVF_EOSD_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE) + +typedef struct { + float x, y, z; + D3DCOLOR color; + float tu, tv; +} vertex_eosd; + +#define D3DFVF_VIDEO_VERTEX (D3DFVF_XYZ | D3DFVF_TEX3) + +typedef struct { + float x, y, z; + // pairs of texture coordinates for up to 3 planes + float t[3][2]; +} vertex_video; + + +struct d3dtex { + // user-requested size + int w, h; + // allocated texture size + int tex_w, tex_h; + // D3DPOOL_SYSTEMMEM (or others) texture: + // - can be locked in order to write (and even read) data + // - can _not_ (probably) be used as texture for rendering + // This is always non-NULL if d3dtex_allocate succeeds. + IDirect3DTexture9 *system; + // D3DPOOL_DEFAULT texture: + // - can't be locked (Probably.) + // - must be used for rendering + // This will be NULL on systems with device_texture_sys != 0. + IDirect3DTexture9 *device; +}; + +struct texplane { + int bytes_per_pixel; + int bits_per_pixel; + // chroma shifts + // e.g. get the plane's width in pixels with (priv->src_width >> shift_x) + int shift_x, shift_y; + D3DFORMAT d3d_format; + struct d3dtex texture; + // temporary locking during uploading the frame (e.g. for draw_slice) + D3DLOCKED_RECT locked_rect; + // value used to clear the image with memset (YUV chroma planes do not use + // the value 0 for this) + uint8_t clearval; +}; /* Global variables "priv" structure. I try to keep their count low. */ -static struct global_priv { - int is_paused; /**< 1 = Movie is paused, - 0 = Movie is not paused */ +typedef struct d3d_priv { + int opt_prefer_stretchrect; + int opt_disable_textures; + int opt_disable_stretchrect; + int opt_disable_shaders; + int opt_only_8bit; + int opt_disable_eosd; + int opt_disable_texture_align; + // debugging + int opt_force_power_of_2; + int opt_texture_memory; + int opt_swap_discard; + int opt_exact_backbuffer; + int opt_16bit_textures; + + struct vo *vo; + int is_clear_needed; /**< 1 = Clear the backbuffer before StretchRect 0 = (default) Don't clear it */ D3DLOCKED_RECT locked_rect; /**< The locked offscreen surface */ @@ -63,8 +147,22 @@ static struct global_priv { fullscreen */ int src_width; /**< Source (movie) width */ int src_height; /**< Source (movie) heigth */ + int src_d_width; /**< Source (movie) aspect corrected width */ + int src_d_height; /**< Source (movie) aspect corrected heigth */ int border_x; /**< horizontal border value for OSD */ int border_y; /**< vertical border value for OSD */ + int image_format; /**< mplayer image format */ + bool use_textures; /**< use 3D texture rendering, instead of + StretchRect */ + bool use_shaders; /**< use shader for YUV color conversion + (or possibly for RGB video equalizers) */ + bool use_2ch_hack; /**< 2 byte YUV formats use 2 channel hack */ + + int plane_count; + struct texplane planes[3]; + + IDirect3DPixelShader9 *pixel_shader; + const BYTE *pixel_shader_data; D3DFORMAT movie_src_fmt; /**< Movie colorspace format (depends on the movie's codec) */ @@ -76,14 +174,14 @@ static struct global_priv { LPDIRECT3D9 d3d_handle; /**< Direct3D Handle */ LPDIRECT3DDEVICE9 d3d_device; /**< The Direct3D Adapter */ + bool d3d_in_scene; /**< BeginScene was called, EndScene not */ IDirect3DSurface9 *d3d_surface; /**< Offscreen Direct3D Surface. MPlayer renders inside it. Uses colorspace priv->movie_src_fmt */ - IDirect3DTexture9 *d3d_texture_osd; /**< Direct3D Texture. Uses RGBA */ - IDirect3DTexture9 *d3d_texture_system; /**< Direct3D Texture. System memory - cannot lock a normal texture. Uses RGBA */ + struct d3dtex texture_osd; /**< RGBA */ IDirect3DSurface9 *d3d_backbuf; /**< Video card's back buffer (used to display next frame) */ + struct d3dtex texture_eosd; /**< A8 */ int cur_backbuf_width; /**< Current backbuffer width */ int cur_backbuf_height; /**< Current backbuffer height */ int is_osd_populated; /**< 1 = OSD texture has something to display, @@ -96,48 +194,82 @@ static struct global_priv { 0 = device requires shadow */ int max_texture_width; /**< from the device capabilities */ int max_texture_height; /**< from the device capabilities */ - int osd_width; /**< current width of the OSD */ - int osd_height; /**< current height of the OSD */ - int osd_texture_width; /**< current width of the OSD texture */ - int osd_texture_height; /**< current height of the OSD texture */ -} *priv; -typedef struct { + D3DMATRIX d3d_colormatrix; + float d3d_depth_vector[4]; + struct mp_csp_details colorspace; + struct mp_csp_equalizer video_eq; + + struct eosd_packer *eosd; /**< EOSD packer (image positions etc.) */ + vertex_eosd *eosd_vb; /**< temporary memory for D3D when rendering EOSD */ +} d3d_priv; + +struct fmt_entry { const unsigned int mplayer_fmt; /**< Given by MPlayer */ const D3DFORMAT fourcc; /**< Required by D3D's test function */ -} struct_fmt_table; +}; /* Map table from reported MPlayer format to the required fourcc. This is needed to perform the format query. */ -static const struct_fmt_table fmt_table[] = { +static const struct fmt_entry fmt_table[] = { + // planar YUV {IMGFMT_YV12, MAKEFOURCC('Y','V','1','2')}, {IMGFMT_I420, MAKEFOURCC('I','4','2','0')}, {IMGFMT_IYUV, MAKEFOURCC('I','Y','U','V')}, {IMGFMT_YVU9, MAKEFOURCC('Y','V','U','9')}, + // packed YUV {IMGFMT_YUY2, D3DFMT_YUY2}, {IMGFMT_UYVY, D3DFMT_UYVY}, + // packed RGB {IMGFMT_BGR32, D3DFMT_X8R8G8B8}, {IMGFMT_RGB32, D3DFMT_X8B8G8R8}, {IMGFMT_BGR24, D3DFMT_R8G8B8}, //untested {IMGFMT_BGR16, D3DFMT_R5G6B5}, {IMGFMT_BGR15, D3DFMT_X1R5G5B5}, {IMGFMT_BGR8 , D3DFMT_R3G3B2}, //untested + // grayscale (can be considered both packed and planar) + {IMGFMT_Y8, D3DFMT_L8}, + {IMGFMT_Y16, D3DFMT_L16}, + {IMGFMT_A8Y8, D3DFMT_A8L8}, + {0}, }; -#define DISPLAY_FORMAT_TABLE_ENTRIES (sizeof(fmt_table) / sizeof(fmt_table[0])) -#define D3DFVF_MY_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1) +static void generate_eosd(d3d_priv *priv, mp_eosd_images_t *); +static void draw_eosd(d3d_priv *priv); +static void update_colorspace(d3d_priv *priv); +static void d3d_clear_video_textures(d3d_priv *priv); +static bool resize_d3d(d3d_priv *priv); +static uint32_t d3d_draw_frame(d3d_priv *priv); +static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, + int x, int y); +static void uninit(struct vo *vo); +static void draw_osd(struct vo *vo, struct osd_state *osd); +static void flip_page(struct vo *vo); +static mp_image_t *get_screenshot(d3d_priv *priv); +static mp_image_t *get_window_screenshot(d3d_priv *priv); -typedef struct { - float x, y, z; /* Position of vertex in 3D space */ - float tu, tv; /* Texture coordinates */ -} struct_vertex; -typedef enum back_buffer_action { - BACKBUFFER_CREATE, - BACKBUFFER_RESET -} back_buffer_action_e; +static void d3d_matrix_identity(D3DMATRIX *m) +{ + memset(m, 0, sizeof(D3DMATRIX)); + m->_11 = m->_22 = m->_33 = m->_44 = 1.0f; +} + +static void d3d_matrix_ortho(D3DMATRIX *m, float left, float right, + float bottom, float top) +{ + d3d_matrix_identity(m); + m->_11 = 2.0f / (right - left); + m->_22 = 2.0f / (top - bottom); + m->_33 = 1.0f; + m->_41 = -(right + left) / (right - left); + m->_42 = -(top + bottom) / (top - bottom); + m->_43 = 0; + m->_44 = 1.0f; +} + /**************************************************************************** * * * * @@ -148,15 +280,28 @@ typedef enum back_buffer_action { * * ****************************************************************************/ +static bool d3d_begin_scene(d3d_priv *priv) +{ + if (!priv->d3d_in_scene) { + if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>BeginScene failed.\n"); + return false; + } + priv->d3d_in_scene = true; + } + return true; +} + /** @brief Calculate scaled fullscreen movie rectangle with * preserved aspect ratio. */ -static void calc_fs_rect(void) +static void calc_fs_rect(d3d_priv *priv) { struct vo_rect src_rect; struct vo_rect dst_rect; struct vo_rect borders; - calc_src_dst_rects(priv->src_width, priv->src_height, &src_rect, &dst_rect, &borders, NULL); + calc_src_dst_rects(priv->vo, priv->src_width, priv->src_height, &src_rect, + &dst_rect, &borders, NULL); priv->fs_movie_rect.left = dst_rect.left; priv->fs_movie_rect.right = dst_rect.right; @@ -170,7 +315,7 @@ static void calc_fs_rect(void) priv->border_y = borders.top; mp_msg(MSGT_VO, MSGL_V, - "<vo_direct3d>Fullscreen movie rectangle: t: %ld, l: %ld, r: %ld, b:%ld\n", + "<vo_direct3d>Video rectangle: t: %ld, l: %ld, r: %ld, b:%ld\n", priv->fs_movie_rect.top, priv->fs_movie_rect.left, priv->fs_movie_rect.right, priv->fs_movie_rect.bottom); @@ -181,54 +326,285 @@ static void calc_fs_rect(void) priv->is_clear_needed = 1; } -/** @brief Destroy D3D Offscreen and Backbuffer surfaces. - */ -static void destroy_d3d_surfaces(void) +// Adjust the texture size *width/*height to fit the requirements of the D3D +// device. The texture size is only increased. +static void d3d_fix_texture_size(d3d_priv *priv, int *width, int *height) { - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>destroy_d3d_surfaces called.\n"); - /* Let's destroy the old (if any) D3D Surfaces */ + int tex_width = *width; + int tex_height = *height; + + // avoid nasty special cases with 0-sized textures and texture sizes + tex_width = FFMAX(tex_width, 1); + tex_height = FFMAX(tex_height, 1); + + if (priv->device_caps_power2_only) { + tex_width = 1; + tex_height = 1; + while (tex_width < *width) tex_width <<= 1; + while (tex_height < *height) tex_height <<= 1; + } + if (priv->device_caps_square_only) + /* device only supports square textures */ + tex_width = tex_height = FFMAX(tex_width, tex_height); + // better round up to a multiple of 16 + if (!priv->opt_disable_texture_align) { + tex_width = (tex_width + 15) & ~15; + tex_height = (tex_height + 15) & ~15; + } + + *width = tex_width; + *height = tex_height; +} + +static void d3dtex_release(d3d_priv *priv, struct d3dtex *tex) +{ + if (tex->system) + IDirect3DTexture9_Release(tex->system); + tex->system = NULL; + + if (tex->device) + IDirect3DTexture9_Release(tex->device); + tex->device = NULL; + + tex->tex_w = tex->tex_h = 0; +} + +static bool d3dtex_allocate(d3d_priv *priv, struct d3dtex *tex, D3DFORMAT fmt, + int w, int h) +{ + d3dtex_release(priv, tex); + + tex->w = w; + tex->h = h; + + int tw = w, th = h; + d3d_fix_texture_size(priv, &tw, &th); + + int memtype = D3DPOOL_SYSTEMMEM; + switch (priv->opt_texture_memory) { + case 1: memtype = D3DPOOL_MANAGED; break; + case 2: memtype = D3DPOOL_DEFAULT; break; + } + + if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, tw, th, 1, + D3DUSAGE_DYNAMIC, fmt, memtype, &tex->system, NULL))) + { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Allocating %dx%d texture in system RAM failed.\n", + w, h); + goto error_exit; + } + + if (!priv->device_texture_sys && !priv->opt_texture_memory) { + if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, tw, th, 1, + D3DUSAGE_DYNAMIC, fmt, D3DPOOL_DEFAULT, &tex->device, NULL))) + { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Allocating %dx%d texture in video RAM failed.\n", + w, h); + goto error_exit; + } + } + + tex->tex_w = tw; + tex->tex_h = th; + + return true; + +error_exit: + d3dtex_release(priv, tex); + return false; +} - if (priv->locked_rect.pBits) - IDirect3DSurface9_UnlockRect(priv->d3d_surface); +static IDirect3DBaseTexture9 *d3dtex_get_render_texture(d3d_priv *priv, + struct d3dtex *tex) +{ + return (IDirect3DBaseTexture9 *)(tex->device ? tex->device : tex->system); +} + +// Copy system texture contents to device texture. +static bool d3dtex_update(d3d_priv *priv, struct d3dtex *tex) +{ + if (!tex->device) + return true; + return !FAILED(IDirect3DDevice9_UpdateTexture(priv->d3d_device, + (IDirect3DBaseTexture9 *)tex->system, + (IDirect3DBaseTexture9 *)tex->device)); +} + +static void d3d_unlock_video_objects(d3d_priv *priv) +{ + bool any_failed = false; + + if (priv->locked_rect.pBits) { + if (FAILED(IDirect3DSurface9_UnlockRect(priv->d3d_surface))) + any_failed = true; + } priv->locked_rect.pBits = NULL; + for (int n = 0; n < priv->plane_count; n++) { + struct texplane *plane = &priv->planes[n]; + if (plane->locked_rect.pBits) { + if (FAILED(IDirect3DTexture9_UnlockRect(plane->texture.system, 0))) + any_failed = true; + } + plane->locked_rect.pBits = NULL; + } + + if (any_failed) { + mp_msg(MSGT_VO, MSGL_V, + "<vo_direct3d>Unlocking video objects failed.\n"); + } +} + +// Free video surface/textures, shaders, etc. +static void d3d_destroy_video_objects(d3d_priv *priv) +{ + d3d_unlock_video_objects(priv); + if (priv->d3d_surface) IDirect3DSurface9_Release(priv->d3d_surface); priv->d3d_surface = NULL; - /* kill the OSD texture and its shadow copy */ - if (priv->d3d_texture_osd) - IDirect3DTexture9_Release(priv->d3d_texture_osd); - priv->d3d_texture_osd = NULL; + for (int n = 0; n < priv->plane_count; n++) { + d3dtex_release(priv, &priv->planes[n].texture); + } - if (priv->d3d_texture_system) - IDirect3DTexture9_Release(priv->d3d_texture_system); - priv->d3d_texture_system = NULL; + if (priv->pixel_shader) + IDirect3DPixelShader9_Release(priv->pixel_shader); + priv->pixel_shader = NULL; +} + +/** @brief Destroy D3D Offscreen and Backbuffer surfaces. + */ +static void destroy_d3d_surfaces(d3d_priv *priv) +{ + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>destroy_d3d_surfaces called.\n"); + + d3d_destroy_video_objects(priv); + + d3dtex_release(priv, &priv->texture_osd); if (priv->d3d_backbuf) IDirect3DSurface9_Release(priv->d3d_backbuf); priv->d3d_backbuf = NULL; + + d3dtex_release(priv, &priv->texture_eosd); + + if (priv->eosd) + eosd_packer_reinit(priv->eosd, 0, 0); + + priv->d3d_in_scene = false; } -/** @brief Create D3D Offscreen and Backbuffer surfaces. Each - * surface is created only if it's not already present. - * @return 1 on success, 0 on failure - */ -static int create_d3d_surfaces(void) +// Allocate video surface or textures, and create shaders if needed. +static bool d3d_configure_video_objects(d3d_priv *priv) { - int osd_width = vo_dwidth, osd_height = vo_dheight; - int tex_width = osd_width, tex_height = osd_height; - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>create_d3d_surfaces called.\n"); + int n; + bool need_clear = false; + + assert(priv->image_format != 0); + + if (priv->use_textures) { + for (n = 0; n < priv->plane_count; n++) { + struct texplane *plane = &priv->planes[n]; + + if (!plane->texture.system) { + if (!d3dtex_allocate(priv, + &plane->texture, + plane->d3d_format, + priv->src_width >> plane->shift_x, + priv->src_height >> plane->shift_y)) + { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Allocating plane %d" + " failed.\n", n); + return false; + } + + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Allocated plane %d:" + " %d bit, shift=%d/%d size=%d/%d (%d/%d).\n", n, + plane->bits_per_pixel, + plane->shift_x, plane->shift_y, + plane->texture.w, plane->texture.h, + plane->texture.tex_w, plane->texture.tex_h); + + need_clear = true; + } + } - if (!priv->d3d_surface && - FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface( - priv->d3d_device, priv->src_width, priv->src_height, - priv->movie_src_fmt, D3DPOOL_DEFAULT, &priv->d3d_surface, NULL))) { - mp_msg(MSGT_VO, MSGL_ERR, - "<vo_direct3d>Allocating offscreen surface failed.\n"); - return 0; + if (need_clear) + d3d_clear_video_textures(priv); + + if (priv->pixel_shader_data) { + if (!priv->pixel_shader && + FAILED(IDirect3DDevice9_CreatePixelShader(priv->d3d_device, + (DWORD *)priv->pixel_shader_data, &priv->pixel_shader))) + { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Failed to create " + "YUV conversion pixel shader.\n"); + return false; + } + } + + } else { + + if (!priv->d3d_surface && + FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface( + priv->d3d_device, priv->src_width, priv->src_height, + priv->movie_src_fmt, D3DPOOL_DEFAULT, &priv->d3d_surface, NULL))) + { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Allocating offscreen surface failed.\n"); + return false; + } } + return true; +} + +static bool d3d_lock_video_textures(d3d_priv *priv) +{ + for (int n = 0; n < priv->plane_count; n++) { + struct texplane *plane = &priv->planes[n]; + + if (!plane->locked_rect.pBits) { + if (FAILED(IDirect3DTexture9_LockRect(plane->texture.system, 0, + &plane->locked_rect, NULL, 0))) + { + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Texture lock failure.\n"); + d3d_unlock_video_objects(priv); + return false; + } + } + } + + return true; +} + +static void d3d_clear_video_textures(d3d_priv *priv) +{ + if (!d3d_lock_video_textures(priv)) + return; + + for (int n = 0; n < priv->plane_count; n++) { + struct texplane *plane = &priv->planes[n]; + memset(plane->locked_rect.pBits, plane->clearval, + plane->locked_rect.Pitch * plane->texture.tex_h); + } + + d3d_unlock_video_objects(priv); +} + +// Recreate and initialize D3D objects if necessary. The amount of work that +// needs to be done can be quite different: it could be that full initialization +// is required, or that some objects need to be created, or that nothing is +// done. +static bool create_d3d_surfaces(d3d_priv *priv) +{ + int osd_width = priv->vo->dwidth, osd_height = priv->vo->dheight; + int tex_width = osd_width, tex_height = osd_height; + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>create_d3d_surfaces called.\n"); + if (!priv->d3d_backbuf && FAILED(IDirect3DDevice9_GetBackBuffer(priv->d3d_device, 0, 0, D3DBACKBUFFER_TYPE_MONO, @@ -237,90 +613,148 @@ static int create_d3d_surfaces(void) return 0; } - /* calculate the best size for the OSD depending on the factors from the device */ - if (priv->device_caps_power2_only) { - tex_width = 1; - tex_height = 1; - while (tex_width < osd_width ) tex_width <<= 1; - while (tex_height < osd_height) tex_height <<= 1; - } - if (priv->device_caps_square_only) - /* device only supports square textures */ - tex_width = tex_height = tex_width > tex_height ? tex_width : tex_height; - // better round up to a multiple of 16 - tex_width = (tex_width + 15) & ~15; - tex_height = (tex_height + 15) & ~15; + if (!d3d_configure_video_objects(priv)) + return 0; + + /* create OSD */ + + d3d_fix_texture_size(priv, &tex_width, &tex_height); - // make sure we respect the size limits without breaking aspect or pow2-requirements - while (tex_width > priv->max_texture_width || tex_height > priv->max_texture_height) { + while (tex_width > priv->max_texture_width + || tex_height > priv->max_texture_height) + { osd_width >>= 1; osd_height >>= 1; tex_width >>= 1; tex_height >>= 1; } - priv->osd_width = osd_width; - priv->osd_height = osd_height; - priv->osd_texture_width = tex_width; - priv->osd_texture_height = tex_height; + if (priv->texture_osd.tex_w < tex_width + || priv->texture_osd.tex_h < tex_height) + { + d3dtex_release(priv, &priv->texture_osd); - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>OSD texture size (%dx%d), requested (%dx%d).\n", - vo_dwidth, vo_dheight, priv->osd_texture_width, priv->osd_texture_height); + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>OSD texture size (%dx%d)," + " requested (%dx%d).\n", osd_width, osd_height, tex_width, + tex_height); - /* create OSD */ - if (!priv->d3d_texture_system && - FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, - priv->osd_texture_width, - priv->osd_texture_height, - 1, - D3DUSAGE_DYNAMIC, - D3DFMT_A8L8, - D3DPOOL_SYSTEMMEM, - &priv->d3d_texture_system, - NULL))) { - mp_msg(MSGT_VO,MSGL_ERR, - "<vo_direct3d>Allocating OSD texture in system RAM failed.\n"); - return 0; - } - - if (!priv->device_texture_sys) { - /* only create if we need a shadow version on the external device */ - if (!priv->d3d_texture_osd && - FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, - priv->osd_texture_width, - priv->osd_texture_height, - 1, - D3DUSAGE_DYNAMIC, - D3DFMT_A8L8, - D3DPOOL_DEFAULT, - &priv->d3d_texture_osd, - NULL))) { - mp_msg(MSGT_VO,MSGL_ERR, - "<vo_direct3d>Allocating OSD texture in video RAM failed.\n"); + if (!d3dtex_allocate(priv, &priv->texture_osd, D3DFMT_A8L8, tex_width, + tex_height)) + { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Allocating OSD texture failed.\n"); return 0; } } + priv->texture_osd.w = osd_width; + priv->texture_osd.h = osd_height; + /* setup default renderstate */ - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_SRCBLEND, D3DBLEND_ONE); - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHAFUNC, D3DCMP_GREATER); - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHAREF, (DWORD)0x0); - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_LIGHTING, FALSE); - IDirect3DDevice9_SetSamplerState(priv->d3d_device, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - IDirect3DDevice9_SetSamplerState(priv->d3d_device, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_ALPHAFUNC, D3DCMP_GREATER); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_ALPHAREF, (DWORD)0x0); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_LIGHTING, FALSE); + + // we use up to 3 samplers for up to 3 YUV planes + for (int n = 0; n < 3; n++) { + IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_MINFILTER, + D3DTEXF_LINEAR); + IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_MAGFILTER, + D3DTEXF_LINEAR); + IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_ADDRESSU, + D3DTADDRESS_CLAMP); + IDirect3DDevice9_SetSamplerState(priv->d3d_device, n, D3DSAMP_ADDRESSV, + D3DTADDRESS_CLAMP); + } + + if (priv->eosd && !priv->texture_eosd.system) + eosd_packer_reinit(priv->eosd, priv->max_texture_width, + priv->max_texture_height); return 1; } +static bool init_d3d(d3d_priv *priv) +{ + D3DDISPLAYMODE disp_mode; + D3DCAPS9 disp_caps; + DWORD texture_caps; + DWORD dev_caps; + + priv->d3d_handle = priv->pDirect3DCreate9(D3D_SDK_VERSION); + if (!priv->d3d_handle) { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Initializing Direct3D failed.\n"); + return false; + } + + if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle, + D3DADAPTER_DEFAULT, + &disp_mode))) { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Reading display mode failed.\n"); + return false; + } + + priv->desktop_fmt = disp_mode.Format; + priv->cur_backbuf_width = disp_mode.Width; + priv->cur_backbuf_height = disp_mode.Height; + + mp_msg(MSGT_VO, MSGL_V, + "<vo_direct3d>Setting backbuffer dimensions to (%dx%d).\n", + disp_mode.Width, disp_mode.Height); + + if (FAILED(IDirect3D9_GetDeviceCaps(priv->d3d_handle, + D3DADAPTER_DEFAULT, + DEVTYPE, + &disp_caps))) + { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Reading display capabilities failed.\n"); + return false; + } + + /* Store relevant information reguarding caps of device */ + texture_caps = disp_caps.TextureCaps; + dev_caps = disp_caps.DevCaps; + priv->device_caps_power2_only = (texture_caps & D3DPTEXTURECAPS_POW2) && + !(texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL); + priv->device_caps_square_only = texture_caps & D3DPTEXTURECAPS_SQUAREONLY; + priv->device_texture_sys = dev_caps & D3DDEVCAPS_TEXTURESYSTEMMEMORY; + priv->max_texture_width = disp_caps.MaxTextureWidth; + priv->max_texture_height = disp_caps.MaxTextureHeight; + + if (priv->opt_force_power_of_2) + priv->device_caps_power2_only = 1; + + mp_msg(MSGT_VO, MSGL_V, + "<vo_direct3d>device_caps_power2_only %d, device_caps_square_only %d\n" + "<vo_direct3d>device_texture_sys %d\n" + "<vo_direct3d>max_texture_width %d, max_texture_height %d\n", + priv->device_caps_power2_only, priv->device_caps_square_only, + priv->device_texture_sys, priv->max_texture_width, + priv->max_texture_height); + + return true; +} + /** @brief Fill D3D Presentation parameters */ -static void fill_d3d_presentparams(D3DPRESENT_PARAMETERS *present_params) +static void fill_d3d_presentparams(d3d_priv *priv, + D3DPRESENT_PARAMETERS *present_params) { /* Prepare Direct3D initialization parameters. */ memset(present_params, 0, sizeof(D3DPRESENT_PARAMETERS)); present_params->Windowed = TRUE; - present_params->SwapEffect = D3DSWAPEFFECT_COPY; + present_params->SwapEffect = + priv->opt_swap_discard ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY; present_params->Flags = D3DPRESENTFLAG_VIDEO; present_params->hDeviceWindow = vo_w32_window; /* w32_common var */ present_params->BackBufferWidth = priv->cur_backbuf_width; @@ -333,177 +767,133 @@ static void fill_d3d_presentparams(D3DPRESENT_PARAMETERS *present_params) } -/** @brief Create a new backbuffer. Create or Reset the D3D - * device. - * @return 1 on success, 0 on failure - */ -static int change_d3d_backbuffer(back_buffer_action_e action) +// Create a new backbuffer. Create or Reset the D3D device. +static bool change_d3d_backbuffer(d3d_priv *priv) { D3DPRESENT_PARAMETERS present_params; - destroy_d3d_surfaces(); + int window_w = priv->vo->dwidth; + int window_h = priv->vo->dheight; /* Grow the backbuffer in the required dimension. */ - if (vo_dwidth > priv->cur_backbuf_width) - priv->cur_backbuf_width = vo_dwidth; + if (window_w > priv->cur_backbuf_width) + priv->cur_backbuf_width = window_w; + + if (window_h > priv->cur_backbuf_height) + priv->cur_backbuf_height = window_h; - if (vo_dheight > priv->cur_backbuf_height) - priv->cur_backbuf_height = vo_dheight; + if (priv->opt_exact_backbuffer) { + priv->cur_backbuf_width = window_w; + priv->cur_backbuf_height = window_h; + } /* The grown backbuffer dimensions are ready and fill_d3d_presentparams * will use them, so we can reset the device. */ - fill_d3d_presentparams(&present_params); - - /* vo_w32_window is w32_common variable. It's a handle to the window. */ - if (action == BACKBUFFER_CREATE && - FAILED(IDirect3D9_CreateDevice(priv->d3d_handle, - D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, vo_w32_window, - D3DCREATE_SOFTWARE_VERTEXPROCESSING, - &present_params, &priv->d3d_device))) { + fill_d3d_presentparams(priv, &present_params); + + if (!priv->d3d_device) { + if (FAILED(IDirect3D9_CreateDevice(priv->d3d_handle, + D3DADAPTER_DEFAULT, + DEVTYPE, vo_w32_window, + D3DCREATE_SOFTWARE_VERTEXPROCESSING + | D3DCREATE_FPU_PRESERVE, + &present_params, &priv->d3d_device))) + { mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Creating Direct3D device failed.\n"); - return 0; - } - - if (action == BACKBUFFER_RESET && - FAILED(IDirect3DDevice9_Reset(priv->d3d_device, &present_params))) { + return 0; + } + } else { + if (FAILED(IDirect3DDevice9_Reset(priv->d3d_device, &present_params))) { mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Reseting Direct3D device failed.\n"); - return 0; + return 0; + } } mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>New backbuffer (%dx%d), VO (%dx%d)\n", present_params.BackBufferWidth, present_params.BackBufferHeight, - vo_dwidth, vo_dheight); - - return 1; -} - -/** @brief Configure initial Direct3D context. The first - * function called to initialize the D3D context. - * @return 1 on success, 0 on failure - */ -static int configure_d3d(void) -{ - D3DDISPLAYMODE disp_mode; - D3DVIEWPORT9 vp = {0, 0, vo_dwidth, vo_dheight, 0, 1}; - - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>configure_d3d called.\n"); - - destroy_d3d_surfaces(); - - /* Get the current desktop display mode, so we can set up a back buffer - * of the same format. */ - if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle, - D3DADAPTER_DEFAULT, - &disp_mode))) { - mp_msg(MSGT_VO, MSGL_ERR, - "<vo_direct3d>Reading adapter display mode failed.\n"); - return 0; - } - - /* Write current Desktop's colorspace format in the global storage. */ - priv->desktop_fmt = disp_mode.Format; - - if (!change_d3d_backbuffer(BACKBUFFER_CREATE)) - return 0; - - if (!create_d3d_surfaces()) - return 0; - - if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device, - &vp))) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Setting viewport failed.\n"); - return 0; - } - - calc_fs_rect(); + window_w, window_h); return 1; } /** @brief Reconfigure the whole Direct3D. Called only - * when the video adapter becomes uncooperative. + * when the video adapter becomes uncooperative. ("Lost" devices) * @return 1 on success, 0 on failure */ -static int reconfigure_d3d(void) +static int reconfigure_d3d(d3d_priv *priv) { mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>reconfigure_d3d called.\n"); - /* Destroy the offscreen, OSD and backbuffer surfaces */ - destroy_d3d_surfaces(); + destroy_d3d_surfaces(priv); - /* Destroy the D3D Device */ if (priv->d3d_device) IDirect3DDevice9_Release(priv->d3d_device); priv->d3d_device = NULL; - /* Stop the whole Direct3D */ - IDirect3D9_Release(priv->d3d_handle); - - /* Initialize Direct3D from the beginning */ - priv->d3d_handle = priv->pDirect3DCreate9(D3D_SDK_VERSION); - if (!priv->d3d_handle) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Initializing Direct3D failed.\n"); + // Force complete destruction of the D3D state. + // Note: this step could be omitted. The resize_d3d call below would detect + // that d3d_device is NULL, and would properly recreate it. I'm not sure why + // the following code to release and recreate the d3d_handle exists. + if (priv->d3d_handle) + IDirect3D9_Release(priv->d3d_handle); + priv->d3d_handle = NULL; + if (!init_d3d(priv)) return 0; - } - /* Configure Direct3D */ - if (!configure_d3d()) + // Proper re-initialization. + if (!resize_d3d(priv)) return 0; return 1; } -/** @brief Resize Direct3D context on window resize. - * @return 1 on success, 0 on failure - */ -static int resize_d3d(void) +// Resize Direct3D context on window resize. +// This function also is called when major initializations need to be done. +static bool resize_d3d(d3d_priv *priv) { - D3DVIEWPORT9 vp = {0, 0, vo_dwidth, vo_dheight, 0, 1}; + D3DVIEWPORT9 vp = {0, 0, priv->vo->dwidth, priv->vo->dheight, 0, 1}; mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>resize_d3d called.\n"); /* Make sure that backbuffer is large enough to accomodate the new viewport dimensions. Grow it if necessary. */ - if (vo_dwidth > priv->cur_backbuf_width || - vo_dheight > priv->cur_backbuf_height) { - if (!change_d3d_backbuffer(BACKBUFFER_RESET)) - return 0; - } - - /* Destroy the OSD textures. They should always match the new dimensions - * of the onscreen window, so on each resize we need new OSD dimensions. - */ - - if (priv->d3d_texture_osd) - IDirect3DTexture9_Release(priv->d3d_texture_osd); - priv->d3d_texture_osd = NULL; + bool backbuf_resize = priv->vo->dwidth > priv->cur_backbuf_width || + priv->vo->dheight > priv->cur_backbuf_height; - if (priv->d3d_texture_system) - IDirect3DTexture9_Release(priv->d3d_texture_system); - priv->d3d_texture_system = NULL; + if (priv->opt_exact_backbuffer) { + backbuf_resize = priv->vo->dwidth != priv->cur_backbuf_width || + priv->vo->dheight != priv->cur_backbuf_height; + } + if (backbuf_resize || !priv->d3d_device) + { + destroy_d3d_surfaces(priv); + if (!change_d3d_backbuffer(priv)) + return 0; + } - /* Recreate the OSD. The function will observe that the offscreen plain - * surface and the backbuffer are not destroyed and will skip their creation, - * effectively recreating only the OSD. - */ + if (!priv->d3d_device) + return 1; - if (!create_d3d_surfaces()) + if (!create_d3d_surfaces(priv)) return 0; - if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device, - &vp))) { + if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device, &vp))) { mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Setting viewport failed.\n"); return 0; } - calc_fs_rect(); + // so that screen coordinates map to D3D ones + D3DMATRIX view; + d3d_matrix_ortho(&view, 0.5f, vp.Width + 0.5f, vp.Height + 0.5f, 0.5f); + IDirect3DDevice9_SetTransform(priv->d3d_device, D3DTS_VIEW, &view); + + calc_fs_rect(priv); #ifdef CONFIG_FREETYPE // font needs to be adjusted @@ -512,23 +902,23 @@ static int resize_d3d(void) // OSD needs to be drawn fresh for new size vo_osd_changed(OSDTYPE_OSD); + priv->vo->want_redraw = true; + return 1; } /** @brief Uninitialize Direct3D and close the window. */ -static void uninit_d3d(void) +static void uninit_d3d(d3d_priv *priv) { mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>uninit_d3d called.\n"); - destroy_d3d_surfaces(); + destroy_d3d_surfaces(priv); - /* Destroy the D3D Device */ if (priv->d3d_device) IDirect3DDevice9_Release(priv->d3d_device); priv->d3d_device = NULL; - /* Stop the whole D3D. */ if (priv->d3d_handle) { mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Stopping Direct3D.\n"); IDirect3D9_Release(priv->d3d_handle); @@ -536,26 +926,38 @@ static void uninit_d3d(void) priv->d3d_handle = NULL; } +static uint32_t d3d_upload_and_render_frame_texture(d3d_priv *priv, + mp_image_t *mpi) +{ + if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) + draw_slice(priv->vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0); + + d3d_unlock_video_objects(priv); + + for (int n = 0; n < priv->plane_count; n++) { + d3dtex_update(priv, &priv->planes[n].texture); + } + + return d3d_draw_frame(priv); +} + /** @brief Render a frame on the screen. * @param mpi mpi structure with the decoded frame inside * @return VO_TRUE on success, VO_ERROR on failure */ -static uint32_t render_d3d_frame(mp_image_t *mpi) +static uint32_t d3d_upload_and_render_frame(d3d_priv *priv, mp_image_t *mpi) { - /* Uncomment when direct rendering is implemented. - * if (mpi->flags & MP_IMGFLAG_DIRECT) ... - */ - - /* If the D3D device is uncooperative (not initialized), return success. - The device will be probed for reinitialization in the next flip_page() */ if (!priv->d3d_device) return VO_TRUE; + if (priv->use_textures) + return d3d_upload_and_render_frame_texture(priv, mpi); + if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) goto skip_upload; if (mpi->flags & MP_IMGFLAG_PLANAR) { /* Copy a planar frame. */ - draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0); + draw_slice(priv->vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0); goto skip_upload; } @@ -572,77 +974,315 @@ static uint32_t render_d3d_frame(mp_image_t *mpi) mpi->height, priv->locked_rect.Pitch, mpi->stride[0]); skip_upload: - /* This unlock is used for both slice_draw path and render_d3d_frame path. */ - if (FAILED(IDirect3DSurface9_UnlockRect(priv->d3d_surface))) { - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Surface unlock failed.\n"); - return VO_ERROR; - } - priv->locked_rect.pBits = NULL; + d3d_unlock_video_objects(priv); + + return d3d_draw_frame(priv); +} + +static uint32_t d3d_draw_frame(d3d_priv *priv) +{ + int n; - if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>BeginScene failed.\n"); + if (!priv->d3d_device) + return VO_TRUE; + + if (!d3d_begin_scene(priv)) return VO_ERROR; - } - if (priv->is_clear_needed) { + if (priv->is_clear_needed || priv->opt_swap_discard) { IDirect3DDevice9_Clear(priv->d3d_device, 0, NULL, D3DCLEAR_TARGET, 0, 0, 0); priv->is_clear_needed = 0; } - if (FAILED(IDirect3DDevice9_StretchRect(priv->d3d_device, - priv->d3d_surface, - &priv->fs_panscan_rect, - priv->d3d_backbuf, - &priv->fs_movie_rect, - D3DTEXF_LINEAR))) { - mp_msg(MSGT_VO, MSGL_ERR, - "<vo_direct3d>Copying frame to the backbuffer failed.\n"); - return VO_ERROR; - } + if (priv->use_textures) { - if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>EndScene failed.\n"); - return VO_ERROR; + for (n = 0; n < priv->plane_count; n++) { + IDirect3DDevice9_SetTexture(priv->d3d_device, n, + d3dtex_get_render_texture(priv, &priv->planes[n].texture)); + } + + RECT rm = priv->fs_movie_rect; + RECT rs = priv->fs_panscan_rect; + + vertex_video vb[] = { + { rm.left, rm.top, 0.0f}, + { rm.right, rm.top, 0.0f}, + { rm.left, rm.bottom, 0.0f}, + { rm.right, rm.bottom, 0.0f} + }; + + float texc[4][2] = { + { rs.left, rs.top}, + { rs.right, rs.top}, + { rs.left, rs.bottom}, + { rs.right, rs.bottom} + }; + + for (n = 0; n < priv->plane_count; n++) { + float s_x = (1.0f / (1 << priv->planes[n].shift_x)) + / priv->planes[n].texture.tex_w; + float s_y = (1.0f / (1 << priv->planes[n].shift_y)) + / priv->planes[n].texture.tex_h; + for (int i = 0; i < 4; i++) { + vb[i].t[n][0] = texc[i][0] * s_x; + vb[i].t[n][1] = texc[i][1] * s_y; + } + } + + if (priv->pixel_shader) { + IDirect3DDevice9_SetPixelShader(priv->d3d_device, priv->pixel_shader); + IDirect3DDevice9_SetPixelShaderConstantF(priv->d3d_device, 0, + &priv->d3d_colormatrix._11, + 4); + IDirect3DDevice9_SetPixelShaderConstantF(priv->d3d_device, 5, + priv->d3d_depth_vector, + 1); + } + + IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_VIDEO_VERTEX); + IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP, + 2, &vb[0], sizeof(vertex_video)); + + IDirect3DDevice9_SetPixelShader(priv->d3d_device, NULL); + + for (n = 0; n < priv->plane_count; n++) { + IDirect3DDevice9_SetTexture(priv->d3d_device, n, NULL); + } + + } else { + if (FAILED(IDirect3DDevice9_StretchRect(priv->d3d_device, + priv->d3d_surface, + &priv->fs_panscan_rect, + priv->d3d_backbuf, + &priv->fs_movie_rect, + D3DTEXF_LINEAR))) { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Copying frame to the backbuffer failed.\n"); + return VO_ERROR; + } } return VO_TRUE; } +// Return the high byte of the value that represents white in chroma (U/V) +static int get_chroma_clear_val(int bit_depth) +{ + return 1 << (bit_depth - 1 & 7); +} -/** @brief Query if movie colorspace is supported by the HW. - * @return 0 on failure, device capabilities (not probed - * currently) on success. - */ -static int query_format(uint32_t movie_fmt) +// this macro is supposed to work on all formats supported by 3D rendering, and +// that produce "reasonable" output (i.e. no mixed up colors) +#define IMGFMT_IS_ANY_RND(x) \ + (IMGFMT_IS_BGR(x) || IMGFMT_IS_RGB(x) || IMGFMT_IS_Y(x)) + +// pixel size in bit for any IMGFMT_IS_ANY_RND(x)==true +// we assume that the actual pixel strides are always aligned on bytes +static int imgfmt_any_rnd_depth(int fmt) { - int i; - for (i = 0; i < DISPLAY_FORMAT_TABLE_ENTRIES; i++) { - if (fmt_table[i].mplayer_fmt == movie_fmt) { - /* Test conversion from Movie colorspace to - * display's target colorspace. */ - if (FAILED(IDirect3D9_CheckDeviceFormatConversion(priv->d3d_handle, - D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, - fmt_table[i].fourcc, - priv->desktop_fmt))) { - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Rejected image format: %s\n", - vo_format_name(fmt_table[i].mplayer_fmt)); + if (IMGFMT_IS_BGR(fmt)) + return IMGFMT_BGR_DEPTH(fmt); + if (IMGFMT_IS_RGB(fmt)) + return IMGFMT_RGB_DEPTH(fmt); + if (IMGFMT_IS_Y(fmt)) + return IMGFMT_Y_DEPTH(fmt); + assert(false); + return 0; +} + +static D3DFORMAT check_format(d3d_priv *priv, uint32_t movie_fmt, + bool as_texture) +{ + const char *type = as_texture ? "texture rendering" : "StretchRect"; + const struct fmt_entry *cur = &fmt_table[0]; + + // Don't try to handle weird packed texture formats (although I don't know + // if D3D9 would even accept any such format for 3D rendering; and we + // certainly don't try any tricks like matching it to RGB formats and + // applying a YUV conversion matrix) + if (as_texture && !IMGFMT_IS_ANY_RND(movie_fmt)) + return 0; + + while (cur->mplayer_fmt) { + if (cur->mplayer_fmt == movie_fmt) { + HRESULT res; + if (as_texture) { + res = IDirect3D9_CheckDeviceFormat(priv->d3d_handle, + D3DADAPTER_DEFAULT, + DEVTYPE, + priv->desktop_fmt, + D3DUSAGE_DYNAMIC | D3DUSAGE_QUERY_FILTER, + D3DRTYPE_TEXTURE, + cur->fourcc); + } else { + /* Test conversion from Movie colorspace to + * display's target colorspace. */ + res = IDirect3D9_CheckDeviceFormatConversion(priv->d3d_handle, + D3DADAPTER_DEFAULT, + DEVTYPE, + cur->fourcc, + priv->desktop_fmt); + } + if (FAILED(res)) { + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Rejected image format " + "(%s): %s\n", type, vo_format_name(cur->mplayer_fmt)); return 0; } - priv->movie_src_fmt = fmt_table[i].fourcc; - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Accepted image format: %s\n", - vo_format_name(fmt_table[i].mplayer_fmt)); - return (VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW - | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN); + mp_msg(MSGT_VO, MSGL_DBG2, "<vo_direct3d>Accepted image format " + "(%s): %s\n", type, vo_format_name(cur->mplayer_fmt)); + return cur->fourcc; } + cur++; } return 0; } +// Check whether YUV conversion with shaders can be done. +// Returns the format the individual planes should use (or 0 on failure) +static D3DFORMAT check_shader_conversion(d3d_priv *priv, uint32_t fmt) +{ + if (priv->opt_disable_shaders) + return 0; + int component_bits; + if (!mp_get_chroma_shift(fmt, NULL, NULL, &component_bits)) + return 0; + if (component_bits < 8 || component_bits > 16) + return 0; + bool is_8bit = component_bits == 8; + if (!is_8bit && priv->opt_only_8bit) + return 0; + int texfmt = IMGFMT_Y8; + if (!is_8bit) { + if (priv->opt_16bit_textures) + texfmt = IMGFMT_Y16; + else + texfmt = IMGFMT_A8Y8; + } + return check_format(priv, texfmt, true); +} + +// Return if the image format can be used. If it can, decide which rendering +// and conversion mode to use. +// If initialize is true, actually setup all variables to use the picked +// rendering mode. +static bool init_rendering_mode(d3d_priv *priv, uint32_t fmt, bool initialize) +{ + int n; + int blit_d3dfmt = check_format(priv, fmt, false); + int texture_d3dfmt = check_format(priv, fmt, true); + int shader_d3dfmt = check_shader_conversion(priv, fmt); + + if (priv->opt_disable_textures) + texture_d3dfmt = 0; + if (priv->opt_disable_shaders) + shader_d3dfmt = 0; + if (priv->opt_disable_stretchrect) + blit_d3dfmt = 0; + + if (!(blit_d3dfmt || shader_d3dfmt || texture_d3dfmt)) + return false; + + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Accepted rendering methods for " + "format='%s': StretchRect=%#x, Texture=%#x, Texture+Shader=%#x.\n", + vo_format_name(fmt), blit_d3dfmt, texture_d3dfmt, shader_d3dfmt); + + if (!initialize) + return true; + + // initialization doesn't fail beyond this point + + priv->use_shaders = false; + priv->use_textures = false; + priv->use_2ch_hack = false; + priv->movie_src_fmt = 0; + priv->pixel_shader_data = NULL; + priv->plane_count = 0; + priv->image_format = fmt; + + if (blit_d3dfmt && priv->opt_prefer_stretchrect) + texture_d3dfmt = shader_d3dfmt = 0; + + if (texture_d3dfmt) { + priv->use_textures = true; + } else if (shader_d3dfmt) { + priv->use_textures = true; + priv->use_shaders = true; + } else { + assert(!!blit_d3dfmt); + } + + if (priv->use_textures) { + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using 3D rendering.\n"); + + struct texplane *planes = &priv->planes[0]; + planes[0].shift_x = planes[0].shift_y = 0; + planes[0].clearval = 0; + + if (!priv->use_shaders) { + assert(IMGFMT_IS_ANY_RND(priv->image_format)); + priv->plane_count = 1; + planes[0].bits_per_pixel = imgfmt_any_rnd_depth(priv->image_format); + planes[0].d3d_format = texture_d3dfmt; + } else { + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using YUV shaders.\n"); + + int sx, sy, component_bits; + mp_get_chroma_shift(priv->image_format, &sx, &sy, &component_bits); + priv->plane_count = 3; + for (n = 0; n < 3; n++) { + planes[n].d3d_format = shader_d3dfmt; + planes[n].bits_per_pixel = component_bits; + if (n > 0) { + planes[n].shift_x = sx; + planes[n].shift_y = sy; + planes[n].clearval = get_chroma_clear_val(component_bits); + } + } + if (shader_d3dfmt != D3DFMT_A8L8) { + priv->pixel_shader_data = d3d_shader_yuv; + } else { + mp_msg(MSGT_VO, MSGL_WARN, "<vo_direct3d>Using YUV 2ch hack.\n"); + + priv->pixel_shader_data = d3d_shader_yuv_2ch; + priv->use_2ch_hack = true; + } + } + + for (n = 0; n < priv->plane_count; n++) { + planes[n].bytes_per_pixel = (planes[n].bits_per_pixel + 7) / 8; + } + + } else { + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Using StretchRect.\n"); + + priv->movie_src_fmt = blit_d3dfmt; + } + + update_colorspace(priv); + + return true; +} + +/** @brief Query if movie colorspace is supported by the HW. + * @return 0 on failure, device capabilities (not probed + * currently) on success. + */ +static int query_format(d3d_priv *priv, uint32_t movie_fmt) +{ + if (!init_rendering_mode(priv, movie_fmt, false)) + return 0; + + int eosd_caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW + | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN; + if (priv->eosd) + eosd_caps |= VFCAP_EOSD | VFCAP_EOSD_UNSCALED; + return eosd_caps; +} + /**************************************************************************** * * * * @@ -653,8 +1293,111 @@ static int query_format(uint32_t movie_fmt) * * ****************************************************************************/ +static void get_2ch_depth_multiplier(int depth, float *out_f1, float *out_f2) { + // How to get these values: + // The suffix i8 and i16 is for values with 8/16 bit fixed point numbers. + // The suffix f is for float, ideally in the range 0.0-1.0. + // c_i8 is a two component vector, sampled from a two channel texture. + // (c_i8.x is the low byte, c_i8.y is the high byte) + // r_f is the resulting color scalar value. + // + // c_i8 = c_f * (2^8-1) + // r_i16 = c_i8.x + c_i8.y * 2^8 + // r_f = r_i16 / (2^16-1) + // = c_f.x * (2^8-1) / (2^16-1) + c_f.y * (2^8-1) * 2^8 / (2^16-1) + // = c_f.x * ((2^8-1) / (2^16-1)) + c_f.y * (2^8 * ((2^8-1) / (2^16-1))) + // out = ((2^8-1) / (2^16-1), 2^8 * ((2^8-1) / (2^16-1))) + // The result color is r_f = dot(c_f, out). + // Same goes for other bit depth, such as 10 bit. Assuming (2^depth-1) is + // the maximum possible value at that depth, you have to scale the value + // r_i16 with it, the factor (2^16-1) in the formula above has to be + // replaced with (2^depth-1). + float factor = (float)((1 << 8) - 1) / (float)((1 << depth) - 1); + *out_f1 = factor; + *out_f2 = 256.0 * factor; +} + +static void update_colorspace(d3d_priv *priv) +{ + float coeff[3][4]; + struct mp_csp_params csp = { .colorspace = priv->colorspace }; + mp_csp_copy_equalizer_values(&csp, &priv->video_eq); + + if (priv->use_shaders) { + if (!priv->use_2ch_hack) { + csp.input_bits = priv->planes[0].bits_per_pixel; + csp.texture_bits = (csp.input_bits + 7) & ~7; + } else { + float f1, f2; + get_2ch_depth_multiplier(priv->planes[0].bits_per_pixel, &f1, &f2); + priv->d3d_depth_vector[0] = f1; + priv->d3d_depth_vector[1] = f2; + priv->d3d_depth_vector[2] = priv->d3d_depth_vector[3] = 0; + // no change + csp.input_bits = 8; + csp.texture_bits = 8; + } + mp_get_yuv2rgb_coeffs(&csp, coeff); + for (int row = 0; row < 3; row++) { + for (int col = 0; col < 4; col++) { + priv->d3d_colormatrix.m[row][col] = coeff[row][col]; + } + } + } +} +const char *options_help_text = "-vo direct3d command line help:\n" +"Example: -vo direct3d:disable-eosd:disable-textures\n" +"Options:\n" +" prefer-stretchrect\n" +" Use IDirect3DDevice9::StretchRect over other methods if possible.\n" +" disable-stretchrect\n" +" Never render the video using IDirect3DDevice9::StretchRect.\n" +" disable-textures\n" +" Never render the video using D3D texture rendering. (Rendering with\n" +" textures + shader will still be allowed. Add disable-shaders to\n" +" completely disable video rendering with textures.)\n" +" disable-shaders\n" +" Never use shaders when rendering video.\n" +" only-8bit\n" +" Never render YUV video with more than 8 bits per component.\n" +" (Using this flag will force software conversion to 8 bit.)\n" +" disable-eosd\n" +" Disable EOSD rendering for subtitles.\n" +" (Using this flag might force the insertion of the 'ass' video filter,\n" +" which will render the subtitles in software.)\n" +" disable-texture-align\n" +" Normally texture sizes are always aligned to 16. With this option\n" +" enabled, the video texture will always have exactly the same size as\n" +" the video itself.\n" +"Debug options. These might be incorrect, might be removed in the future, might\n" +"crash, might cause slow downs, etc. Contact the developers if you actually need\n" +"any of these for performance or proper operation.\n" +" force-power-of-2\n" +" Always force textures to power of 2, even if the device reports\n" +" non-power-of-2 texture sizes as supported.\n" +" texture-memory=N\n" +" Only affects operation with shaders/texturing enabled, and (E)OSD.\n" +" Values for N:\n" +" 0 default, will often use an additional shadow texture + copy\n" +" 1 use D3DPOOL_MANAGED\n" +" 2 use D3DPOOL_DEFAULT\n" +" 3 use D3DPOOL_SYSTEMMEM, but without shadow texture\n" +" swap-discard\n" +" Use D3DSWAPEFFECT_DISCARD, which might be faster.\n" +" Might be slower too, as it must (?) clear every frame.\n" +" exact-backbuffer\n" +" Always resize the backbuffer to window size.\n" +" no16bit-textures\n" +" Don't use textures with a 16 bit color channel for YUV formats that\n" +" use more than 8 bits per component. Instead, use D3DFMT_A8L8 textures\n" +" and compute the values sampled from the 2 channels back into one.\n" +" Might be slower, since the shader becomes slightly more complicated.\n" +" Might work better, if your drivers either don't support D3DFMT_L16,\n" +" or if either the texture unit or the shaders don't operate in at least\n" +" 16 bit precision.\n" +""; /** @brief libvo Callback: Preinitialize the video card. * Preinit the hardware just enough to be queried about @@ -663,147 +1406,185 @@ static int query_format(uint32_t movie_fmt) * @return 0 on success, -1 on failure */ -static int preinit(const char *arg) +static int preinit_internal(struct vo *vo, const char *arg, bool allow_shaders) { - D3DDISPLAYMODE disp_mode; - D3DCAPS9 disp_caps; - DWORD texture_caps; - DWORD dev_caps; + d3d_priv *priv = talloc_zero(vo, d3d_priv); + vo->priv = priv; - /* Set to zero all global variables. */ - priv = calloc(1, sizeof(struct global_priv)); - if (!priv) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Allocating private memory failed.\n"); - goto err_out; - } + *priv = (d3d_priv) { + .vo = vo, - /* FIXME - > Please use subopt-helper.h for this, see vo_gl.c:preinit for - > an example of how to use it. - */ + .opt_16bit_textures = true, - priv->d3d9_dll = LoadLibraryA("d3d9.dll"); - if (!priv->d3d9_dll) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Unable to dynamically load d3d9.dll\n"); - goto err_out; - } + .colorspace = MP_CSP_DETAILS_DEFAULTS, + .video_eq = { MP_CSP_EQ_CAPS_COLORMATRIX }, + }; - priv->pDirect3DCreate9 = (void *)GetProcAddress(priv->d3d9_dll, "Direct3DCreate9"); - if (!priv->pDirect3DCreate9) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Unable to find entry point of Direct3DCreate9\n"); - goto err_out; + if (!allow_shaders) { + priv->opt_disable_shaders = priv->opt_disable_textures = true; } - priv->d3d_handle = priv->pDirect3DCreate9(D3D_SDK_VERSION); - if (!priv->d3d_handle) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Initializing Direct3D failed.\n"); - goto err_out; + const opt_t subopts[] = { + {"prefer-stretchrect", OPT_ARG_BOOL, &priv->opt_prefer_stretchrect}, + {"disable-textures", OPT_ARG_BOOL, &priv->opt_disable_textures}, + {"disable-stretchrect", OPT_ARG_BOOL, &priv->opt_disable_stretchrect}, + {"disable-shaders", OPT_ARG_BOOL, &priv->opt_disable_shaders}, + {"only-8bit", OPT_ARG_BOOL, &priv->opt_only_8bit}, + {"disable-eosd", OPT_ARG_BOOL, &priv->opt_disable_eosd}, + {"force-power-of-2", OPT_ARG_BOOL, &priv->opt_force_power_of_2}, + {"disable-texture-align", OPT_ARG_BOOL, &priv->opt_disable_texture_align}, + {"texture-memory", OPT_ARG_INT, &priv->opt_texture_memory}, + {"swap-discard", OPT_ARG_BOOL, &priv->opt_swap_discard}, + {"exact-backbuffer", OPT_ARG_BOOL, &priv->opt_exact_backbuffer}, + {"16bit-textures", OPT_ARG_BOOL, &priv->opt_16bit_textures}, + {NULL} + }; + if (subopt_parse(arg, subopts) != 0) { + mp_msg(MSGT_VO, MSGL_FATAL, options_help_text); + return -1; } - if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle, - D3DADAPTER_DEFAULT, - &disp_mode))) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Reading display mode failed.\n"); + if (!priv->opt_disable_eosd) + priv->eosd = eosd_packer_create(priv); + + priv->d3d9_dll = LoadLibraryA("d3d9.dll"); + if (!priv->d3d9_dll) { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Unable to dynamically load d3d9.dll\n"); goto err_out; } - /* Store in priv->desktop_fmt the user desktop's colorspace. Usually XRGB. */ - priv->desktop_fmt = disp_mode.Format; - priv->cur_backbuf_width = disp_mode.Width; - priv->cur_backbuf_height = disp_mode.Height; - - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Setting backbuffer dimensions to (%dx%d).\n", - disp_mode.Width, disp_mode.Height); - - if (FAILED(IDirect3D9_GetDeviceCaps(priv->d3d_handle, - D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, - &disp_caps))) { - mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Reading display capabilities failed.\n"); + priv->pDirect3DCreate9 = (void *)GetProcAddress(priv->d3d9_dll, + "Direct3DCreate9"); + if (!priv->pDirect3DCreate9) { + mp_msg(MSGT_VO, MSGL_ERR, + "<vo_direct3d>Unable to find entry point of Direct3DCreate9\n"); goto err_out; } - /* Store relevant information reguarding caps of device */ - texture_caps = disp_caps.TextureCaps; - dev_caps = disp_caps.DevCaps; - priv->device_caps_power2_only = (texture_caps & D3DPTEXTURECAPS_POW2) && - !(texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL); - priv->device_caps_square_only = texture_caps & D3DPTEXTURECAPS_SQUAREONLY; - priv->device_texture_sys = dev_caps & D3DDEVCAPS_TEXTURESYSTEMMEMORY; - priv->max_texture_width = disp_caps.MaxTextureWidth; - priv->max_texture_height = disp_caps.MaxTextureHeight; - - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>device_caps_power2_only %d, device_caps_square_only %d\n" - "<vo_direct3d>device_texture_sys %d\n" - "<vo_direct3d>max_texture_width %d, max_texture_height %d\n", - priv->device_caps_power2_only, priv->device_caps_square_only, - priv->device_texture_sys, priv->max_texture_width, - priv->max_texture_height); + if (!init_d3d(priv)) + goto err_out; /* w32_common framework call. Configures window on the screen, gets * fullscreen dimensions and does other useful stuff. */ + global_vo = vo; if (!vo_w32_init()) { - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Configuring onscreen window failed.\n"); + mp_msg(MSGT_VO, MSGL_V, + "<vo_direct3d>Configuring onscreen window failed.\n"); goto err_out; } return 0; err_out: - uninit(); + uninit(vo); return -1; } +static int preinit_standard(struct vo *vo, const char *arg) +{ + return preinit_internal(vo, arg, false); +} +static int preinit_shaders(struct vo *vo, const char *arg) +{ + return preinit_internal(vo, arg, true); +} /** @brief libvo Callback: Handle control requests. * @return VO_TRUE on success, VO_NOTIMPL when not implemented */ -static int control(uint32_t request, void *data) +static int control(struct vo *vo, uint32_t request, void *data) { + d3d_priv *priv = vo->priv; + switch (request) { case VOCTRL_QUERY_FORMAT: - return query_format(*(uint32_t*) data); - case VOCTRL_GET_IMAGE: /* Direct Rendering. Not implemented yet. */ + return query_format(priv, *(uint32_t*) data); + case VOCTRL_GET_IMAGE: mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Direct Rendering request. Not implemented yet.\n"); return VO_NOTIMPL; case VOCTRL_DRAW_IMAGE: - return render_d3d_frame(data); + return d3d_upload_and_render_frame(priv, data); case VOCTRL_FULLSCREEN: vo_w32_fullscreen(); - resize_d3d(); + resize_d3d(priv); return VO_TRUE; case VOCTRL_RESET: return VO_NOTIMPL; - case VOCTRL_PAUSE: - priv->is_paused = 1; + case VOCTRL_REDRAW_FRAME: + priv->is_clear_needed = 1; + d3d_draw_frame(priv); return VO_TRUE; - case VOCTRL_RESUME: - priv->is_paused = 0; + case VOCTRL_SET_YUV_COLORSPACE: + priv->colorspace = *(struct mp_csp_details *)data; + update_colorspace(priv); + vo->want_redraw = true; return VO_TRUE; - case VOCTRL_SET_EQUALIZER: - return VO_NOTIMPL; - case VOCTRL_GET_EQUALIZER: - return VO_NOTIMPL; + case VOCTRL_GET_YUV_COLORSPACE: + if (!priv->use_shaders) + break; // no idea what the heck D3D YUV uses + *(struct mp_csp_details *)data = priv->colorspace; + return VO_TRUE; + case VOCTRL_SET_EQUALIZER: { + if (!priv->use_shaders) + break; + struct voctrl_set_equalizer_args *args = data; + if (mp_csp_equalizer_set(&priv->video_eq, args->name, args->value) < 0) + return VO_NOTIMPL; + update_colorspace(priv); + vo->want_redraw = true; + return VO_TRUE; + } + case VOCTRL_GET_EQUALIZER: { + if (!priv->use_shaders) + break; + struct voctrl_get_equalizer_args *args = data; + return mp_csp_equalizer_get(&priv->video_eq, args->name, args->valueptr) + >= 0 ? VO_TRUE : VO_NOTIMPL; + } case VOCTRL_ONTOP: vo_w32_ontop(); return VO_TRUE; case VOCTRL_BORDER: vo_w32_border(); - resize_d3d(); return VO_TRUE; case VOCTRL_UPDATE_SCREENINFO: w32_update_xinerama_info(); return VO_TRUE; case VOCTRL_SET_PANSCAN: - calc_fs_rect(); + calc_fs_rect(priv); return VO_TRUE; case VOCTRL_GET_PANSCAN: return VO_TRUE; + case VOCTRL_DRAW_EOSD: + if (!data) + return VO_FALSE; + assert(priv->eosd); + generate_eosd(priv, data); + draw_eosd(priv); + return VO_TRUE; + case VOCTRL_GET_EOSD_RES: { + assert(priv->eosd); + struct mp_eosd_res *r = data; + r->w = vo->dwidth; + r->h = vo->dheight; + r->ml = r->mr = priv->border_x; + r->mt = r->mb = priv->border_y; + return VO_TRUE; } - return VO_FALSE; + case VOCTRL_SCREENSHOT: { + struct voctrl_screenshot_args *args = data; + if (args->full_window) + args->out_image = get_window_screenshot(priv); + else + args->out_image = get_screenshot(priv); + return !!args->out_image; + } + } + return VO_NOTIMPL; } /** @brief libvo Callback: Configre the Direct3D adapter. @@ -812,40 +1593,39 @@ static int control(uint32_t request, void *data) * @param d_width Screen (destination) width * @param d_height Screen (destination) height * @param options Options bitmap - * @param title Window title * @param format Movie colorspace format (using MPlayer's * defines, e.g. IMGFMT_YUY2) * @return 0 on success, VO_ERROR on failure */ -static int config(uint32_t width, uint32_t height, uint32_t d_width, - uint32_t d_height, uint32_t options, char *title, +static int config(struct vo *vo, uint32_t width, uint32_t height, + uint32_t d_width, uint32_t d_height, uint32_t options, uint32_t format) { - - priv->src_width = width; - priv->src_height = height; + d3d_priv *priv = vo->priv; /* w32_common framework call. Creates window on the screen with * the given coordinates. */ if (!vo_w32_config(d_width, d_height, options)) { - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Creating onscreen window failed.\n"); + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Creating window failed.\n"); return VO_ERROR; } - /* "config" may be called several times, so if this is not the first - * call, we should destroy Direct3D adapter and surfaces before - * calling configure_d3d, which will create them again. - */ + priv->src_d_width = d_width; + priv->src_d_height = d_height; - destroy_d3d_surfaces(); + if ((priv->image_format != format) + || (priv->src_width != width) + || (priv->src_height != height)) + { + d3d_destroy_video_objects(priv); - /* Destroy the D3D Device */ - if (priv->d3d_device) - IDirect3DDevice9_Release(priv->d3d_device); - priv->d3d_device = NULL; + priv->src_width = width; + priv->src_height = height; + init_rendering_mode(priv, format, true); + } - if (!configure_d3d()) + if (!resize_d3d(priv)) return VO_ERROR; return 0; /* Success */ @@ -854,42 +1634,54 @@ static int config(uint32_t width, uint32_t height, uint32_t d_width, /** @brief libvo Callback: Flip next already drawn frame on the * screen. */ -static void flip_page(void) +static void flip_page(struct vo *vo) { - RECT rect = {0, 0, vo_dwidth, vo_dheight}; + d3d_priv *priv = vo->priv; + + if (priv->d3d_device && priv->d3d_in_scene) { + if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>EndScene failed.\n"); + } + } + priv->d3d_in_scene = false; + + RECT rect = {0, 0, vo->dwidth, vo->dheight}; if (!priv->d3d_device || FAILED(IDirect3DDevice9_Present(priv->d3d_device, &rect, 0, 0, 0))) { mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Trying to reinitialize uncooperative video adapter.\n"); - if (!reconfigure_d3d()) { + if (!reconfigure_d3d(priv)) { mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Reinitialization failed.\n"); return; + } else { + mp_msg(MSGT_VO, MSGL_V, + "<vo_direct3d>Video adapter reinitialized.\n"); } - else - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Video adapter reinitialized.\n"); } } /** @brief libvo Callback: Uninitializes all pointers and closes * all D3D related stuff, */ -static void uninit(void) +static void uninit(struct vo *vo) { + d3d_priv *priv = vo->priv; + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>uninit called.\n"); - uninit_d3d(); + uninit_d3d(priv); vo_w32_uninit(); /* w32_common framework call */ if (priv->d3d9_dll) FreeLibrary(priv->d3d9_dll); priv->d3d9_dll = NULL; - free(priv); - priv = NULL; } /** @brief libvo Callback: Handles video window events. */ -static void check_events(void) +static void check_events(struct vo *vo) { + d3d_priv *priv = vo->priv; + int flags; /* w32_common framework call. Handles video window events. * Updates global libvo's vo_dwidth/vo_dheight upon resize @@ -897,25 +1689,50 @@ static void check_events(void) */ flags = vo_w32_check_events(); if (flags & VO_EVENT_RESIZE) - resize_d3d(); + resize_d3d(priv); - if ((flags & VO_EVENT_EXPOSE) && priv->is_paused) - flip_page(); + if (flags & VO_EVENT_EXPOSE) + vo->want_redraw = true; +} + +static int draw_slice_textures(d3d_priv *priv, uint8_t *src[], int stride[], + int w, int h, int x, int y) +{ + if (!d3d_lock_video_textures(priv)) + return VO_FALSE; + + for (int n = 0; n < priv->plane_count; n++) { + struct texplane *plane = &priv->planes[n]; + + int dst_stride = plane->locked_rect.Pitch; + uint8_t *pdst = (uint8_t*)plane->locked_rect.pBits + + (y >> plane->shift_y) * dst_stride + + (x >> plane->shift_x) * plane->bytes_per_pixel; + + memcpy_pic(pdst, src[n], (w >> plane->shift_x) * plane->bytes_per_pixel, + h >> plane->shift_y, dst_stride, stride[n]); + } + + return 0; } /** @brief libvo Callback: Draw slice * @return 0 on success */ -static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y ) +static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, + int x, int y) { + d3d_priv *priv = vo->priv; + + if (!priv->d3d_device) + return 0; + char *my_src; /**< Pointer to the source image */ char *dst; /**< Pointer to the destination image */ int uv_stride; /**< Stride of the U/V planes */ - /* If the D3D device is uncooperative (not initialized), return success. - The device will be probed for reinitialization in the next flip_page() */ - if (!priv->d3d_device) - return 0; + if (priv->use_textures) + return draw_slice_textures(priv, src, stride, w, h, x, y); /* Lock the offscreen surface if it's not already locked. */ if (!priv->locked_rect.pBits) { @@ -964,12 +1781,175 @@ static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y ) return 0; /* Success */ } -/** @brief libvo Callback: Unused function - */ -static int draw_frame(uint8_t *src[]) +static bool get_screenshot_from_surface(d3d_priv *priv, mp_image_t *image) { - mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>draw_frame called.\n"); - return VO_FALSE; + if (!priv->locked_rect.pBits) { + if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface, + &priv->locked_rect, NULL, 0))) { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Surface lock failed.\n"); + return false; + } + } + + if (image->flags & MP_IMGFLAG_PLANAR) { + char *src; + int w = priv->src_width; + int h = priv->src_height; + int swapped = priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2'); + int plane1 = swapped ? 2 : 1; + int plane2 = swapped ? 1 : 2; + + int uv_stride = priv->locked_rect.Pitch / 2; + + /* Copy Y */ + src = priv->locked_rect.pBits; + memcpy_pic(image->planes[0], src, w, h, priv->locked_rect.Pitch, + image->stride[0]); + + w /= 2; + h /= 2; + + /* Copy U */ + src = priv->locked_rect.pBits; + src = src + priv->locked_rect.Pitch * priv->src_height; + + memcpy_pic(image->planes[plane1], src, w, h, uv_stride, + image->stride[1]); + + /* Copy V */ + src = priv->locked_rect.pBits; + src = src + priv->locked_rect.Pitch * priv->src_height + + uv_stride * (priv->src_height / 2); + + memcpy_pic(image->planes[plane2], src, w, h, uv_stride, + image->stride[2]); + } else { + // packed YUV or RGB + memcpy_pic(image->planes[0], priv->locked_rect.pBits, image->stride[0], + image->height, priv->locked_rect.Pitch, image->stride[0]); + } + + d3d_unlock_video_objects(priv); + return true; +} + +static bool get_screenshot_from_texture(d3d_priv *priv, mp_image_t *image) +{ + if (!d3d_lock_video_textures(priv)) { + d3d_unlock_video_objects(priv); + return false; + } + + assert(image->num_planes == priv->plane_count); + + for (int n = 0; n < priv->plane_count; n++) { + struct texplane *plane = &priv->planes[n]; + + int width = priv->src_width >> plane->shift_x; + int height = priv->src_height >> plane->shift_y; + + memcpy_pic(image->planes[n], plane->locked_rect.pBits, + width * plane->bytes_per_pixel, height, + image->stride[n], plane->locked_rect.Pitch); + } + + return true; +} + +static mp_image_t *get_screenshot(d3d_priv *priv) +{ + if (!priv->d3d_device) + return NULL; + + mp_image_t *image = alloc_mpi(priv->src_width, priv->src_height, + priv->image_format); + + bool res; + + if (priv->use_textures) + res = get_screenshot_from_texture(priv, image); + else + res = get_screenshot_from_surface(priv, image); + + if (!res) { + free_mp_image(image); + return NULL; + } + + image->w = priv->src_d_width; + image->h = priv->src_d_height; + + return image; +} + +static mp_image_t *get_window_screenshot(d3d_priv *priv) +{ + D3DDISPLAYMODE mode; + mp_image_t *image = NULL; + RECT window_rc; + RECT screen_rc; + RECT visible; + POINT pt; + D3DLOCKED_RECT locked_rect; + int width, height; + + if (FAILED(IDirect3DDevice9_GetDisplayMode(priv->d3d_device, 0, &mode))) { + mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>GetDisplayMode failed.\n"); + goto error_exit; + } + + IDirect3DSurface9 *surface = NULL; + if (FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(priv->d3d_device, + mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, + NULL))) + { + mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>Couldn't create surface.\n"); + goto error_exit; + } + + if (FAILED(IDirect3DDevice9_GetFrontBufferData(priv->d3d_device, 0, + surface))) + { + mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>Couldn't copy frontbuffer.\n"); + goto error_exit; + } + + GetClientRect(vo_w32_window, &window_rc); + pt = (POINT) { 0, 0 }; + ClientToScreen(vo_w32_window, &pt); + window_rc.left = pt.x; + window_rc.top = pt.y; + window_rc.right += window_rc.left; + window_rc.bottom += window_rc.top; + + screen_rc = (RECT) { 0, 0, mode.Width, mode.Height }; + + if (!IntersectRect(&visible, &screen_rc, &window_rc)) + goto error_exit; + width = visible.right - visible.left; + height = visible.bottom - visible.top; + if (width < 1 || height < 1) + goto error_exit; + + image = alloc_mpi(width, height, IMGFMT_BGR32); + + IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0); + + memcpy_pic(image->planes[0], (char*)locked_rect.pBits + visible.top * + locked_rect.Pitch + visible.left * 4, width * 4, height, + image->stride[0], locked_rect.Pitch); + + IDirect3DSurface9_UnlockRect(surface); + IDirect3DSurface9_Release(surface); + + return image; + +error_exit: + if (image) + free_mp_image(image); + if (surface) + IDirect3DSurface9_Release(surface); + return NULL; } /** @brief Maps MPlayer alpha to D3D @@ -996,120 +1976,293 @@ static void vo_draw_alpha_l8a8(int w, int h, unsigned char* src, } } +struct draw_osd_closure { + d3d_priv *priv; + D3DLOCKED_RECT locked_rect; +}; + /** @brief Callback function to render the OSD to the texture */ -static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src, - unsigned char *srca, int stride) +static void draw_alpha(void *pctx, int x0, int y0, int w, int h, + unsigned char *src, unsigned char *srca, int stride) { - D3DLOCKED_RECT locked_rect; /**< Offscreen surface we lock in order - to copy MPlayer's frame inside it.*/ - - if (FAILED(IDirect3DTexture9_LockRect(priv->d3d_texture_system, 0, - &locked_rect, NULL, 0))) { - mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>OSD texture lock failed.\n"); - return; - } + struct draw_osd_closure *ctx = pctx; + D3DLOCKED_RECT locked_rect = ctx->locked_rect; vo_draw_alpha_l8a8(w, h, src, srca, stride, - (unsigned char *)locked_rect.pBits + locked_rect.Pitch*y0 + 2*x0, locked_rect.Pitch); + (unsigned char *)locked_rect.pBits + locked_rect.Pitch * y0 + 2 * x0, + locked_rect.Pitch); - /* this unlock is used for both slice_draw path and D3DRenderFrame path */ - if (FAILED(IDirect3DTexture9_UnlockRect(priv->d3d_texture_system, 0))) { - mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>OSD texture unlock failed.\n"); - return; - } - - priv->is_osd_populated = 1; + ctx->priv->is_osd_populated = 1; } /** @brief libvo Callback: Draw OSD/Subtitles, */ -static void draw_osd(void) +static void draw_osd(struct vo *vo, struct osd_state *osd) { - // we can not render OSD if we lost the device e.g. because it was uncooperative + d3d_priv *priv = vo->priv; + if (!priv->d3d_device) return; if (vo_osd_changed(0)) { - D3DLOCKED_RECT locked_rect; /**< Offscreen surface we lock in order - to copy MPlayer's frame inside it.*/ + struct draw_osd_closure ctx = { priv }; /* clear the OSD */ - if (FAILED(IDirect3DTexture9_LockRect(priv->d3d_texture_system, 0, - &locked_rect, NULL, 0))) { + if (FAILED(IDirect3DTexture9_LockRect(priv->texture_osd.system, 0, + &ctx.locked_rect, NULL, 0))) { mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture lock failed.\n"); return; } /* clear the whole texture to avoid issues due to interpolation */ - memset(locked_rect.pBits, 0, locked_rect.Pitch * priv->osd_texture_height); - - /* this unlock is used for both slice_draw path and D3DRenderFrame path */ - if (FAILED(IDirect3DTexture9_UnlockRect(priv->d3d_texture_system, 0))) { - mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture unlock failed.\n"); - return; - } + memset(ctx.locked_rect.pBits, 0, + ctx.locked_rect.Pitch * priv->texture_osd.tex_h); priv->is_osd_populated = 0; /* required for if subs are in the boarder region */ priv->is_clear_needed = 1; - vo_draw_text_ext(priv->osd_width, priv->osd_height, priv->border_x, priv->border_y, - priv->border_x, priv->border_y, priv->src_width, priv->src_height, draw_alpha); + osd_draw_text_ext(osd, priv->texture_osd.w, priv->texture_osd.h, + priv->border_x, priv->border_y, + priv->border_x, priv->border_y, + priv->src_width, priv->src_height, draw_alpha, &ctx); - if (!priv->device_texture_sys) - { - /* only DMA to the shadow if its required */ - if (FAILED(IDirect3DDevice9_UpdateTexture(priv->d3d_device, - (IDirect3DBaseTexture9 *)priv->d3d_texture_system, - (IDirect3DBaseTexture9 *)priv->d3d_texture_osd))) { - mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>OSD texture transfer failed.\n"); - return; - } + if (FAILED(IDirect3DTexture9_UnlockRect(priv->texture_osd.system, 0))) { + mp_msg(MSGT_VO,MSGL_ERR, + "<vo_direct3d>OSD texture unlock failed.\n"); + return; } - } - /* update OSD */ + d3dtex_update(priv, &priv->texture_osd); + } if (priv->is_osd_populated) { - - struct_vertex osd_quad_vb[] = { - {-1.0f, 1.0f, 0.0f, 0, 0 }, - { 1.0f, 1.0f, 0.0f, 1, 0 }, - {-1.0f,-1.0f, 0.0f, 0, 1 }, - { 1.0f,-1.0f, 0.0f, 1, 1 } + float tw = (float)priv->texture_osd.w / priv->texture_osd.tex_w; + float th = (float)priv->texture_osd.h / priv->texture_osd.tex_h; + + vertex_osd osd_quad_vb[] = { + { 0, 0, 0.0f, 0, 0 }, + { vo->dwidth, 0, 0.0f, tw, 0 }, + { 0, vo->dheight, 0.0f, 0, th }, + { vo->dwidth, vo->dheight, 0.0f, tw, th } }; - /* calculate the texture coordinates */ - osd_quad_vb[1].tu = - osd_quad_vb[3].tu = (float)priv->osd_width / priv->osd_texture_width; - osd_quad_vb[2].tv = - osd_quad_vb[3].tv = (float)priv->osd_height / priv->osd_texture_height; + d3d_begin_scene(priv); - if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) { - mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>BeginScene failed.\n"); - return; - } - - /* turn on alpha test */ - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHABLENDENABLE, TRUE); - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHATESTENABLE, TRUE); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_ALPHABLENDENABLE, TRUE); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_ALPHATESTENABLE, TRUE); - /* need to use a texture here (done here as we may be able to texture from system memory) */ IDirect3DDevice9_SetTexture(priv->d3d_device, 0, - (IDirect3DBaseTexture9 *)(priv->device_texture_sys - ? priv->d3d_texture_system : priv->d3d_texture_osd)); + d3dtex_get_render_texture(priv, &priv->texture_osd)); - IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_MY_VERTEX); - IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP, 2, osd_quad_vb, sizeof(struct_vertex)); + IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_OSD_VERTEX); + IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP, + 2, osd_quad_vb, sizeof(vertex_osd)); - /* turn off alpha test */ - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHATESTENABLE, FALSE); - IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHABLENDENABLE, FALSE); + IDirect3DDevice9_SetTexture(priv->d3d_device, 0, NULL); - if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) { - mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>EndScene failed.\n"); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_ALPHATESTENABLE, FALSE); + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_ALPHABLENDENABLE, FALSE); + } +} + +static void d3d_realloc_eosd_texture(d3d_priv *priv) +{ + int new_w = priv->eosd->surface.w; + int new_h = priv->eosd->surface.h; + + d3d_fix_texture_size(priv, &new_w, &new_h); + + if (new_w == priv->texture_eosd.tex_w && new_h == priv->texture_eosd.tex_h) + return; + + mp_msg(MSGT_VO, MSGL_DBG2, "<vo_direct3d>reallocate EOSD surface.\n"); + + // fortunately, we don't need to keep the old image data + // we can always free it + d3dtex_release(priv, &priv->texture_eosd); + + d3dtex_allocate(priv, &priv->texture_eosd, D3DFMT_A8, new_w, new_h); +} + +static D3DCOLOR ass_to_d3d_color(uint32_t color) +{ + uint32_t r = (color >> 24) & 0xff; + uint32_t g = (color >> 16) & 0xff; + uint32_t b = (color >> 8) & 0xff; + uint32_t a = 0xff - (color & 0xff); + return D3DCOLOR_ARGB(a, r, g, b); +} + +static void generate_eosd(d3d_priv *priv, mp_eosd_images_t *imgs) +{ + if (!priv->d3d_device) + return; + + bool need_reposition, need_upload, need_resize; + eosd_packer_generate(priv->eosd, imgs, &need_reposition, &need_upload, + &need_resize); + + if (!need_reposition) + return; + // even if the texture size is unchanged, the texture might have been free'd + d3d_realloc_eosd_texture(priv); + if (!priv->texture_eosd.system) + return; // failed to allocate + + // reupload all EOSD images + + // we need 2 primitives per quad which makes 6 vertices (we could reduce the + // number of vertices by using an indexed vertex array, but it's probably + // not worth doing) + priv->eosd_vb = talloc_realloc_size(priv->eosd, priv->eosd_vb, + priv->eosd->targets_count + * sizeof(vertex_eosd) * 6); + + if (need_upload) { + struct eosd_rect rc; + eosd_packer_calculate_source_bb(priv->eosd, &rc); + RECT dirty_rc = { rc.x0, rc.y0, rc.x1, rc.y1 }; + + D3DLOCKED_RECT locked_rect; + + if (FAILED(IDirect3DTexture9_LockRect(priv->texture_eosd.system, 0, + &locked_rect, &dirty_rc, 0))) + { + mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>EOSD texture lock failed.\n"); return; } + + //memset(locked_rect.pBits, 0, locked_rect.Pitch * priv->texture_eosd.tex_h); + + for (int i = 0; i < priv->eosd->targets_count; i++) { + struct eosd_target *target = &priv->eosd->targets[i]; + ASS_Image *img = target->ass_img; + char *src = img->bitmap; + char *dst = (char*)locked_rect.pBits + target->source.x0 + + locked_rect.Pitch * target->source.y0; + for (int y = 0; y < img->h; y++) { + memcpy(dst, src, img->w); + src += img->stride; + dst += locked_rect.Pitch; + } + } + + if (FAILED(IDirect3DTexture9_UnlockRect(priv->texture_eosd.system, 0))) { + mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>EOSD texture unlock failed.\n"); + return; + } + + d3dtex_update(priv, &priv->texture_eosd); + } + + float eosd_w = priv->texture_eosd.tex_w; + float eosd_h = priv->texture_eosd.tex_h; + + for (int i = 0; i < priv->eosd->targets_count; i++) { + struct eosd_target *target = &priv->eosd->targets[i]; + + D3DCOLOR color = ass_to_d3d_color(target->ass_img->color); + + float x0 = target->dest.x0; + float y0 = target->dest.y0; + float x1 = target->dest.x1; + float y1 = target->dest.y1; + float tx0 = target->source.x0 / eosd_w; + float ty0 = target->source.y0 / eosd_h; + float tx1 = target->source.x1 / eosd_w; + float ty1 = target->source.y1 / eosd_h; + + vertex_eosd *v = &priv->eosd_vb[i*6]; + v[0] = (vertex_eosd) { x0, y0, 0, color, tx0, ty0 }; + v[1] = (vertex_eosd) { x1, y0, 0, color, tx1, ty0 }; + v[2] = (vertex_eosd) { x0, y1, 0, color, tx0, ty1 }; + v[3] = (vertex_eosd) { x1, y1, 0, color, tx1, ty1 }; + v[4] = v[2]; + v[5] = v[1]; } } + +static void draw_eosd(d3d_priv *priv) +{ + if (!priv->d3d_device) + return; + + if (!priv->eosd->targets_count) + return; + + d3d_begin_scene(priv); + + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_ALPHABLENDENABLE, TRUE); + + IDirect3DDevice9_SetTexture(priv->d3d_device, 0, + d3dtex_get_render_texture(priv, &priv->texture_eosd)); + + // do not use the color value from the A8 texture, because that is black + IDirect3DDevice9_SetRenderState(priv->d3d_device,D3DRS_TEXTUREFACTOR, + 0xFFFFFFFF); + IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0, + D3DTSS_COLORARG1, D3DTA_TFACTOR); + + IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0, + D3DTSS_ALPHAOP, D3DTOP_MODULATE); + + IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_EOSD_VERTEX); + IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLELIST, + priv->eosd->targets_count * 2, + priv->eosd_vb, sizeof(vertex_eosd)); + + IDirect3DDevice9_SetTextureStageState(priv->d3d_device,0, + D3DTSS_COLORARG1, D3DTA_TEXTURE); + IDirect3DDevice9_SetTextureStageState(priv->d3d_device, 0, + D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + + IDirect3DDevice9_SetTexture(priv->d3d_device, 0, NULL); + + IDirect3DDevice9_SetRenderState(priv->d3d_device, + D3DRS_ALPHABLENDENABLE, FALSE); +} + +#define AUTHOR "Georgi Petrov (gogothebee) <gogothebee@gmail.com> and others" + +const struct vo_driver video_out_direct3d = { + .is_new = true, + .info = &(const vo_info_t) { + "Direct3D 9 Renderer", + "direct3d", + AUTHOR, + "" + }, + .preinit = preinit_standard, + .config = config, + .control = control, + .draw_slice = draw_slice, + .draw_osd = draw_osd, + .flip_page = flip_page, + .check_events = check_events, + .uninit = uninit, +}; + +const struct vo_driver video_out_direct3d_shaders = { + .is_new = true, + .info = &(const vo_info_t) { + "Direct3D 9 Renderer (using shaders for YUV conversion)", + "direct3d_shaders", + AUTHOR, + "" + }, + .preinit = preinit_shaders, + .config = config, + .control = control, + .draw_slice = draw_slice, + .draw_osd = draw_osd, + .flip_page = flip_page, + .check_events = check_events, + .uninit = uninit, +}; diff --git a/libvo/vo_directfb2.c b/libvo/vo_directfb2.c index d21c9e37c6..b50adba614 100644 --- a/libvo/vo_directfb2.c +++ b/libvo/vo_directfb2.c @@ -1279,7 +1279,7 @@ static uint32_t put_image(mp_image_t *mpi){ p = FFMIN(mpi->w, pitch); - src = mpi->planes[0]+mpi->y*mpi->stride[0]+mpi->x; + src = mpi->planes[0]; for (i=0;i<mpi->h;i++) { fast_memcpy(dst+i*pitch,src+i*mpi->stride[0],p); @@ -1290,14 +1290,14 @@ static uint32_t put_image(mp_image_t *mpi){ dst += pitch*height; p = p/2; - src = mpi->planes[2]+mpi->y*mpi->stride[2]+mpi->x/2; + src = mpi->planes[2]; for (i=0;i<mpi->h/2;i++) { fast_memcpy(dst+i*pitch/2,src+i*mpi->stride[2],p); } dst += pitch*height/4; - src = mpi->planes[1]+mpi->y*mpi->stride[1]+mpi->x/2; + src = mpi->planes[1]; for (i=0;i<mpi->h/2;i++) { fast_memcpy(dst+i*pitch/2,src+i*mpi->stride[1],p); @@ -1307,14 +1307,14 @@ static uint32_t put_image(mp_image_t *mpi){ dst += pitch*height; p = p/2; - src = mpi->planes[1]+mpi->y*mpi->stride[1]+mpi->x/2; + src = mpi->planes[1]; for (i=0;i<mpi->h/2;i++) { fast_memcpy(dst+i*pitch/2,src+i*mpi->stride[1],p); } dst += pitch*height/4; - src = mpi->planes[2]+mpi->y*mpi->stride[2]+mpi->x/2; + src = mpi->planes[2]; for (i=0;i<mpi->h/2;i++) { fast_memcpy(dst+i*pitch/2,src+i*mpi->stride[2],p); @@ -1356,11 +1356,11 @@ static uint32_t put_image(mp_image_t *mpi){ if (frame) { DFBCHECK (frame->Lock(frame,DSLF_WRITE,(void *)&dst,&pitch)); framelocked = 1; - mem2agpcpy_pic(dst,mpi->planes[0] + mpi->y * mpi->stride[0] + mpi->x * (mpi->bpp >> 3) ,mpi->w * (mpi->bpp >> 3),mpi->h,pitch,mpi->stride[0]); + mem2agpcpy_pic(dst,mpi->planes[0] ,mpi->w * (mpi->bpp >> 3),mpi->h,pitch,mpi->stride[0]); } else { DFBCHECK (primary->Lock(primary,DSLF_WRITE,(void *)&dst,&pitch)); primarylocked = 1; - mem2agpcpy_pic(dst + yoffset * pitch + xoffset * (mpi->bpp >> 3),mpi->planes[0] + mpi->y * mpi->stride[0] + mpi->x * (mpi->bpp >> 3) ,mpi->w * (mpi->bpp >> 3),mpi->h,pitch,mpi->stride[0]); + mem2agpcpy_pic(dst + yoffset * pitch + xoffset * (mpi->bpp >> 3),mpi->planes[0] ,mpi->w * (mpi->bpp >> 3),mpi->h,pitch,mpi->stride[0]); }; unlock(); diff --git a/libvo/vo_directx.c b/libvo/vo_directx.c index d504e23567..f4ab2a7f7f 100644 --- a/libvo/vo_directx.c +++ b/libvo/vo_directx.c @@ -36,31 +36,31 @@ #include "sub/sub.h" #include "w32_common.h" -static LPDIRECTDRAWCOLORCONTROL g_cc = NULL; //color control interface -static LPDIRECTDRAW7 g_lpdd = NULL; //DirectDraw Object -static LPDIRECTDRAWSURFACE7 g_lpddsPrimary = NULL; //Primary Surface: viewport through the Desktop -static LPDIRECTDRAWSURFACE7 g_lpddsOverlay = NULL; //Overlay Surface -static LPDIRECTDRAWSURFACE7 g_lpddsBack = NULL; //Back surface -static LPDIRECTDRAWCLIPPER g_lpddclipper; //clipper object, can only be used without overlay -static DDSURFACEDESC2 ddsdsf; //surface descripiton needed for locking -static HINSTANCE hddraw_dll; //handle to ddraw.dll -static RECT rd; //rect of our stretched image -static RECT rs; //rect of our source image -static HBRUSH colorbrush = NULL; // Handle to colorkey brush -static HBRUSH blackbrush = NULL; // Handle to black brush +static LPDIRECTDRAWCOLORCONTROL g_cc = NULL; //color control interface +static LPDIRECTDRAW7 g_lpdd = NULL; //DirectDraw Object +static LPDIRECTDRAWSURFACE7 g_lpddsPrimary = NULL; //Primary Surface: viewport through the Desktop +static LPDIRECTDRAWSURFACE7 g_lpddsOverlay = NULL; //Overlay Surface +static LPDIRECTDRAWSURFACE7 g_lpddsBack = NULL; //Back surface +static LPDIRECTDRAWCLIPPER g_lpddclipper; //clipper object, can only be used without overlay +static DDSURFACEDESC2 ddsdsf; //surface descripiton needed for locking +static HINSTANCE hddraw_dll; //handle to ddraw.dll +static RECT rd; //rect of our stretched image +static RECT rs; //rect of our source image +static HBRUSH colorbrush = NULL; // Handle to colorkey brush +static HBRUSH blackbrush = NULL; // Handle to black brush static uint32_t image_width, image_height; //image width and height -static uint8_t *image=NULL; //image data -static void* tmp_image = NULL; -static uint32_t image_format=0; //image format +static uint8_t *image = NULL; //image data +static void *tmp_image = NULL; +static uint32_t image_format = 0; //image format static uint32_t primary_image_format; -static uint32_t vm_height=0; -static uint32_t vm_width=0; -static uint32_t vm_bpp=0; +static uint32_t vm_height = 0; +static uint32_t vm_width = 0; +static uint32_t vm_bpp = 0; static uint32_t dstride; //surface stride static uint32_t nooverlay = 0; //NonOverlay mode -static DWORD destcolorkey; //colorkey for our surface -static COLORREF windowcolor = RGB(0,0,16); //windowcolor == colorkey -static int adapter_count=0; +static DWORD destcolorkey; //colorkey for our surface +static COLORREF windowcolor = RGB(0, 0, 16); //windowcolor == colorkey +static int adapter_count = 0; static GUID selected_guid; static GUID *selected_guid_ptr = NULL; @@ -70,296 +70,291 @@ static GUID *selected_guid_ptr = NULL; * the linking stage. *****************************************************************************/ #define IID_IDirectDraw7 MP_IID_IDirectDraw7 -static const GUID MP_IID_IDirectDraw7 = -{ - 0x15e65ec0,0x3b9c,0x11d2,{0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b} +static const GUID MP_IID_IDirectDraw7 = { + 0x15e65ec0, 0x3b9c, 0x11d2, { 0xb9, 0x2f, 0x00, 0x60, 0x97, 0x97, 0xea, 0x5b } }; #define IID_IDirectDrawColorControl MP_IID_IDirectDrawColorControl -static const GUID MP_IID_IDirectDrawColorControl = -{ - 0x4b9f0ee0,0x0d7e,0x11d0,{0x9b,0x06,0x00,0xa0,0xc9,0x03,0xa3,0xb8} +static const GUID MP_IID_IDirectDrawColorControl = { + 0x4b9f0ee0, 0x0d7e, 0x11d0, { 0x9b, 0x06, 0x00, 0xa0, 0xc9, 0x03, 0xa3, 0xb8 } }; - -typedef struct directx_fourcc_caps -{ - char* img_format_name; //human readable name - uint32_t img_format; //as MPlayer image format - uint32_t drv_caps; //what hw supports with this format - DDPIXELFORMAT g_ddpfOverlay; //as Directx Sourface description -} directx_fourcc_caps; - - -static directx_fourcc_caps g_ddpf[] = -{ - {"YV12 ",IMGFMT_YV12 ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','V','1','2'),0,0,0,0,0}}, - {"I420 ",IMGFMT_I420 ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('I','4','2','0'),0,0,0,0,0}}, //yv12 with swapped uv - {"IYUV ",IMGFMT_IYUV ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('I','Y','U','V'),0,0,0,0,0}}, //same as i420 - {"YVU9 ",IMGFMT_YVU9 ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','V','U','9'),0,0,0,0,0}}, - {"YUY2 ",IMGFMT_YUY2 ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','U','Y','2'),0,0,0,0,0}}, - {"UYVY ",IMGFMT_UYVY ,0,{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('U','Y','V','Y'),0,0,0,0,0}}, - {"BGR8 ",IMGFMT_BGR8 ,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 8, 0x00000000, 0x00000000, 0x00000000, 0}}, - {"RGB15",IMGFMT_RGB15,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000001F, 0x000003E0, 0x00007C00, 0}}, //RGB 5:5:5 - {"BGR15",IMGFMT_BGR15,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x00007C00, 0x000003E0, 0x0000001F, 0}}, - {"RGB16",IMGFMT_RGB16,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000001F, 0x000007E0, 0x0000F800, 0}}, //RGB 5:6:5 - {"BGR16",IMGFMT_BGR16,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000F800, 0x000007E0, 0x0000001F, 0}}, - {"RGB24",IMGFMT_RGB24,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0}}, - {"BGR24",IMGFMT_BGR24,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0}}, - {"RGB32",IMGFMT_RGB32,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0}}, - {"BGR32",IMGFMT_BGR32,0,{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0}} +struct directx_fourcc_caps { + char img_format_name[6]; //human readable name + uint32_t img_format; //as MPlayer image format + DDPIXELFORMAT g_ddpfOverlay; //as Directx Sourface description +} static const g_ddpf[] = { + { "YV12 ", IMGFMT_YV12, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'V', '1', '2'), 0, 0, 0, 0, 0 } }, + { "I420 ", IMGFMT_I420, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('I', '4', '2', '0'), 0, 0, 0, 0, 0 } }, //yv12 with swapped uv + { "IYUV ", IMGFMT_IYUV, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('I', 'Y', 'U', 'V'), 0, 0, 0, 0, 0 } }, //same as i420 + { "YVU9 ", IMGFMT_YVU9, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'V', 'U', '9'), 0, 0, 0, 0, 0 } }, + { "YUY2 ", IMGFMT_YUY2, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'U', 'Y', '2'), 0, 0, 0, 0, 0 } }, + { "UYVY ", IMGFMT_UYVY, { sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('U', 'Y', 'V', 'Y'), 0, 0, 0, 0, 0 } }, + { "BGR8 ", IMGFMT_BGR8, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 8, 0x00000000, 0x00000000, 0x00000000, 0 } }, + { "RGB15", IMGFMT_RGB15, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000001F, 0x000003E0, 0x00007C00, 0 } }, //RGB 5:5:5 + { "BGR15", IMGFMT_BGR15, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x00007C00, 0x000003E0, 0x0000001F, 0 } }, + { "RGB16", IMGFMT_RGB16, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000001F, 0x000007E0, 0x0000F800, 0 } }, //RGB 5:6:5 + { "BGR16", IMGFMT_BGR16, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000F800, 0x000007E0, 0x0000001F, 0 } }, + { "RGB24", IMGFMT_RGB24, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0 } }, + { "BGR24", IMGFMT_BGR24, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0 } }, + { "RGB32", IMGFMT_RGB32, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0 } }, + { "BGR32", IMGFMT_BGR32, { sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0 } } }; #define NUM_FORMATS (sizeof(g_ddpf) / sizeof(g_ddpf[0])) -static const vo_info_t info = -{ - "Directx DDraw YUV/RGB/BGR renderer", - "directx", - "Sascha Sommer <saschasommer@freenet.de>", - "" +// what hw supports with corresponding format in g_ddpf +static uint32_t drv_caps[NUM_FORMATS]; + +static const vo_info_t info = { + "Directx DDraw YUV/RGB/BGR renderer", + "directx", + "Sascha Sommer <saschasommer@freenet.de>", + "" }; const LIBVO_EXTERN(directx) static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src, - unsigned char *srca, int stride) + unsigned char *srca, int stride) { - switch(image_format) { - case IMGFMT_YV12 : - case IMGFMT_I420 : - case IMGFMT_IYUV : - case IMGFMT_YVU9 : - vo_draw_alpha_yv12(w,h,src,srca,stride,((uint8_t *) image) + dstride*y0 + x0,dstride); - break; - case IMGFMT_YUY2 : - vo_draw_alpha_yuy2(w,h,src,srca,stride,((uint8_t *) image)+ dstride*y0 + 2*x0 ,dstride); - break; - case IMGFMT_UYVY : - vo_draw_alpha_yuy2(w,h,src,srca,stride,((uint8_t *) image) + dstride*y0 + 2*x0 + 1,dstride); - break; - case IMGFMT_RGB15: + switch (image_format) { + case IMGFMT_YV12: + case IMGFMT_I420: + case IMGFMT_IYUV: + case IMGFMT_YVU9: + vo_draw_alpha_yv12(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + x0, dstride); + break; + case IMGFMT_YUY2: + vo_draw_alpha_yuy2(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 2 * x0, dstride); + break; + case IMGFMT_UYVY: + vo_draw_alpha_yuy2(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 2 * x0 + 1, dstride); + break; + case IMGFMT_RGB15: case IMGFMT_BGR15: - vo_draw_alpha_rgb15(w,h,src,srca,stride,((uint8_t *) image)+dstride*y0+2*x0,dstride); - break; + vo_draw_alpha_rgb15(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 2 * x0, dstride); + break; case IMGFMT_RGB16: - case IMGFMT_BGR16: - vo_draw_alpha_rgb16(w,h,src,srca,stride,((uint8_t *) image)+dstride*y0+2*x0,dstride); - break; + case IMGFMT_BGR16: + vo_draw_alpha_rgb16(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 2 * x0, dstride); + break; case IMGFMT_RGB24: - case IMGFMT_BGR24: - vo_draw_alpha_rgb24(w,h,src,srca,stride,((uint8_t *) image)+dstride*y0+4*x0,dstride); - break; + case IMGFMT_BGR24: + vo_draw_alpha_rgb24(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 4 * x0, dstride); + break; case IMGFMT_RGB32: - case IMGFMT_BGR32: - vo_draw_alpha_rgb32(w,h,src,srca,stride,((uint8_t *) image)+dstride*y0+4*x0,dstride); - break; + case IMGFMT_BGR32: + vo_draw_alpha_rgb32(w, h, src, srca, stride, ((uint8_t *)image) + dstride * y0 + 4 * x0, dstride); + break; } } static void draw_osd(void) { - vo_draw_text(image_width,image_height,draw_alpha); + vo_draw_text(image_width, image_height, draw_alpha); } -static int -query_format(uint32_t format) +static int query_format(uint32_t format) { - uint32_t i=0; - while ( i < NUM_FORMATS ) - { - if (g_ddpf[i].img_format == format) - return g_ddpf[i].drv_caps; - i++; - } + int i; + for (i = 0; i < NUM_FORMATS; i++) + if (g_ddpf[i].img_format == format) + return drv_caps[i]; return 0; } +struct errmap { + HRESULT err; + const char *errstr; +} static const dd_errmap[] = { + {DDERR_INCOMPATIBLEPRIMARY, "incompatible primary surface"}, + {DDERR_INVALIDCAPS, "invalid caps"}, + {DDERR_INVALIDOBJECT, "invalid object"}, + {DDERR_INVALIDPARAMS, "invalid parameters"}, + {DDERR_INVALIDRECT, "invalid rectangle"}, + {DDERR_INVALIDSURFACETYPE, "invalid surfacetype"}, + {DDERR_NODIRECTDRAWHW, "no directdraw hardware"}, + {DDERR_NOEMULATION, "can't emulate"}, + {DDERR_NOFLIPHW, "hardware can't do flip"}, + {DDERR_NOOVERLAYHW, "hardware can't do overlay"}, + {DDERR_NOSTRETCHHW, "hardware can't stretch: try to size the window back"}, + {DDERR_OUTOFMEMORY, "not enough system memory"}, + {DDERR_OUTOFVIDEOMEMORY, "not enough video memory"}, + {DDERR_UNSUPPORTED, "unsupported"}, + {DDERR_UNSUPPORTEDMODE, "unsupported mode"}, + {DDERR_HEIGHTALIGN, "height align"}, + {DDERR_XALIGN, "x align"}, + {DDERR_SURFACELOST, "surfaces lost"}, + {0, NULL} +}; + +static const char *dd_errstr(HRESULT res) +{ + int i; + for (i = 0; dd_errmap[i].errstr; i++) + if (dd_errmap[i].err == res) + return dd_errmap[i].errstr; + return "unknown error"; +} + static uint32_t Directx_CreatePrimarySurface(void) { - DDSURFACEDESC2 ddsd; + DDSURFACEDESC2 ddsd = { .dwSize = sizeof(ddsd) }; //cleanup - if(g_lpddsPrimary)g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary); - g_lpddsPrimary=NULL; + if (g_lpddsPrimary) + g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary); + g_lpddsPrimary = NULL; - if(vidmode)g_lpdd->lpVtbl->SetDisplayMode(g_lpdd,vm_width,vm_height,vm_bpp,vo_refresh_rate,0); - ZeroMemory(&ddsd, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); + if (vidmode) + g_lpdd->lpVtbl->SetDisplayMode(g_lpdd, vm_width, vm_height, vm_bpp, vo_refresh_rate, 0); //set flags and create a primary surface. ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; - if(g_lpdd->lpVtbl->CreateSurface(g_lpdd,&ddsd, &g_lpddsPrimary, NULL )== DD_OK) - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>primary surface created\n"); - else - { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>could not create primary surface\n"); - return 1; - } - return 0; + if (g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsd, &g_lpddsPrimary, NULL) == DD_OK) + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>primary surface created\n"); + else { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>could not create primary surface\n"); + return 1; + } + return 0; } static uint32_t Directx_CreateOverlay(uint32_t imgfmt) { HRESULT ddrval; - DDSURFACEDESC2 ddsdOverlay; - uint32_t i=0; - while ( i < NUM_FORMATS && imgfmt != g_ddpf[i].img_format) - { - i++; - } - if (!g_lpdd || !g_lpddsPrimary || i == NUM_FORMATS) + DDSURFACEDESC2 ddsdOverlay = { + .dwSize = sizeof(ddsdOverlay), + .ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY, + .dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_BACKBUFFERCOUNT | DDSD_PIXELFORMAT, + .dwWidth = image_width, + .dwHeight = image_height, + .dwBackBufferCount = 2, + }; + uint32_t i = 0; + while (i < NUM_FORMATS && imgfmt != g_ddpf[i].img_format) + i++; + if (!g_lpdd || !g_lpddsPrimary || i == NUM_FORMATS) return 1; //cleanup - if (g_lpddsOverlay)g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay); - if (g_lpddsBack)g_lpddsBack->lpVtbl->Release(g_lpddsBack); - g_lpddsOverlay= NULL; - g_lpddsBack = NULL; - //create our overlay - ZeroMemory(&ddsdOverlay, sizeof(ddsdOverlay)); - ddsdOverlay.dwSize = sizeof(ddsdOverlay); - ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY; - ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_BACKBUFFERCOUNT| DDSD_PIXELFORMAT; - ddsdOverlay.dwWidth=image_width; - ddsdOverlay.dwHeight=image_height; - ddsdOverlay.dwBackBufferCount=2; - ddsdOverlay.ddpfPixelFormat=g_ddpf[i].g_ddpfOverlay; - if(vo_doublebuffering) //tribblebuffering - { - if (g_lpdd->lpVtbl->CreateSurface(g_lpdd,&ddsdOverlay, &g_lpddsOverlay, NULL)== DD_OK) - { - mp_msg(MSGT_VO, MSGL_V,"<vo_directx><INFO>overlay with format %s created\n",g_ddpf[i].img_format_name); + if (g_lpddsOverlay) + g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay); + if (g_lpddsBack) + g_lpddsBack->lpVtbl->Release(g_lpddsBack); + g_lpddsOverlay = NULL; + g_lpddsBack = NULL; + //create our overlay + ddsdOverlay.ddpfPixelFormat = g_ddpf[i].g_ddpfOverlay; + if (vo_doublebuffering) { //tribblebuffering + if (g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsdOverlay, &g_lpddsOverlay, NULL) == DD_OK) { + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>overlay with format %s created\n", g_ddpf[i].img_format_name); //get the surface directly attached to the primary (the back buffer) ddsdOverlay.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; - if(g_lpddsOverlay->lpVtbl->GetAttachedSurface(g_lpddsOverlay,&ddsdOverlay.ddsCaps, &g_lpddsBack) != DD_OK) - { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't get attached surface\n"); - return 1; - } - return 0; - } - vo_doublebuffering=0; //disable tribblebuffering - mp_msg(MSGT_VO, MSGL_V,"<vo_directx><WARN>cannot create tribblebuffer overlay with format %s\n",g_ddpf[i].img_format_name); - } - //single buffer - mp_msg(MSGT_VO, MSGL_V,"<vo_directx><INFO>using singlebuffer overlay\n"); - ddsdOverlay.dwBackBufferCount=0; - ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY; - ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT; - ddsdOverlay.ddpfPixelFormat=g_ddpf[i].g_ddpfOverlay; - // try to create the overlay surface - ddrval = g_lpdd->lpVtbl->CreateSurface(g_lpdd,&ddsdOverlay, &g_lpddsOverlay, NULL); - if(ddrval != DD_OK) - { - if(ddrval == DDERR_INVALIDPIXELFORMAT)mp_msg(MSGT_VO,MSGL_V,"<vo_directx><ERROR> invalid pixelformat: %s\n",g_ddpf[i].img_format_name); - else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>"); - switch(ddrval) - { - case DDERR_INCOMPATIBLEPRIMARY: - {mp_msg(MSGT_VO, MSGL_ERR,"incompatible primary surface\n");break;} - case DDERR_INVALIDCAPS: - {mp_msg(MSGT_VO, MSGL_ERR,"invalid caps\n");break;} - case DDERR_INVALIDOBJECT: - {mp_msg(MSGT_VO, MSGL_ERR,"invalid object\n");break;} - case DDERR_INVALIDPARAMS: - {mp_msg(MSGT_VO, MSGL_ERR,"invalid parameters\n");break;} - case DDERR_NODIRECTDRAWHW: - {mp_msg(MSGT_VO, MSGL_ERR,"no directdraw hardware\n");break;} - case DDERR_NOEMULATION: - {mp_msg(MSGT_VO, MSGL_ERR,"can't emulate\n");break;} - case DDERR_NOFLIPHW: - {mp_msg(MSGT_VO, MSGL_ERR,"hardware can't do flip\n");break;} - case DDERR_NOOVERLAYHW: - {mp_msg(MSGT_VO, MSGL_ERR,"hardware can't do overlay\n");break;} - case DDERR_OUTOFMEMORY: - {mp_msg(MSGT_VO, MSGL_ERR,"not enough system memory\n");break;} - case DDERR_UNSUPPORTEDMODE: - {mp_msg(MSGT_VO, MSGL_ERR,"unsupported mode\n");break;} - case DDERR_OUTOFVIDEOMEMORY: - {mp_msg(MSGT_VO, MSGL_ERR,"not enough video memory\n");break;} - default: - mp_msg(MSGT_VO, MSGL_ERR,"create surface failed with 0x%xu\n",(unsigned)ddrval); - } - return 1; - } + if (g_lpddsOverlay->lpVtbl->GetAttachedSurface(g_lpddsOverlay, &ddsdOverlay.ddsCaps, &g_lpddsBack) != DD_OK) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't get attached surface\n"); + return 1; + } + return 0; + } + vo_doublebuffering = 0; //disable tribblebuffering + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><WARN>cannot create tribblebuffer overlay with format %s\n", g_ddpf[i].img_format_name); + } + //single buffer + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>using singlebuffer overlay\n"); + ddsdOverlay.dwBackBufferCount = 0; + ddsdOverlay.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY; + ddsdOverlay.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + ddsdOverlay.ddpfPixelFormat = g_ddpf[i].g_ddpfOverlay; + // try to create the overlay surface + ddrval = g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsdOverlay, &g_lpddsOverlay, NULL); + if (ddrval != DD_OK) { + if (ddrval == DDERR_INVALIDPIXELFORMAT) + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR> invalid pixelformat: %s\n", g_ddpf[i].img_format_name); + else + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>create surface failed: %s (0x%x)\n", dd_errstr(ddrval), (unsigned int)ddrval); + return 1; + } g_lpddsBack = g_lpddsOverlay; - return 0; + return 0; } static uint32_t Directx_CreateBackpuffer(void) { - DDSURFACEDESC2 ddsd; - //cleanup - if (g_lpddsBack)g_lpddsBack->lpVtbl->Release(g_lpddsBack); - g_lpddsBack=NULL; - ZeroMemory(&ddsd, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; - ddsd.dwFlags= DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; - ddsd.dwWidth=image_width; - ddsd.dwHeight=image_height; - if(g_lpdd->lpVtbl->CreateSurface( g_lpdd, &ddsd, &g_lpddsBack, 0 ) != DD_OK ) - { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't create backpuffer\n"); - return 1; - } - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>backbuffer created\n"); - return 0; + DDSURFACEDESC2 ddsd = { + .dwSize = sizeof(ddsd), + .ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY, + .dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT, + .dwWidth = image_width, + .dwHeight = image_height, + }; + //cleanup + if (g_lpddsBack) + g_lpddsBack->lpVtbl->Release(g_lpddsBack); + g_lpddsBack = NULL; + if (g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsd, &g_lpddsBack, 0) != DD_OK) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't create backpuffer\n"); + return 1; + } + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>backbuffer created\n"); + return 0; } static void uninit(void) { - if (g_cc != NULL) - { - g_cc->lpVtbl->Release(g_cc); - } - g_cc=NULL; - if (g_lpddclipper != NULL) g_lpddclipper->lpVtbl->Release(g_lpddclipper); - g_lpddclipper = NULL; - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>clipper released\n"); - if (g_lpddsBack != NULL) g_lpddsBack->lpVtbl->Release(g_lpddsBack); - g_lpddsBack = NULL; - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>back surface released\n"); - if(vo_doublebuffering && !nooverlay) - { - if (g_lpddsOverlay != NULL)g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay); + if (g_cc) + g_cc->lpVtbl->Release(g_cc); + g_cc = NULL; + if (g_lpddclipper) + g_lpddclipper->lpVtbl->Release(g_lpddclipper); + g_lpddclipper = NULL; + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>clipper released\n"); + if (g_lpddsBack) + g_lpddsBack->lpVtbl->Release(g_lpddsBack); + g_lpddsBack = NULL; + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>back surface released\n"); + if (vo_doublebuffering && !nooverlay) { + if (g_lpddsOverlay) + g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay); g_lpddsOverlay = NULL; - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>overlay surface released\n"); - } - if (g_lpddsPrimary != NULL) g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary); + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>overlay surface released\n"); + } + if (g_lpddsPrimary) + g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary); g_lpddsPrimary = NULL; - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>primary released\n"); - if (colorbrush) DeleteObject(colorbrush); - colorbrush = NULL; - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>GDI resources deleted\n"); - if (g_lpdd != NULL){ - if(vidmode)g_lpdd->lpVtbl->RestoreDisplayMode(g_lpdd); - g_lpdd->lpVtbl->Release(g_lpdd); - } - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>directdrawobject released\n"); - FreeLibrary( hddraw_dll); - hddraw_dll= NULL; - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>ddraw.dll freed\n"); - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>uninitialized\n"); - vo_w32_uninit(); + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>primary released\n"); + if (colorbrush) + DeleteObject(colorbrush); + colorbrush = NULL; + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>GDI resources deleted\n"); + if (g_lpdd) { + if (vidmode) + g_lpdd->lpVtbl->RestoreDisplayMode(g_lpdd); + g_lpdd->lpVtbl->Release(g_lpdd); + } + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>directdrawobject released\n"); + FreeLibrary(hddraw_dll); + hddraw_dll = NULL; + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>ddraw.dll freed\n"); + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>uninitialized\n"); + vo_w32_uninit(); } -static BOOL WINAPI EnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm) +static BOOL WINAPI EnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm) { if (!lpGUID) lpDriverDescription = "Primary Display Adapter"; - mp_msg(MSGT_VO, MSGL_INFO ,"<vo_directx> adapter %d: %s", adapter_count, lpDriverDescription); + mp_msg(MSGT_VO, MSGL_INFO, "<vo_directx> adapter %d: %s", adapter_count, lpDriverDescription); - if(adapter_count == vo_adapter_num){ + if (adapter_count == vo_adapter_num) { if (!lpGUID) selected_guid_ptr = NULL; - else - { - selected_guid = *lpGUID; + else { + selected_guid = *lpGUID; selected_guid_ptr = &selected_guid; } - mp_msg(MSGT_VO, MSGL_INFO ,"\t\t<--"); + mp_msg(MSGT_VO, MSGL_INFO, "\t\t<--"); } - mp_msg(MSGT_VO, MSGL_INFO ,"\n"); + mp_msg(MSGT_VO, MSGL_INFO, "\n"); adapter_count++; @@ -368,265 +363,245 @@ static BOOL WINAPI EnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, L static uint32_t Directx_InitDirectDraw(void) { - HRESULT (WINAPI *OurDirectDrawCreateEx)(GUID *,LPVOID *, REFIID,IUnknown FAR *); - DDSURFACEDESC2 ddsd; - LPDIRECTDRAWENUMERATEEX OurDirectDrawEnumerateEx; + HRESULT (WINAPI *OurDirectDrawCreateEx)(GUID *, LPVOID *, REFIID, IUnknown FAR *); + DDSURFACEDESC2 ddsd = { + .dwSize = sizeof(ddsd), + .dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT, + }; + LPDIRECTDRAWENUMERATEEX OurDirectDrawEnumerateEx; - adapter_count = 0; + adapter_count = 0; - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>Initing DirectDraw\n" ); + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>Initing DirectDraw\n"); - //load direct draw DLL: based on videolans code - hddraw_dll = LoadLibrary("DDRAW.DLL"); - if( hddraw_dll == NULL ) - { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>failed loading ddraw.dll\n" ); - return 1; + //load direct draw DLL: based on videolans code + hddraw_dll = LoadLibrary("DDRAW.DLL"); + if (!hddraw_dll) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>failed loading ddraw.dll\n"); + return 1; } - if(vo_adapter_num){ //display other than default - OurDirectDrawEnumerateEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hddraw_dll,"DirectDrawEnumerateExA"); - if (!OurDirectDrawEnumerateEx){ - FreeLibrary( hddraw_dll ); + if (vo_adapter_num) { //display other than default + OurDirectDrawEnumerateEx = (LPDIRECTDRAWENUMERATEEX)GetProcAddress(hddraw_dll, "DirectDrawEnumerateExA"); + if (!OurDirectDrawEnumerateEx) { + FreeLibrary(hddraw_dll); hddraw_dll = NULL; - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>failed geting proc address: DirectDrawEnumerateEx\n"); - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>no directx 7 or higher installed\n"); + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>failed geting proc address: DirectDrawEnumerateEx\n"); + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>no directx 7 or higher installed\n"); return 1; } // enumerate all display devices attached to the desktop - OurDirectDrawEnumerateEx(EnumCallbackEx, NULL, DDENUM_ATTACHEDSECONDARYDEVICES ); + OurDirectDrawEnumerateEx(EnumCallbackEx, NULL, DDENUM_ATTACHEDSECONDARYDEVICES); - if(vo_adapter_num >= adapter_count) - mp_msg(MSGT_VO, MSGL_ERR,"Selected adapter (%d) doesn't exist: Default Display Adapter selected\n",vo_adapter_num); + if (vo_adapter_num >= adapter_count) + mp_msg(MSGT_VO, MSGL_ERR, "Selected adapter (%d) doesn't exist: Default Display Adapter selected\n", vo_adapter_num); } - OurDirectDrawCreateEx = (void *)GetProcAddress(hddraw_dll, "DirectDrawCreateEx"); - if ( OurDirectDrawCreateEx == NULL ) - { - FreeLibrary( hddraw_dll ); - hddraw_dll = NULL; - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>failed geting proc address: DirectDrawCreateEx\n"); - return 1; - } - - // initialize DirectDraw and create directx v7 object - if (OurDirectDrawCreateEx(selected_guid_ptr, (VOID**)&g_lpdd, &IID_IDirectDraw7, NULL ) != DD_OK ) - { - FreeLibrary( hddraw_dll ); + OurDirectDrawCreateEx = (void *)GetProcAddress(hddraw_dll, "DirectDrawCreateEx"); + if (!OurDirectDrawCreateEx) { + FreeLibrary(hddraw_dll); hddraw_dll = NULL; - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't initialize ddraw\n"); - return 1; + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>failed geting proc address: DirectDrawCreateEx\n"); + return 1; } - //get current screen siz for selected monitor ... - ddsd.dwSize=sizeof(ddsd); - ddsd.dwFlags=DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT; - g_lpdd->lpVtbl->GetDisplayMode(g_lpdd, &ddsd); - if(vo_screenwidth && vo_screenheight) - { - vm_height=vo_screenheight; - vm_width=vo_screenwidth; - } - else - { - vm_height=ddsd.dwHeight; - vm_width=ddsd.dwWidth; - } + // initialize DirectDraw and create directx v7 object + if (OurDirectDrawCreateEx(selected_guid_ptr, (VOID **)&g_lpdd, &IID_IDirectDraw7, NULL) != DD_OK) { + FreeLibrary(hddraw_dll); + hddraw_dll = NULL; + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't initialize ddraw\n"); + return 1; + } + //get current screen siz for selected monitor ... + g_lpdd->lpVtbl->GetDisplayMode(g_lpdd, &ddsd); + if (vo_screenwidth && vo_screenheight) { + vm_height = vo_screenheight; + vm_width = vo_screenwidth; + } else { + vm_height = ddsd.dwHeight; + vm_width = ddsd.dwWidth; + } - if(vo_dbpp)vm_bpp=vo_dbpp; - else vm_bpp=ddsd.ddpfPixelFormat.dwRGBBitCount; + if (vo_dbpp) + vm_bpp = vo_dbpp; + else + vm_bpp = ddsd.ddpfPixelFormat.dwRGBBitCount; - if(vidmode){ - if (g_lpdd->lpVtbl->SetCooperativeLevel(g_lpdd, vo_w32_window, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN) != DD_OK) - { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't set cooperativelevel for exclusive mode\n"); + if (vidmode) { + if (g_lpdd->lpVtbl->SetCooperativeLevel(g_lpdd, vo_w32_window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN) != DD_OK) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't set cooperativelevel for exclusive mode\n"); + return 1; + } + /*SetDisplayMode(ddobject,width,height,bpp,refreshrate,aditionalflags)*/ + if (g_lpdd->lpVtbl->SetDisplayMode(g_lpdd, vm_width, vm_height, vm_bpp, 0, 0) != DD_OK) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't set displaymode\n"); return 1; - } - /*SetDisplayMode(ddobject,width,height,bpp,refreshrate,aditionalflags)*/ - if(g_lpdd->lpVtbl->SetDisplayMode(g_lpdd,vm_width, vm_height, vm_bpp,0,0) != DD_OK) - { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't set displaymode\n"); - return 1; - } - mp_msg(MSGT_VO, MSGL_V,"<vo_directx><INFO>Initialized adapter %i for %i x %i @ %i \n",vo_adapter_num,vm_width,vm_height,vm_bpp); - return 0; - } - if (g_lpdd->lpVtbl->SetCooperativeLevel(g_lpdd, vo_w32_window, DDSCL_NORMAL) != DD_OK) // or DDSCL_SETFOCUSWINDOW - { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>could not set cooperativelevel for hardwarecheck\n"); - return 1; + } + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>Initialized adapter %i for %i x %i @ %i \n", vo_adapter_num, vm_width, vm_height, vm_bpp); + return 0; } - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>DirectDraw Initialized\n"); - return 0; + if (g_lpdd->lpVtbl->SetCooperativeLevel(g_lpdd, vo_w32_window, DDSCL_NORMAL) != DD_OK) { // or DDSCL_SETFOCUSWINDOW + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>could not set cooperativelevel for hardwarecheck\n"); + return 1; + } + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>DirectDraw Initialized\n"); + return 0; } static uint32_t Directx_ManageDisplay(void) { - HRESULT ddrval; - DDCAPS capsDrv; - DDOVERLAYFX ovfx; - DWORD dwUpdateFlags=0; - int width,height; - - rd.left = vo_dx - xinerama_x; - rd.top = vo_dy - xinerama_y; - width = vo_dwidth; - height = vo_dheight; - - aspect(&width, &height, A_WINZOOM); - panscan_calc_windowed(); - width += vo_panscan_x; - height += vo_panscan_y; - width = FFMIN(width, vo_screenwidth); - height = FFMIN(height, vo_screenheight); + HRESULT ddrval; + DDCAPS capsDrv = { .dwSize = sizeof(capsDrv) }; + DDOVERLAYFX ovfx = { .dwSize = sizeof(ovfx) }; + DWORD dwUpdateFlags = 0; + int width, height; + + POINT origin = { 0, 0 }; + ClientToScreen(vo_w32_window, &origin); + + rd.left = origin.x - xinerama_x; + rd.top = origin.y - xinerama_y; + width = vo_dwidth; + height = vo_dheight; + + if (aspect_scaling()) { + aspect(&width, &height, A_WINZOOM); + panscan_calc_windowed(); + width += vo_panscan_x; + height += vo_panscan_y; + } rd.left += (vo_dwidth - width ) / 2; rd.top += (vo_dheight - height) / 2; - rd.right=rd.left+width; - rd.bottom=rd.top+height; - - /*ok, let's workaround some overlay limitations*/ - if(!nooverlay) - { - uint32_t uStretchFactor1000; //minimum stretch - uint32_t xstretch1000,ystretch1000; - /*get driver capabilities*/ - ZeroMemory(&capsDrv, sizeof(capsDrv)); - capsDrv.dwSize = sizeof(capsDrv); - if(g_lpdd->lpVtbl->GetCaps(g_lpdd,&capsDrv, NULL) != DD_OK)return 1; - /*get minimum stretch, depends on display adaptor and mode (refresh rate!) */ - uStretchFactor1000 = capsDrv.dwMinOverlayStretch>1000 ? capsDrv.dwMinOverlayStretch : 1000; - rd.right = ((width+rd.left)*uStretchFactor1000+999)/1000; - rd.bottom = (height+rd.top)*uStretchFactor1000/1000; + rd.right = rd.left + width; + rd.bottom = rd.top + height; + + /*ok, let's workaround some overlay limitations*/ + if (!nooverlay) { + uint32_t uStretchFactor1000; //minimum stretch + uint32_t xstretch1000, ystretch1000; + + if (!width || !height) { + // window is minimized, so we should hide the overlay in case + // colorkeying is not used or working. + // In addition trying to set width/height to 0 would crash + g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay, NULL, g_lpddsPrimary, NULL, DDOVER_HIDE, NULL); + return 0; + } + + /*get driver capabilities*/ + if (g_lpdd->lpVtbl->GetCaps(g_lpdd, &capsDrv, NULL) != DD_OK) + return 1; + /*get minimum stretch, depends on display adaptor and mode (refresh rate!) */ + uStretchFactor1000 = capsDrv.dwMinOverlayStretch > 1000 ? capsDrv.dwMinOverlayStretch : 1000; + rd.right = ((width + rd.left) * uStretchFactor1000 + 999) / 1000; + rd.bottom = (height + rd.top) * uStretchFactor1000 / 1000; /*calculate xstretch1000 and ystretch1000*/ - xstretch1000 = ((rd.right - rd.left)* 1000)/image_width ; - ystretch1000 = ((rd.bottom - rd.top)* 1000)/image_height; - rs.left=0; - rs.right=image_width; - rs.top=0; - rs.bottom=image_height; - if(rd.left < 0)rs.left=(-rd.left*1000)/xstretch1000; - if(rd.top < 0)rs.top=(-rd.top*1000)/ystretch1000; - if(rd.right > vo_screenwidth)rs.right=((vo_screenwidth-rd.left)*1000)/xstretch1000; - if(rd.bottom > vo_screenheight)rs.bottom=((vo_screenheight-rd.top)*1000)/ystretch1000; - /*do not allow to zoom or shrink if hardware isn't able to do so*/ - if((width < image_width)&& !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKX)) - { - if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKXN)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can only shrinkN\n"); - else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't shrink x\n"); - rd.right=rd.left+image_width; - } - else if((width > image_width)&& !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHX)) - { - if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHXN)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can only stretchN\n"); - else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't stretch x\n"); - rd.right = rd.left+image_width; - } - if((height < image_height) && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKY)) - { - if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKYN)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can only shrinkN\n"); - else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't shrink y\n"); - rd.bottom = rd.top + image_height; - } - else if((height > image_height ) && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHY)) - { - if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHYN)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can only stretchN\n"); - else mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't stretch y\n"); - rd.bottom = rd.top + image_height; - } - /*the last thing to check are alignment restrictions - these expressions (x & -y) just do alignment by dropping low order bits... - so to round up, we add first, then truncate*/ - if((capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYSRC) && capsDrv.dwAlignBoundarySrc) - rs.left = (rs.left + capsDrv.dwAlignBoundarySrc / 2) & -(signed)(capsDrv.dwAlignBoundarySrc); - if((capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC) && capsDrv.dwAlignSizeSrc) - rs.right = rs.left + ((rs.right - rs.left + capsDrv.dwAlignSizeSrc / 2) & -(signed) (capsDrv.dwAlignSizeSrc)); - if((capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYDEST) && capsDrv.dwAlignBoundaryDest) - rd.left = (rd.left + capsDrv.dwAlignBoundaryDest / 2) & -(signed)(capsDrv.dwAlignBoundaryDest); - if((capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST) && capsDrv.dwAlignSizeDest) - rd.right = rd.left + ((rd.right - rd.left) & -(signed) (capsDrv.dwAlignSizeDest)); - /*create an overlay FX structure to specify a destination color key*/ - ZeroMemory(&ovfx, sizeof(ovfx)); - ovfx.dwSize = sizeof(ovfx); - if(vo_fs||vidmode) - { - ovfx.dckDestColorkey.dwColorSpaceLowValue = 0; + xstretch1000 = ((rd.right - rd.left) * 1000) / image_width; + ystretch1000 = ((rd.bottom - rd.top) * 1000) / image_height; + rs.left = 0; + rs.right = image_width; + rs.top = 0; + rs.bottom = image_height; + if (rd.left < 0) + rs.left = (-rd.left * 1000) / xstretch1000; + if (rd.top < 0) + rs.top = (-rd.top * 1000) / ystretch1000; + if (rd.right > vo_screenwidth) + rs.right = ((vo_screenwidth - rd.left) * 1000) / xstretch1000; + if (rd.bottom > vo_screenheight) + rs.bottom = ((vo_screenheight - rd.top) * 1000) / ystretch1000; + /*do not allow to zoom or shrink if hardware isn't able to do so*/ + if (width < image_width && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKX)) { + if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKXN) + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can only shrinkN\n"); + else + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't shrink x\n"); + rd.right = rd.left + image_width; + } else if (width > image_width && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHX)) { + if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHXN) + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can only stretchN\n"); + else + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't stretch x\n"); + rd.right = rd.left + image_width; + } + if (height < image_height && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKY)) { + if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSHRINKYN) + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can only shrinkN\n"); + else + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't shrink y\n"); + rd.bottom = rd.top + image_height; + } else if (height > image_height && !(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHY)) { + if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYSTRETCHYN) + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can only stretchN\n"); + else + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't stretch y\n"); + rd.bottom = rd.top + image_height; + } + /*the last thing to check are alignment restrictions + * these expressions (x & -y) just do alignment by dropping low order bits... + * so to round up, we add first, then truncate*/ + if ((capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYSRC) && capsDrv.dwAlignBoundarySrc) + rs.left = (rs.left + capsDrv.dwAlignBoundarySrc / 2) & - (signed)(capsDrv.dwAlignBoundarySrc); + if ((capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC) && capsDrv.dwAlignSizeSrc) + rs.right = rs.left + ((rs.right - rs.left + capsDrv.dwAlignSizeSrc / 2) & - (signed)(capsDrv.dwAlignSizeSrc)); + if ((capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYDEST) && capsDrv.dwAlignBoundaryDest) + rd.left = (rd.left + capsDrv.dwAlignBoundaryDest / 2) & - (signed)(capsDrv.dwAlignBoundaryDest); + if ((capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST) && capsDrv.dwAlignSizeDest) + rd.right = rd.left + ((rd.right - rd.left) & - (signed)(capsDrv.dwAlignSizeDest)); + /*create an overlay FX structure to specify a destination color key*/ + if (vo_fs || vidmode) { + ovfx.dckDestColorkey.dwColorSpaceLowValue = 0; ovfx.dckDestColorkey.dwColorSpaceHighValue = 0; - } - else - { - ovfx.dckDestColorkey.dwColorSpaceLowValue = destcolorkey; + } else { + ovfx.dckDestColorkey.dwColorSpaceLowValue = destcolorkey; ovfx.dckDestColorkey.dwColorSpaceHighValue = destcolorkey; - } + } // set the flags we'll send to UpdateOverlay //DDOVER_AUTOFLIP|DDOVERFX_MIRRORLEFTRIGHT|DDOVERFX_MIRRORUPDOWN could be useful?; dwUpdateFlags = DDOVER_SHOW | DDOVER_DDFX; /*if hardware can't do colorkeying set the window on top*/ - if(capsDrv.dwCKeyCaps & DDCKEYCAPS_DESTOVERLAY) dwUpdateFlags |= DDOVER_KEYDESTOVERRIDE; - else if (!tmp_image) vo_ontop = 1; - } - else - { + if (capsDrv.dwCKeyCaps & DDCKEYCAPS_DESTOVERLAY) + dwUpdateFlags |= DDOVER_KEYDESTOVERRIDE; + else if (!tmp_image) + vo_ontop = 1; + } else { g_lpddclipper->lpVtbl->SetHWnd(g_lpddclipper, 0, vo_w32_window); } /*make sure the overlay is inside the screen*/ - if(rd.left<0)rd.left=0; - if(rd.right>vo_screenwidth)rd.right=vo_screenwidth; - if(rd.top<0)rd.top=0; - if(rd.bottom>vo_screenheight)rd.bottom=vo_screenheight; + rd.top = FFMAX(rd.top, 0); + rd.left = FFMAX(rd.left, 0); + rd.bottom = FFMIN(rd.bottom, vo_screenheight); + rd.right = FFMIN(rd.right, vo_screenwidth); - /*for nonoverlay mode we are finished, for overlay mode we have to display the overlay first*/ - if(nooverlay)return 0; + /*for nonoverlay mode we are finished, for overlay mode we have to display the overlay first*/ + if (nooverlay) + return 0; // printf("overlay: %i %i %ix%i\n",rd.left,rd.top,rd.right - rd.left,rd.bottom - rd.top); - ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay,&rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx); - if(FAILED(ddrval)) - { + ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay, &rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx); + if (FAILED(ddrval)) { // one cause might be the driver lied about minimum stretch // we should try upping the destination size a bit, or // perhaps shrinking the source size - mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>UpdateOverlay failed\n" ); - mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>Overlay:x1:%li,y1:%li,x2:%li,y2:%li,w:%li,h:%li\n",rd.left,rd.top,rd.right,rd.bottom,rd.right - rd.left,rd.bottom - rd.top ); - mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>"); - switch (ddrval) - { - case DDERR_NOSTRETCHHW: - {mp_msg(MSGT_VO, MSGL_ERR ,"hardware can't stretch: try to size the window back\n");break;} - case DDERR_INVALIDRECT: - {mp_msg(MSGT_VO, MSGL_ERR ,"invalid rectangle\n");break;} - case DDERR_INVALIDPARAMS: - {mp_msg(MSGT_VO, MSGL_ERR ,"invalid parameters\n");break;} - case DDERR_HEIGHTALIGN: - {mp_msg(MSGT_VO, MSGL_ERR ,"height align\n");break;} - case DDERR_XALIGN: - {mp_msg(MSGT_VO, MSGL_ERR ,"x align\n");break;} - case DDERR_UNSUPPORTED: - {mp_msg(MSGT_VO, MSGL_ERR ,"unsupported\n");break;} - case DDERR_INVALIDSURFACETYPE: - {mp_msg(MSGT_VO, MSGL_ERR ,"invalid surfacetype\n");break;} - case DDERR_INVALIDOBJECT: - {mp_msg(MSGT_VO, MSGL_ERR ,"invalid object\n");break;} - case DDERR_SURFACELOST: - { - mp_msg(MSGT_VO, MSGL_ERR ,"surfaces lost\n"); - g_lpddsOverlay->lpVtbl->Restore( g_lpddsOverlay ); //restore and try again - g_lpddsPrimary->lpVtbl->Restore( g_lpddsPrimary ); - ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay,&rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx); - if(ddrval !=DD_OK)mp_msg(MSGT_VO, MSGL_FATAL ,"<vo_directx><FATAL ERROR>UpdateOverlay failed again\n" ); - break; - } - default: - mp_msg(MSGT_VO, MSGL_ERR ," 0x%xu\n",(unsigned)ddrval); - } - /*ok we can't do anything about it -> hide overlay*/ - if(ddrval != DD_OK) - { - ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay,NULL, g_lpddsPrimary, NULL, DDOVER_HIDE, NULL); + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>UpdateOverlay failed\n"); + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>Overlay:x1:%li,y1:%li,x2:%li,y2:%li,w:%li,h:%li\n", rd.left, rd.top, rd.right, rd.bottom, rd.right - rd.left, rd.bottom - rd.top); + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>%s (0x%x)\n", dd_errstr(ddrval), (unsigned int)ddrval); + if (ddrval == DDERR_SURFACELOST) { + g_lpddsOverlay->lpVtbl->Restore(g_lpddsOverlay); //restore and try again + g_lpddsPrimary->lpVtbl->Restore(g_lpddsPrimary); + ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay, &rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx); + if (ddrval != DD_OK) + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>UpdateOverlay failed again\n"); + } + /*ok we can't do anything about it -> hide overlay*/ + if (ddrval != DD_OK) { + ddrval = g_lpddsOverlay->lpVtbl->UpdateOverlay(g_lpddsOverlay, NULL, g_lpddsPrimary, NULL, DDOVER_HIDE, NULL); return 1; - } - } + } + } return 0; } @@ -647,413 +622,409 @@ static void check_events(void) //find out supported overlay pixelformats static uint32_t Directx_CheckOverlayPixelformats(void) { - DDCAPS capsDrv; - HRESULT ddrval; - DDSURFACEDESC2 ddsdOverlay; - uint32_t i; - uint32_t formatcount = 0; - //get driver caps to determine overlay support - ZeroMemory(&capsDrv, sizeof(capsDrv)); - capsDrv.dwSize = sizeof(capsDrv); - ddrval = g_lpdd->lpVtbl->GetCaps(g_lpdd,&capsDrv, NULL); - if (FAILED(ddrval)) - { - mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>failed getting ddrawcaps\n"); - return 1; - } - if (!(capsDrv.dwCaps & DDCAPS_OVERLAY)) - { - mp_msg(MSGT_VO, MSGL_ERR ,"<vo_directx><ERROR>Your card doesn't support overlay\n"); - return 1; - } - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>testing supported overlay pixelformats\n"); + DDCAPS capsDrv = { .dwSize = sizeof(capsDrv) }; + HRESULT ddrval; + DDSURFACEDESC2 ddsdOverlay = { + .dwSize = sizeof(ddsdOverlay), + .ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY, + .dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT, + .dwWidth = 300, + .dwHeight = 280, + .dwBackBufferCount = 0, + }; + uint32_t i; + uint32_t formatcount = 0; + //get driver caps to determine overlay support + ddrval = g_lpdd->lpVtbl->GetCaps(g_lpdd, &capsDrv, NULL); + if (FAILED(ddrval)) { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>failed getting ddrawcaps\n"); + return 1; + } + if (!(capsDrv.dwCaps & DDCAPS_OVERLAY)) { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>Your card doesn't support overlay\n"); + return 1; + } + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>testing supported overlay pixelformats\n"); //it is not possible to query for pixel formats supported by the //overlay hardware: try out various formats till one works - ZeroMemory(&ddsdOverlay, sizeof(ddsdOverlay)); - ddsdOverlay.dwSize = sizeof(ddsdOverlay); - ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY; - ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH| DDSD_PIXELFORMAT; - ddsdOverlay.dwWidth=300; - ddsdOverlay.dwHeight=280; - ddsdOverlay.dwBackBufferCount=0; //try to create an overlay surface using one of the pixel formats in our global list - i=0; - do - { - ddsdOverlay.ddpfPixelFormat=g_ddpf[i].g_ddpfOverlay; - ddrval = g_lpdd->lpVtbl->CreateSurface(g_lpdd,&ddsdOverlay, &g_lpddsOverlay, NULL); - if (ddrval == DD_OK) - { - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><FORMAT OVERLAY>%i %s supported\n",i,g_ddpf[i].img_format_name); - g_ddpf[i].drv_caps = VFCAP_CSP_SUPPORTED |VFCAP_OSD |VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP; - formatcount++;} - else mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><FORMAT OVERLAY>%i %s not supported\n",i,g_ddpf[i].img_format_name); - if (g_lpddsOverlay != NULL) {g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay);g_lpddsOverlay = NULL;} - } while( ++i < NUM_FORMATS ); - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>Your card supports %i of %i overlayformats\n",formatcount, NUM_FORMATS); - if (formatcount == 0) - { - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><WARN>Your card supports overlay, but we couldn't create one\n"); - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>This can have the following reasons:\n"); - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>- you are already using an overlay with another app\n"); - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>- you don't have enough videomemory\n"); - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>- vo_directx doesn't support the cards overlay pixelformat\n"); - return 1; - } - if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYMIRRORLEFTRIGHT)mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>can mirror left right\n"); //I don't have hardware which - if(capsDrv.dwFXCaps & DDFXCAPS_OVERLAYMIRRORUPDOWN )mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>can mirror up down\n"); //supports those send me one and I'll implement ;) - return 0; + for (i = 0; i < NUM_FORMATS; i++) { + ddsdOverlay.ddpfPixelFormat = g_ddpf[i].g_ddpfOverlay; + ddrval = g_lpdd->lpVtbl->CreateSurface(g_lpdd, &ddsdOverlay, &g_lpddsOverlay, NULL); + if (ddrval == DD_OK) { + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><FORMAT OVERLAY>%i %s supported\n", i, g_ddpf[i].img_format_name); + drv_caps[i] = VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP; + formatcount++; + } else + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><FORMAT OVERLAY>%i %s not supported\n", i, g_ddpf[i].img_format_name); + if (g_lpddsOverlay) { + g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay); + g_lpddsOverlay = NULL; + } + } + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>Your card supports %i of %i overlayformats\n", formatcount, NUM_FORMATS); + if (formatcount == 0) { + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><WARN>Your card supports overlay, but we couldn't create one\n"); + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>This can have the following reasons:\n"); + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>- you are already using an overlay with another app\n"); + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>- you don't have enough videomemory\n"); + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>- vo_directx doesn't support the cards overlay pixelformat\n"); + return 1; + } + if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYMIRRORLEFTRIGHT) + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>can mirror left right\n"); //I don't have hardware which + if (capsDrv.dwFXCaps & DDFXCAPS_OVERLAYMIRRORUPDOWN) + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>can mirror up down\n"); //supports those send me one and I'll implement ;) + return 0; } //find out the Pixelformat of the Primary Surface static uint32_t Directx_CheckPrimaryPixelformat(void) { - uint32_t i=0; + int i; uint32_t formatcount = 0; - DDPIXELFORMAT ddpf; - DDSURFACEDESC2 ddsd; - HDC hdc; - HRESULT hres; - COLORREF rgbT=RGB(0,0,0); - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>checking primary surface\n"); - memset( &ddpf, 0, sizeof( DDPIXELFORMAT )); - ddpf.dwSize = sizeof( DDPIXELFORMAT ); + DDPIXELFORMAT ddpf = { .dwSize = sizeof(ddpf) }; + DDSURFACEDESC2 ddsd; + HDC hdc; + HRESULT hres; + COLORREF rgbT = RGB(0, 0, 0); + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>checking primary surface\n"); //we have to create a primary surface first - if(Directx_CreatePrimarySurface()!=0)return 1; - if(g_lpddsPrimary->lpVtbl->GetPixelFormat( g_lpddsPrimary, &ddpf ) != DD_OK ) - { - mp_msg(MSGT_VO, MSGL_FATAL ,"<vo_directx><FATAL ERROR>can't get pixelformat\n"); - return 1; - } - while ( i < NUM_FORMATS ) - { - if (g_ddpf[i].g_ddpfOverlay.dwRGBBitCount == ddpf.dwRGBBitCount) - { - if (g_ddpf[i].g_ddpfOverlay.dwRBitMask == ddpf.dwRBitMask) - { - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><FORMAT PRIMARY>%i %s supported\n",i,g_ddpf[i].img_format_name); - g_ddpf[i].drv_caps = VFCAP_CSP_SUPPORTED |VFCAP_OSD; - formatcount++; - primary_image_format=g_ddpf[i].img_format; - } - } - i++; + if (Directx_CreatePrimarySurface() != 0) + return 1; + if (g_lpddsPrimary->lpVtbl->GetPixelFormat(g_lpddsPrimary, &ddpf) != DD_OK) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't get pixelformat\n"); + return 1; + } + for (i = 0; i < NUM_FORMATS; i++) { + if (g_ddpf[i].g_ddpfOverlay.dwRGBBitCount == ddpf.dwRGBBitCount) { + if (g_ddpf[i].g_ddpfOverlay.dwRBitMask == ddpf.dwRBitMask) { + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><FORMAT PRIMARY>%i %s supported\n", i, g_ddpf[i].img_format_name); + drv_caps[i] = VFCAP_CSP_SUPPORTED | VFCAP_OSD; + formatcount++; + primary_image_format = g_ddpf[i].img_format; + } + } } //get the colorkey for overlay mode - destcolorkey = CLR_INVALID; - if (windowcolor != CLR_INVALID && g_lpddsPrimary->lpVtbl->GetDC(g_lpddsPrimary,&hdc) == DD_OK) - { + destcolorkey = CLR_INVALID; + if (windowcolor != CLR_INVALID && g_lpddsPrimary->lpVtbl->GetDC(g_lpddsPrimary, &hdc) == DD_OK) { rgbT = GetPixel(hdc, 0, 0); SetPixel(hdc, 0, 0, windowcolor); - g_lpddsPrimary->lpVtbl->ReleaseDC(g_lpddsPrimary,hdc); + g_lpddsPrimary->lpVtbl->ReleaseDC(g_lpddsPrimary, hdc); } // read back the converted color ddsd.dwSize = sizeof(ddsd); - while ((hres = g_lpddsPrimary->lpVtbl->Lock(g_lpddsPrimary,NULL, &ddsd, 0, NULL)) == DDERR_WASSTILLDRAWING) + while ((hres = g_lpddsPrimary->lpVtbl->Lock(g_lpddsPrimary, NULL, &ddsd, 0, NULL)) == DDERR_WASSTILLDRAWING) ; - if (hres == DD_OK) - { - destcolorkey = *(DWORD *) ddsd.lpSurface; + if (hres == DD_OK) { + destcolorkey = *(DWORD *)ddsd.lpSurface; if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32) destcolorkey &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1; - g_lpddsPrimary->lpVtbl->Unlock(g_lpddsPrimary,NULL); + g_lpddsPrimary->lpVtbl->Unlock(g_lpddsPrimary, NULL); } - if (windowcolor != CLR_INVALID && g_lpddsPrimary->lpVtbl->GetDC(g_lpddsPrimary,&hdc) == DD_OK) - { + if (windowcolor != CLR_INVALID && g_lpddsPrimary->lpVtbl->GetDC(g_lpddsPrimary, &hdc) == DD_OK) { SetPixel(hdc, 0, 0, rgbT); - g_lpddsPrimary->lpVtbl->ReleaseDC(g_lpddsPrimary,hdc); + g_lpddsPrimary->lpVtbl->ReleaseDC(g_lpddsPrimary, hdc); } - //release primary - g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary); - g_lpddsPrimary = NULL; - if(formatcount==0) - { - mp_msg(MSGT_VO, MSGL_FATAL ,"<vo_directx><FATAL ERROR>Unknown Pixelformat\n"); - return 1; - } - return 0; + //release primary + g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary); + g_lpddsPrimary = NULL; + if (formatcount == 0) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>Unknown Pixelformat\n"); + return 1; + } + return 0; } static int preinit(const char *arg) { - if(arg) - { - if(strstr(arg,"noaccel")) - { - mp_msg(MSGT_VO,MSGL_V,"<vo_directx><INFO>disabled overlay\n"); - nooverlay = 1; - } - } + if (arg) { + if (strstr(arg, "noaccel")) { + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>disabled overlay\n"); + nooverlay = 1; + } + } windowcolor = vo_colorkey; - colorbrush = CreateSolidBrush(windowcolor); - blackbrush = (HBRUSH)GetStockObject(BLACK_BRUSH); - if (!vo_w32_init()) - return 1; - if (!vo_w32_config(100, 100, VOFLAG_HIDDEN)) - return 1; - - if (Directx_InitDirectDraw()!= 0)return 1; //init DirectDraw - - if (Directx_CheckPrimaryPixelformat()!=0)return 1; - if (!nooverlay && Directx_CheckOverlayPixelformats() == 0) //check for supported hardware - { - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>hardware supports overlay\n"); - nooverlay = 0; - } - else //if we can't have overlay we create a backpuffer with the same imageformat as the primary surface - { - mp_msg(MSGT_VO, MSGL_V ,"<vo_directx><INFO>using backpuffer\n"); - nooverlay = 1; - } - mp_msg(MSGT_VO, MSGL_DBG3 ,"<vo_directx><INFO>preinit succesfully finished\n"); - return 0; + colorbrush = CreateSolidBrush(windowcolor); + blackbrush = (HBRUSH)GetStockObject(BLACK_BRUSH); + if (!vo_w32_init()) + return 1; + if (!vo_w32_config(100, 100, VOFLAG_HIDDEN)) + return 1; + + if (Directx_InitDirectDraw() != 0) + return 1; //init DirectDraw + + if (Directx_CheckPrimaryPixelformat() != 0) + return 1; + if (!nooverlay && Directx_CheckOverlayPixelformats() == 0) { //check for supported hardware + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>hardware supports overlay\n"); + nooverlay = 0; + } else { //if we can't have overlay we create a backpuffer with the same imageformat as the primary surface + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>using backpuffer\n"); + nooverlay = 1; + } + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>preinit succesfully finished\n"); + return 0; } -static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y ) +static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y) { - uint8_t *s; + uint8_t *s; uint8_t *d; - uint32_t uvstride=dstride/2; - // copy Y - d=image+dstride*y+x; - s=src[0]; - mem2agpcpy_pic(d,s,w,h,dstride,stride[0]); - - w/=2;h/=2;x/=2;y/=2; - - // copy U - d=image+dstride*image_height + uvstride*y+x; - if(image_format == IMGFMT_YV12)s=src[2]; - else s=src[1]; - mem2agpcpy_pic(d,s,w,h,uvstride,stride[1]); - - // copy V - d=image+dstride*image_height +uvstride*(image_height/2) + uvstride*y+x; - if(image_format == IMGFMT_YV12)s=src[1]; - else s=src[2]; - mem2agpcpy_pic(d,s,w,h,uvstride,stride[2]); + uint32_t uvstride = dstride / 2; + // copy Y + d = image + dstride * y + x; + s = src[0]; + mem2agpcpy_pic(d, s, w, h, dstride, stride[0]); + + w /= 2; + h /= 2; + x /= 2; + y /= 2; + + // copy U + d = image + dstride * image_height + uvstride * y + x; + if (image_format == IMGFMT_YV12) + s = src[2]; + else + s = src[1]; + mem2agpcpy_pic(d, s, w, h, uvstride, stride[1]); + + // copy V + d = image + dstride * image_height + uvstride * (image_height / 2) + uvstride * y + x; + if (image_format == IMGFMT_YV12) + s = src[1]; + else + s = src[2]; + mem2agpcpy_pic(d, s, w, h, uvstride, stride[2]); return 0; } static void flip_page(void) { - HRESULT dxresult; - g_lpddsBack->lpVtbl->Unlock (g_lpddsBack,NULL); - if (vo_doublebuffering) - { - // flip to the next image in the sequence - dxresult = g_lpddsOverlay->lpVtbl->Flip( g_lpddsOverlay,NULL, DDFLIP_WAIT); - if(dxresult == DDERR_SURFACELOST) - { - mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR><vo_directx><INFO>Restoring Surface\n"); - g_lpddsBack->lpVtbl->Restore( g_lpddsBack ); - // restore overlay and primary before calling - // Directx_ManageDisplay() to avoid error messages - g_lpddsOverlay->lpVtbl->Restore( g_lpddsOverlay ); - g_lpddsPrimary->lpVtbl->Restore( g_lpddsPrimary ); - // update overlay in case we return from screensaver - Directx_ManageDisplay(); - dxresult = g_lpddsOverlay->lpVtbl->Flip( g_lpddsOverlay,NULL, DDFLIP_WAIT); - } - if(dxresult != DD_OK)mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>can't flip page\n"); + HRESULT dxresult; + g_lpddsBack->lpVtbl->Unlock(g_lpddsBack, NULL); + if (vo_doublebuffering) { + // flip to the next image in the sequence + dxresult = g_lpddsOverlay->lpVtbl->Flip(g_lpddsOverlay, NULL, DDFLIP_WAIT); + if (dxresult == DDERR_SURFACELOST) { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR><vo_directx><INFO>Restoring Surface\n"); + g_lpddsBack->lpVtbl->Restore(g_lpddsBack); + // restore overlay and primary before calling + // Directx_ManageDisplay() to avoid error messages + g_lpddsOverlay->lpVtbl->Restore(g_lpddsOverlay); + g_lpddsPrimary->lpVtbl->Restore(g_lpddsPrimary); + // update overlay in case we return from screensaver + Directx_ManageDisplay(); + dxresult = g_lpddsOverlay->lpVtbl->Flip(g_lpddsOverlay, NULL, DDFLIP_WAIT); + } + if (dxresult != DD_OK) + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>can't flip page\n"); } - if(nooverlay) - { - DDBLTFX ddbltfx; + if (nooverlay) { // ask for the "NOTEARING" option - memset( &ddbltfx, 0, sizeof(DDBLTFX) ); - ddbltfx.dwSize = sizeof(DDBLTFX); - ddbltfx.dwDDFX = DDBLTFX_NOTEARING; + DDBLTFX ddbltfx = { + .dwSize = sizeof(ddbltfx), + .dwDDFX = DDBLTFX_NOTEARING, + }; g_lpddsPrimary->lpVtbl->Blt(g_lpddsPrimary, &rd, g_lpddsBack, NULL, DDBLT_WAIT, &ddbltfx); - } - if (g_lpddsBack->lpVtbl->Lock(g_lpddsBack,NULL,&ddsdsf, DDLOCK_NOSYSLOCK | DDLOCK_WAIT , NULL) == DD_OK) { - if(vo_directrendering && (dstride != ddsdsf.lPitch)){ - mp_msg(MSGT_VO,MSGL_WARN,"<vo_directx><WARN>stride changed !!!! disabling direct rendering\n"); - vo_directrendering=0; - } - free(tmp_image); - tmp_image = NULL; - dstride = ddsdsf.lPitch; - image = ddsdsf.lpSurface; - } else if (!tmp_image) { - mp_msg(MSGT_VO, MSGL_WARN, "<vo_directx><WARN>Locking the surface failed, rendering to a hidden surface!\n"); - tmp_image = image = calloc(1, image_height * dstride * 2); - } + } + if (g_lpddsBack->lpVtbl->Lock(g_lpddsBack, NULL, &ddsdsf, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL) == DD_OK) { + if (vo_directrendering && (dstride != ddsdsf.lPitch)) { + mp_msg(MSGT_VO, MSGL_WARN, "<vo_directx><WARN>stride changed !!!! disabling direct rendering\n"); + vo_directrendering = 0; + } + free(tmp_image); + tmp_image = NULL; + dstride = ddsdsf.lPitch; + image = ddsdsf.lpSurface; + } else if (!tmp_image) { + mp_msg(MSGT_VO, MSGL_WARN, "<vo_directx><WARN>Locking the surface failed, rendering to a hidden surface!\n"); + tmp_image = image = calloc(1, image_height * dstride * 2); + } } static int draw_frame(uint8_t *src[]) { - fast_memcpy( image, *src, dstride * image_height ); - return 0; + fast_memcpy(image, *src, dstride * image_height); + return 0; } static uint32_t get_image(mp_image_t *mpi) { - if(mpi->flags&MP_IMGFLAG_READABLE) {mp_msg(MSGT_VO, MSGL_V,"<vo_directx><ERROR>slow video ram\n");return VO_FALSE;} - if(mpi->type==MP_IMGTYPE_STATIC) {mp_msg(MSGT_VO, MSGL_V,"<vo_directx><ERROR>not static\n");return VO_FALSE;} - if((mpi->width==dstride) || (mpi->flags&(MP_IMGFLAG_ACCEPT_STRIDE|MP_IMGFLAG_ACCEPT_WIDTH))) - { - if(mpi->flags&MP_IMGFLAG_PLANAR) - { - if(image_format == IMGFMT_YV12) - { - mpi->planes[2]= image + dstride*image_height; - mpi->planes[1]= image + dstride*image_height+ dstride*image_height/4; - mpi->stride[1]=mpi->stride[2]=dstride/2; - } - else if(image_format == IMGFMT_IYUV || image_format == IMGFMT_I420) - { - mpi->planes[1]= image + dstride*image_height; - mpi->planes[2]= image + dstride*image_height+ dstride*image_height/4; - mpi->stride[1]=mpi->stride[2]=dstride/2; - } - else if(image_format == IMGFMT_YVU9) - { - mpi->planes[2] = image + dstride*image_height; - mpi->planes[1] = image + dstride*image_height+ dstride*image_height/16; - mpi->stride[1]=mpi->stride[2]=dstride/4; - } - } - mpi->planes[0]=image; - mpi->stride[0]=dstride; - mpi->width=image_width; - mpi->height=image_height; - mpi->flags|=MP_IMGFLAG_DIRECT; + if (mpi->flags & MP_IMGFLAG_READABLE) { + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR>slow video ram\n"); + return VO_FALSE; + } + if (mpi->type == MP_IMGTYPE_STATIC) { + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR>not static\n"); + return VO_FALSE; + } + if (mpi->width == dstride || (mpi->flags & (MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_ACCEPT_WIDTH))) { + if (mpi->flags & MP_IMGFLAG_PLANAR) { + if (image_format == IMGFMT_YV12) { + mpi->planes[2] = image + dstride * image_height; + mpi->planes[1] = image + dstride * image_height + dstride * image_height / 4; + mpi->stride[1] = mpi->stride[2] = dstride / 2; + } else if (image_format == IMGFMT_IYUV || image_format == IMGFMT_I420) { + mpi->planes[1] = image + dstride * image_height; + mpi->planes[2] = image + dstride * image_height + dstride * image_height / 4; + mpi->stride[1] = mpi->stride[2] = dstride / 2; + } else if (image_format == IMGFMT_YVU9) { + mpi->planes[2] = image + dstride * image_height; + mpi->planes[1] = image + dstride * image_height + dstride * image_height / 16; + mpi->stride[1] = mpi->stride[2] = dstride / 4; + } + } + mpi->planes[0] = image; + mpi->stride[0] = dstride; + mpi->width = image_width; + mpi->height = image_height; + mpi->flags |= MP_IMGFLAG_DIRECT; mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>Direct Rendering ENABLED\n"); return VO_TRUE; } return VO_FALSE; } -static uint32_t put_image(mp_image_t *mpi){ - - uint8_t *d; - uint8_t *s; - uint32_t x = mpi->x; - uint32_t y = mpi->y; - uint32_t w = mpi->w; - uint32_t h = mpi->h; - - if((mpi->flags&MP_IMGFLAG_DIRECT)||(mpi->flags&MP_IMGFLAG_DRAW_CALLBACK)) - { - mp_msg(MSGT_VO, MSGL_DBG3 ,"<vo_directx><INFO>put_image: nothing to do: drawslices\n"); - return VO_TRUE; +static uint32_t put_image(mp_image_t *mpi) +{ + uint8_t *d; + uint8_t *s; + uint32_t x = 0; + uint32_t y = 0; + uint32_t w = mpi->w; + uint32_t h = mpi->h; + + if ((mpi->flags & MP_IMGFLAG_DIRECT) || (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) { + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>put_image: nothing to do: drawslices\n"); + return VO_TRUE; } - if (mpi->flags&MP_IMGFLAG_PLANAR) - { - - if(image_format!=IMGFMT_YVU9)draw_slice(mpi->planes,mpi->stride,mpi->w,mpi->h,0,0); - else - { - // copy Y - d=image+dstride*y+x; - s=mpi->planes[0]; - mem2agpcpy_pic(d,s,w,h,dstride,mpi->stride[0]); - w/=4;h/=4;x/=4;y/=4; - // copy V - d=image+dstride*image_height + dstride*y/4+x; - s=mpi->planes[2]; - mem2agpcpy_pic(d,s,w,h,dstride/4,mpi->stride[1]); - // copy U - d=image+dstride*image_height + dstride*image_height/16 + dstride/4*y+x; - s=mpi->planes[1]; - mem2agpcpy_pic(d,s,w,h,dstride/4,mpi->stride[2]); - } - } - else //packed - { - mem2agpcpy_pic(image, mpi->planes[0], w * (mpi->bpp / 8), h, dstride, mpi->stride[0]); - } - return VO_TRUE; + if (mpi->flags & MP_IMGFLAG_PLANAR) { + if (image_format != IMGFMT_YVU9) + draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0); + else { + // copy Y + d = image + dstride * y + x; + s = mpi->planes[0]; + mem2agpcpy_pic(d, s, w, h, dstride, mpi->stride[0]); + w /= 4; + h /= 4; + x /= 4; + y /= 4; + // copy V + d = image + dstride * image_height + dstride * y / 4 + x; + s = mpi->planes[2]; + mem2agpcpy_pic(d, s, w, h, dstride / 4, mpi->stride[1]); + // copy U + d = image + dstride * image_height + dstride * image_height / 16 + dstride / 4 * y + x; + s = mpi->planes[1]; + mem2agpcpy_pic(d, s, w, h, dstride / 4, mpi->stride[2]); + } + } else { //packed + mem2agpcpy_pic(image, mpi->planes[0], w * (mpi->bpp / 8), h, dstride, mpi->stride[0]); + } + return VO_TRUE; } -static int -config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t options, char *title, uint32_t format) +static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t options, char *title, uint32_t format) { - image_format = format; - image_width = width; - image_height = height; - if(format != primary_image_format)nooverlay = 0; + image_format = format; + image_width = width; + image_height = height; + if (format != primary_image_format) + nooverlay = 0; /*release all directx objects*/ - if (g_cc != NULL)g_cc->lpVtbl->Release(g_cc); - g_cc=NULL; - if(g_lpddclipper)g_lpddclipper->lpVtbl->Release(g_lpddclipper); - g_lpddclipper=NULL; - if (g_lpddsBack != NULL) g_lpddsBack->lpVtbl->Release(g_lpddsBack); + if (g_cc) + g_cc->lpVtbl->Release(g_cc); + g_cc = NULL; + if (g_lpddclipper) + g_lpddclipper->lpVtbl->Release(g_lpddclipper); + g_lpddclipper = NULL; + if (g_lpddsBack) + g_lpddsBack->lpVtbl->Release(g_lpddsBack); g_lpddsBack = NULL; - if(vo_doublebuffering) - if (g_lpddsOverlay != NULL)g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay); + if (vo_doublebuffering) + if (g_lpddsOverlay) + g_lpddsOverlay->lpVtbl->Release(g_lpddsOverlay); g_lpddsOverlay = NULL; - if (g_lpddsPrimary != NULL) g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary); + if (g_lpddsPrimary) + g_lpddsPrimary->lpVtbl->Release(g_lpddsPrimary); g_lpddsPrimary = NULL; - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>overlay surfaces released\n"); - - if (!vo_w32_config(d_width, d_height, options)) - return 1; - - if (WinID == -1) - SetWindowText(vo_w32_window,title); - - - /*create the surfaces*/ - if(Directx_CreatePrimarySurface())return 1; - - //create palette for 256 color mode - if(image_format==IMGFMT_BGR8){ - LPDIRECTDRAWPALETTE ddpalette=NULL; - LPPALETTEENTRY palette=calloc(256, sizeof(*palette)); - int i; - for(i=0; i<256; i++){ - palette[i].peRed = ((i >> 5) & 0x07) * 255 / 7; - palette[i].peGreen = ((i >> 2) & 0x07) * 255 / 7; - palette[i].peBlue = ((i >> 0) & 0x03) * 255 / 3; - palette[i].peFlags = PC_NOCOLLAPSE; - } - g_lpdd->lpVtbl->CreatePalette(g_lpdd,DDPCAPS_8BIT|DDPCAPS_INITIALIZE,palette,&ddpalette,NULL); - g_lpddsPrimary->lpVtbl->SetPalette(g_lpddsPrimary,ddpalette); - free(palette); - ddpalette->lpVtbl->Release(ddpalette); - } - - if (!nooverlay && Directx_CreateOverlay(image_format)) - { - if(format == primary_image_format)nooverlay=1; /*overlay creation failed*/ - else { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't use overlay mode: please use -vo directx:noaccel\n"); - return 1; - } - } - if(nooverlay) - { - if(Directx_CreateBackpuffer()) - { - mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't get the driver to work on your system :(\n"); - return 1; - } - mp_msg(MSGT_VO, MSGL_V,"<vo_directx><INFO>back surface created\n"); - vo_doublebuffering = 0; - /*create clipper for nonoverlay mode*/ - if(g_lpdd->lpVtbl->CreateClipper(g_lpdd, 0, &g_lpddclipper,NULL)!= DD_OK){mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't create clipper\n");return 1;} - if(g_lpddclipper->lpVtbl->SetHWnd (g_lpddclipper, 0, vo_w32_window)!= DD_OK){mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't associate clipper with window\n");return 1;} - if(g_lpddsPrimary->lpVtbl->SetClipper (g_lpddsPrimary,g_lpddclipper)!=DD_OK){mp_msg(MSGT_VO, MSGL_FATAL,"<vo_directx><FATAL ERROR>can't associate primary surface with clipper\n");return 1;} - mp_msg(MSGT_VO, MSGL_DBG3,"<vo_directx><INFO>clipper succesfully created\n"); - }else{ - if(DD_OK != g_lpddsOverlay->lpVtbl->QueryInterface(g_lpddsOverlay,&IID_IDirectDrawColorControl,(void**)&g_cc)) - mp_msg(MSGT_VO, MSGL_V,"<vo_directx><WARN>unable to get DirectDraw ColorControl interface\n"); - } - Directx_ManageDisplay(); - memset(&ddsdsf, 0,sizeof(DDSURFACEDESC2)); - ddsdsf.dwSize = sizeof (DDSURFACEDESC2); - if (g_lpddsBack->lpVtbl->Lock(g_lpddsBack,NULL,&ddsdsf, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL) == DD_OK) { + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>overlay surfaces released\n"); + + if (!vo_w32_config(d_width, d_height, options)) + return 1; + + /*create the surfaces*/ + if (Directx_CreatePrimarySurface()) + return 1; + + //create palette for 256 color mode + if (image_format == IMGFMT_BGR8) { + LPDIRECTDRAWPALETTE ddpalette = NULL; + LPPALETTEENTRY palette = calloc(256, sizeof(*palette)); + int i; + for (i = 0; i < 256; i++) { + palette[i].peRed = ((i >> 5) & 0x07) * 255 / 7; + palette[i].peGreen = ((i >> 2) & 0x07) * 255 / 7; + palette[i].peBlue = ((i >> 0) & 0x03) * 255 / 3; + palette[i].peFlags = PC_NOCOLLAPSE; + } + g_lpdd->lpVtbl->CreatePalette(g_lpdd, DDPCAPS_8BIT | DDPCAPS_INITIALIZE, palette, &ddpalette, NULL); + g_lpddsPrimary->lpVtbl->SetPalette(g_lpddsPrimary, ddpalette); + free(palette); + ddpalette->lpVtbl->Release(ddpalette); + } + + if (!nooverlay && Directx_CreateOverlay(image_format)) { + if (format == primary_image_format) + nooverlay = 1; /*overlay creation failed*/ + else { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't use overlay mode: please use -vo directx:noaccel\n"); + return 1; + } + } + if (nooverlay) { + if (Directx_CreateBackpuffer()) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't get the driver to work on your system :(\n"); + return 1; + } + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><INFO>back surface created\n"); + vo_doublebuffering = 0; + /*create clipper for nonoverlay mode*/ + if (g_lpdd->lpVtbl->CreateClipper(g_lpdd, 0, &g_lpddclipper, NULL) != DD_OK) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't create clipper\n"); + return 1; + } + if (g_lpddclipper->lpVtbl->SetHWnd(g_lpddclipper, 0, vo_w32_window) != DD_OK) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't associate clipper with window\n"); + return 1; + } + if (g_lpddsPrimary->lpVtbl->SetClipper(g_lpddsPrimary, g_lpddclipper) != DD_OK) { + mp_msg(MSGT_VO, MSGL_FATAL, "<vo_directx><FATAL ERROR>can't associate primary surface with clipper\n"); + return 1; + } + mp_msg(MSGT_VO, MSGL_DBG3, "<vo_directx><INFO>clipper succesfully created\n"); + } else { + if (DD_OK != g_lpddsOverlay->lpVtbl->QueryInterface(g_lpddsOverlay, &IID_IDirectDrawColorControl, (void **)&g_cc)) + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><WARN>unable to get DirectDraw ColorControl interface\n"); + } + Directx_ManageDisplay(); + memset(&ddsdsf, 0, sizeof(DDSURFACEDESC2)); + ddsdsf.dwSize = sizeof(DDSURFACEDESC2); + if (g_lpddsBack->lpVtbl->Lock(g_lpddsBack, NULL, &ddsdsf, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL) == DD_OK) { dstride = ddsdsf.lPitch; - image = ddsdsf.lpSurface; + image = ddsdsf.lpSurface; return 0; - } - mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR>Initial Lock on the Surface failed.\n"); - return 1; + } + mp_msg(MSGT_VO, MSGL_V, "<vo_directx><ERROR>Initial Lock on the Surface failed.\n"); + return 1; } //function to set color controls @@ -1063,121 +1034,114 @@ config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uin // saturation [0, 20000] static uint32_t color_ctrl_set(const char *what, int value) { - uint32_t r = VO_NOTIMPL; - DDCOLORCONTROL dcc; - //printf("\n*** %s = %d\n", what, value); - if (!g_cc) { - //printf("\n *** could not get color control interface!!!\n"); - return VO_NOTIMPL; - } - ZeroMemory(&dcc, sizeof(dcc)); - dcc.dwSize = sizeof(dcc); - - if (!strcmp(what, "brightness")) { - dcc.dwFlags = DDCOLOR_BRIGHTNESS; - dcc.lBrightness = (value + 100) * 10000 / 200; - r = VO_TRUE; - } else if (!strcmp(what, "contrast")) { - dcc.dwFlags = DDCOLOR_CONTRAST; - dcc.lContrast = (value + 100) * 20000 / 200; - r = VO_TRUE; - } else if (!strcmp(what, "hue")) { - dcc.dwFlags = DDCOLOR_HUE; - dcc.lHue = value * 180 / 100; - r = VO_TRUE; - } else if (!strcmp(what, "saturation")) { - dcc.dwFlags = DDCOLOR_SATURATION; - dcc.lSaturation = (value + 100) * 20000 / 200; - r = VO_TRUE; - } - - if (r == VO_TRUE) { - g_cc->lpVtbl->SetColorControls(g_cc, &dcc); - } - return r; + uint32_t r = VO_NOTIMPL; + DDCOLORCONTROL dcc = { .dwSize = sizeof(dcc) }; + //printf("\n*** %s = %d\n", what, value); + if (!g_cc) { + //printf("\n *** could not get color control interface!!!\n"); + return VO_NOTIMPL; + } + + if (!strcmp(what, "brightness")) { + dcc.dwFlags = DDCOLOR_BRIGHTNESS; + dcc.lBrightness = (value + 100) * 10000 / 200; + r = VO_TRUE; + } else if (!strcmp(what, "contrast")) { + dcc.dwFlags = DDCOLOR_CONTRAST; + dcc.lContrast = (value + 100) * 20000 / 200; + r = VO_TRUE; + } else if (!strcmp(what, "hue")) { + dcc.dwFlags = DDCOLOR_HUE; + dcc.lHue = value * 180 / 100; + r = VO_TRUE; + } else if (!strcmp(what, "saturation")) { + dcc.dwFlags = DDCOLOR_SATURATION; + dcc.lSaturation = (value + 100) * 20000 / 200; + r = VO_TRUE; + } + + if (r == VO_TRUE) { + g_cc->lpVtbl->SetColorControls(g_cc, &dcc); + } + return r; } //analoguous to color_ctrl_set static uint32_t color_ctrl_get(const char *what, int *value) { - uint32_t r = VO_NOTIMPL; - DDCOLORCONTROL dcc; - if (!g_cc) { - //printf("\n *** could not get color control interface!!!\n"); - return VO_NOTIMPL; - } - ZeroMemory(&dcc, sizeof(dcc)); - dcc.dwSize = sizeof(dcc); - - if (g_cc->lpVtbl->GetColorControls(g_cc, &dcc) != DD_OK) { - return r; - } - - if (!strcmp(what, "brightness") && (dcc.dwFlags & DDCOLOR_BRIGHTNESS)) { - *value = dcc.lBrightness * 200 / 10000 - 100; - r = VO_TRUE; - } else if (!strcmp(what, "contrast") && (dcc.dwFlags & DDCOLOR_CONTRAST)) { - *value = dcc.lContrast * 200 / 20000 - 100; - r = VO_TRUE; - } else if (!strcmp(what, "hue") && (dcc.dwFlags & DDCOLOR_HUE)) { - *value = dcc.lHue * 100 / 180; - r = VO_TRUE; - } else if (!strcmp(what, "saturation") && (dcc.dwFlags & DDCOLOR_SATURATION)) { - *value = dcc.lSaturation * 200 / 20000 - 100; - r = VO_TRUE; - } + uint32_t r = VO_NOTIMPL; + DDCOLORCONTROL dcc = { .dwSize = sizeof(dcc) }; + if (!g_cc) { + //printf("\n *** could not get color control interface!!!\n"); + return VO_NOTIMPL; + } + + if (g_cc->lpVtbl->GetColorControls(g_cc, &dcc) != DD_OK) { + return r; + } + + if (!strcmp(what, "brightness") && (dcc.dwFlags & DDCOLOR_BRIGHTNESS)) { + *value = dcc.lBrightness * 200 / 10000 - 100; + r = VO_TRUE; + } else if (!strcmp(what, "contrast") && (dcc.dwFlags & DDCOLOR_CONTRAST)) { + *value = dcc.lContrast * 200 / 20000 - 100; + r = VO_TRUE; + } else if (!strcmp(what, "hue") && (dcc.dwFlags & DDCOLOR_HUE)) { + *value = dcc.lHue * 100 / 180; + r = VO_TRUE; + } else if (!strcmp(what, "saturation") && (dcc.dwFlags & DDCOLOR_SATURATION)) { + *value = dcc.lSaturation * 200 / 20000 - 100; + r = VO_TRUE; + } // printf("\n*** %s = %d\n", what, *value); - return r; + return r; } static int control(uint32_t request, void *data) { switch (request) { - - case VOCTRL_GET_IMAGE: - return get_image(data); + case VOCTRL_GET_IMAGE: + return get_image(data); case VOCTRL_QUERY_FORMAT: - return query_format(*((uint32_t*)data)); - case VOCTRL_DRAW_IMAGE: + return query_format(*(uint32_t *)data); + case VOCTRL_DRAW_IMAGE: return put_image(data); case VOCTRL_BORDER: vo_w32_border(); Directx_ManageDisplay(); - return VO_TRUE; + return VO_TRUE; case VOCTRL_ONTOP: vo_w32_ontop(); - return VO_TRUE; + return VO_TRUE; case VOCTRL_ROOTWIN: - if(WinID != -1) return VO_TRUE; - if(vidmode) - { - mp_msg(MSGT_VO, MSGL_ERR,"<vo_directx><ERROR>rootwin has no meaning in exclusive mode\n"); - } - else - { - if(vo_rootwin) vo_rootwin = 0; - else vo_rootwin = 1; - Directx_ManageDisplay(); - } - return VO_TRUE; - case VOCTRL_FULLSCREEN: - { - vo_w32_fullscreen(); + if (WinID != -1) + return VO_TRUE; + if (vidmode) { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_directx><ERROR>rootwin has no meaning in exclusive mode\n"); + } else { + if (vo_rootwin) + vo_rootwin = 0; + else + vo_rootwin = 1; Directx_ManageDisplay(); - return VO_TRUE; - } - case VOCTRL_SET_EQUALIZER: { - struct voctrl_set_equalizer_args *args = data; - return color_ctrl_set(args->name, args->value); - } - case VOCTRL_GET_EQUALIZER: { - struct voctrl_get_equalizer_args *args = data; - return color_ctrl_get(args->name, args->valueptr); - } + } + return VO_TRUE; + case VOCTRL_FULLSCREEN: + vo_w32_fullscreen(); + Directx_ManageDisplay(); + return VO_TRUE; + case VOCTRL_SET_EQUALIZER: { + struct voctrl_set_equalizer_args *args = data; + return color_ctrl_set(args->name, args->value); + } + case VOCTRL_GET_EQUALIZER: { + struct voctrl_get_equalizer_args *args = data; + return color_ctrl_get(args->name, args->valueptr); + } case VOCTRL_UPDATE_SCREENINFO: w32_update_xinerama_info(); return VO_TRUE; - }; + } return VO_NOTIMPL; } diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c index 12749426ab..adb5c55a74 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -38,6 +38,7 @@ #include "osd.h" #include "sub/font_load.h" #include "sub/sub.h" +#include "eosd_packer.h" #include "gl_common.h" #include "aspect.h" @@ -49,19 +50,17 @@ static int preinit_nosw(struct vo *vo, const char *arg); //! How many parts the OSD may consist of at most #define MAX_OSD_PARTS 20 -#define LARGE_EOSD_TEX_SIZE 512 -#define TINYTEX_SIZE 16 -#define TINYTEX_COLS (LARGE_EOSD_TEX_SIZE / TINYTEX_SIZE) -#define TINYTEX_MAX (TINYTEX_COLS * TINYTEX_COLS) -#define SMALLTEX_SIZE 32 -#define SMALLTEX_COLS (LARGE_EOSD_TEX_SIZE / SMALLTEX_SIZE) -#define SMALLTEX_MAX (SMALLTEX_COLS * SMALLTEX_COLS) - //for gl_priv.use_yuv #define MASK_ALL_YUV (~(1 << YUV_CONVERSION_NONE)) #define MASK_NOT_COMBINERS (~((1 << YUV_CONVERSION_NONE) | (1 << YUV_CONVERSION_COMBINERS))) #define MASK_GAMMA_SUPPORT (MASK_NOT_COMBINERS & ~(1 << YUV_CONVERSION_FRAGMENT)) +struct vertex_eosd { + float x, y; + uint8_t color[4]; + float u, v; +}; + struct gl_priv { MPGLContext *glctx; GL *gl; @@ -74,17 +73,17 @@ struct gl_priv { //! Alpha textures for OSD GLuint osdatex[MAX_OSD_PARTS]; #endif - GLuint *eosdtex; - GLuint largeeosdtex[2]; + GLuint eosd_texture; + int eosd_texture_width, eosd_texture_height; + struct eosd_packer *eosd; + struct vertex_eosd *eosd_va; //! Display lists that draw the OSD parts GLuint osdDispList[MAX_OSD_PARTS]; #ifndef FAST_OSD GLuint osdaDispList[MAX_OSD_PARTS]; #endif - GLuint eosdDispList; //! How many parts the OSD currently consists of int osdtexCnt; - int eosdtexCnt; int osd_color; int use_ycbcr; @@ -94,6 +93,7 @@ struct gl_priv { int lscale; int cscale; float filter_strength; + float noise_strength; int yuvconvtype; int use_rectangle; int err_shown; @@ -215,7 +215,8 @@ static void update_yuvconv(struct vo *vo) mp_csp_copy_equalizer_values(&cparams, &p->video_eq); gl_conversion_params_t params = { p->target, p->yuvconvtype, cparams, - p->texture_width, p->texture_height, 0, 0, p->filter_strength + p->texture_width, p->texture_height, 0, 0, p->filter_strength, + p->noise_strength }; mp_get_chroma_shift(p->image_format, &xs, &ys, &depth); params.chrom_texw = params.texw >> xs; @@ -286,150 +287,111 @@ static void clearOSD(struct vo *vo) } /** - * \brief remove textures, display list and free memory used by EOSD + * \brief construct display list from ass image list + * \param img image list to create OSD from. */ -static void clearEOSD(struct vo *vo) +static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs) { struct gl_priv *p = vo->priv; GL *gl = p->gl; - if (p->eosdDispList) - gl->DeleteLists(p->eosdDispList, 1); - p->eosdDispList = 0; - if (p->eosdtexCnt) - gl->DeleteTextures(p->eosdtexCnt, p->eosdtex); - p->eosdtexCnt = 0; - free(p->eosdtex); - p->eosdtex = NULL; -} + bool need_repos, need_upload, need_allocate; + eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload, + &need_allocate); -static inline int is_tinytex(ASS_Image *i, int tinytexcur) -{ - return i->w < TINYTEX_SIZE && i->h < TINYTEX_SIZE - && tinytexcur < TINYTEX_MAX; -} + if (!need_repos) + return; -static inline int is_smalltex(ASS_Image *i, int smalltexcur) -{ - return i->w < SMALLTEX_SIZE && i->h < SMALLTEX_SIZE - && smalltexcur < SMALLTEX_MAX; -} + if (!p->eosd_texture) + gl->GenTextures(1, &p->eosd_texture); -static inline void tinytex_pos(int tinytexcur, int *x, int *y) -{ - *x = (tinytexcur % TINYTEX_COLS) * TINYTEX_SIZE; - *y = (tinytexcur / TINYTEX_COLS) * TINYTEX_SIZE; -} + gl->BindTexture(p->target, p->eosd_texture); -static inline void smalltex_pos(int smalltexcur, int *x, int *y) -{ - *x = (smalltexcur % SMALLTEX_COLS) * SMALLTEX_SIZE; - *y = (smalltexcur / SMALLTEX_COLS) * SMALLTEX_SIZE; + if (need_allocate) { + texSize(vo, p->eosd->surface.w, p->eosd->surface.h, + &p->eosd_texture_width, &p->eosd_texture_height); + // xxx it doesn't need to be cleared, that's a waste of time + glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, + GL_NEAREST, p->eosd_texture_width, + p->eosd_texture_height, 0); + } + + // 2 triangles primitives per quad = 6 vertices per quad + // not using GL_QUADS, as it is deprecated in OpenGL 3.x and later + p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va, + p->eosd->targets_count + * sizeof(struct vertex_eosd) * 6); + + float eosd_w = p->eosd_texture_width; + float eosd_h = p->eosd_texture_height; + + if (p->use_rectangle == 1) + eosd_w = eosd_h = 1.0f; + + for (int n = 0; n < p->eosd->targets_count; n++) { + struct eosd_target *target = &p->eosd->targets[n]; + ASS_Image *i = target->ass_img; + + if (need_upload) { + glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap, + i->stride, target->source.x0, target->source.y0, + i->w, i->h, 0); + } + + uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff, + (i->color >> 8) & 0xff, 255 - (i->color & 0xff) }; + + float x0 = target->dest.x0; + float y0 = target->dest.y0; + float x1 = target->dest.x1; + float y1 = target->dest.y1; + float tx0 = target->source.x0 / eosd_w; + float ty0 = target->source.y0 / eosd_h; + float tx1 = target->source.x1 / eosd_w; + float ty1 = target->source.y1 / eosd_h; + +#define COLOR_INIT {color[0], color[1], color[2], color[3]} + struct vertex_eosd *va = &p->eosd_va[n * 6]; + va[0] = (struct vertex_eosd) { x0, y0, COLOR_INIT, tx0, ty0 }; + va[1] = (struct vertex_eosd) { x0, y1, COLOR_INIT, tx0, ty1 }; + va[2] = (struct vertex_eosd) { x1, y0, COLOR_INIT, tx1, ty0 }; + va[3] = (struct vertex_eosd) { x1, y1, COLOR_INIT, tx1, ty1 }; + va[4] = va[2]; + va[5] = va[1]; +#undef COLOR_INIT + } + + gl->BindTexture(p->target, 0); } -/** - * \brief construct display list from ass image list - * \param img image list to create OSD from. - * A value of NULL has the same effect as clearEOSD() - */ -static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs) +// Note: relies on state being setup, like projection matrix and blending +static void drawEOSD(struct vo *vo) { struct gl_priv *p = vo->priv; GL *gl = p->gl; - int sx, sy; - int tinytexcur = 0; - int smalltexcur = 0; - GLuint *curtex; - GLint scale_type = p->scaled_osd ? GL_LINEAR : GL_NEAREST; - ASS_Image *img = imgs->imgs; - ASS_Image *i; - - if (imgs->changed == 0) // there are elements, but they are unchanged + if (p->eosd->targets_count == 0) return; - if (img && imgs->changed == 1) // there are elements, but they just moved - goto skip_upload; - clearEOSD(vo); - if (!img) - return; - if (!p->largeeosdtex[0]) { - gl->GenTextures(2, p->largeeosdtex); - for (int n = 0; n < 2; n++) { - gl->BindTexture(p->target, p->largeeosdtex[n]); - glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, - GL_UNSIGNED_BYTE, scale_type, - LARGE_EOSD_TEX_SIZE, LARGE_EOSD_TEX_SIZE, 0); - } - } - for (i = img; i; i = i->next) { - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) - continue; - if (is_tinytex(i, tinytexcur)) - tinytexcur++; - else if (is_smalltex(i, smalltexcur)) - smalltexcur++; - else - p->eosdtexCnt++; - } - mp_msg(MSGT_VO, MSGL_DBG2, "EOSD counts (tiny, small, all): %i, %i, %i\n", - tinytexcur, smalltexcur, p->eosdtexCnt); - if (p->eosdtexCnt) { - p->eosdtex = calloc(p->eosdtexCnt, sizeof(GLuint)); - gl->GenTextures(p->eosdtexCnt, p->eosdtex); - } - tinytexcur = smalltexcur = 0; - for (i = img, curtex = p->eosdtex; i; i = i->next) { - int x = 0, y = 0; - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) { - mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); - continue; - } - if (is_tinytex(i, tinytexcur)) { - tinytex_pos(tinytexcur, &x, &y); - gl->BindTexture(p->target, p->largeeosdtex[0]); - tinytexcur++; - } else if (is_smalltex(i, smalltexcur)) { - smalltex_pos(smalltexcur, &x, &y); - gl->BindTexture(p->target, p->largeeosdtex[1]); - smalltexcur++; - } else { - texSize(vo, i->w, i->h, &sx, &sy); - gl->BindTexture(p->target, *curtex++); - glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, - GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); - } - glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap, - i->stride, x, y, i->w, i->h, 0); - } - p->eosdDispList = gl->GenLists(1); -skip_upload: - gl->NewList(p->eosdDispList, GL_COMPILE); - tinytexcur = smalltexcur = 0; - for (i = img, curtex = p->eosdtex; i; i = i->next) { - int x = 0, y = 0; - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) - continue; - gl->Color4ub(i->color >> 24, (i->color >> 16) & 0xff, - (i->color >> 8) & 0xff, 255 - (i->color & 0xff)); - if (is_tinytex(i, tinytexcur)) { - tinytex_pos(tinytexcur, &x, &y); - sx = sy = LARGE_EOSD_TEX_SIZE; - gl->BindTexture(p->target, p->largeeosdtex[0]); - tinytexcur++; - } else if (is_smalltex(i, smalltexcur)) { - smalltex_pos(smalltexcur, &x, &y); - sx = sy = LARGE_EOSD_TEX_SIZE; - gl->BindTexture(p->target, p->largeeosdtex[1]); - smalltexcur++; - } else { - texSize(vo, i->w, i->h, &sx, &sy); - gl->BindTexture(p->target, *curtex++); - } - glDrawTex(gl, i->dst_x, i->dst_y, i->w, i->h, x, y, i->w, i->h, sx, sy, - p->use_rectangle == 1, 0, 0); - } - gl->EndList(); + gl->BindTexture(p->target, p->eosd_texture); + + struct vertex_eosd *va = p->eosd_va; + size_t stride = sizeof(struct vertex_eosd); + + gl->VertexPointer(2, GL_FLOAT, stride, &va[0].x); + gl->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &va[0].color[0]); + gl->TexCoordPointer(2, GL_FLOAT, stride, &va[0].u); + + gl->EnableClientState(GL_VERTEX_ARRAY); + gl->EnableClientState(GL_TEXTURE_COORD_ARRAY); + gl->EnableClientState(GL_COLOR_ARRAY); + + gl->DrawArrays(GL_TRIANGLES, 0, p->eosd->targets_count * 6); + + gl->DisableClientState(GL_VERTEX_ARRAY); + gl->DisableClientState(GL_TEXTURE_COORD_ARRAY); + gl->DisableClientState(GL_COLOR_ARRAY); + gl->BindTexture(p->target, 0); } @@ -451,10 +413,10 @@ static void uninitGl(struct vo *vo) gl->DeleteTextures(i, p->default_texs); p->default_texs[0] = 0; clearOSD(vo); - clearEOSD(vo); - if (p->largeeosdtex[0]) - gl->DeleteTextures(2, p->largeeosdtex); - p->largeeosdtex[0] = 0; + if (p->eosd_texture) + gl->DeleteTextures(1, &p->eosd_texture); + eosd_packer_reinit(p->eosd, 0, 0); + p->eosd_texture = 0; if (gl->DeleteBuffers && p->buffer) gl->DeleteBuffers(1, &p->buffer); p->buffer = 0; @@ -634,6 +596,10 @@ static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height) update_yuvconv(vo); } + GLint max_texture_size; + gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); + eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size); + resize(vo, d_width, d_height); gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -787,7 +753,7 @@ static void do_render_osd(struct vo *vo, int type) GL *gl = p->gl; int draw_osd = (type & RENDER_OSD) && p->osdtexCnt > 0; - int draw_eosd = (type & RENDER_EOSD) && p->eosdDispList; + int draw_eosd = (type & RENDER_EOSD); if (!draw_osd && !draw_eosd) return; // set special rendering parameters @@ -800,7 +766,7 @@ static void do_render_osd(struct vo *vo, int type) gl->Enable(GL_BLEND); if (draw_eosd) { gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl->CallList(p->eosdDispList); + drawEOSD(vo); } if (draw_osd) { gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff, @@ -848,7 +814,7 @@ static void do_render(struct vo *vo) // Enable(GL_TEXTURE_2D); // BindTexture(GL_TEXTURE_2D, texture_id); - gl->Color3f(1, 1, 1); + gl->Color4f(1, 1, 1, 1); if (p->is_yuv || p->custom_prog) glEnableYUVConversion(gl, p->target, p->yuvconvtype); if (p->stereo_mode) { @@ -1092,7 +1058,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi) slice = 0; // always "upload" full texture } glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[0], - stride[0], mpi->x, mpi->y, w, h, slice); + stride[0], 0, 0, w, h, slice); if (p->is_yuv) { int xs, ys; mp_get_chroma_shift(p->image_format, &xs, &ys, NULL); @@ -1103,8 +1069,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi) } gl->ActiveTexture(GL_TEXTURE1); glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[1], - stride[1], mpi->x >> xs, mpi->y >> ys, w >> xs, h >> ys, - slice); + stride[1], 0, 0, w >> xs, h >> ys, slice); if ((mpi->flags & MP_IMGFLAG_DIRECT) && !(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) { gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]); gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER); @@ -1112,8 +1077,7 @@ static uint32_t draw_image(struct vo *vo, mp_image_t *mpi) } gl->ActiveTexture(GL_TEXTURE2); glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[2], - stride[2], mpi->x >> xs, mpi->y >> ys, w >> xs, h >> ys, - slice); + stride[2], 0, 0, w >> xs, h >> ys, slice); gl->ActiveTexture(GL_TEXTURE0); } if (mpi->flags & MP_IMGFLAG_DIRECT) { @@ -1218,8 +1182,12 @@ static void uninit(struct vo *vo) p->gl = NULL; } -static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, - enum MPGLType gltype) +static int backend_valid(void *arg) +{ + return mpgl_find_backend(*(const char **)arg) >= 0; +} + +static int preinit_internal(struct vo *vo, const char *arg, int allow_sw) { struct gl_priv *p = talloc_zero(vo, struct gl_priv); vo->priv = p; @@ -1240,10 +1208,13 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, .osd_color = 0xffffff, }; + p->eosd = eosd_packer_create(vo); + //essentially unused; for legacy warnings only int user_colorspace = 0; int levelconv = -1; int aspect = -1; + char *backend_arg = NULL; const opt_t subopts[] = { {"manyfmts", OPT_ARG_BOOL, &p->many_fmts, NULL}, @@ -1256,6 +1227,7 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, {"lscale", OPT_ARG_INT, &p->lscale, int_non_neg}, {"cscale", OPT_ARG_INT, &p->cscale, int_non_neg}, {"filter-strength", OPT_ARG_FLOAT, &p->filter_strength, NULL}, + {"noise-strength", OPT_ARG_FLOAT, &p->noise_strength, NULL}, {"ati-hack", OPT_ARG_BOOL, &p->ati_hack, NULL}, {"force-pbo", OPT_ARG_BOOL, &p->force_pbo, NULL}, {"glfinish", OPT_ARG_BOOL, &p->use_glFinish, NULL}, @@ -1267,6 +1239,7 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, {"mipmapgen", OPT_ARG_BOOL, &p->mipmap_gen, NULL}, {"osdcolor", OPT_ARG_INT, &p->osd_color, NULL}, {"stereo", OPT_ARG_INT, &p->stereo_mode, NULL}, + {"backend", OPT_ARG_MSTRZ,&backend_arg, backend_valid}, // Removed options. // They are only parsed to notify the user about the replacements. {"aspect", OPT_ARG_BOOL, &aspect, NULL}, @@ -1323,6 +1296,8 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, " as lscale but for chroma (2x slower with little visible effect).\n" " filter-strength=<value>\n" " set the effect strength for some lscale/cscale filters\n" + " noise-strength=<value>\n" + " set how much noise to add. 1.0 is suitable for dithering to 6 bit.\n" " customprog=<filename>\n" " use a custom YUV conversion program\n" " customtex=<filename>\n" @@ -1340,6 +1315,12 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, " 1: side-by-side to red-cyan stereo\n" " 2: side-by-side to green-magenta stereo\n" " 3: side-by-side to quadbuffer stereo\n" + " backend=<sys>\n" + " auto: auto-select (default)\n" + " cocoa: Cocoa/OSX\n" + " win: Win32/WGL\n" + " x11: X11/GLX\n" + " sdl: SDL\n" "\n"); return -1; } @@ -1359,7 +1340,11 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, " been removed, using yuv=2 instead.\n"); p->use_yuv = 2; } - p->glctx = init_mpglcontext(gltype, vo); + + int backend = backend_arg ? mpgl_find_backend(backend_arg) : GLTYPE_AUTO; + free(backend_arg); + + p->glctx = init_mpglcontext(backend, vo); if (!p->glctx) goto err_out; p->gl = p->glctx->gl; @@ -1383,7 +1368,7 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, // acceleration and so on. Destroy that window to make sure all state // associated with it is lost. uninit(vo); - p->glctx = init_mpglcontext(gltype, vo); + p->glctx = init_mpglcontext(backend, vo); if (!p->glctx) goto err_out; p->gl = p->glctx->gl; @@ -1403,7 +1388,7 @@ err_out: static int preinit(struct vo *vo, const char *arg) { - return preinit_internal(vo, arg, 1, GLTYPE_AUTO); + return preinit_internal(vo, arg, 1); } static int control(struct vo *vo, uint32_t request, void *data) @@ -1528,7 +1513,7 @@ const struct vo_driver video_out_gl = { static int preinit_nosw(struct vo *vo, const char *arg) { - return preinit_internal(vo, arg, 0, GLTYPE_AUTO); + return preinit_internal(vo, arg, 0); } const struct vo_driver video_out_gl_nosw = @@ -1549,28 +1534,3 @@ const struct vo_driver video_out_gl_nosw = .check_events = check_events, .uninit = uninit, }; - -#ifdef CONFIG_GL_SDL -static int preinit_sdl(struct vo *vo, const char *arg) -{ - return preinit_internal(vo, arg, 1, GLTYPE_SDL); -} - -const struct vo_driver video_out_gl_sdl = { - .is_new = true, - .info = &(const vo_info_t) { - "OpenGL with SDL", - "gl_sdl", - "Reimar Doeffinger <Reimar.Doeffinger@gmx.de>", - "" - }, - .preinit = preinit_sdl, - .config = config, - .control = control, - .draw_slice = draw_slice, - .draw_osd = draw_osd, - .flip_page = flip_page, - .check_events = check_events, - .uninit = uninit, -}; -#endif diff --git a/libvo/vo_gl3.c b/libvo/vo_gl3.c new file mode 100644 index 0000000000..1947839816 --- /dev/null +++ b/libvo/vo_gl3.c @@ -0,0 +1,2418 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * You can alternatively redistribute this file 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <stdbool.h> +#include <assert.h> +#include "config.h" + +#include <libavutil/avutil.h> + +#ifdef CONFIG_LCMS2 +#include <lcms2.h> +#include "stream/stream.h" +#endif + +#include "talloc.h" +#include "bstr.h" +#include "mp_msg.h" +#include "subopt-helper.h" +#include "video_out.h" +#include "libmpcodecs/vfcap.h" +#include "libmpcodecs/mp_image.h" +#include "geometry.h" +#include "osd.h" +#include "sub/font_load.h" +#include "sub/sub.h" +#include "eosd_packer.h" + +#include "gl_common.h" +#include "filter_kernels.h" +#include "aspect.h" +#include "fastmemcpy.h" +#include "sub/ass_mp.h" + +// Generated from libvo/vo_gl3_shaders.glsl +#include "libvo/vo_gl3_shaders.h" + +// How many parts the OSD may consist of at most. +#define MAX_OSD_PARTS 20 + +// Pixel width of 1D lookup textures. +#define LOOKUP_TEXTURE_SIZE 256 + +// Texture units 0-2 are used by the video, with unit 0 for free use. +// Units 3-4 are used for scaler LUTs. +#define TEXUNIT_SCALERS 3 +#define TEXUNIT_3DLUT 5 +#define TEXUNIT_DITHER 6 + +// lscale/cscale arguments that map directly to shader filter routines. +// Note that the convolution filters are not included in this list. +static const char *fixed_scale_filters[] = { + "bilinear", + "bicubic_fast", + "sharpen3", + "sharpen5", + NULL +}; + +struct lut_tex_format { + int pixels; + GLint internal_format; + GLenum format; +}; + +// Indexed with filter_kernel->size. +// This must match the weightsN functions in the shader. +// Each entry uses (size+3)/4 pixels per LUT entry, and size/pixels components +// per pixel. +struct lut_tex_format lut_tex_formats[] = { + [2] = {1, GL_RG16F, GL_RG}, + [4] = {1, GL_RGBA16F, GL_RGBA}, + [6] = {2, GL_RGB16F, GL_RGB}, + [8] = {2, GL_RGBA16F, GL_RGBA}, + [12] = {3, GL_RGBA16F, GL_RGBA}, + [16] = {4, GL_RGBA16F, GL_RGBA}, +}; + +// must be sorted, and terminated with 0 +static const int filter_sizes[] = {2, 4, 6, 8, 12, 16, 0}; + +struct vertex { + float position[2]; + uint8_t color[4]; + float texcoord[2]; +}; + +#define VERTEX_ATTRIB_POSITION 0 +#define VERTEX_ATTRIB_COLOR 1 +#define VERTEX_ATTRIB_TEXCOORD 2 + +// 2 triangles primitives per quad = 6 vertices per quad +// (GL_QUAD is deprecated, strips can't be used with EOSD image lists) +#define VERTICES_PER_QUAD 6 + +struct texplane { + int shift_x, shift_y; + GLuint gl_texture; + int gl_buffer; + int buffer_size; + void *buffer_ptr; +}; + +struct scaler { + int index; + const char *name; + float params[2]; + struct filter_kernel *kernel; + GLuint gl_lut; + const char *lut_name; + + // kernel points here + struct filter_kernel kernel_storage; +}; + +struct fbotex { + GLuint fbo; + GLuint texture; + int tex_w, tex_h; // size of .texture + int vp_w, vp_h; // viewport of fbo / used part of the texture +}; + +struct gl_priv { + struct vo *vo; + MPGLContext *glctx; + GL *gl; + const char *shader_version; + + int use_indirect; + int use_gamma; + int use_srgb; + int use_scale_sep; + int use_fancy_downscaling; + int use_lut_3d; + int use_npot; + int use_pbo; + int use_glFinish; + int use_gl_debug; + int use_gl2; + + int dither_depth; + int swap_interval; + GLint fbo_format; + int stereo_mode; + int osd_color; + + GLuint vertex_buffer; + GLuint vao; + + GLuint osd_program, eosd_program; + GLuint indirect_program, scale_sep_program, final_program; + + GLuint osd_textures[MAX_OSD_PARTS]; + int osd_textures_count; + struct vertex osd_va[MAX_OSD_PARTS * VERTICES_PER_QUAD]; + + GLuint eosd_texture; + int eosd_texture_width, eosd_texture_height; + GLuint eosd_buffer; + struct vertex *eosd_va; + struct eosd_packer *eosd; + + GLuint lut_3d_texture; + int lut_3d_w, lut_3d_h, lut_3d_d; + void *lut_3d_data; + + GLuint dither_texture; + float dither_quantization; + float dither_multiply; + + uint32_t image_width; + uint32_t image_height; + uint32_t image_format; + int texture_width; + int texture_height; + + bool is_yuv; + bool is_linear_rgb; + + // per pixel (full pixel when packed, each component when planar) + int plane_bytes; + int plane_bits; + int component_bits; + + GLint gl_internal_format; + GLenum gl_format; + GLenum gl_type; + + int plane_count; + struct texplane planes[3]; + + struct fbotex indirect_fbo; // RGB target + struct fbotex scale_sep_fbo; // first pass when doing 2 pass scaling + + // state for luma (0) and chroma (1) scalers + struct scaler scalers[2]; + // luma scaler parameters (the same are used for chroma) + float scaler_params[2]; + + struct mp_csp_details colorspace; + struct mp_csp_equalizer video_eq; + + int mpi_flipped; + int vo_flipped; + + struct vo_rect src_rect; // displayed part of the source video + struct vo_rect dst_rect; // video rectangle on output window + int border_x, border_y; // OSD borders + int vp_x, vp_y, vp_w, vp_h; // GL viewport +}; + +struct fmt_entry { + int mp_format; + GLint internal_format; + GLenum format; + int component_bits; + GLenum type; +}; + +static const struct fmt_entry mp_to_gl_formats[] = { + {IMGFMT_RGB48NE, GL_RGB16, GL_RGB, 16, GL_UNSIGNED_SHORT}, + {IMGFMT_RGB24, GL_RGB, GL_RGB, 8, GL_UNSIGNED_BYTE}, + {IMGFMT_RGBA, GL_RGBA, GL_RGBA, 8, GL_UNSIGNED_BYTE}, + {IMGFMT_RGB15, GL_RGBA, GL_RGBA, 5, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + {IMGFMT_RGB16, GL_RGB, GL_RGB, 6, GL_UNSIGNED_SHORT_5_6_5_REV}, + {IMGFMT_BGR15, GL_RGBA, GL_BGRA, 5, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + {IMGFMT_BGR16, GL_RGB, GL_RGB, 6, GL_UNSIGNED_SHORT_5_6_5}, + {IMGFMT_BGR24, GL_RGB, GL_BGR, 8, GL_UNSIGNED_BYTE}, + {IMGFMT_BGRA, GL_RGBA, GL_BGRA, 8, GL_UNSIGNED_BYTE}, + {0}, +}; + + +static const char help_text[]; + +static void uninit_rendering(struct gl_priv *p); +static void delete_shaders(struct gl_priv *p); + + +static void default_tex_params(struct GL *gl, GLenum target, GLint filter) +{ + gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); + gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); + gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +static void debug_check_gl(struct gl_priv *p, const char *msg) +{ + if (p->use_gl_debug) + glCheckError(p->gl, msg); +} + +static void tex_size(struct gl_priv *p, int w, int h, int *texw, int *texh) +{ + if (p->use_npot) { + *texw = w; + *texh = h; + } else { + *texw = 32; + while (*texw < w) + *texw *= 2; + *texh = 32; + while (*texh < h) + *texh *= 2; + } +} + +static void draw_triangles(struct gl_priv *p, struct vertex *vb, int vert_count) +{ + GL *gl = p->gl; + + assert(vert_count % 3 == 0); + + gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer); + gl->BufferData(GL_ARRAY_BUFFER, vert_count * sizeof(struct vertex), vb, + GL_DYNAMIC_DRAW); + gl->BindBuffer(GL_ARRAY_BUFFER, 0); + + gl->BindVertexArray(p->vao); + gl->DrawArrays(GL_TRIANGLES, 0, vert_count); + gl->BindVertexArray(0); + + debug_check_gl(p, "after rendering"); +} + +// Write a textured quad to a vertex array. +// va = destination vertex array, VERTICES_PER_QUAD entries will be overwritten +// x0, y0, x1, y1 = destination coordinates of the quad +// tx0, ty0, tx1, ty1 = source texture coordinates (usually in pixels) +// texture_w, texture_h = size of the texture, or an inverse factor +// color = optional color for all vertices, NULL for opaque white +// flip = flip vertically +static void write_quad(struct vertex *va, + float x0, float y0, float x1, float y1, + float tx0, float ty0, float tx1, float ty1, + float texture_w, float texture_h, + const uint8_t color[4], bool flip) +{ + static const uint8_t white[4] = { 255, 255, 255, 255 }; + + if (!color) + color = white; + + tx0 /= texture_w; + ty0 /= texture_h; + tx1 /= texture_w; + ty1 /= texture_h; + + if (flip) { + float tmp = ty0; + ty0 = ty1; + ty1 = tmp; + } + +#define COLOR_INIT {color[0], color[1], color[2], color[3]} + va[0] = (struct vertex) { {x0, y0}, COLOR_INIT, {tx0, ty0} }; + va[1] = (struct vertex) { {x0, y1}, COLOR_INIT, {tx0, ty1} }; + va[2] = (struct vertex) { {x1, y0}, COLOR_INIT, {tx1, ty0} }; + va[3] = (struct vertex) { {x1, y1}, COLOR_INIT, {tx1, ty1} }; + va[4] = va[2]; + va[5] = va[1]; +#undef COLOR_INIT +} + +static void fbotex_init(struct gl_priv *p, struct fbotex *fbo, int w, int h) +{ + GL *gl = p->gl; + + assert(!fbo->fbo); + assert(!fbo->texture); + + tex_size(p, w, h, &fbo->tex_w, &fbo->tex_h); + + fbo->vp_w = w; + fbo->vp_h = h; + + mp_msg(MSGT_VO, MSGL_V, "[gl] Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h); + + gl->GenFramebuffers(1, &fbo->fbo); + gl->GenTextures(1, &fbo->texture); + gl->BindTexture(GL_TEXTURE_2D, fbo->texture); + gl->TexImage2D(GL_TEXTURE_2D, 0, p->fbo_format, fbo->tex_w, fbo->tex_h, 0, + GL_RGB, GL_UNSIGNED_BYTE, NULL); + default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR); + gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo); + gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, fbo->texture, 0); + + if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) + != GL_FRAMEBUFFER_COMPLETE) + { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] Error: framebuffer completeness " + "check failed!\n"); + } + + gl->BindFramebuffer(GL_FRAMEBUFFER, 0); + + debug_check_gl(p, "after creating framebuffer & associated texture"); +} + +static void fbotex_uninit(struct gl_priv *p, struct fbotex *fbo) +{ + GL *gl = p->gl; + + gl->DeleteFramebuffers(1, &fbo->fbo); + gl->DeleteTextures(1, &fbo->texture); + *fbo = (struct fbotex) {0}; +} + +static void matrix_ortho2d(float m[3][3], float x0, float x1, + float y0, float y1) +{ + memset(m, 0, 9 * sizeof(float)); + m[0][0] = 2.0f / (x1 - x0); + m[1][1] = 2.0f / (y1 - y0); + m[2][0] = -(x1 + x0) / (x1 - x0); + m[2][1] = -(y1 + y0) / (y1 - y0); + m[2][2] = 1.0f; +} + +static void update_uniforms(struct gl_priv *p, GLuint program) +{ + GL *gl = p->gl; + GLint loc; + + if (program == 0) + return; + + gl->UseProgram(program); + + struct mp_csp_params cparams = { + .colorspace = p->colorspace, + .input_bits = p->plane_bits, + .texture_bits = (p->plane_bits + 7) & ~7, + }; + mp_csp_copy_equalizer_values(&cparams, &p->video_eq); + + loc = gl->GetUniformLocation(program, "transform"); + if (loc >= 0) { + float matrix[3][3]; + matrix_ortho2d(matrix, 0, p->vp_w, p->vp_h, 0); + gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]); + } + + loc = gl->GetUniformLocation(program, "colormatrix"); + if (loc >= 0) { + float yuv2rgb[3][4] = {{0}}; + if (p->is_yuv) + mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb); + gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &yuv2rgb[0][0]); + } + + gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"), + 1.0 / cparams.rgamma, + 1.0 / cparams.ggamma, + 1.0 / cparams.bgamma); + + gl->Uniform1i(gl->GetUniformLocation(program, "texture1"), 0); + gl->Uniform1i(gl->GetUniformLocation(program, "texture2"), 1); + gl->Uniform1i(gl->GetUniformLocation(program, "texture3"), 2); + + gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT); + + for (int n = 0; n < 2; n++) { + const char *lut = p->scalers[n].lut_name; + if (lut) + gl->Uniform1i(gl->GetUniformLocation(program, lut), + TEXUNIT_SCALERS + n); + } + + gl->Uniform1i(gl->GetUniformLocation(program, "dither"), TEXUNIT_DITHER); + gl->Uniform1f(gl->GetUniformLocation(program, "dither_quantization"), + p->dither_quantization); + gl->Uniform1f(gl->GetUniformLocation(program, "dither_multiply"), + p->dither_multiply); + + float sparam1 = p->scaler_params[0]; + gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1"), + isnan(sparam1) ? 0.5f : sparam1); + + gl->UseProgram(0); + + debug_check_gl(p, "update_uniforms()"); +} + +static void update_all_uniforms(struct gl_priv *p) +{ + update_uniforms(p, p->osd_program); + update_uniforms(p, p->eosd_program); + update_uniforms(p, p->indirect_program); + update_uniforms(p, p->scale_sep_program); + update_uniforms(p, p->final_program); +} + +#define SECTION_HEADER "#!section " + +static char *get_section(void *talloc_ctx, struct bstr source, + const char *section) +{ + char *res = talloc_strdup(talloc_ctx, ""); + bool copy = false; + while (source.len) { + struct bstr line = bstr_getline(source, &source); + if (bstr_eatstart(&line, bstr(SECTION_HEADER))) { + copy = bstrcmp0(line, section) == 0; + } else if (copy) { + res = talloc_asprintf_append_buffer(res, "%.*s\n", BSTR_P(line)); + } + } + return res; +} + +static char *t_concat(void *talloc_ctx, const char *s1, const char *s2) +{ + return talloc_asprintf(talloc_ctx, "%s%s", s1, s2); +} + +static GLuint create_shader(GL *gl, GLenum type, const char *header, + const char *source) +{ + void *tmp = talloc_new(NULL); + const char *full_source = t_concat(tmp, header, source); + + GLuint shader = gl->CreateShader(type); + gl->ShaderSource(shader, 1, &full_source, NULL); + gl->CompileShader(shader); + GLint status; + gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status); + GLint log_length; + gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + + int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DBG2) : MSGL_ERR; + const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment"; + if (mp_msg_test(MSGT_VO, pri)) { + mp_msg(MSGT_VO, pri, "[gl] %s shader source:\n", typestr); + mp_log_source(MSGT_VO, pri, full_source); + } + if (log_length > 1) { + GLchar *log = talloc_zero_size(tmp, log_length + 1); + gl->GetShaderInfoLog(shader, log_length, NULL, log); + mp_msg(MSGT_VO, pri, "[gl] %s shader compile log (status=%d):\n%s\n", + typestr, status, log); + } + + talloc_free(tmp); + + return shader; +} + +static void prog_create_shader(GL *gl, GLuint program, GLenum type, + const char *header, const char *source) +{ + GLuint shader = create_shader(gl, type, header, source); + gl->AttachShader(program, shader); + gl->DeleteShader(shader); +} + +static void link_shader(GL *gl, GLuint program) +{ + gl->LinkProgram(program); + GLint status; + gl->GetProgramiv(program, GL_LINK_STATUS, &status); + GLint log_length; + gl->GetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); + + int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DBG2) : MSGL_ERR; + if (mp_msg_test(MSGT_VO, pri)) { + GLchar *log = talloc_zero_size(NULL, log_length + 1); + gl->GetProgramInfoLog(program, log_length, NULL, log); + mp_msg(MSGT_VO, pri, "[gl] shader link log (status=%d): %s\n", + status, log); + talloc_free(log); + } +} + +static void bind_attrib_locs(GL *gl, GLuint program) +{ + gl->BindAttribLocation(program, VERTEX_ATTRIB_POSITION, "vertex_position"); + gl->BindAttribLocation(program, VERTEX_ATTRIB_COLOR, "vertex_color"); + gl->BindAttribLocation(program, VERTEX_ATTRIB_TEXCOORD, "vertex_texcoord"); +} + +static GLuint create_program(GL *gl, const char *name, const char *header, + const char *vertex, const char *frag) +{ + mp_msg(MSGT_VO, MSGL_V, "[gl] compiling shader program '%s'\n", name); + mp_msg(MSGT_VO, MSGL_V, "[gl] header:\n"); + mp_log_source(MSGT_VO, MSGL_V, header); + GLuint prog = gl->CreateProgram(); + prog_create_shader(gl, prog, GL_VERTEX_SHADER, header, vertex); + prog_create_shader(gl, prog, GL_FRAGMENT_SHADER, header, frag); + bind_attrib_locs(gl, prog); + link_shader(gl, prog); + return prog; +} + +static void shader_def(char **shader, const char *name, + const char *value) +{ + *shader = talloc_asprintf_append(*shader, "#define %s %s\n", name, value); +} + +static void shader_def_opt(char **shader, const char *name, bool b) +{ + if (b) + shader_def(shader, name, "1"); +} + +static void shader_setup_scaler(char **shader, struct scaler *scaler, int pass) +{ + const char *target = scaler->index == 0 ? "SAMPLE_L" : "SAMPLE_C"; + if (!scaler->kernel) { + *shader = talloc_asprintf_append(*shader, "#define %s sample_%s\n", + target, scaler->name); + } else { + int size = scaler->kernel->size; + if (pass != -1) { + // The direction/pass assignment is rather arbitrary, but fixed in + // other parts of the code (like FBO setup). + const char *direction = pass == 0 ? "0, 1" : "1, 0"; + *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1) " + "sample_convolution_sep%d(vec2(%s), %s, p0, p1)\n", + target, size, direction, scaler->lut_name); + } else { + *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1) " + "sample_convolution%d(%s, p0, p1)\n", + target, size, scaler->lut_name); + } + } +} + +// return false if RGB or 4:4:4 YUV +static bool input_is_subsampled(struct gl_priv *p) +{ + for (int i = 0; i < p->plane_count; i++) + if (p->planes[i].shift_x || p->planes[i].shift_y) + return true; + return false; +} + +static void compile_shaders(struct gl_priv *p) +{ + GL *gl = p->gl; + + delete_shaders(p); + + void *tmp = talloc_new(NULL); + + struct bstr src = { (char*)vo_gl3_shaders, sizeof(vo_gl3_shaders) }; + char *vertex_shader = get_section(tmp, src, "vertex_all"); + char *shader_prelude = get_section(tmp, src, "prelude"); + char *s_video = get_section(tmp, src, "frag_video"); + char *s_eosd = get_section(tmp, src, "frag_eosd"); + char *s_osd = get_section(tmp, src, "frag_osd"); + + char *header = talloc_asprintf(tmp, "#version %s\n%s", p->shader_version, + shader_prelude); + + char *header_eosd = talloc_strdup(tmp, header); + shader_def_opt(&header_eosd, "USE_3DLUT", p->use_lut_3d); + + p->eosd_program = + create_program(gl, "eosd", header_eosd, vertex_shader, s_eosd); + + p->osd_program = + create_program(gl, "osd", header, vertex_shader, s_osd); + + char *header_conv = talloc_strdup(tmp, ""); + char *header_final = talloc_strdup(tmp, ""); + char *header_sep = NULL; + + bool convert_input_to_linear = !p->is_linear_rgb + && (p->use_srgb || p->use_lut_3d); + + shader_def_opt(&header_conv, "USE_PLANAR", p->plane_count > 1); + shader_def_opt(&header_conv, "USE_GBRP", p->image_format == IMGFMT_GBRP); + shader_def_opt(&header_conv, "USE_YGRAY", p->is_yuv && p->plane_count == 1); + shader_def_opt(&header_conv, "USE_COLORMATRIX", p->is_yuv); + shader_def_opt(&header_conv, "USE_LINEAR_CONV", convert_input_to_linear); + + shader_def_opt(&header_final, "USE_LINEAR_CONV_INV", p->use_lut_3d); + shader_def_opt(&header_final, "USE_GAMMA_POW", p->use_gamma); + shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d); + shader_def_opt(&header_final, "USE_DITHER", p->dither_texture != 0); + + if (p->use_scale_sep && p->scalers[0].kernel) { + header_sep = talloc_strdup(tmp, ""); + shader_def_opt(&header_sep, "FIXED_SCALE", true); + shader_setup_scaler(&header_sep, &p->scalers[0], 0); + shader_setup_scaler(&header_final, &p->scalers[0], 1); + } else { + shader_setup_scaler(&header_final, &p->scalers[0], -1); + } + + // We want to do scaling in linear light. Scaling is closely connected to + // texture sampling due to how the shader is structured (or if GL bilinear + // scaling is used). The purpose of the "indirect" pass is to convert the + // input video to linear RGB. + // Another purpose is reducing input to a single texture for scaling. + bool use_indirect = p->use_indirect; + + // Don't sample from input video textures before converting the input to + // linear light. (Unneeded when sRGB textures are used.) + if (convert_input_to_linear) + use_indirect = true; + + // It doesn't make sense to scale the chroma with cscale in the 1. scale + // step and with lscale in the 2. step. If the chroma is subsampled, a + // convolution filter wouldn't even work entirely correctly, because the + // luma scaler would sample two texels instead of one per tap for chroma. + // Also, even with 4:4:4 YUV or planar RGB, the indirection might be faster, + // because the shader can't use one scaler for sampling from 3 textures. It + // has to fetch the coefficients for each texture separately, even though + // they're the same (this is not an inherent restriction, but would require + // to restructure the shader). + if (header_sep && p->plane_count > 1) + use_indirect = true; + + if (input_is_subsampled(p)) { + shader_setup_scaler(&header_conv, &p->scalers[1], -1); + } else { + // Force using the luma scaler on chroma. If the "indirect" stage is + // used, the actual scaling will happen in the next stage. + shader_def(&header_conv, "SAMPLE_C", + use_indirect ? "sample_bilinear" : "SAMPLE_L"); + } + + if (use_indirect) { + // We don't use filtering for the Y-plane (luma), because it's never + // scaled in this scenario. + shader_def(&header_conv, "SAMPLE_L", "sample_bilinear"); + shader_def_opt(&header_conv, "FIXED_SCALE", true); + header_conv = t_concat(tmp, header, header_conv); + p->indirect_program = + create_program(gl, "indirect", header_conv, vertex_shader, s_video); + } else if (header_sep) { + header_sep = t_concat(tmp, header_sep, header_conv); + } else { + header_final = t_concat(tmp, header_final, header_conv); + } + + if (header_sep) { + header_sep = t_concat(tmp, header, header_sep); + p->scale_sep_program = + create_program(gl, "scale_sep", header_sep, vertex_shader, s_video); + } + + header_final = t_concat(tmp, header, header_final); + p->final_program = + create_program(gl, "final", header_final, vertex_shader, s_video); + + debug_check_gl(p, "shader compilation"); + + talloc_free(tmp); +} + +static void delete_program(GL *gl, GLuint *prog) +{ + gl->DeleteProgram(*prog); + *prog = 0; +} + +static void delete_shaders(struct gl_priv *p) +{ + GL *gl = p->gl; + + delete_program(gl, &p->osd_program); + delete_program(gl, &p->eosd_program); + delete_program(gl, &p->indirect_program); + delete_program(gl, &p->scale_sep_program); + delete_program(gl, &p->final_program); +} + +static double get_scale_factor(struct gl_priv *p) +{ + double sx = p->dst_rect.width / (double)p->src_rect.width; + double sy = p->dst_rect.height / (double)p->src_rect.height; + // xxx: actually we should use different scalers in X/Y directions if the + // scale factors are different due to anamorphic content + return FFMIN(sx, sy); +} + +static bool update_scale_factor(struct gl_priv *p, struct filter_kernel *kernel) +{ + double scale = get_scale_factor(p); + if (!p->use_fancy_downscaling && scale < 1.0) + scale = 1.0; + return mp_init_filter(kernel, filter_sizes, FFMAX(1.0, 1.0 / scale)); +} + +static void init_scaler(struct gl_priv *p, struct scaler *scaler) +{ + GL *gl = p->gl; + + assert(scaler->name); + + scaler->kernel = NULL; + + const struct filter_kernel *t_kernel = mp_find_filter_kernel(scaler->name); + if (!t_kernel) + return; + + scaler->kernel_storage = *t_kernel; + scaler->kernel = &scaler->kernel_storage; + + for (int n = 0; n < 2; n++) { + if (!isnan(p->scaler_params[n])) + scaler->kernel->params[n] = p->scaler_params[n]; + } + + update_scale_factor(p, scaler->kernel); + + int size = scaler->kernel->size; + assert(size < FF_ARRAY_ELEMS(lut_tex_formats)); + struct lut_tex_format *fmt = &lut_tex_formats[size]; + bool use_2d = fmt->pixels > 1; + bool is_luma = scaler->index == 0; + scaler->lut_name = use_2d + ? (is_luma ? "lut_l_2d" : "lut_c_2d") + : (is_luma ? "lut_l_1d" : "lut_c_1d"); + + gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_SCALERS + scaler->index); + GLenum target = use_2d ? GL_TEXTURE_2D : GL_TEXTURE_1D; + + if (!scaler->gl_lut) + gl->GenTextures(1, &scaler->gl_lut); + + gl->BindTexture(target, scaler->gl_lut); + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4); + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + float *weights = talloc_array(NULL, float, LOOKUP_TEXTURE_SIZE * size); + mp_compute_lut(scaler->kernel, LOOKUP_TEXTURE_SIZE, weights); + if (use_2d) { + gl->TexImage2D(GL_TEXTURE_2D, 0, fmt->internal_format, fmt->pixels, + LOOKUP_TEXTURE_SIZE, 0, fmt->format, GL_FLOAT, + weights); + } else { + gl->TexImage1D(GL_TEXTURE_1D, 0, fmt->internal_format, + LOOKUP_TEXTURE_SIZE, 0, fmt->format, GL_FLOAT, + weights); + } + talloc_free(weights); + + gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + gl->ActiveTexture(GL_TEXTURE0); + + debug_check_gl(p, "after initializing scaler"); +} + +static void make_dither_matrix(unsigned char *m, int size) +{ + m[0] = 0; + for (int sz = 1; sz < size; sz *= 2) { + int offset[] = {sz*size, sz, sz * (size+1), 0}; + for (int i = 0; i < 4; i++) + for (int y = 0; y < sz * size; y += size) + for (int x = 0; x < sz; x++) + m[x+y+offset[i]] = m[x+y] * 4 + (3-i) * 256/size/size; + } +} + +static void init_dither(struct gl_priv *p) +{ + GL *gl = p->gl; + + // Assume 8 bits per component if unknown. + int dst_depth = p->glctx->depth_g ? p->glctx->depth_g : 8; + if (p->dither_depth > 0) + dst_depth = p->dither_depth; + + int src_depth = p->component_bits; + if (p->use_lut_3d) + src_depth = 16; + + if (dst_depth >= src_depth || p->dither_depth < 0 || src_depth < 0) + return; + + mp_msg(MSGT_VO, MSGL_V, "[gl] Dither %d->%d.\n", src_depth, dst_depth); + + // This defines how many bits are considered significant for output on + // screen. The superfluous bits will be used for rounded according to the + // dither matrix. The precision of the source implicitly decides how many + // dither patterns can be visible. + p->dither_quantization = (1 << dst_depth) - 1; + int size = 8; + p->dither_multiply = p->dither_quantization + 1.0 / (size*size); + unsigned char dither[256]; + make_dither_matrix(dither, size); + + gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_DITHER); + gl->GenTextures(1, &p->dither_texture); + gl->BindTexture(GL_TEXTURE_2D, p->dither_texture); + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED, size, size, 0, GL_RED, + GL_UNSIGNED_BYTE, dither); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + gl->ActiveTexture(GL_TEXTURE0); +} + +static void reinit_rendering(struct gl_priv *p) +{ + mp_msg(MSGT_VO, MSGL_V, "[gl] Reinit rendering.\n"); + + debug_check_gl(p, "before scaler initialization"); + + uninit_rendering(p); + + init_dither(p); + + init_scaler(p, &p->scalers[0]); + init_scaler(p, &p->scalers[1]); + + compile_shaders(p); + + if (p->indirect_program && !p->indirect_fbo.fbo) + fbotex_init(p, &p->indirect_fbo, p->texture_width, p->texture_height); +} + +static void uninit_rendering(struct gl_priv *p) +{ + GL *gl = p->gl; + + delete_shaders(p); + + for (int n = 0; n < 2; n++) { + gl->DeleteTextures(1, &p->scalers->gl_lut); + p->scalers->gl_lut = 0; + p->scalers->lut_name = NULL; + p->scalers->kernel = NULL; + } + + gl->DeleteTextures(1, &p->dither_texture); + p->dither_texture = 0; +} + +static void init_lut_3d(struct gl_priv *p) +{ + GL *gl = p->gl; + + gl->GenTextures(1, &p->lut_3d_texture); + gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_3DLUT); + gl->BindTexture(GL_TEXTURE_3D, p->lut_3d_texture); + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4); + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gl->TexImage3D(GL_TEXTURE_3D, 0, GL_RGB16, p->lut_3d_w, p->lut_3d_h, + p->lut_3d_d, 0, GL_RGB, GL_UNSIGNED_SHORT, p->lut_3d_data); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + gl->ActiveTexture(GL_TEXTURE0); + + debug_check_gl(p, "after 3d lut creation"); +} + +static void init_video(struct gl_priv *p) +{ + GL *gl = p->gl; + + if (p->use_lut_3d && !p->lut_3d_texture) + init_lut_3d(p); + + if (!p->is_yuv && (p->use_srgb || p->use_lut_3d)) { + p->is_linear_rgb = true; + p->gl_internal_format = GL_SRGB; + } + + int eq_caps = MP_CSP_EQ_CAPS_GAMMA; + if (p->is_yuv) + eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX; + p->video_eq.capabilities = eq_caps; + + debug_check_gl(p, "before video texture creation"); + + tex_size(p, p->image_width, p->image_height, + &p->texture_width, &p->texture_height); + + for (int n = 0; n < p->plane_count; n++) { + struct texplane *plane = &p->planes[n]; + + int w = p->texture_width >> plane->shift_x; + int h = p->texture_height >> plane->shift_y; + + mp_msg(MSGT_VO, MSGL_V, "[gl] Texture for plane %d: %dx%d\n", n, w, h); + + gl->ActiveTexture(GL_TEXTURE0 + n); + gl->GenTextures(1, &plane->gl_texture); + gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture); + + gl->TexImage2D(GL_TEXTURE_2D, 0, p->gl_internal_format, w, h, 0, + p->gl_format, p->gl_type, NULL); + default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR); + } + gl->ActiveTexture(GL_TEXTURE0); + + debug_check_gl(p, "after video texture creation"); + + reinit_rendering(p); +} + +static void uninit_video(struct gl_priv *p) +{ + GL *gl = p->gl; + + uninit_rendering(p); + + for (int n = 0; n < 3; n++) { + struct texplane *plane = &p->planes[n]; + + gl->DeleteTextures(1, &plane->gl_texture); + plane->gl_texture = 0; + gl->DeleteBuffers(1, &plane->gl_buffer); + plane->gl_buffer = 0; + plane->buffer_ptr = NULL; + plane->buffer_size = 0; + } + + fbotex_uninit(p, &p->indirect_fbo); + fbotex_uninit(p, &p->scale_sep_fbo); +} + +static void render_to_fbo(struct gl_priv *p, struct fbotex *fbo, int w, int h, + int tex_w, int tex_h) +{ + GL *gl = p->gl; + + gl->Viewport(0, 0, fbo->vp_w, fbo->vp_h); + gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo); + + struct vertex vb[VERTICES_PER_QUAD]; + write_quad(vb, -1, -1, 1, 1, + 0, 0, w, h, + tex_w, tex_h, + NULL, false); + draw_triangles(p, vb, VERTICES_PER_QUAD); + + gl->BindFramebuffer(GL_FRAMEBUFFER, 0); + gl->Viewport(p->vp_x, p->vp_y, p->vp_w, p->vp_h); + +} + +static void handle_pass(struct gl_priv *p, struct fbotex **source, + struct fbotex *fbo, GLuint program) +{ + GL *gl = p->gl; + + if (!program) + return; + + gl->BindTexture(GL_TEXTURE_2D, (*source)->texture); + gl->UseProgram(program); + render_to_fbo(p, fbo, (*source)->vp_w, (*source)->vp_h, + (*source)->tex_w, (*source)->tex_h); + *source = fbo; +} + +static void do_render(struct gl_priv *p) +{ + GL *gl = p->gl; + struct vertex vb[VERTICES_PER_QUAD]; + bool is_flipped = p->mpi_flipped ^ p->vo_flipped; + + // Order of processing: + // [indirect -> [scale_sep ->]] final + + struct fbotex dummy = { + .vp_w = p->image_width, .vp_h = p->image_height, + .tex_w = p->texture_width, .tex_h = p->texture_height, + .texture = p->planes[0].gl_texture, + }; + struct fbotex *source = &dummy; + + handle_pass(p, &source, &p->indirect_fbo, p->indirect_program); + handle_pass(p, &source, &p->scale_sep_fbo, p->scale_sep_program); + + gl->BindTexture(GL_TEXTURE_2D, source->texture); + gl->UseProgram(p->final_program); + + float final_texw = p->image_width * source->tex_w / (float)source->vp_w; + float final_texh = p->image_height * source->tex_h / (float)source->vp_h; + + if (p->use_srgb && !p->use_lut_3d) + gl->Enable(GL_FRAMEBUFFER_SRGB); + + if (p->stereo_mode) { + int w = p->src_rect.width; + int imgw = p->image_width; + + glEnable3DLeft(gl, p->stereo_mode); + + write_quad(vb, + p->dst_rect.left, p->dst_rect.top, + p->dst_rect.right, p->dst_rect.bottom, + p->src_rect.left / 2, p->src_rect.top, + p->src_rect.left / 2 + w / 2, p->src_rect.bottom, + final_texw, final_texh, + NULL, is_flipped); + draw_triangles(p, vb, VERTICES_PER_QUAD); + + glEnable3DRight(gl, p->stereo_mode); + + write_quad(vb, + p->dst_rect.left, p->dst_rect.top, + p->dst_rect.right, p->dst_rect.bottom, + p->src_rect.left / 2 + imgw / 2, p->src_rect.top, + p->src_rect.left / 2 + imgw / 2 + w / 2, p->src_rect.bottom, + final_texw, final_texh, + NULL, is_flipped); + draw_triangles(p, vb, VERTICES_PER_QUAD); + + glDisable3D(gl, p->stereo_mode); + } else { + write_quad(vb, + p->dst_rect.left, p->dst_rect.top, + p->dst_rect.right, p->dst_rect.bottom, + p->src_rect.left, p->src_rect.top, + p->src_rect.right, p->src_rect.bottom, + final_texw, final_texh, + NULL, is_flipped); + draw_triangles(p, vb, VERTICES_PER_QUAD); + } + + gl->Disable(GL_FRAMEBUFFER_SRGB); + + gl->UseProgram(0); + + debug_check_gl(p, "after video rendering"); +} + +static void update_window_sized_objects(struct gl_priv *p) +{ + if (p->scale_sep_program) { + if (p->dst_rect.height > p->scale_sep_fbo.tex_h) { + fbotex_uninit(p, &p->scale_sep_fbo); + // Round up to an arbitrary alignment to make window resizing or + // panscan controls smoother (less texture reallocations). + int height = FFALIGN(p->dst_rect.height, 256); + fbotex_init(p, &p->scale_sep_fbo, p->image_width, height); + } + p->scale_sep_fbo.vp_w = p->image_width; + p->scale_sep_fbo.vp_h = p->dst_rect.height; + } +} + +static void resize(struct gl_priv *p) +{ + GL *gl = p->gl; + struct vo *vo = p->vo; + + mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n", vo->dwidth, vo->dheight); + p->vp_x = 0, p->vp_y = 0; + if (WinID >= 0) { + int w = vo->dwidth, h = vo->dheight; + int old_y = vo->dheight; + geometry(&p->vp_x, &p->vp_y, &w, &h, vo->dwidth, vo->dheight); + p->vp_y = old_y - h - p->vp_y; + } + p->vp_w = vo->dwidth, p->vp_h = vo->dheight; + gl->Viewport(p->vp_x, p->vp_y, p->vp_w, p->vp_h); + + struct vo_rect borders; + calc_src_dst_rects(vo, p->image_width, p->image_height, &p->src_rect, + &p->dst_rect, &borders, NULL); + p->border_x = borders.left; + p->border_y = borders.top; + + bool need_scaler_reinit = false; // filter size change needed + bool need_scaler_update = false; // filter LUT change needed + bool too_small = false; + for (int n = 0; n < 2; n++) { + if (p->scalers[n].kernel) { + struct filter_kernel tkernel = *p->scalers[n].kernel; + struct filter_kernel old = tkernel; + bool ok = update_scale_factor(p, &tkernel); + too_small |= !ok; + need_scaler_reinit |= (tkernel.size != old.size); + need_scaler_update |= (tkernel.inv_scale != old.inv_scale); + } + } + if (need_scaler_reinit) { + reinit_rendering(p); + } else if (need_scaler_update) { + init_scaler(p, &p->scalers[0]); + init_scaler(p, &p->scalers[1]); + } + if (too_small) + mp_msg(MSGT_VO, MSGL_WARN, "[gl] Can't downscale that much, window " + "output may look suboptimal.\n"); + + update_window_sized_objects(p); + update_all_uniforms(p); + +#ifdef CONFIG_FREETYPE + // adjust font size to display size + force_load_font = 1; +#endif + vo_osd_changed(OSDTYPE_OSD); + + gl->Clear(GL_COLOR_BUFFER_BIT); + vo->want_redraw = true; +} + +static void flip_page(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (p->use_glFinish) + gl->Finish(); + + p->glctx->swapGlBuffers(p->glctx); + + if (p->dst_rect.left > p->vp_x || p->dst_rect.top > p->vp_y + || p->dst_rect.right < p->vp_x + p->vp_w + || p->dst_rect.bottom < p->vp_y + p->vp_h) + { + gl->Clear(GL_COLOR_BUFFER_BIT); + } +} + +static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, + int x, int y) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + p->mpi_flipped = stride[0] < 0; + + for (int n = 0; n < p->plane_count; n++) { + gl->ActiveTexture(GL_TEXTURE0 + n); + gl->BindTexture(GL_TEXTURE_2D, p->planes[n].gl_texture); + int xs = p->planes[n].shift_x, ys = p->planes[n].shift_y; + glUploadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type, src[n], + stride[n], x >> xs, y >> ys, w >> xs, h >> ys, 0); + } + gl->ActiveTexture(GL_TEXTURE0); + + return 0; +} + +static uint32_t get_image(struct vo *vo, mp_image_t *mpi) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (!p->use_pbo) + return VO_FALSE; + + // We don't support alpha planes. (Disabling PBOs with normal draw calls is + // an undesired, but harmless side-effect.) + if (mpi->num_planes != p->plane_count) + return VO_FALSE; + + if (mpi->flags & MP_IMGFLAG_READABLE) + return VO_FALSE; + if (mpi->type != MP_IMGTYPE_STATIC && mpi->type != MP_IMGTYPE_TEMP && + (mpi->type != MP_IMGTYPE_NUMBERED || mpi->number)) + return VO_FALSE; + mpi->flags &= ~MP_IMGFLAG_COMMON_PLANE; + for (int n = 0; n < p->plane_count; n++) { + struct texplane *plane = &p->planes[n]; + mpi->stride[n] = (mpi->width >> plane->shift_x) * p->plane_bytes; + int needed_size = (mpi->height >> plane->shift_y) * mpi->stride[n]; + if (!plane->gl_buffer) + gl->GenBuffers(1, &plane->gl_buffer); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer); + if (needed_size > plane->buffer_size) { + plane->buffer_size = needed_size; + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, plane->buffer_size, + NULL, GL_DYNAMIC_DRAW); + } + if (!plane->buffer_ptr) + plane->buffer_ptr = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, + GL_WRITE_ONLY); + mpi->planes[n] = plane->buffer_ptr; + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + mpi->flags |= MP_IMGFLAG_DIRECT; + return VO_TRUE; +} + +static uint32_t draw_image(struct gl_priv *p, mp_image_t *mpi) +{ + GL *gl = p->gl; + int n; + + assert(mpi->num_planes >= p->plane_count); + + mp_image_t mpi2 = *mpi; + int w = mpi->w, h = mpi->h; + if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) + goto skip_upload; + mpi2.flags = 0; + mpi2.type = MP_IMGTYPE_TEMP; + mpi2.width = mpi2.w; + mpi2.height = mpi2.h; + if (!(mpi->flags & MP_IMGFLAG_DIRECT) + && !p->planes[0].buffer_ptr + && get_image(p->vo, &mpi2) == VO_TRUE) + { + for (n = 0; n < p->plane_count; n++) { + struct texplane *plane = &p->planes[n]; + int xs = plane->shift_x, ys = plane->shift_y; + int line_bytes = (mpi->w >> xs) * p->plane_bytes; + memcpy_pic(mpi2.planes[n], mpi->planes[n], line_bytes, mpi->h >> ys, + mpi2.stride[n], mpi->stride[n]); + } + mpi = &mpi2; + } + p->mpi_flipped = mpi->stride[0] < 0; + for (n = 0; n < p->plane_count; n++) { + struct texplane *plane = &p->planes[n]; + int xs = plane->shift_x, ys = plane->shift_y; + void *plane_ptr = mpi->planes[n]; + if (mpi->flags & MP_IMGFLAG_DIRECT) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer); + if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER)) + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Video PBO upload failed. " + "Remove the 'pbo' suboption.\n"); + plane->buffer_ptr = NULL; + plane_ptr = NULL; // PBO offset 0 + } + gl->ActiveTexture(GL_TEXTURE0 + n); + gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture); + glUploadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type, plane_ptr, + mpi->stride[n], 0, 0, w >> xs, h >> ys, 0); + } + gl->ActiveTexture(GL_TEXTURE0); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +skip_upload: + do_render(p); + return VO_TRUE; +} + +static mp_image_t *get_screenshot(struct gl_priv *p) +{ + GL *gl = p->gl; + + mp_image_t *image = alloc_mpi(p->texture_width, p->texture_height, + p->image_format); + + // NOTE about image formats with alpha plane: we don't even have the alpha + // anymore. We never upload it to any texture, as it would be a waste of + // time. On the other hand, we can't find a "similar", non-alpha image + // format easily. So we just leave the alpha plane of the newly allocated + // image as-is, and hope that the alpha is ignored by the receiver of the + // screenshot. (If not, code should be added to make it fully opaque.) + + for (int n = 0; n < p->plane_count; n++) { + gl->ActiveTexture(GL_TEXTURE0 + n); + gl->BindTexture(GL_TEXTURE_2D, p->planes[n].gl_texture); + glDownloadTex(gl, GL_TEXTURE_2D, p->gl_format, p->gl_type, + image->planes[n], image->stride[n]); + } + gl->ActiveTexture(GL_TEXTURE0); + + image->width = p->image_width; + image->height = p->image_height; + + image->w = p->vo->aspdat.prew; + image->h = p->vo->aspdat.preh; + + return image; +} + +static mp_image_t *get_window_screenshot(struct gl_priv *p) +{ + GL *gl = p->gl; + + mp_image_t *image = alloc_mpi(p->vp_w, p->vp_h, IMGFMT_RGB24); + gl->PixelStorei(GL_PACK_ALIGNMENT, 4); + gl->PixelStorei(GL_PACK_ROW_LENGTH, 0); + gl->ReadBuffer(GL_FRONT); + // flip image while reading + for (int y = 0; y < p->vp_h; y++) { + gl->ReadPixels(p->vp_x, p->vp_y + p->vp_h - y - 1, p->vp_w, 1, + GL_RGB, GL_UNSIGNED_BYTE, + image->planes[0] + y * image->stride[0]); + } + return image; +} + +static void clear_osd(struct gl_priv *p) +{ + GL *gl = p->gl; + + if (!p->osd_textures_count) + return; + gl->DeleteTextures(p->osd_textures_count, p->osd_textures); + p->osd_textures_count = 0; +} + +static void create_osd_texture(void *ctx, int x0, int y0, int w, int h, + unsigned char *src, unsigned char *srca, + int stride) +{ + struct gl_priv *p = ctx; + GL *gl = p->gl; + + if (w <= 0 || h <= 0 || stride < w) { + mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); + return; + } + + if (p->osd_textures_count >= MAX_OSD_PARTS) { + mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the developers!\n"); + return; + } + + int sx, sy; + tex_size(p, w, h, &sx, &sy); + + gl->GenTextures(1, &p->osd_textures[p->osd_textures_count]); + gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[p->osd_textures_count]); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RG, sx, sy, 0, GL_RG, GL_UNSIGNED_BYTE, + NULL); + default_tex_params(gl, GL_TEXTURE_2D, GL_NEAREST); + unsigned char *tmp = malloc(stride * h * 2); + // Convert alpha from weird MPlayer scale. + for (int i = 0; i < h * stride; i++) { + tmp[i*2+0] = src[i]; + tmp[i*2+1] = -srca[i]; + } + glUploadTex(gl, GL_TEXTURE_2D, GL_RG, GL_UNSIGNED_BYTE, tmp, stride * 2, + 0, 0, w, h, 0); + free(tmp); + + gl->BindTexture(GL_TEXTURE_2D, 0); + + uint8_t color[4] = {(p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff, + p->osd_color & 0xff, 0xff - (p->osd_color >> 24)}; + + write_quad(&p->osd_va[p->osd_textures_count * VERTICES_PER_QUAD], + x0, y0, x0 + w, y0 + h, 0, 0, w, h, + sx, sy, color, false); + + p->osd_textures_count++; +} + +static void draw_osd(struct vo *vo, struct osd_state *osd) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (vo_osd_changed(0)) { + clear_osd(p); + osd_draw_text_ext(osd, vo->dwidth, vo->dheight, p->border_x, + p->border_y, p->border_x, + p->border_y, p->image_width, + p->image_height, create_osd_texture, p); + } + + if (p->osd_textures_count > 0) { + gl->Enable(GL_BLEND); + // OSD bitmaps use premultiplied alpha. + gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + gl->UseProgram(p->osd_program); + + for (int n = 0; n < p->osd_textures_count; n++) { + gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[n]); + draw_triangles(p, &p->osd_va[n * VERTICES_PER_QUAD], + VERTICES_PER_QUAD); + } + + gl->UseProgram(0); + + gl->Disable(GL_BLEND); + gl->BindTexture(GL_TEXTURE_2D, 0); + } +} + +static void gen_eosd(struct gl_priv *p, mp_eosd_images_t *imgs) +{ + GL *gl = p->gl; + + bool need_repos, need_upload, need_allocate; + eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload, + &need_allocate); + + if (!need_repos) + return; + + if (!p->eosd_texture) { + gl->GenTextures(1, &p->eosd_texture); + gl->GenBuffers(1, &p->eosd_buffer); + } + + gl->BindTexture(GL_TEXTURE_2D, p->eosd_texture); + + if (need_allocate) { + tex_size(p, p->eosd->surface.w, p->eosd->surface.h, + &p->eosd_texture_width, &p->eosd_texture_height); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED, + p->eosd_texture_width, p->eosd_texture_height, 0, + GL_RED, GL_UNSIGNED_BYTE, NULL); + default_tex_params(gl, GL_TEXTURE_2D, GL_NEAREST); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->eosd_buffer); + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, + p->eosd->surface.w * p->eosd->surface.h, + NULL, + GL_DYNAMIC_COPY); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + + p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va, + p->eosd->targets_count + * sizeof(struct vertex) + * VERTICES_PER_QUAD); + + if (need_upload && p->use_pbo) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->eosd_buffer); + char *data = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + if (!data) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error: can't upload subtitles! " + "Subtitles will look corrupted.\n"); + } else { + for (int n = 0; n < p->eosd->targets_count; n++) { + struct eosd_target *target = &p->eosd->targets[n]; + ASS_Image *i = target->ass_img; + + void *pdata = data + target->source.y0 * p->eosd->surface.w + + target->source.x0; + + memcpy_pic(pdata, i->bitmap, i->w, i->h, + p->eosd->surface.w, i->stride); + } + if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER)) + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] EOSD PBO upload failed. " + "Remove the 'pbo' suboption.\n"); + struct eosd_rect rc; + eosd_packer_calculate_source_bb(p->eosd, &rc); + glUploadTex(gl, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, NULL, + p->eosd->surface.w, rc.x0, rc.y0, + rc.x1 - rc.x0, rc.y1 - rc.y0, 0); + } + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } else if (need_upload) { + // non-PBO upload + for (int n = 0; n < p->eosd->targets_count; n++) { + struct eosd_target *target = &p->eosd->targets[n]; + ASS_Image *i = target->ass_img; + + glUploadTex(gl, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, i->bitmap, + i->stride, target->source.x0, target->source.y0, + i->w, i->h, 0); + } + } + + gl->BindTexture(GL_TEXTURE_2D, 0); + + debug_check_gl(p, "EOSD upload"); + + for (int n = 0; n < p->eosd->targets_count; n++) { + struct eosd_target *target = &p->eosd->targets[n]; + ASS_Image *i = target->ass_img; + uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff, + (i->color >> 8) & 0xff, 255 - (i->color & 0xff) }; + + write_quad(&p->eosd_va[n * VERTICES_PER_QUAD], + target->dest.x0, target->dest.y0, + target->dest.x1, target->dest.y1, + target->source.x0, target->source.y0, + target->source.x1, target->source.y1, + p->eosd_texture_width, p->eosd_texture_height, + color, false); + } +} + +static void draw_eosd(struct gl_priv *p, mp_eosd_images_t *imgs) +{ + GL *gl = p->gl; + + gen_eosd(p, imgs); + + if (p->eosd->targets_count == 0) + return; + + gl->Enable(GL_BLEND); + gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl->BindTexture(GL_TEXTURE_2D, p->eosd_texture); + gl->UseProgram(p->eosd_program); + draw_triangles(p, p->eosd_va, p->eosd->targets_count * VERTICES_PER_QUAD); + gl->UseProgram(0); + gl->BindTexture(GL_TEXTURE_2D, 0); + gl->Disable(GL_BLEND); +} + +static void setup_vertex_array(GL *gl) +{ + size_t stride = sizeof(struct vertex); + + gl->EnableVertexAttribArray(VERTEX_ATTRIB_POSITION); + gl->VertexAttribPointer(VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, + stride, (void*)offsetof(struct vertex, position)); + + gl->EnableVertexAttribArray(VERTEX_ATTRIB_COLOR); + gl->VertexAttribPointer(VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, + stride, (void*)offsetof(struct vertex, color)); + + gl->EnableVertexAttribArray(VERTEX_ATTRIB_TEXCOORD); + gl->VertexAttribPointer(VERTEX_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, + stride, (void*)offsetof(struct vertex, texcoord)); +} + +static int init_gl(struct gl_priv *p) +{ + GL *gl = p->gl; + + debug_check_gl(p, "before init_gl"); + + const char *vendor = gl->GetString(GL_VENDOR); + const char *version = gl->GetString(GL_VERSION); + const char *renderer = gl->GetString(GL_RENDERER); + const char *glsl = gl->GetString(GL_SHADING_LANGUAGE_VERSION); + mp_msg(MSGT_VO, MSGL_V, "[gl] GL_RENDERER='%s', GL_VENDOR='%s', " + "GL_VERSION='%s', GL_SHADING_LANGUAGE_VERSION='%s'" + "\n", renderer, vendor, version, glsl); + mp_msg(MSGT_VO, MSGL_V, "[gl] Display depth: R=%d, G=%d, B=%d\n", + p->glctx->depth_r, p->glctx->depth_g, p->glctx->depth_b); + + GLint major, minor; + gl->GetIntegerv(GL_MAJOR_VERSION, &major); + gl->GetIntegerv(GL_MINOR_VERSION, &minor); + + p->shader_version = "130"; + + // Hack for OSX: it only creates 3.2 contexts. + if (MPGL_VER(major, minor) >= MPGL_VER(3, 2)) + p->shader_version = "150"; + + gl->Disable(GL_DITHER); + gl->Disable(GL_BLEND); + gl->Disable(GL_DEPTH_TEST); + gl->DepthMask(GL_FALSE); + gl->Disable(GL_CULL_FACE); + gl->DrawBuffer(GL_BACK); + + gl->GenBuffers(1, &p->vertex_buffer); + gl->GenVertexArrays(1, &p->vao); + + gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer); + gl->BindVertexArray(p->vao); + setup_vertex_array(gl); + gl->BindBuffer(GL_ARRAY_BUFFER, 0); + gl->BindVertexArray(0); + + GLint max_texture_size; + gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); + eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size); + + gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f); + gl->Clear(GL_COLOR_BUFFER_BIT); + if (gl->SwapInterval && p->swap_interval >= 0) + gl->SwapInterval(p->swap_interval); + + debug_check_gl(p, "after init_gl"); + + return 1; +} + +static void uninit_gl(struct gl_priv *p) +{ + GL *gl = p->gl; + + // NOTE: GL functions might not be loaded yet + if (!(p->glctx && p->gl->DeleteTextures)) + return; + + uninit_video(p); + + gl->DeleteVertexArrays(1, &p->vao); + p->vao = 0; + gl->DeleteBuffers(1, &p->vertex_buffer); + p->vertex_buffer = 0; + + clear_osd(p); + gl->DeleteTextures(1, &p->eosd_texture); + p->eosd_texture = 0; + gl->DeleteBuffers(1, &p->eosd_buffer); + p->eosd_buffer = 0; + eosd_packer_reinit(p->eosd, 0, 0); + + gl->DeleteTextures(1, &p->lut_3d_texture); + p->lut_3d_texture = 0; +} + +static bool init_format(int fmt, struct gl_priv *init) +{ + bool supported = false; + struct gl_priv dummy; + if (!init) + init = &dummy; + + mp_image_t dummy_img = {0}; + mp_image_setfmt(&dummy_img, fmt); + + init->image_format = fmt; + init->component_bits = -1; + + // RGB/packed formats + for (const struct fmt_entry *e = mp_to_gl_formats; e->mp_format; e++) { + if (e->mp_format == fmt) { + supported = true; + init->plane_bits = dummy_img.bpp; + init->gl_format = e->format; + init->gl_internal_format = e->internal_format; + init->component_bits = e->component_bits; + init->gl_type = e->type; + break; + } + } + + // YUV/planar formats + if (!supported && mp_get_chroma_shift(fmt, NULL, NULL, &init->plane_bits)) { + init->gl_format = GL_RED; + init->component_bits = init->plane_bits; + if (init->plane_bits == 8) { + supported = true; + init->gl_internal_format = GL_RED; + init->gl_type = GL_UNSIGNED_BYTE; + } else if (IMGFMT_IS_YUVP16_NE(fmt)) { + supported = true; + init->gl_internal_format = GL_R16; + init->gl_type = GL_UNSIGNED_SHORT; + } + } + + // RGB/planar + if (!supported && fmt == IMGFMT_GBRP) { + supported = true; + init->plane_bits = init->component_bits = 8; + init->gl_format = GL_RED; + init->gl_internal_format = GL_RED; + init->gl_type = GL_UNSIGNED_BYTE; + } + + if (!supported) + return false; + + init->plane_bytes = (init->plane_bits + 7) / 8; + init->is_yuv = dummy_img.flags & MP_IMGFLAG_YUV; + init->is_linear_rgb = false; + + // NOTE: we throw away the additional alpha plane, if one exists. + init->plane_count = dummy_img.num_planes > 2 ? 3 : 1; + assert(dummy_img.num_planes >= init->plane_count); + assert(dummy_img.num_planes <= init->plane_count + 1); + + for (int n = 0; n < init->plane_count; n++) { + struct texplane *plane = &init->planes[n]; + + plane->shift_x = n > 0 ? dummy_img.chroma_x_shift : 0; + plane->shift_y = n > 0 ? dummy_img.chroma_y_shift : 0; + } + + return true; +} + +static int query_format(uint32_t format) +{ + int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP | + VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE | + VFCAP_OSD | VFCAP_EOSD | VFCAP_EOSD_UNSCALED; + if (!init_format(format, NULL)) + return 0; + return caps; +} + +static bool config_window(struct gl_priv *p, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + if (p->stereo_mode == GL_3D_QUADBUFFER) + flags |= VOFLAG_STEREO; + + int mpgl_version = MPGL_VER(3, 0); + int mpgl_flags = p->use_gl_debug ? MPGLFLAG_DEBUG : 0; + + if (p->use_gl2) + mpgl_version = MPGL_VER(2, 1); + + if (create_mpglcontext(p->glctx, mpgl_flags, mpgl_version, d_width, + d_height, flags) == SET_WINDOW_FAILED) + return false; + + if (!p->vertex_buffer) + init_gl(p); + + return true; +} + +static int config(struct vo *vo, uint32_t width, uint32_t height, + uint32_t d_width, uint32_t d_height, uint32_t flags, + uint32_t format) +{ + struct gl_priv *p = vo->priv; + + if (!config_window(p, d_width, d_height, flags)) + return -1; + + p->vo_flipped = !!(flags & VOFLAG_FLIPPING); + + if (p->image_format != format || p->image_width != width + || p->image_height != height) + { + uninit_video(p); + p->image_height = height; + p->image_width = width; + init_format(format, p); + init_video(p); + } + + resize(p); + + return 0; +} + +static void check_events(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + + int e = p->glctx->check_events(vo); + if (e & VO_EVENT_REINIT) { + uninit_gl(p); + init_gl(p); + init_video(p); + resize(p); + } + if (e & VO_EVENT_RESIZE) + resize(p); + if (e & VO_EVENT_EXPOSE) + vo->want_redraw = true; +} + +static int control(struct vo *vo, uint32_t request, void *data) +{ + struct gl_priv *p = vo->priv; + + switch (request) { + case VOCTRL_QUERY_FORMAT: + return query_format(*(uint32_t *)data); + case VOCTRL_GET_IMAGE: + return get_image(vo, data); + case VOCTRL_DRAW_IMAGE: + return draw_image(p, data); + case VOCTRL_DRAW_EOSD: + if (!data) + return VO_FALSE; + draw_eosd(p, data); + return VO_TRUE; + case VOCTRL_GET_EOSD_RES: { + mp_eosd_res_t *r = data; + r->w = vo->dwidth; + r->h = vo->dheight; + r->ml = r->mr = p->border_x; + r->mt = r->mb = p->border_y; + return VO_TRUE; + } + case VOCTRL_ONTOP: + if (!p->glctx->ontop) + break; + p->glctx->ontop(vo); + return VO_TRUE; + case VOCTRL_FULLSCREEN: + p->glctx->fullscreen(vo); + resize(p); + return VO_TRUE; + case VOCTRL_BORDER: + if (!p->glctx->border) + break; + p->glctx->border(vo); + resize(p); + return VO_TRUE; + case VOCTRL_GET_PANSCAN: + return VO_TRUE; + case VOCTRL_SET_PANSCAN: + resize(p); + return VO_TRUE; + case VOCTRL_GET_EQUALIZER: { + struct voctrl_get_equalizer_args *args = data; + return mp_csp_equalizer_get(&p->video_eq, args->name, args->valueptr) + >= 0 ? VO_TRUE : VO_NOTIMPL; + } + case VOCTRL_SET_EQUALIZER: { + struct voctrl_set_equalizer_args *args = data; + if (mp_csp_equalizer_set(&p->video_eq, args->name, args->value) < 0) + return VO_NOTIMPL; + if (!p->use_gamma && p->video_eq.values[MP_CSP_EQ_GAMMA] != 0) { + mp_msg(MSGT_VO, MSGL_V, "[gl] Auto-enabling gamma.\n"); + p->use_gamma = true; + compile_shaders(p); + } + update_all_uniforms(p); + vo->want_redraw = true; + return VO_TRUE; + } + case VOCTRL_SET_YUV_COLORSPACE: { + if (p->is_yuv) { + p->colorspace = *(struct mp_csp_details *)data; + update_all_uniforms(p); + vo->want_redraw = true; + } + return VO_TRUE; + } + case VOCTRL_GET_YUV_COLORSPACE: + *(struct mp_csp_details *)data = p->colorspace; + return VO_TRUE; + case VOCTRL_UPDATE_SCREENINFO: + if (!p->glctx->update_xinerama_info) + break; + p->glctx->update_xinerama_info(vo); + return VO_TRUE; + case VOCTRL_SCREENSHOT: { + struct voctrl_screenshot_args *args = data; + if (args->full_window) + args->out_image = get_window_screenshot(p); + else + args->out_image = get_screenshot(p); + return true; + } + case VOCTRL_REDRAW_FRAME: + do_render(p); + return true; + } + return VO_NOTIMPL; +} + +static void uninit(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + + uninit_gl(p); + uninit_mpglcontext(p->glctx); + p->glctx = NULL; + p->gl = NULL; +} + +#ifdef CONFIG_LCMS2 + +static void lcms2_error_handler(cmsContext ctx, cmsUInt32Number code, + const char *msg) +{ + mp_msg(MSGT_VO, MSGL_ERR, "[gl] lcms2: %s\n", msg); +} + +static struct bstr load_file(struct gl_priv *p, void *talloc_ctx, + const char *filename) +{ + struct bstr res = {0}; + stream_t *s = open_stream(filename, p->vo->opts, NULL); + if (s) { + res = stream_read_complete(s, talloc_ctx, 1000000000, 0); + free_stream(s); + } + return res; +} + +#define LUT3D_CACHE_HEADER "mplayer2 3dlut cache 1.0\n" + +static bool load_icc(struct gl_priv *p, const char *icc_file, + const char *icc_cache, int icc_intent, + int s_r, int s_g, int s_b) +{ + void *tmp = talloc_new(p); + uint16_t *output = talloc_array(tmp, uint16_t, s_r * s_g * s_b * 3); + + if (icc_intent == -1) + icc_intent = INTENT_ABSOLUTE_COLORIMETRIC; + + mp_msg(MSGT_VO, MSGL_INFO, "[gl] Opening ICC profile '%s'\n", icc_file); + struct bstr iccdata = load_file(p, tmp, icc_file); + if (!iccdata.len) + goto error_exit; + + char *cache_info = talloc_asprintf(tmp, "intent=%d, size=%dx%dx%d\n", + icc_intent, s_r, s_g, s_b); + + // check cache + if (icc_cache) { + mp_msg(MSGT_VO, MSGL_INFO, "[gl] Opening 3D LUT cache in file '%s'.\n", + icc_cache); + struct bstr cachedata = load_file(p, tmp, icc_cache); + if (bstr_eatstart(&cachedata, bstr(LUT3D_CACHE_HEADER)) + && bstr_eatstart(&cachedata, bstr(cache_info)) + && bstr_eatstart(&cachedata, iccdata) + && cachedata.len == talloc_get_size(output)) + { + memcpy(output, cachedata.start, cachedata.len); + goto done; + } else { + mp_msg(MSGT_VO, MSGL_WARN, "[gl] 3D LUT cache invalid!\n"); + } + } + + cmsSetLogErrorHandler(lcms2_error_handler); + + cmsHPROFILE profile = cmsOpenProfileFromMem(iccdata.start, iccdata.len); + if (!profile) + goto error_exit; + + cmsCIExyY d65; + cmsWhitePointFromTemp(&d65, 6504); + static const cmsCIExyYTRIPLE bt709prim = { + .Red = {0.64, 0.33, 1.0}, + .Green = {0.30, 0.60, 1.0}, + .Blue = {0.15, 0.06, 1.0}, + }; + cmsToneCurve *tonecurve = cmsBuildGamma(NULL, 2.2); + cmsHPROFILE vid_profile = cmsCreateRGBProfile(&d65, &bt709prim, + (cmsToneCurve*[3]){tonecurve, tonecurve, tonecurve}); + cmsFreeToneCurve(tonecurve); + cmsHTRANSFORM trafo = cmsCreateTransform(vid_profile, TYPE_RGB_16, + profile, TYPE_RGB_16, + icc_intent, + cmsFLAGS_HIGHRESPRECALC); + cmsCloseProfile(profile); + cmsCloseProfile(vid_profile); + + if (!trafo) + goto error_exit; + + // transform a (s_r)x(s_g)x(s_b) cube, with 3 components per channel + uint16_t *input = talloc_array(tmp, uint16_t, s_r * 3); + for (int b = 0; b < s_b; b++) { + for (int g = 0; g < s_g; g++) { + for (int r = 0; r < s_r; r++) { + input[r * 3 + 0] = r * 65535 / (s_r - 1); + input[r * 3 + 1] = g * 65535 / (s_g - 1); + input[r * 3 + 2] = b * 65535 / (s_b - 1); + } + size_t base = (b * s_r * s_g + g * s_r) * 3; + cmsDoTransform(trafo, input, output + base, s_r); + } + } + + cmsDeleteTransform(trafo); + + if (icc_cache) { + FILE *out = fopen(icc_cache, "wb"); + if (out) { + fprintf(out, "%s%s", LUT3D_CACHE_HEADER, cache_info); + fwrite(iccdata.start, iccdata.len, 1, out); + fwrite(output, talloc_get_size(output), 1, out); + fclose(out); + } + } + +done: + + p->lut_3d_data = talloc_steal(p, output); + p->lut_3d_w = s_r, p->lut_3d_h = s_g, p->lut_3d_d = s_b; + p->use_lut_3d = true; + + talloc_free(tmp); + return true; + +error_exit: + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error loading ICC profile.\n"); + talloc_free(tmp); + return false; +} + +#else /* CONFIG_LCMS2 */ + +static bool load_icc(struct gl_priv *p, ...) +{ + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] LCMS2 support not compiled.\n"); + return false; +} + +#endif /* CONFIG_LCMS2 */ + +static bool parse_3dlut_size(const char *s, int *p1, int *p2, int *p3) +{ + if (sscanf(s, "%dx%dx%d", p1, p2, p3) != 3) + return false; + for (int n = 0; n < 3; n++) { + int s = ((int[]) { *p1, *p2, *p3 })[n]; + if (s < 2 || s > 256 || ((s - 1) & s)) + return false; + } + return true; +} + +static int lut3d_size_valid(void *arg) +{ + char *s = *(char **)arg; + int p1, p2, p3; + return parse_3dlut_size(s, &p1, &p2, &p3); +} + +static int backend_valid(void *arg) +{ + return mpgl_find_backend(*(const char **)arg) >= 0; +} + +struct fbo_format { + const char *name; + GLint format; +}; + +const struct fbo_format fbo_formats[] = { + {"rgb", GL_RGB}, + {"rgba", GL_RGBA}, + {"rgb8", GL_RGB8}, + {"rgb16", GL_RGB16}, + {"rgb16f", GL_RGB16F}, + {"rgb32f", GL_RGB32F}, + {0} +}; + +static GLint find_fbo_format(const char *name) +{ + for (const struct fbo_format *fmt = fbo_formats; fmt->name; fmt++) { + if (strcmp(fmt->name, name) == 0) + return fmt->format; + } + return -1; +} + +static int fbo_format_valid(void *arg) +{ + return find_fbo_format(*(const char **)arg) >= 0; +} + +static bool can_use_filter_kernel(const struct filter_kernel *kernel) +{ + if (!kernel) + return false; + struct filter_kernel k = *kernel; + return mp_init_filter(&k, filter_sizes, 1); +} + +static const char* handle_scaler_opt(const char *name) +{ + const struct filter_kernel *kernel = mp_find_filter_kernel(name); + if (can_use_filter_kernel(kernel)) + return kernel->name; + + for (const char **filter = fixed_scale_filters; *filter; filter++) { + if (strcmp(*filter, name) == 0) + return *filter; + } + + return NULL; +} + +static int scaler_valid(void *arg) +{ + return handle_scaler_opt(*(const char **)arg) != NULL; +} + +#if 0 +static void print_scalers(void) +{ + mp_msg(MSGT_VO, MSGL_INFO, "Available scalers:\n"); + for (const char **e = fixed_scale_filters; *e; e++) { + mp_msg(MSGT_VO, MSGL_INFO, " %s\n", *e); + } + for (const struct filter_kernel *e = mp_filter_kernels; e->name; e++) { + if (can_use_filter_kernel(e)) + mp_msg(MSGT_VO, MSGL_INFO, " %s\n", e->name); + } +} +#endif + +static int preinit(struct vo *vo, const char *arg) +{ + struct gl_priv *p = talloc_zero(vo, struct gl_priv); + vo->priv = p; + + *p = (struct gl_priv) { + .vo = vo, + .colorspace = MP_CSP_DETAILS_DEFAULTS, + .use_npot = 1, + .use_pbo = 0, + .swap_interval = 1, + .osd_color = 0xffffff, + .fbo_format = GL_RGB16, + .use_scale_sep = 1, + .use_fancy_downscaling = 1, + .scalers = { + { .index = 0, .name = "lanczos2" }, + { .index = 1, .name = "bilinear" }, + }, + .scaler_params = {NAN, NAN}, + }; + + + char *scalers[2] = {0}; + char *backend_arg = NULL; + char *fbo_format = NULL; + char *icc_profile = NULL; + char *icc_cache = NULL; + int icc_intent = -1; + char *icc_size_str = NULL; + + const opt_t subopts[] = { + {"gamma", OPT_ARG_BOOL, &p->use_gamma}, + {"srgb", OPT_ARG_BOOL, &p->use_srgb}, + {"npot", OPT_ARG_BOOL, &p->use_npot}, + {"pbo", OPT_ARG_BOOL, &p->use_pbo}, + {"glfinish", OPT_ARG_BOOL, &p->use_glFinish}, + {"swapinterval", OPT_ARG_INT, &p->swap_interval}, + {"osdcolor", OPT_ARG_INT, &p->osd_color}, + {"stereo", OPT_ARG_INT, &p->stereo_mode}, + {"lscale", OPT_ARG_MSTRZ, &scalers[0], scaler_valid}, + {"cscale", OPT_ARG_MSTRZ, &scalers[1], scaler_valid}, + {"lparam1", OPT_ARG_FLOAT, &p->scaler_params[0]}, + {"lparam2", OPT_ARG_FLOAT, &p->scaler_params[1]}, + {"fancy-downscaling", OPT_ARG_BOOL, &p->use_fancy_downscaling}, + {"debug", OPT_ARG_BOOL, &p->use_gl_debug}, + {"force-gl2", OPT_ARG_BOOL, &p->use_gl2}, + {"indirect", OPT_ARG_BOOL, &p->use_indirect}, + {"scale-sep", OPT_ARG_BOOL, &p->use_scale_sep}, + {"fbo-format", OPT_ARG_MSTRZ, &fbo_format, fbo_format_valid}, + {"backend", OPT_ARG_MSTRZ, &backend_arg, backend_valid}, + {"icc-profile", OPT_ARG_MSTRZ, &icc_profile}, + {"icc-cache", OPT_ARG_MSTRZ, &icc_cache}, + {"icc-intent", OPT_ARG_INT, &icc_intent}, + {"3dlut-size", OPT_ARG_MSTRZ, &icc_size_str, + lut3d_size_valid}, + {"dither-depth", OPT_ARG_INT, &p->dither_depth}, + {NULL} + }; + + if (subopt_parse(arg, subopts) != 0) { + mp_msg(MSGT_VO, MSGL_FATAL, help_text); + goto err_out; + } + + int backend = backend_arg ? mpgl_find_backend(backend_arg) : GLTYPE_AUTO; + free(backend_arg); + + if (fbo_format) + p->fbo_format = find_fbo_format(fbo_format); + free(fbo_format); + + for (int n = 0; n < 2; n++) { + if (scalers[n]) + p->scalers[n].name = handle_scaler_opt(scalers[n]); + free(scalers[n]); + } + + int s_r = 128, s_g = 256, s_b = 64; + if (icc_size_str) + parse_3dlut_size(icc_size_str, &s_r, &s_g, &s_b); + free(icc_size_str); + + bool success = true; + if (icc_profile) { + success = load_icc(p, icc_profile, icc_cache, icc_intent, + s_r, s_g, s_b); + } + free(icc_profile); + free(icc_cache); + + if (!success) + goto err_out; + + p->eosd = eosd_packer_create(vo); + + p->glctx = init_mpglcontext(backend, vo); + if (!p->glctx) + goto err_out; + p->gl = p->glctx->gl; + + if (true) { + if (!config_window(p, 320, 200, VOFLAG_HIDDEN)) + goto err_out; + // We created a window to test whether the GL context could be + // created and so on. Destroy that window to make sure all state + // associated with it is lost. + uninit(vo); + p->glctx = init_mpglcontext(backend, vo); + if (!p->glctx) + goto err_out; + p->gl = p->glctx->gl; + } + + return 0; + +err_out: + uninit(vo); + return -1; +} + +const struct vo_driver video_out_gl3 = { + .is_new = true, + .info = &(const vo_info_t) { + "OpenGL 3.x", + "gl3", + "Based on vo_gl.c by Reimar Doeffinger", + "" + }, + .preinit = preinit, + .config = config, + .control = control, + .draw_slice = draw_slice, + .draw_osd = draw_osd, + .flip_page = flip_page, + .check_events = check_events, + .uninit = uninit, +}; + +static const char help_text[] = +"\n--vo=gl3 command line help:\n" +"Example: mplayer --vo=gl3:scale-sep:lscale=lanczos2\n" +"\nOptions:\n" +" lscale=<filter>\n" +" Set the scaling filter. Possible choices:\n" +" bilinear: bilinear texture filtering (fastest).\n" +" bicubic_fast: bicubic filter (without lookup texture).\n" +" sharpen3: unsharp masking (sharpening) with radius=3.\n" +" sharpen5: unsharp masking (sharpening) with radius=5.\n" +" lanczos2: Lanczos with radius=2 (default, recommended).\n" +" lanczos3: Lanczos with radius=3 (not recommended).\n" +" mitchell: Mitchell-Netravali.\n" +" Default: lanczos2\n" +" lparam1=<value> / lparam2=<value>\n" +" Set parameters for configurable filters. Affects chroma scaler\n" +" as well.\n" +" Filters which use this:\n" +" mitchell: b and c params (defaults: b=1/3 c=1/3)\n" +" kaiser: (defaults: 6.33 6.33)\n" +" sharpen3: lparam1 sets sharpening strength (default: 0.5)\n" +" sharpen5: as with sharpen3\n" +" osdcolor=<0xAARRGGBB>\n" +" Use the given color for the OSD.\n" +" stereo=<n>\n" +" 0: normal display\n" +" 1: side-by-side to red-cyan stereo\n" +" 2: side-by-side to green-magenta stereo\n" +" 3: side-by-side to quadbuffer stereo\n" +" srgb\n" +" Enable gamma-correct scaling by working in linear light. This\n" +" makes use of sRGB textures and framebuffers.\n" +" This option forces the options 'indirect' and 'gamma'.\n" +" NOTE: for BT.709 colorspaces, a gamma of 2.35 is assumed. For\n" +" other YUV colorspaces, 2.2 is assumed. RGB input is always\n" +" assumed to be in sRGB.\n" +" pbo\n" +" Enable use of PBOs. This is faster, but can sometimes lead to\n" +" sparodic and temporary image corruption.\n" +" dither-depth=<n>\n" +" Positive non-zero values select the target bit depth.\n" +" -1: Disable any dithering done by mplayer.\n" +" 0: Automatic selection. If output bit depth can't be detected,\n" +" 8 bits per component are assumed.\n" +" 8: Dither to 8 bit output.\n" +" Default: 0.\n" +" Note that dithering will always be disabled if the bit depth\n" +" of the video is lower or qual to the detected dither-depth.\n" +" If color management is enabled, input depth is assumed to be\n" +" 16 bits, because the 3D LUT output is 16 bit wide.\n" +" debug\n" +" Check for OpenGL errors, i.e. call glGetError(). Also request a\n" +" debug OpenGL context.\n" +"Less useful options:\n" +" swapinterval=<n>\n" +" Interval in displayed frames between to buffer swaps.\n" +" 1 is equivalent to enable VSYNC, 0 to disable VSYNC.\n" +" no-scale-sep\n" +" When using a separable scale filter for luma, usually two filter\n" +" passes are done. This is often faster. However, it forces\n" +" conversion to RGB in an extra pass, so it can actually be slower\n" +" if used with fast filters on small screen resolutions. Using\n" +" this options will make rendering a single operation.\n" +" Note that chroma scalers are always done as 1-pass filters.\n" +" cscale=<n>\n" +" As lscale but for chroma (2x slower with little visible effect).\n" +" Note that with some scaling filters, upscaling is always done in\n" +" RGB. If chroma is not subsampled, this option is ignored, and the\n" +" luma scaler is used instead. Setting this option is often useless.\n" +" no-fancy-downscaling\n" +" When using convolution based filters, don't extend the filter\n" +" size when downscaling. Trades downscaling performance for\n" +" reduced quality.\n" +" no-npot\n" +" Force use of power-of-2 texture sizes. For debugging only.\n" +" Borders will look discolored due to filtering.\n" +" glfinish\n" +" Call glFinish() before swapping buffers\n" +" backend=<sys>\n" +" auto: auto-select (default)\n" +" cocoa: Cocoa/OSX\n" +" win: Win32/WGL\n" +" x11: X11/GLX\n" +" indirect\n" +" Do YUV conversion and scaling as separate passes. This will\n" +" first render the video into a video-sized RGB texture, and\n" +" draw the result on screen. The luma scaler is used to scale\n" +" the RGB image when rendering to screen. The chroma scaler\n" +" is used only on YUV conversion, and only if the video uses\n" +" chroma-subsampling.\n" +" This mechanism is disabled on RGB input.\n" +" fbo-format=<fmt>\n" +" Selects the internal format of any FBO textures used.\n" +" fmt can be one of: rgb, rgba, rgb8, rgb16, rgb16f, rgb32f\n" +" Default: rgb16.\n" +" gamma\n" +" Always enable gamma control. (Disables delayed enabling.)\n" +" force-gl2\n" +" Create a legacy GL context. This will randomly malfunction\n" +" if the proper extensions are not supported.\n" +"Color management:\n" +" icc-profile=<file>\n" +" Load an ICC profile and use it to transform linear RGB to\n" +" screen output. Needs LittleCMS2 support compiled in.\n" +" icc-cache=<file>\n" +" Store and load the 3D LUT created from the ICC profile in\n" +" this file. This can be used to speed up loading, since\n" +" LittleCMS2 can take a while to create the 3D LUT.\n" +" Note that this file will be at most about 100 MB big.\n" +" icc-intent=<value>\n" +" 0: perceptual\n" +" 1: relative colorimetric\n" +" 2: saturation\n" +" 3: absolute colorimetric (default)\n" +" 3dlut-size=<r>x<g>x<b>\n" +" Size of the 3D LUT generated from the ICC profile in each\n" +" dimension. Default is 128x256x64.\n" +" Sizes must be a power of two, and 256 at most.\n" +"\n"; diff --git a/libvo/vo_gl3_shaders.glsl b/libvo/vo_gl3_shaders.glsl new file mode 100644 index 0000000000..f67e55e6f5 --- /dev/null +++ b/libvo/vo_gl3_shaders.glsl @@ -0,0 +1,316 @@ +/* + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// Note that this file is not directly passed as shader, but run through some +// text processing functions, and in fact contains multiple vertex and fragment +// shaders. + +// inserted at the beginning of all shaders +#!section prelude +#!section vertex_all +uniform mat3 transform; +uniform sampler3D lut_3d; + +in vec2 vertex_position; +in vec4 vertex_color; +out vec4 color; +in vec2 vertex_texcoord; +out vec2 texcoord; + +void main() { + vec3 position = vec3(vertex_position, 1); +#ifndef FIXED_SCALE + position = transform * position; +#endif + gl_Position = vec4(position, 1); + color = vertex_color; +#ifdef USE_3DLUT + color = vec4(texture(lut_3d, color.rgb).rgb, color.a); +#endif + texcoord = vertex_texcoord; +} + +#!section frag_eosd +uniform sampler2D texture1; + +in vec2 texcoord; +in vec4 color; +out vec4 out_color; + +void main() { + out_color = vec4(color.rgb, color.a * texture(texture1, texcoord).r); +} + +#!section frag_osd +uniform sampler2D texture1; + +in vec2 texcoord; +in vec4 color; +out vec4 out_color; + +void main() { + out_color = texture(texture1, texcoord).rrrg * color; +} + +#!section frag_video +uniform sampler2D texture1; +uniform sampler2D texture2; +uniform sampler2D texture3; +uniform sampler1D lut_c_1d; +uniform sampler1D lut_l_1d; +uniform sampler2D lut_c_2d; +uniform sampler2D lut_l_2d; +uniform sampler3D lut_3d; +uniform sampler2D dither; +uniform mat4x3 colormatrix; +uniform vec3 inv_gamma; +uniform float conv_gamma; +uniform float dither_quantization; +uniform float dither_multiply; +uniform float filter_param1; + +in vec2 texcoord; +out vec4 out_color; + +vec4 sample_bilinear(sampler2D tex, vec2 texcoord) { + return texture(tex, texcoord); +} + +// Explanation how bicubic scaling with only 4 texel fetches is done: +// http://www.mate.tue.nl/mate/pdfs/10318.pdf +// 'Efficient GPU-Based Texture Interpolation using Uniform B-Splines' +// Explanation why this algorithm normally always blurs, even with unit scaling: +// http://bigwww.epfl.ch/preprints/ruijters1001p.pdf +// 'GPU Prefilter for Accurate Cubic B-spline Interpolation' +vec4 calcweights(float s) { + vec4 t = vec4(-0.5, 0.1666, 0.3333, -0.3333) * s + vec4(1, 0, -0.5, 0.5); + t = t * s + vec4(0, 0, -0.5, 0.5); + t = t * s + vec4(-0.6666, 0, 0.8333, 0.1666); + vec2 a = vec2(1 / t.z, 1 / t.w); + t.xy = t.xy * a + vec2(1, 1); + t.x = t.x + s; + t.y = t.y - s; + return t; +} + +vec4 sample_bicubic_fast(sampler2D tex, vec2 texcoord) { + vec2 texsize = textureSize(tex, 0); + vec2 pt = 1 / texsize; + vec2 fcoord = fract(texcoord * texsize + vec2(0.5, 0.5)); + vec4 parmx = calcweights(fcoord.x); + vec4 parmy = calcweights(fcoord.y); + vec4 cdelta; + cdelta.xz = parmx.rg * vec2(-pt.x, pt.x); + cdelta.yw = parmy.rg * vec2(-pt.y, pt.y); + // first y-interpolation + vec4 ar = texture(tex, texcoord + cdelta.xy); + vec4 ag = texture(tex, texcoord + cdelta.xw); + vec4 ab = mix(ag, ar, parmy.b); + // second y-interpolation + vec4 br = texture(tex, texcoord + cdelta.zy); + vec4 bg = texture(tex, texcoord + cdelta.zw); + vec4 aa = mix(bg, br, parmy.b); + // x-interpolation + return mix(aa, ab, parmx.b); +} + +float[2] weights2(sampler1D lookup, float f) { + vec4 c = texture(lookup, f); + return float[2](c.r, c.g); +} + +float[4] weights4(sampler1D lookup, float f) { + vec4 c = texture(lookup, f); + return float[4](c.r, c.g, c.b, c.a); +} + +float[6] weights6(sampler2D lookup, float f) { + vec4 c1 = texture(lookup, vec2(0.25, f)); + vec4 c2 = texture(lookup, vec2(0.75, f)); + return float[6](c1.r, c1.g, c1.b, c2.r, c2.g, c2.b); +} + +float[8] weights8(sampler2D lookup, float f) { + vec4 c1 = texture(lookup, vec2(0.25, f)); + vec4 c2 = texture(lookup, vec2(0.75, f)); + return float[8](c1.r, c1.g, c1.b, c1.a, c2.r, c2.g, c2.b, c2.a); +} + +float[12] weights12(sampler2D lookup, float f) { + vec4 c1 = texture(lookup, vec2(1.0/6.0, f)); + vec4 c2 = texture(lookup, vec2(0.5, f)); + vec4 c3 = texture(lookup, vec2(5.0/6.0, f)); + return float[12](c1.r, c1.g, c1.b, c1.a, + c2.r, c2.g, c2.b, c2.a, + c3.r, c3.g, c3.b, c3.a); +} + +float[16] weights16(sampler2D lookup, float f) { + vec4 c1 = texture(lookup, vec2(0.125, f)); + vec4 c2 = texture(lookup, vec2(0.375, f)); + vec4 c3 = texture(lookup, vec2(0.625, f)); + vec4 c4 = texture(lookup, vec2(0.875, f)); + return float[16](c1.r, c1.g, c1.b, c1.a, c2.r, c2.g, c2.b, c2.a, + c3.r, c3.g, c3.b, c3.a, c4.r, c4.g, c4.b, c4.a); +} + +#define CONVOLUTION_SEP_N(NAME, N) \ + vec4 NAME(sampler2D tex, vec2 texcoord, vec2 pt, float weights[N]) { \ + vec4 res = vec4(0); \ + for (int n = 0; n < N; n++) { \ + res += weights[n] * texture(tex, texcoord + pt * n); \ + } \ + return res; \ + } + +CONVOLUTION_SEP_N(convolution_sep2, 2) +CONVOLUTION_SEP_N(convolution_sep4, 4) +CONVOLUTION_SEP_N(convolution_sep6, 6) +CONVOLUTION_SEP_N(convolution_sep8, 8) +CONVOLUTION_SEP_N(convolution_sep12, 12) +CONVOLUTION_SEP_N(convolution_sep16, 16) + +// The dir parameter is (0, 1) or (1, 0), and we expect the shader compiler to +// remove all the redundant multiplications and additions. +#define SAMPLE_CONVOLUTION_SEP_N(NAME, N, SAMPLERT, CONV_FUNC, WEIGHTS_FUNC)\ + vec4 NAME(vec2 dir, SAMPLERT lookup, sampler2D tex, vec2 texcoord) { \ + vec2 texsize = textureSize(tex, 0); \ + vec2 pt = (1 / texsize) * dir; \ + float fcoord = dot(fract(texcoord * texsize - 0.5), dir); \ + vec2 base = texcoord - fcoord * pt; \ + return CONV_FUNC(tex, base - pt * (N / 2 - 1), pt, \ + WEIGHTS_FUNC(lookup, fcoord)); \ + } + +SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep2, 2, sampler1D, convolution_sep2, weights2) +SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep4, 4, sampler1D, convolution_sep4, weights4) +SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep6, 6, sampler2D, convolution_sep6, weights6) +SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep8, 8, sampler2D, convolution_sep8, weights8) +SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep12, 12, sampler2D, convolution_sep12, weights12) +SAMPLE_CONVOLUTION_SEP_N(sample_convolution_sep16, 16, sampler2D, convolution_sep16, weights16) + + +#define CONVOLUTION_N(NAME, N) \ + vec4 NAME(sampler2D tex, vec2 texcoord, vec2 pt, float taps_x[N], \ + float taps_y[N]) { \ + vec4 res = vec4(0); \ + for (int y = 0; y < N; y++) { \ + vec4 line = vec4(0); \ + for (int x = 0; x < N; x++) \ + line += taps_x[x] * texture(tex, texcoord + pt * vec2(x, y));\ + res += taps_y[y] * line; \ + } \ + return res; \ + } + +CONVOLUTION_N(convolution2, 2) +CONVOLUTION_N(convolution4, 4) +CONVOLUTION_N(convolution6, 6) +CONVOLUTION_N(convolution8, 8) +CONVOLUTION_N(convolution12, 12) +CONVOLUTION_N(convolution16, 16) + +#define SAMPLE_CONVOLUTION_N(NAME, N, SAMPLERT, CONV_FUNC, WEIGHTS_FUNC) \ + vec4 NAME(SAMPLERT lookup, sampler2D tex, vec2 texcoord) { \ + vec2 texsize = textureSize(tex, 0); \ + vec2 pt = 1 / texsize; \ + vec2 fcoord = fract(texcoord * texsize - 0.5); \ + vec2 base = texcoord - fcoord * pt; \ + return CONV_FUNC(tex, base - pt * (N / 2 - 1), pt, \ + WEIGHTS_FUNC(lookup, fcoord.x), \ + WEIGHTS_FUNC(lookup, fcoord.y)); \ + } + +SAMPLE_CONVOLUTION_N(sample_convolution2, 2, sampler1D, convolution2, weights2) +SAMPLE_CONVOLUTION_N(sample_convolution4, 4, sampler1D, convolution4, weights4) +SAMPLE_CONVOLUTION_N(sample_convolution6, 6, sampler2D, convolution6, weights6) +SAMPLE_CONVOLUTION_N(sample_convolution8, 8, sampler2D, convolution8, weights8) +SAMPLE_CONVOLUTION_N(sample_convolution12, 12, sampler2D, convolution12, weights12) +SAMPLE_CONVOLUTION_N(sample_convolution16, 16, sampler2D, convolution16, weights16) + + +// Unsharp masking +vec4 sample_sharpen3(sampler2D tex, vec2 texcoord) { + vec2 texsize = textureSize(tex, 0); + vec2 pt = 1 / texsize; + vec2 st = pt * 0.5; + vec4 p = texture(tex, texcoord); + vec4 sum = texture(tex, texcoord + st * vec2(+1, +1)) + + texture(tex, texcoord + st * vec2(+1, -1)) + + texture(tex, texcoord + st * vec2(-1, +1)) + + texture(tex, texcoord + st * vec2(-1, -1)); + return p + (p - 0.25 * sum) * filter_param1; +} + +vec4 sample_sharpen5(sampler2D tex, vec2 texcoord) { + vec2 texsize = textureSize(tex, 0); + vec2 pt = 1 / texsize; + vec2 st1 = pt * 1.2; + vec4 p = texture(tex, texcoord); + vec4 sum1 = texture(tex, texcoord + st1 * vec2(+1, +1)) + + texture(tex, texcoord + st1 * vec2(+1, -1)) + + texture(tex, texcoord + st1 * vec2(-1, +1)) + + texture(tex, texcoord + st1 * vec2(-1, -1)); + vec2 st2 = pt * 1.5; + vec4 sum2 = texture(tex, texcoord + st2 * vec2(+1, 0)) + + texture(tex, texcoord + st2 * vec2( 0, +1)) + + texture(tex, texcoord + st2 * vec2(-1, 0)) + + texture(tex, texcoord + st2 * vec2( 0, -1)); + vec4 t = p * 0.859375 + sum2 * -0.1171875 + sum1 * -0.09765625; + return p + t * filter_param1; +} + +void main() { +#ifdef USE_PLANAR + vec3 color = vec3(SAMPLE_L(texture1, texcoord).r, + SAMPLE_C(texture2, texcoord).r, + SAMPLE_C(texture3, texcoord).r); +#else + vec3 color = SAMPLE_L(texture1, texcoord).rgb; +#endif +#ifdef USE_GBRP + color.gbr = color; +#endif +#ifdef USE_YGRAY + // NOTE: actually slightly wrong for 16 bit input video, and completely + // wrong for 9/10 bit input + color.gb = vec2(128.0/255.0); +#endif +#ifdef USE_COLORMATRIX + color = mat3(colormatrix) * color + colormatrix[3]; +#endif +#ifdef USE_LINEAR_CONV + color = pow(color, vec3(2.2)); +#endif +#ifdef USE_LINEAR_CONV_INV + // Convert from linear RGB to gamma RGB before putting it through the 3D-LUT + // in the final stage. + color = pow(color, vec3(1.0/2.2)); +#endif +#ifdef USE_GAMMA_POW + color = pow(color, inv_gamma); +#endif +#ifdef USE_3DLUT + color = texture(lut_3d, color).rgb; +#endif +#ifdef USE_DITHER + float dither = texture(dither, gl_FragCoord.xy / textureSize(dither, 0)).r; + color = floor(color * dither_multiply + dither ) / dither_quantization; +#endif + out_color = vec4(color, 1); +} diff --git a/libvo/vo_svga.c b/libvo/vo_svga.c index 3b8a107c7d..19a454e354 100644 --- a/libvo/vo_svga.c +++ b/libvo/vo_svga.c @@ -237,8 +237,8 @@ static uint32_t svga_draw_image(mp_image_t *mpi){ PageStore[cpage].locks=PAGE_BUSY; // these variables are used in loops - x = mpi->x; - y = mpi->y; + x = 0; + y = 0; w = mpi->w; h = mpi->h; stride = mpi->stride[0]; diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c index 0971ae47ea..a7a13bc36f 100644 --- a/libvo/vo_vdpau.c +++ b/libvo/vo_vdpau.c @@ -56,6 +56,7 @@ #include "libavutil/mathematics.h" #include "sub/ass_mp.h" +#include "eosd_packer.h" #define WRAP_ADD(x, a, m) ((a) < 0 \ ? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \ @@ -85,9 +86,6 @@ /* number of palette entries */ #define PALETTE_SIZE 256 -/* Initial size of EOSD surface in pixels (x*x) */ -#define EOSD_SURFACE_INITIAL_SIZE 256 - /* Pixelformat used for output surfaces */ #define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8 @@ -173,24 +171,8 @@ struct vdpctx { // EOSD // Pool of surfaces - struct eosd_bitmap_surface { - VdpBitmapSurface surface; - int w; - int h; - uint32_t max_width; - uint32_t max_height; - } eosd_surface; - - // List of surfaces to be rendered - struct eosd_target { - VdpRect source; - VdpRect dest; - VdpColor color; - } *eosd_targets; - int eosd_targets_size; - int *eosd_scratch; - - int eosd_render_count; + VdpBitmapSurface eosd_surface; + struct eosd_packer *eosd_packer; // Video equalizer struct mp_csp_equalizer video_eq; @@ -797,13 +779,16 @@ static int initialize_vdpau_objects(struct vo *vo) if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0) return -1; + int max_width = 0, max_height = 0; vdp_st = vdp-> bitmap_surface_query_capabilities(vc->vdp_device, VDP_RGBA_FORMAT_A8, &(VdpBool){0}, - &vc->eosd_surface.max_width, - &vc->eosd_surface.max_height); + &max_width, + &max_height); CHECK_ST_WARNING("Query to get max EOSD surface size failed"); + eosd_packer_reinit(vc->eosd_packer, max_width, max_height); + forget_frames(vo); resize(vo); return 0; @@ -823,11 +808,9 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo) for (int i = 0; i <= MAX_OUTPUT_SURFACES; i++) vc->output_surfaces[i] = VDP_INVALID_HANDLE; vc->vdp_device = VDP_INVALID_HANDLE; - vc->eosd_surface = (struct eosd_bitmap_surface){ - .surface = VDP_INVALID_HANDLE, - }; + vc->eosd_surface = VDP_INVALID_HANDLE; + eosd_packer_reinit(vc->eosd_packer, 0, 0); vc->output_surface_width = vc->output_surface_height = -1; - vc->eosd_render_count = 0; } static int handle_preemption(struct vo *vo) @@ -1023,6 +1006,8 @@ static void draw_osd_I8A8(void *ctx, int x0, int y0, int w, int h, "vdp_output_surface_render_output_surface"); } +#define EOSD_VDP_RC(r) &(VdpRect){r.x0, r.y0, r.x1, r.y1} + static void draw_eosd(struct vo *vo) { struct vdpctx *vc = vo->priv; @@ -1045,200 +1030,66 @@ static void draw_eosd(struct vo *vo) .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD, }; - for (i = 0; i < vc->eosd_render_count; i++) { + for (i = 0; i < vc->eosd_packer->targets_count; i++) { + struct eosd_target *target = &vc->eosd_packer->targets[i]; + VdpColor color = { + .alpha = 1.0 - ((target->color >> 0) & 0xff) / 255.0, + .blue = ((target->color >> 8) & 0xff) / 255.0, + .green = ((target->color >> 16) & 0xff) / 255.0, + .red = ((target->color >> 24) & 0xff) / 255.0, + }; vdp_st = vdp-> output_surface_render_bitmap_surface(output_surface, - &vc->eosd_targets[i].dest, - vc->eosd_surface.surface, - &vc->eosd_targets[i].source, - &vc->eosd_targets[i].color, + EOSD_VDP_RC(target->dest), + vc->eosd_surface, + EOSD_VDP_RC(target->source), + &color, &blend_state, VDP_OUTPUT_SURFACE_RENDER_ROTATE_0); CHECK_ST_WARNING("EOSD: Error when rendering"); } } -#define HEIGHT_SORT_BITS 4 -static int size_index(struct eosd_target *r) -{ - unsigned int h = r->source.y1; - int n = av_log2_16bit(h); - return (n << HEIGHT_SORT_BITS) - + (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1); -} - -/* Pack the given rectangles into an area of size w * h. - * The size of each rectangle is read from .source.x1/.source.y1. - * The height of each rectangle must be at least 1 and less than 65536. - * The .source rectangle is then set corresponding to the packed position. - * 'scratch' must point to work memory for num_rects+16 ints. - * Return 0 on success, -1 if the rectangles did not fit in w*h. - * - * The rectangles are placed in rows in order approximately sorted by - * height (the approximate sorting is simpler than a full one would be, - * and allows the algorithm to work in linear time). Additionally, to - * reduce wasted space when there are a few tall rectangles, empty - * lower-right parts of rows are filled recursively when the size of - * rectangles in the row drops past a power-of-two threshold. So if a - * row starts with rectangles of size 3x50, 10x40 and 5x20 then the - * free rectangle with corners (13, 20)-(w, 50) is filled recursively. - */ -static int pack_rectangles(struct eosd_target *rects, int num_rects, - int w, int h, int *scratch) -{ - int bins[16 << HEIGHT_SORT_BITS]; - int sizes[16 << HEIGHT_SORT_BITS] = {}; - for (int i = 0; i < num_rects; i++) - sizes[size_index(rects + i)]++; - int idx = 0; - for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) { - for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) { - bins[i + j] = idx; - idx += sizes[i + j]; - } - scratch[idx++] = -1; - } - for (int i = 0; i < num_rects; i++) - scratch[bins[size_index(rects + i)]++] = i; - for (int i = 0; i < 16; i++) - bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS]; - struct { - int size, x, bottom; - } stack[16] = {{15, 0, h}}, s = {}; - int stackpos = 1; - int y; - while (stackpos) { - y = s.bottom; - s = stack[--stackpos]; - s.size++; - while (s.size--) { - int maxy = -1; - int obj; - while ((obj = scratch[bins[s.size]]) >= 0) { - int bottom = y + rects[obj].source.y1; - if (bottom > s.bottom) - break; - int right = s.x + rects[obj].source.x1; - if (right > w) - break; - bins[s.size]++; - rects[obj].source.x0 = s.x; - rects[obj].source.x1 += s.x; - rects[obj].source.y0 = y; - rects[obj].source.y1 += y; - num_rects--; - if (maxy <= 0) - stack[stackpos++] = s; - s.x = right; - maxy = FFMAX(maxy, bottom); - } - if (maxy > 0) - s.bottom = maxy; - } - } - return num_rects ? -1 : 0; -} - static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs) { struct vdpctx *vc = vo->priv; struct vdp_functions *vdp = vc->vdp; VdpStatus vdp_st; + struct eosd_packer *packer = vc->eosd_packer; int i; - ASS_Image *img = imgs->imgs; - ASS_Image *p; - struct eosd_bitmap_surface *sfc = &vc->eosd_surface; - bool need_upload = false; - - if (imgs->changed == 0) - return; // Nothing changed, no need to redraw - - vc->eosd_render_count = 0; - - if (!img) - return; // There's nothing to render! - - if (imgs->changed == 1) - goto eosd_skip_upload; - - need_upload = true; - bool reallocate = false; - while (1) { - for (p = img, i = 0; p; p = p->next) { - if (p->w <= 0 || p->h <= 0) - continue; - // Allocate new space for surface/target arrays - if (i >= vc->eosd_targets_size) { - vc->eosd_targets_size = FFMAX(vc->eosd_targets_size * 2, 512); - vc->eosd_targets = - talloc_realloc_size(vc, vc->eosd_targets, - vc->eosd_targets_size - * sizeof(*vc->eosd_targets)); - vc->eosd_scratch = - talloc_realloc_size(vc, vc->eosd_scratch, - (vc->eosd_targets_size + 16) - * sizeof(*vc->eosd_scratch)); - } - vc->eosd_targets[i].source.x1 = p->w; - vc->eosd_targets[i].source.y1 = p->h; - i++; - } - if (pack_rectangles(vc->eosd_targets, i, sfc->w, sfc->h, - vc->eosd_scratch) >= 0) - break; - int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE), - sfc->max_width); - int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE), - sfc->max_height); - if (w == sfc->w && h == sfc->h) { - mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on " - "a surface with the maximum supported size\n"); - return; - } else { - sfc->w = w; - sfc->h = h; - } - reallocate = true; - } - if (reallocate) { - if (sfc->surface != VDP_INVALID_HANDLE) { - vdp_st = vdp->bitmap_surface_destroy(sfc->surface); + + bool need_repos, need_upload, need_resize; + eosd_packer_generate(packer, imgs, &need_repos, &need_upload, &need_resize); + + if (!need_upload) + return; + + if (need_resize) { + if (vc->eosd_surface != VDP_INVALID_HANDLE) { + vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface); CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy"); } - mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for " - "EOSD bitmaps.\n", sfc->w, sfc->h); vdp_st = vdp->bitmap_surface_create(vc->vdp_device, VDP_RGBA_FORMAT_A8, - sfc->w, sfc->h, true, - &sfc->surface); + packer->surface.w, packer->surface.h, + true, &vc->eosd_surface); if (vdp_st != VDP_STATUS_OK) - sfc->surface = VDP_INVALID_HANDLE; + vc->eosd_surface = VDP_INVALID_HANDLE; CHECK_ST_WARNING("EOSD: error when creating surface"); } -eosd_skip_upload: - if (sfc->surface == VDP_INVALID_HANDLE) + if (vc->eosd_surface == VDP_INVALID_HANDLE) return; - for (p = img; p; p = p->next) { - if (p->w <= 0 || p->h <= 0) - continue; - struct eosd_target *target = &vc->eosd_targets[vc->eosd_render_count]; - if (need_upload) { - vdp_st = vdp-> - bitmap_surface_put_bits_native(sfc->surface, - (const void *) &p->bitmap, - &p->stride, &target->source); - CHECK_ST_WARNING("EOSD: putbits failed"); - } - // Render dest, color, etc. - target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0; - target->color.blue = ((p->color >> 8) & 0xff) / 255.0; - target->color.green = ((p->color >> 16) & 0xff) / 255.0; - target->color.red = ((p->color >> 24) & 0xff) / 255.0; - target->dest.x0 = p->dst_x; - target->dest.y0 = p->dst_y; - target->dest.x1 = p->w + p->dst_x; - target->dest.y1 = p->h + p->dst_y; - vc->eosd_render_count++; + + for (i = 0; i < vc->eosd_packer->targets_count; i++) { + struct eosd_target *target = &vc->eosd_packer->targets[i]; + ASS_Image *p = target->ass_img; + vdp_st = vdp-> + bitmap_surface_put_bits_native(vc->eosd_surface, + (const void *) &p->bitmap, + &p->stride, + EOSD_VDP_RC(target->source)); + CHECK_ST_WARNING("EOSD: putbits failed"); + target->ass_img = NULL; } } @@ -1604,8 +1455,8 @@ static void destroy_vdpau_objects(struct vo *vo) CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy"); } - if (vc->eosd_surface.surface != VDP_INVALID_HANDLE) { - vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface.surface); + if (vc->eosd_surface != VDP_INVALID_HANDLE) { + vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface); CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy"); } @@ -1640,6 +1491,8 @@ static int preinit(struct vo *vo, const char *arg) struct vdpctx *vc = talloc_zero(vo, struct vdpctx); vo->priv = vc; + vc->eosd_packer = eosd_packer_create(vo); + // Mark everything as invalid first so uninit() can tell what has been // allocated mark_vdpau_objects_uninitialized(vo); diff --git a/libvo/w32_common.c b/libvo/w32_common.c index 475327ce5c..513cf2820c 100644 --- a/libvo/w32_common.c +++ b/libvo/w32_common.c @@ -18,6 +18,7 @@ #include <stdio.h> #include <limits.h> +#include <assert.h> #include <windows.h> #include <windowsx.h> @@ -30,6 +31,8 @@ #include "aspect.h" #include "w32_common.h" #include "mp_fifo.h" +#include "osdep/io.h" +#include "talloc.h" #ifndef WM_XBUTTONDOWN # define WM_XBUTTONDOWN 0x020B @@ -43,16 +46,25 @@ #define WIN_ID_TO_HWND(x) ((HWND)(uint32_t)(x)) -static const char classname[] = "MPlayer - The Movie Player"; +static const wchar_t classname[] = L"mplayer2"; int vo_vm = 0; static int depthonscreen; -// last non-fullscreen extends +// last non-fullscreen extends (updated only on fullscreen or on initialization) static int prev_width; static int prev_height; static int prev_x; static int prev_y; +// whether the window position and size were intialized +static bool window_bounds_initialized; + +static bool current_fs; + +static int window_x; +static int window_y; + +// video size static uint32_t o_dwidth; static uint32_t o_dheight; @@ -64,13 +76,12 @@ static HDC dev_hdc; static int event_flags; static int mon_cnt; -static HMONITOR (WINAPI* myMonitorFromWindow)(HWND, DWORD); -static BOOL (WINAPI* myGetMonitorInfo)(HMONITOR, LPMONITORINFO); -static BOOL (WINAPI* myEnumDisplayMonitors)(HDC, LPCRECT, MONITORENUMPROC, LPARAM); +static bool key_state[256]; static const struct mp_keymap vk_map[] = { // special keys - {VK_ESCAPE, KEY_ESC}, {VK_BACK, KEY_BS}, {VK_TAB, KEY_TAB}, {VK_CONTROL, KEY_CTRL}, + {VK_ESCAPE, KEY_ESC}, {VK_BACK, KEY_BS}, {VK_TAB, KEY_TAB}, + {VK_RETURN, KEY_ENTER}, {VK_PAUSE, KEY_PAUSE}, {VK_SNAPSHOT, KEY_PRINT}, // cursor keys {VK_LEFT, KEY_LEFT}, {VK_UP, KEY_UP}, {VK_RIGHT, KEY_RIGHT}, {VK_DOWN, KEY_DOWN}, @@ -82,7 +93,7 @@ static const struct mp_keymap vk_map[] = { // F-keys {VK_F1, KEY_F+1}, {VK_F2, KEY_F+2}, {VK_F3, KEY_F+3}, {VK_F4, KEY_F+4}, {VK_F5, KEY_F+5}, {VK_F6, KEY_F+6}, {VK_F7, KEY_F+7}, {VK_F8, KEY_F+8}, - {VK_F9, KEY_F+9}, {VK_F10, KEY_F+10}, {VK_F11, KEY_F+11}, {VK_F1, KEY_F+12}, + {VK_F9, KEY_F+9}, {VK_F10, KEY_F+10}, {VK_F11, KEY_F+11}, {VK_F12, KEY_F+12}, // numpad {VK_NUMPAD0, KEY_KP0}, {VK_NUMPAD1, KEY_KP1}, {VK_NUMPAD2, KEY_KP2}, {VK_NUMPAD3, KEY_KP3}, {VK_NUMPAD4, KEY_KP4}, {VK_NUMPAD5, KEY_KP5}, @@ -92,10 +103,53 @@ static const struct mp_keymap vk_map[] = { {0, 0} }; +static void vo_rect_add_window_borders(RECT *rc) +{ + AdjustWindowRect(rc, GetWindowLong(vo_window, GWL_STYLE), 0); +} + +// basically a reverse AdjustWindowRect (win32 doesn't appear to have this) +static void subtract_window_borders(RECT *rc) +{ + RECT b = { 0, 0, 0, 0 }; + vo_rect_add_window_borders(&b); + rc->left -= b.left; + rc->top -= b.top; + rc->right -= b.right; + rc->bottom -= b.bottom; +} + +// turn a WMSZ_* input value in v into the border that should be resized +// returns: 0=left, 1=top, 2=right, 3=bottom, -1=undefined +static int get_resize_border(int v) { + switch (v) { + case WMSZ_LEFT: return 3; + case WMSZ_TOP: return 2; + case WMSZ_RIGHT: return 3; + case WMSZ_BOTTOM: return 2; + case WMSZ_TOPLEFT: return 1; + case WMSZ_TOPRIGHT: return 1; + case WMSZ_BOTTOMLEFT: return 3; + case WMSZ_BOTTOMRIGHT: return 3; + default: return -1; + } +} + +static int mod_state(void) +{ + int res = 0; + if (key_state[VK_CONTROL]) + res |= KEY_MODIFIER_CTRL; + if (key_state[VK_SHIFT]) + res |= KEY_MODIFIER_SHIFT; + if (key_state[VK_MENU]) + res |= KEY_MODIFIER_ALT; + return res; +} + static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { RECT r; POINT p; - int mpkey; switch (message) { case WM_ERASEBKGND: // no need to erase background seperately return 1; @@ -107,30 +161,39 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l p.x = 0; p.y = 0; ClientToScreen(vo_window, &p); - vo_dx = p.x; - vo_dy = p.y; + window_x = p.x; + window_y = p.y; + mp_msg(MSGT_VO, MSGL_V, "[vo] move window: %d:%d\n", + window_x, window_y); break; case WM_SIZE: event_flags |= VO_EVENT_RESIZE; GetClientRect(vo_window, &r); vo_dwidth = r.right; vo_dheight = r.bottom; + mp_msg(MSGT_VO, MSGL_V, "[vo] resize window: %d:%d\n", + vo_dwidth, vo_dheight); break; - case WM_WINDOWPOSCHANGING: + case WM_SIZING: if (vo_keepaspect && !vo_fs && WinID < 0) { - WINDOWPOS *wpos = (WINDOWPOS*)lParam; - int xborder, yborder; - r.left = r.top = 0; - r.right = wpos->cx; - r.bottom = wpos->cy; - AdjustWindowRect(&r, GetWindowLong(vo_window, GWL_STYLE), 0); - xborder = (r.right - r.left) - wpos->cx; - yborder = (r.bottom - r.top) - wpos->cy; - wpos->cx -= xborder; wpos->cy -= yborder; - aspect_fit(global_vo, &wpos->cx, &wpos->cy, wpos->cx, wpos->cy); - wpos->cx += xborder; wpos->cy += yborder; + RECT *rc = (RECT*)lParam; + // get client area of the windows if it had the rect rc + // (subtracting the window borders) + r = *rc; + subtract_window_borders(&r); + int c_w = r.right - r.left, c_h = r.bottom - r.top; + float aspect = global_vo->aspdat.asp; + int d_w = c_h * aspect - c_w; + int d_h = c_w / aspect - c_h; + int d_corners[4] = { d_w, d_h, -d_w, -d_h }; + int corners[4] = { rc->left, rc->top, rc->right, rc->bottom }; + int corner = get_resize_border(wParam); + if (corner >= 0) + corners[corner] -= d_corners[corner]; + *rc = (RECT) { corners[0], corners[1], corners[2], corners[3] }; + return TRUE; } - return 0; + break; case WM_CLOSE: mplayer_put_key(KEY_CLOSE_WIN); break; @@ -143,16 +206,47 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l } break; case WM_KEYDOWN: - mpkey = lookup_keymap_table(vk_map, wParam); + case WM_SYSKEYDOWN: { + key_state[wParam & 0xFF] = true; + int mpkey = lookup_keymap_table(vk_map, wParam); if (mpkey) - mplayer_put_key(mpkey); + mplayer_put_key(mpkey | mod_state()); + if (wParam == VK_F10) + return 0; + break; + } + case WM_KEYUP: + case WM_SYSKEYUP: + key_state[wParam & 0xFF] = false; break; case WM_CHAR: - mplayer_put_key(wParam); + case WM_SYSCHAR: { + int mods = mod_state(); + int code = wParam; + // Apparently Ctrl+A to Ctrl+Z is special cased, and produces + // character codes from 1-26. Work it around. + // Also, enter/return (including the keypad variant) and CTRL+J both + // map to wParam==10. As a workaround, check VK_RETURN to + // distinguish these two key combinations. + if ((mods & KEY_MODIFIER_CTRL) && code >= 1 && code <= 26 + && !key_state[VK_RETURN]) + code = code - 1 + (mods & KEY_MODIFIER_SHIFT ? 'A' : 'a'); + if (code >= 32 && code < (1<<21)) { + mplayer_put_key(code | mods); + // At least with Alt+char, not calling DefWindowProcW stops + // Windows from emitting a beep. + return 0; + } + break; + } + case WM_SETFOCUS: + case WM_KILLFOCUS: + // prevent modifier keys from getting stuck + memset(key_state, 0, sizeof(key_state)); break; case WM_LBUTTONDOWN: if (!vo_nomouse_input && (vo_fs || (wParam & MK_CONTROL))) { - mplayer_put_key(MOUSE_BTN0); + mplayer_put_key(MOUSE_BTN0 | mod_state()); break; } if (!vo_fs) { @@ -163,11 +257,11 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l break; case WM_MBUTTONDOWN: if (!vo_nomouse_input) - mplayer_put_key(MOUSE_BTN1); + mplayer_put_key(MOUSE_BTN1 | mod_state()); break; case WM_RBUTTONDOWN: if (!vo_nomouse_input) - mplayer_put_key(MOUSE_BTN2); + mplayer_put_key(MOUSE_BTN2 | mod_state()); break; case WM_MOUSEMOVE: vo_mouse_movement(global_vo, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); @@ -176,23 +270,23 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l if (!vo_nomouse_input) { int x = GET_WHEEL_DELTA_WPARAM(wParam); if (x > 0) - mplayer_put_key(MOUSE_BTN3); + mplayer_put_key(MOUSE_BTN3 | mod_state()); else - mplayer_put_key(MOUSE_BTN4); + mplayer_put_key(MOUSE_BTN4 | mod_state()); } break; case WM_XBUTTONDOWN: if (!vo_nomouse_input) { int x = HIWORD(wParam); if (x == 1) - mplayer_put_key(MOUSE_BTN5); + mplayer_put_key(MOUSE_BTN5 | mod_state()); else // if (x == 2) - mplayer_put_key(MOUSE_BTN6); + mplayer_put_key(MOUSE_BTN6 | mod_state()); } break; } - return DefWindowProc(hWnd, message, wParam, lParam); + return DefWindowProcW(hWnd, message, wParam, lParam); } /** @@ -215,9 +309,9 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l int vo_w32_check_events(void) { MSG msg; event_flags = 0; - while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); - DispatchMessage(&msg); + DispatchMessageW(&msg); } if (WinID >= 0) { BOOL res; @@ -230,8 +324,8 @@ int vo_w32_check_events(void) { } p.x = 0; p.y = 0; ClientToScreen(vo_window, &p); - if (p.x != vo_dx || p.y != vo_dy) { - vo_dx = p.x; vo_dy = p.y; + if (p.x != window_x || p.y != window_y) { + window_x = p.x; window_y = p.y; event_flags |= VO_EVENT_MOVE; } res = GetClientRect(WIN_ID_TO_HWND(WinID), &r); @@ -281,18 +375,18 @@ void w32_update_xinerama_info(void) { if (tmp) vo_screenwidth = tmp; tmp = GetSystemMetrics(SM_CYVIRTUALSCREEN); if (tmp) vo_screenheight = tmp; - } else if (xinerama_screen == -1 && myMonitorFromWindow && myGetMonitorInfo) { + } else if (xinerama_screen == -1) { MONITORINFO mi; - HMONITOR m = myMonitorFromWindow(vo_window, MONITOR_DEFAULTTOPRIMARY); + HMONITOR m = MonitorFromWindow(vo_window, MONITOR_DEFAULTTOPRIMARY); mi.cbSize = sizeof(mi); - myGetMonitorInfo(m, &mi); + GetMonitorInfoW(m, &mi); xinerama_x = mi.rcMonitor.left; xinerama_y = mi.rcMonitor.top; vo_screenwidth = mi.rcMonitor.right - mi.rcMonitor.left; vo_screenheight = mi.rcMonitor.bottom - mi.rcMonitor.top; - } else if (xinerama_screen > 0 && myEnumDisplayMonitors) { + } else if (xinerama_screen > 0) { mon_cnt = 0; - myEnumDisplayMonitors(NULL, NULL, mon_enum, 0); + EnumDisplayMonitors(NULL, NULL, mon_enum, 0); } aspect_save_screenres(vo_screenwidth, vo_screenheight); } @@ -348,19 +442,34 @@ static void changeMode(void) { static void resetMode(void) { if (vo_vm) - ChangeDisplaySettings(0, 0); + ChangeDisplaySettings(0, 0); } -static int createRenderingContext(void) { +// Update the window title, position, size, and border style from vo_* values. +static int reinit_window_state(void) { + const LONG NO_FRAME = WS_POPUP; + const LONG FRAME = WS_OVERLAPPEDWINDOW | WS_SIZEBOX; HWND layer = HWND_NOTOPMOST; RECT r; - int style = (vo_border && !vo_fs) ? - (WS_OVERLAPPEDWINDOW | WS_SIZEBOX) : WS_POPUP; if (WinID >= 0) return 1; - if (vo_fs || vo_ontop) layer = HWND_TOPMOST; + wchar_t *title = mp_from_utf8(NULL, vo_get_window_title(global_vo)); + SetWindowTextW(vo_window, title); + talloc_free(title); + + bool toggle_fs = current_fs != vo_fs; + current_fs = vo_fs; + + LONG style = GetWindowLong(vo_window, GWL_STYLE); + style &= ~(NO_FRAME | FRAME); + style |= (vo_border && !vo_fs) ? FRAME : NO_FRAME; + + if (vo_fs || vo_ontop) + layer = HWND_TOPMOST; + + // xxx not sure if this can trigger any unwanted messages (WM_MOVE/WM_SIZE) if (vo_fs) { changeMode(); while (ShowCursor(0) >= 0) /**/ ; @@ -369,37 +478,54 @@ static int createRenderingContext(void) { while (ShowCursor(1) < 0) /**/ ; } updateScreenProperties(); - ShowWindow(vo_window, SW_HIDE); - SetWindowLong(vo_window, GWL_STYLE, style); + if (vo_fs) { - prev_width = vo_dwidth; - prev_height = vo_dheight; - prev_x = vo_dx; - prev_y = vo_dy; + // Save window position and size when switching to fullscreen. + if (toggle_fs) { + prev_width = vo_dwidth; + prev_height = vo_dheight; + prev_x = window_x; + prev_y = window_y; + mp_msg(MSGT_VO, MSGL_V, "[vo] save window bounds: %d:%d:%d:%d\n", + prev_x, prev_y, prev_width, prev_height); + } vo_dwidth = vo_screenwidth; vo_dheight = vo_screenheight; - vo_dx = xinerama_x; - vo_dy = xinerama_y; + window_x = xinerama_x; + window_y = xinerama_y; } else { - // make sure there are no "stale" resize events - // that would set vo_d* to wrong values - vo_w32_check_events(); - vo_dwidth = prev_width; - vo_dheight = prev_height; - vo_dx = prev_x; - vo_dy = prev_y; - // HACK around what probably is a windows focus bug: - // when pressing 'f' on the console, then 'f' again to - // return to windowed mode, any input into the video - // window is lost forever. - SetFocus(vo_window); + if (toggle_fs) { + // Restore window position and size when switching from fullscreen. + mp_msg(MSGT_VO, MSGL_V, "[vo] restore window bounds: %d:%d:%d:%d\n", + prev_x, prev_y, prev_width, prev_height); + vo_dwidth = prev_width; + vo_dheight = prev_height; + window_x = prev_x; + window_y = prev_y; + } } - r.left = vo_dx; + + r.left = window_x; r.right = r.left + vo_dwidth; - r.top = vo_dy; + r.top = window_y; r.bottom = r.top + vo_dheight; - AdjustWindowRect(&r, style, 0); - SetWindowPos(vo_window, layer, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_SHOWWINDOW); + + SetWindowLong(vo_window, GWL_STYLE, style); + vo_rect_add_window_borders(&r); + + mp_msg(MSGT_VO, MSGL_V, "[vo] reset window bounds: %ld:%ld:%ld:%ld\n", + r.left, r.top, r.right - r.left, r.bottom - r.top); + + SetWindowPos(vo_window, layer, r.left, r.top, r.right - r.left, + r.bottom - r.top, SWP_FRAMECHANGED); + // For some reason, moving SWP_SHOWWINDOW to a second call works better + // with wine: returning from fullscreen doesn't cause a bogus resize to + // screen size. + // It's not needed on Windows XP or wine with a virtual desktop. + // It doesn't seem to have any negative effects. + SetWindowPos(vo_window, NULL, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + return 1; } @@ -409,10 +535,6 @@ static int createRenderingContext(void) { * This function should be called in libvo's "config" callback. * It configures a window and shows it on the screen. * - * Global libvo variables changed: - * vo_fs - * vo_vm - * * \return 1 - Success, 0 - Failure */ int vo_w32_config(uint32_t width, uint32_t height, uint32_t flags) { @@ -442,34 +564,54 @@ int vo_w32_config(uint32_t width, uint32_t height, uint32_t flags) { // we already have a fully initialized window, so nothing needs to be done if (flags & VOFLAG_HIDDEN) return 1; - // store original size for videomode switching + + bool reset_size = !(o_dwidth == width && o_dheight == height); + o_dwidth = width; o_dheight = height; + // the desired size is ignored in wid mode, it always matches the window size. if (WinID < 0) { - // the desired size is ignored in wid mode, it always matches the window size. - prev_width = vo_dwidth = width; - prev_height = vo_dheight = height; - prev_x = vo_dx; - prev_y = vo_dy; + if (window_bounds_initialized) { + // restore vo_dwidth/vo_dheight, which are reset against our will + // in vo_config() + RECT r; + GetClientRect(vo_window, &r); + vo_dwidth = r.right; + vo_dheight = r.bottom; + } else { + // first vo_config call; vo_config() will always set vo_dx/dy so + // that the window is centered on the screen, and this is the only + // time we actually want to use vo_dy/dy (this is not sane, and + // video_out.h should provide a function to query the initial + // window position instead) + window_bounds_initialized = true; + reset_size = true; + window_x = prev_x = vo_dx; + window_y = prev_y = vo_dy; + } + if (reset_size) { + prev_width = vo_dwidth = width; + prev_height = vo_dheight = height; + } } vo_fs = flags & VOFLAG_FULLSCREEN; vo_vm = flags & VOFLAG_MODESWITCHING; - return createRenderingContext(); + return reinit_window_state(); } /** * \brief return the name of the selected device if it is indepedant * \return pointer to string, must be freed. */ -static char *get_display_name(void) { - DISPLAY_DEVICE disp; +static wchar_t *get_display_name(void) { + DISPLAY_DEVICEW disp; disp.cb = sizeof(disp); - EnumDisplayDevices(NULL, vo_adapter_num, &disp, 0); + EnumDisplayDevicesW(NULL, vo_adapter_num, &disp, 0); if (disp.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) return NULL; - return strdup(disp.DeviceName); + return wcsdup(disp.DeviceName); } /** @@ -490,24 +632,23 @@ static char *get_display_name(void) { */ int vo_w32_init(void) { HICON mplayerIcon = 0; - char exedir[MAX_PATH]; - HINSTANCE user32; - char *dev; + wchar_t exedir[MAX_PATH]; + wchar_t *dev; if (vo_window) return 1; - hInstance = GetModuleHandle(0); + hInstance = GetModuleHandleW(NULL); - if (GetModuleFileName(0, exedir, MAX_PATH)) - mplayerIcon = ExtractIcon(hInstance, exedir, 0); + if (GetModuleFileNameW(0, exedir, MAX_PATH)) + mplayerIcon = ExtractIconW(hInstance, exedir, 0); if (!mplayerIcon) mplayerIcon = LoadIcon(0, IDI_APPLICATION); { - WNDCLASSEX wcex = { sizeof wcex, CS_OWNDC | CS_DBLCLKS, WndProc, 0, 0, hInstance, mplayerIcon, LoadCursor(0, IDC_ARROW), NULL, 0, classname, mplayerIcon }; + WNDCLASSEXW wcex = { sizeof wcex, CS_OWNDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, mplayerIcon, LoadCursor(0, IDC_ARROW), NULL, 0, classname, mplayerIcon }; - if (!RegisterClassEx(&wcex)) { + if (!RegisterClassExW(&wcex)) { mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to register window class!\n"); return 0; } @@ -518,12 +659,12 @@ int vo_w32_init(void) { RECT r; GetClientRect(WIN_ID_TO_HWND(WinID), &r); vo_dwidth = r.right; vo_dheight = r.bottom; - vo_window = CreateWindowEx(WS_EX_NOPARENTNOTIFY, classname, classname, + vo_window = CreateWindowExW(WS_EX_NOPARENTNOTIFY, classname, classname, WS_CHILD | WS_VISIBLE, 0, 0, vo_dwidth, vo_dheight, WIN_ID_TO_HWND(WinID), 0, hInstance, 0); EnableWindow(vo_window, 0); } else - vo_window = CreateWindowEx(0, classname, classname, + vo_window = CreateWindowExW(0, classname, classname, vo_border ? (WS_OVERLAPPEDWINDOW | WS_SIZEBOX) : WS_POPUP, CW_USEDEFAULT, 0, 100, 100, 0, 0, hInstance, 0); if (!vo_window) { @@ -531,18 +672,9 @@ int vo_w32_init(void) { return 0; } - myMonitorFromWindow = NULL; - myGetMonitorInfo = NULL; - myEnumDisplayMonitors = NULL; - user32 = GetModuleHandle("user32.dll"); - if (user32) { - myMonitorFromWindow = (void *)GetProcAddress(user32, "MonitorFromWindow"); - myGetMonitorInfo = GetProcAddress(user32, "GetMonitorInfoA"); - myEnumDisplayMonitors = GetProcAddress(user32, "EnumDisplayMonitors"); - } dev_hdc = 0; dev = get_display_name(); - if (dev) dev_hdc = CreateDC(dev, NULL, NULL, NULL); + if (dev) dev_hdc = CreateDCW(dev, NULL, NULL, NULL); free(dev); updateScreenProperties(); @@ -569,7 +701,7 @@ int vo_w32_init(void) { void vo_w32_fullscreen(void) { vo_fs = !vo_fs; - createRenderingContext(); + reinit_window_state(); } /** @@ -582,7 +714,7 @@ void vo_w32_fullscreen(void) { */ void vo_w32_border(void) { vo_border = !vo_border; - createRenderingContext(); + reinit_window_state(); } /** @@ -597,7 +729,7 @@ void vo_w32_ontop( void ) { vo_ontop = !vo_ontop; if (!vo_fs) { - createRenderingContext(); + reinit_window_state(); } } @@ -617,7 +749,8 @@ void vo_w32_uninit(void) { dev_hdc = 0; DestroyWindow(vo_window); vo_window = 0; - UnregisterClass(classname, 0); + UnregisterClassW(classname, 0); + o_dwidth = o_dheight = 0; } /** diff --git a/libvo/x11_common.c b/libvo/x11_common.c index ee50ba5575..2fae9f480a 100644 --- a/libvo/x11_common.c +++ b/libvo/x11_common.c @@ -1020,7 +1020,8 @@ static void vo_x11_set_property_string(struct vo *vo, Atom name, const char *t) XTextProperty prop = {0}; if (Xutf8TextListToTextProperty(x11->display, (char **)&t, 1, - XStdICCTextStyle, &prop) == Success) { + XStdICCTextStyle, &prop) == Success) + { XSetTextProperty(x11->display, x11->window, &prop, name); } else { // Strictly speaking this violates the ICCCM, but there's no way we |