From 81c677f28b93bb526f98be9c0e5d144c932000cd Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 16 May 2014 15:18:17 +0800 Subject: [PATCH] Feature: #183 custom window shader & #193 --no-fading-destroyed-argb - Add --glx-fshader-win, which specifies a custom fragment shader for painting windows. compton-default-fshader-win.glsl is the shader with default behavior, and compton-fake-transparency-fshader-win.glsl provides a template of fake transparency. (#183) - Add --force-win-blend to force all windows to be painted with blending. - Add --no-fading-destroyed-argb, as a workaround of bugs in some WMs. (#193) --- compton-default-fshader-win.glsl | 11 + compton-fake-transparency-fshader-win.glsl | 19 ++ compton.sample.conf | 3 +- src/common.h | 76 +++++- src/compton.c | 73 +++++- src/compton.h | 27 ++- src/opengl.c | 257 +++++++++++++++------ 7 files changed, 375 insertions(+), 91 deletions(-) create mode 100644 compton-default-fshader-win.glsl create mode 100644 compton-fake-transparency-fshader-win.glsl diff --git a/compton-default-fshader-win.glsl b/compton-default-fshader-win.glsl new file mode 100644 index 00000000..e3195238 --- /dev/null +++ b/compton-default-fshader-win.glsl @@ -0,0 +1,11 @@ +uniform float opacity; +uniform bool invert_color; +uniform sampler2D tex; + +void main() { + vec4 c = texture2D(tex, gl_TexCoord[0]); + if (invert_color) + c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a); + c *= opacity; + gl_FragColor = c; +} diff --git a/compton-fake-transparency-fshader-win.glsl b/compton-fake-transparency-fshader-win.glsl new file mode 100644 index 00000000..88e36533 --- /dev/null +++ b/compton-fake-transparency-fshader-win.glsl @@ -0,0 +1,19 @@ +uniform float opacity; +uniform bool invert_color; +uniform sampler2D tex; + +void main() { + vec4 c = texture2D(tex, gl_TexCoord[0]); + { + // Change vec4(1.0, 1.0, 1.0, 1.0) to your desired color + vec4 vdiff = abs(vec4(1.0, 1.0, 1.0, 1.0) - c); + float diff = max(max(max(vdiff.r, vdiff.g), vdiff.b), vdiff.a); + // Change 0.8 to your desired opacity + if (diff < 0.001) + c *= 0.8; + } + if (invert_color) + c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a); + c *= opacity; + gl_FragColor = c; +} diff --git a/compton.sample.conf b/compton.sample.conf index 46d50569..6d021470 100644 --- a/compton.sample.conf +++ b/compton.sample.conf @@ -48,6 +48,7 @@ fading = true; fade-in-step = 0.03; fade-out-step = 0.03; # no-fading-openclose = true; +# no-fading-destroyed-argb = true; fade-exclude = [ ]; # Other @@ -84,5 +85,5 @@ glx-swap-method = "undefined"; # Window type settings wintypes: { - tooltip = { fade = true; shadow = false; opacity = 0.75; focus = true; }; + tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; }; }; diff --git a/src/common.h b/src/common.h index bbbfeae8..0f8cc39a 100644 --- a/src/common.h +++ b/src/common.h @@ -469,6 +469,25 @@ typedef struct { /// Height of the textures. int height; } glx_blur_cache_t; + +typedef struct { + /// GLSL program. + GLuint prog; + /// Location of uniform "opacity" in window GLSL program. + GLint unifm_opacity; + /// Location of uniform "invert_color" in blur GLSL program. + GLint unifm_invert_color; + /// Location of uniform "tex" in window GLSL program. + GLint unifm_tex; +} glx_prog_main_t; + +#define GLX_PROG_MAIN_INIT { \ + .prog = 0, \ + .unifm_opacity = -1, \ + .unifm_invert_color = -1, \ + .unifm_tex = -1, \ +} + #endif typedef struct { @@ -536,10 +555,12 @@ typedef struct _options_t { int glx_swap_method; /// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring. bool glx_use_gpushader4; - /// Whether to try to detect WM windows and mark them as focused. - bool mark_wmwin_focused; - /// Whether to mark override-redirect windows as focused. - bool mark_ovredir_focused; + /// Custom fragment shader for painting windows, as a string. + char *glx_fshader_win_str; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + /// Custom GLX program used for painting window. + glx_prog_main_t glx_prog_win; +#endif /// Whether to fork to background. bool fork_after_register; /// Whether to detect rounded corners. @@ -547,6 +568,8 @@ typedef struct _options_t { /// Whether to paint on X Composite overlay window instead of root /// window. bool paint_on_overlay; + /// Force painting of window content with blending. + bool force_win_blend; /// Resize damage for a specific number of pixels. int resize_damage; /// Whether to unredirect all windows if a full-screen opaque window @@ -622,6 +645,8 @@ typedef struct _options_t { time_ms_t fade_delta; /// Whether to disable fading on window open/close. bool no_fading_openclose; + /// Whether to disable fading on ARGB managed destroyed windows. + bool no_fading_destroyed_argb; /// Fading blacklist. A linked list of conditions. c2_lptr_t *fade_blacklist; @@ -672,6 +697,10 @@ typedef struct _options_t { // === Focus related === /// Consider windows of specific types to be always focused. bool wintype_focus[NUM_WINTYPES]; + /// Whether to try to detect WM windows and mark them as focused. + bool mark_wmwin_focused; + /// Whether to mark override-redirect windows as focused. + bool mark_ovredir_focused; /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. bool use_ewmh_active_win; /// A list of windows always to be considered focused. @@ -1965,6 +1994,14 @@ win_is_fullscreen(session_t *ps, const win *w) { && (!w->bounding_shaped || w->rounded_corners); } +/** + * Check if a window will be painted solid. + */ +static inline bool +win_is_solid(session_t *ps, const win *w) { + return WMODE_SOLID == w->mode && !ps->o.force_win_blend; +} + /** * Determine if a window has a specific property. * @@ -2073,6 +2110,13 @@ glx_on_root_change(session_t *ps); bool glx_init_blur(session_t *ps); +#ifdef CONFIG_VSYNC_OPENGL_GLSL +bool +glx_load_prog_main(session_t *ps, + const char *vshader_str, const char *fshader_str, + glx_prog_main_t *pprogram); +#endif + bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, unsigned width, unsigned height, unsigned depth); @@ -2108,10 +2152,24 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg); bool -glx_render(session_t *ps, const glx_texture_t *ptex, +glx_render_(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, - double opacity, bool neg, - XserverRegion reg_tgt, const reg_data_t *pcache_reg); + double opacity, bool argb, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +#define \ + glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ + glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) +#else +#define \ + glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ + glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg) +#endif void glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); @@ -2122,6 +2180,10 @@ glx_create_shader(GLenum shader_type, const char *shader_str); GLuint glx_create_program(const GLuint * const shaders, int nshaders); + +GLuint +glx_create_program_from_str(const char *vert_shader_str, + const char *frag_shader_str); #endif /** diff --git a/src/compton.c b/src/compton.c index eea98653..dce4d096 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1206,7 +1206,7 @@ paint_preprocess(session_t *ps, win *list) { // If the window is solid, we add the window region to the // ignored region - if (WMODE_SOLID == w->mode) { + if (win_is_solid(ps, w)) { if (!w->frame_opacity) { if (w->border_size) w->reg_ignore = copy_region(ps, w->border_size); @@ -1240,7 +1240,7 @@ paint_preprocess(session_t *ps, win *list) { // is not correctly set. if (ps->o.unredir_if_possible && is_highest && to_paint) { is_highest = false; - if (WMODE_SOLID == w->mode + if (win_is_solid(ps, w) && (!w->frame_opacity || !win_has_frame(w)) && win_is_fullscreen(ps, w) && !w->unredir_if_possible_excluded) @@ -1316,7 +1316,7 @@ win_paint_shadow(session_t *ps, win *w, render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height, w->shadow_opacity, true, false, - w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg); + w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg, NULL); } /** @@ -1472,7 +1472,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, // Minimize the region we try to blur, if the window itself is not // opaque, only the frame is. XserverRegion reg_noframe = None; - if (WMODE_SOLID == w->mode) { + if (win_is_solid(ps, w)) { XserverRegion reg_all = border_size(ps, w, false); reg_noframe = win_get_region_noframe(ps, w, false); XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); @@ -1496,10 +1496,14 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, } static void -render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, +render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, Picture pict, glx_texture_t *ptex, - XserverRegion reg_paint, const reg_data_t *pcache_reg) { + XserverRegion reg_paint, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ) { switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: @@ -1515,7 +1519,7 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_render(ps, ptex, x, y, dx, dy, wid, hei, - ps->glx_z, opacity, neg, reg_paint, pcache_reg); + ps->glx_z, opacity, argb, neg, reg_paint, pcache_reg, pprogram); ps->glx_z += 1; break; #endif @@ -1911,7 +1915,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t if (!is_region_empty(ps, reg_paint, &cache_reg)) { set_tgt_clip(ps, reg_paint, &cache_reg); // Blur window background - if (w->blur_background && (WMODE_SOLID != w->mode + if (w->blur_background && (!win_is_solid(ps, w) || (ps->o.blur_background_frame && w->frame_opacity))) { win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg); } @@ -1992,7 +1996,8 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t glFlush(); glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, - ps->root_width, ps->root_height, 0, 1.0, false, region_real, NULL); + ps->root_width, ps->root_height, 0, 1.0, false, false, + region_real, NULL, NULL); // No break here! case BKEND_GLX: if (ps->o.glx_use_copysubbuffermesa) @@ -2396,6 +2401,12 @@ win_determine_fade(session_t *ps, win *w) { w->fade = w->fade_force; else if (ps->o.no_fading_openclose && w->in_openclose) w->fade = false; + else if (ps->o.no_fading_destroyed_argb && w->destroyed + && WMODE_ARGB == w->mode && w->client_win && w->client_win != w->id) { + w->fade = false; + // Prevent it from being overwritten by last-paint value + w->fade_last = false; + } // Ignore other possible causes of fading state changes after window // gets unmapped else if (IsViewable != w->a.map_state) { @@ -2548,8 +2559,8 @@ win_set_blur_background(session_t *ps, win *w, bool blur_background_new) { // Only consider window damaged if it's previously painted with background // blurred - if ((WMODE_SOLID != w->mode - || (ps->o.blur_background_frame && w->frame_opacity))) + if (!win_is_solid(ps, w) + || (ps->o.blur_background_frame && w->frame_opacity)) add_damage_win(ps, w); } @@ -3206,6 +3217,9 @@ destroy_win(session_t *ps, Window id) { w->destroyed = true; + if (ps->o.no_fading_destroyed_argb) + win_determine_fade(ps, w); + // Set fading callback set_fade_callback(ps, w, destroy_callback, false); @@ -4482,6 +4496,9 @@ usage(int ret) { " Mark windows that have no WM frame as active.\n" "--no-fading-openclose\n" " Do not fade on window open/close.\n" + "--no-fading-destroyed-argb\n" + " Do not fade destroyed ARGB windows with WM frame. Workaround of bugs\n" + " in Openbox, Fluxbox, etc.\n" "--shadow-ignore-shaped\n" " Do not paint shadows on shaped windows. (Deprecated, use\n" " --shadow-exclude \'bounding_shaped\' or\n" @@ -4674,6 +4691,12 @@ usage(int ret) { #else #define WARNING #endif + "--glx-fshader-win shader\n" + " GLX backend: Use specified GLSL fragment shader for rendering window\n" + " contents.\n" + "--force-win-blend\n" + " Force all windows to be painted with blending. Useful if you have a\n" + " --glx-fshader-win that could turn opaque pixels transparent.\n" "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" " man page for more details." WARNING "\n" @@ -5367,6 +5390,9 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { wintype_arr_enable(ps->o.wintype_fade); // --no-fading-open-close lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose); + // --no-fading-destroyed-argb + lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb", + &ps->o.no_fading_destroyed_argb); // --shadow-red config_lookup_float(&cfg, "shadow-red", &ps->o.shadow_red); // --shadow-green @@ -5583,6 +5609,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "xrender-sync", no_argument, NULL, 312 }, { "xrender-sync-fence", no_argument, NULL, 313 }, { "show-all-xerrors", no_argument, NULL, 314 }, + { "no-fading-destroyed-argb", no_argument, NULL, 315 }, + { "force-win-blend", no_argument, NULL, 316 }, + { "glx-fshader-win", required_argument, NULL, 317 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5838,6 +5867,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { P_CASEBOOL(311, vsync_use_glfinish); P_CASEBOOL(312, xrender_sync); P_CASEBOOL(313, xrender_sync_fence); + P_CASEBOOL(315, no_fading_destroyed_argb); + P_CASEBOOL(316, force_win_blend); + case 317: + ps->o.glx_fshader_win_str = mstrcpy(optarg); + break; default: usage(1); break; @@ -6776,6 +6810,9 @@ session_init(session_t *ps_old, int argc, char **argv) { .backend = BKEND_XRENDER, .glx_no_stencil = false, .glx_copy_from_front = false, +#ifdef CONFIG_VSYNC_OPENGL_GLSL + .glx_prog_win = GLX_PROG_MAIN_INIT, +#endif .mark_wmwin_focused = false, .mark_ovredir_focused = false, .fork_after_register = false, @@ -6818,6 +6855,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, .no_fading_openclose = false, + .no_fading_destroyed_argb = false, .fade_blacklist = NULL, .wintype_opacity = { 0.0 }, @@ -7146,6 +7184,17 @@ session_init(session_t *ps_old, int argc, char **argv) { #endif } + // Initialize window GL shader + if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) { +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str, &ps->o.glx_prog_win)) + exit(1); +#else + printf_errf("(): GLSL supported not compiled in, can't load shader."); + exit(1); +#endif + } + // Initialize software optimization if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); @@ -7227,7 +7276,6 @@ session_init(session_t *ps_old, int argc, char **argv) { cxfree(children); } - if (ps->o.track_focus) { recheck_focus(ps); } @@ -7395,6 +7443,7 @@ session_destroy(session_t *ps) { free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); + free(ps->o.glx_fshader_win_str); free_xinerama_info(ps); #ifdef CONFIG_VSYNC_OPENGL diff --git a/src/compton.h b/src/compton.h index 7eff0a39..524e81ed 100644 --- a/src/compton.h +++ b/src/compton.h @@ -675,20 +675,37 @@ static win * paint_preprocess(session_t *ps, win *list); static void -render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, +render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, Picture pict, glx_texture_t *ptex, - XserverRegion reg_paint, const reg_data_t *pcache_reg); + XserverRegion reg_paint, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +#define \ + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ + render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) +#else +#define \ + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ + render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg) +#endif static inline void -win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, Picture pict) { +win_render(session_t *ps, win *w, int x, int y, int wid, int hei, + double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, + Picture pict) { const int dx = (w ? w->a.x: 0) + x; const int dy = (w ? w->a.y: 0) + y; - const bool argb = (w && w->mode == WMODE_ARGB); + const bool argb = (w && (WMODE_ARGB == w->mode || ps->o.force_win_blend)); const bool neg = (w && w->invert_color); render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, - pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint, pcache_reg); + pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), + reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL)); } static inline void diff --git a/src/opengl.c b/src/opengl.c index 951d8e50..424407f9 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -259,6 +259,23 @@ glx_init_end: return success; } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + +static void +glx_free_prog_main(session_t *ps, glx_prog_main_t *pprogram) { + if (!pprogram) + return; + if (pprogram->prog) { + glDeleteProgram(pprogram->prog); + pprogram->prog = 0; + } + pprogram->unifm_opacity = -1; + pprogram->unifm_invert_color = -1; + pprogram->unifm_tex = -1; +} + +#endif + /** * Destroy GLX related resources. */ @@ -273,6 +290,10 @@ glx_destroy(session_t *ps) { if (ppass->prog) glDeleteProgram(ppass->prog); } + + glx_free_prog_main(ps, &ps->o.glx_prog_win); + + glx_check_err(ps); #endif // Free FBConfigs @@ -439,6 +460,7 @@ glx_init_blur(session_t *ps) { P_GET_UNIFM_LOC("offset_x", unifm_offset_x); P_GET_UNIFM_LOC("offset_y", unifm_offset_y); } + #undef P_GET_UNIFM_LOC } free(extension); @@ -458,6 +480,43 @@ glx_init_blur(session_t *ps) { #endif } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + +/** + * Load a GLSL main program from shader strings. + */ +bool +glx_load_prog_main(session_t *ps, + const char *vshader_str, const char *fshader_str, + glx_prog_main_t *pprogram) { + assert(pprogram); + + // Build program + pprogram->prog = glx_create_program_from_str(vshader_str, fshader_str); + if (!pprogram->prog) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + + // Get uniform addresses +#define P_GET_UNIFM_LOC(name, target) { \ + pprogram->target = glGetUniformLocation(pprogram->prog, name); \ + if (pprogram->target < 0) { \ + printf_errf("(): Failed to get location of uniform '" name "'. Might be troublesome."); \ + } \ + } + P_GET_UNIFM_LOC("opacity", unifm_opacity); + P_GET_UNIFM_LOC("invert_color", unifm_invert_color); + P_GET_UNIFM_LOC("tex", unifm_tex); +#undef P_GET_UNIFM_LOC + + glx_check_err(ps); + + return true; +} + +#endif + /** * @brief Update the FBConfig of given depth. */ @@ -1314,10 +1373,14 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, * @brief Render a region with texture data. */ bool -glx_render(session_t *ps, const glx_texture_t *ptex, +glx_render_(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, - double opacity, bool neg, - XserverRegion reg_tgt, const reg_data_t *pcache_reg) { + double opacity, bool argb, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ) { if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; @@ -1328,8 +1391,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return true; #endif - const bool argb = (GLX_TEXTURE_FORMAT_RGBA_EXT == - ps->glx_fbconfigs[ptex->depth]->texture_fmt); + argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT == + ps->glx_fbconfigs[ptex->depth]->texture_fmt); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + const bool has_prog = pprogram && pprogram->prog; +#endif bool dual_texture = false; // It's required by legacy versions of OpenGL to enable texture target @@ -1350,77 +1416,96 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glColor4f(opacity, opacity, opacity, opacity); } - // Color negation - if (neg) { - // Simple color negation - if (!glIsEnabled(GL_BLEND)) { - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(GL_COPY_INVERTED); - } - // ARGB texture color negation - else if (argb) { - dual_texture = true; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (!has_prog) +#endif + { + // The default, fixed-function path + // Color negation + if (neg) { + // Simple color negation + if (!glIsEnabled(GL_BLEND)) { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_COPY_INVERTED); + } + // ARGB texture color negation + else if (argb) { + dual_texture = true; - // Use two texture stages because the calculation is too complicated, - // thanks to madsy for providing code - // Texture stage 0 - glActiveTexture(GL_TEXTURE0); + // Use two texture stages because the calculation is too complicated, + // thanks to madsy for providing code + // Texture stage 0 + glActiveTexture(GL_TEXTURE0); - // Negation for premultiplied color: color = A - C - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + // Negation for premultiplied color: color = A - C + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - // Pass texture alpha through - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - - // Texture stage 1 - glActiveTexture(GL_TEXTURE1); - glEnable(ptex->target); - glBindTexture(ptex->target, ptex->texture); + // Pass texture alpha through + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + // Texture stage 1 + glActiveTexture(GL_TEXTURE1); + glEnable(ptex->target); + glBindTexture(ptex->target, ptex->texture); - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); - glActiveTexture(GL_TEXTURE0); - } - // RGB blend color negation - else { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + glActiveTexture(GL_TEXTURE0); + } + // RGB blend color negation + else { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } } } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + else { + // Programmable path + assert(pprogram->prog); + glUseProgram(pprogram->prog); + if (pprogram->unifm_opacity >= 0) + glUniform1f(pprogram->unifm_opacity, opacity); + if (pprogram->unifm_invert_color >= 0) + glUniform1i(pprogram->unifm_invert_color, neg); + if (pprogram->unifm_tex >= 0) + glUniform1i(pprogram->unifm_tex, 0); + } +#endif #ifdef DEBUG_GLX printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); @@ -1503,6 +1588,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glActiveTexture(GL_TEXTURE0); } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (has_prog) + glUseProgram(0); +#endif + glx_check_err(ps); return true; @@ -1625,7 +1715,7 @@ glx_create_shader(GLenum shader_type, const char *shader_str) { bool success = false; GLuint shader = glCreateShader(shader_type); if (!shader) { - printf_errf("(): Failed to create shader with type %d.", shader_type); + printf_errf("(): Failed to create shader with type %#x.", shader_type); goto glx_create_shader_end; } glShaderSource(shader, 1, &shader_str, NULL); @@ -1701,5 +1791,40 @@ glx_create_program_end: return program; } + +/** + * @brief Create a program from vertex and fragment shader strings. + */ +GLuint +glx_create_program_from_str(const char *vert_shader_str, + const char *frag_shader_str) { + GLuint vert_shader = 0; + GLuint frag_shader = 0; + GLuint prog = 0; + + if (vert_shader_str) + vert_shader = glx_create_shader(GL_VERTEX_SHADER, vert_shader_str); + if (frag_shader_str) + frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, frag_shader_str); + + { + GLuint shaders[2]; + int count = 0; + if (vert_shader) + shaders[count++] = vert_shader; + if (frag_shader) + shaders[count++] = frag_shader; + assert(count <= sizeof(shaders) / sizeof(shaders[0])); + if (count) + prog = glx_create_program(shaders, count); + } + + if (vert_shader) + glDeleteShader(vert_shader); + if (frag_shader) + glDeleteShader(frag_shader); + + return prog; +} #endif