aboutsummaryrefslogtreecommitdiffhomepage
path: root/libvo
diff options
context:
space:
mode:
Diffstat (limited to 'libvo')
-rw-r--r--libvo/cocoa_common.h3
-rw-r--r--libvo/cocoa_common.m47
-rw-r--r--libvo/d3d_shader_yuv.h142
-rw-r--r--libvo/d3d_shader_yuv.hlsl44
-rw-r--r--libvo/d3d_shader_yuv_2ch.h170
-rw-r--r--libvo/eosd_packer.c254
-rw-r--r--libvo/eosd_packer.h71
-rw-r--r--libvo/filter_kernels.c279
-rw-r--r--libvo/filter_kernels.h45
-rw-r--r--libvo/gl_common.c845
-rw-r--r--libvo/gl_common.h342
-rw-r--r--libvo/gl_header_fixes.h231
-rw-r--r--libvo/mga_template.c2
-rw-r--r--libvo/video_out.c16
-rw-r--r--libvo/vo_direct3d.c2137
-rw-r--r--libvo/vo_directfb2.c14
-rw-r--r--libvo/vo_directx.c1742
-rw-r--r--libvo/vo_gl.c326
-rw-r--r--libvo/vo_gl3.c2418
-rw-r--r--libvo/vo_gl3_shaders.glsl316
-rw-r--r--libvo/vo_svga.c4
-rw-r--r--libvo/vo_vdpau.c255
-rw-r--r--libvo/w32_common.c355
-rw-r--r--libvo/x11_common.c3
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