deshade is a library that allows you to dump and replace the shaders of any OpenGL application, which includes: GL2 + extensions, GL3, GL4, EGL, GLES2, GLES3 and GLvnd without recompiling the application for Linux.
To build just run make
make
By default, deshade will not dump an application shaders to disk to
be replaced, unless a shaders directory exists where the application
is invoked.
mkdir shaders
LD_PRELOAD=./deshade.so application
The shaders will be written to the shaders directory, their names
will be the hash of their contents, with the following extension scheme:
_vs.glsl for vertex shaders, _fs.glsl for fragment shaders, _gs.glsl
for geometry shaders, _cs.glsl for compute shaders, _tsc.glsl for
tesseleation control shaders and _tse.glsl for tesselation evaluation
shaders.
Modifying the contents of one of the dumpped shaders in the shaders
directory will take effect the next time the application is launched
with deshade.
A debug log is also written to deshade.txt containing introspection
information, if deshade fails to work check this for more information.
You can also link an application against deshade but it must come before OpenGL on the linker command line. This isn't officially supported.
There are several ways OpenGL can be loaded.
- application dlopens
libGL.so - application dlopens
libGLX_{vendor}.so, where vendor can benvidia,intel,amd,mesa, etc - application fetches OpenGL functions with
glXGetProcAddressorglXGetProcAddressARB - application links directly with
-lGL- this loads functions through GLX
- or, this loads functions through
__glx_Mainimports if usingGLvnd
deshade attempts to supplement all of these so that regardless of how the application acquires an OpenGL context this should work.
Applications which use multiple OpenGL contexts per thread may fail to
work due to the way deshade only maintains one set of function pointers
for the first context. Support for multiple contexts per thread would
require supplementing the glXCreateContext, glXDestroyContext and
glXMakeCurrent functions.
deshade exploits internal glibc dynamic linker functions to replace the
dynamic linker itself to handle any applications that get OpenGL
through dlopen, as a result this library is glibc specific and will
not work on BSDs or musl based distributions.
Here's an example of dumping the shaders mpv uses for -vo=gpu
[deshade]# LD_PRELOAD=./deshade.so mpv test.mp4
Playing: test.mp4
(+) Video --vid=1 (*) (h264 1280x720 23.976fps)
(+) Audio --aid=1 --alang=eng (*) (aac 2ch 48000Hz)
AO: [pulse] 48000Hz stereo 2ch float
VO: [gpu] 1280x720 yuv420p
AV: 00:04:21 / 00:10:58 (39%) A-V: 0.000
Exiting... (Quit)
[deshade]# cat deshade.txt
... several lines of dlopen, dlclose and dlsym
Intercepted: dlsym(0x7f0178209470 /* libGLX_nvidia.so.0 */, "__glx_Main" = 0x7f018c592c10 /* replaced with 0x7f01a110a9b0 */
Intercepted: "glXGetProcAddress" 0x7f01995c1500 /* replaced with 0x7f01a110a800 */
Intercepted: "glXGetProcAddressARB" 0x7f01995c1510 /* replaced with 0x7f01a110a650 */
... more lines of dlopen, dlclose and dlsym
Intercepted: "glCreateShader" 0x7f019582b460 /* replaced with 0x7f01a110b570 */
Intercepted: "glDeleteShader" 0x7f019582bc40 /* replaced with 0x7f01a110b3d0 */
Intercepted: "glShaderSource" 0x7f019583a240 /* replaced with 0x7f01a110ba00 */
... more lines of dlopen, dlclose and dlsym
Created vertex shader "2"
Dumpped vertex shader "0600301E6C0B14E9C07851EEE07C575F"
Source vertex shader "0600301E6C0B14E9C07851EEE07C575F"
Deleted vertex shader "2"
Created fragment shader "3"
Dumpped fragment shader "A893C72D6B1A1007AF56E9B6D4F18267"
Source fragment shader "A893C72D6B1A1007AF56E9B6D4F18267"
Deleted fragment shader "3"
Created vertex shader "5"
Dumpped vertex shader "1938651636DAB20B6300B7DDD78654FA"
Source vertex shader "1938651636DAB20B6300B7DDD78654FA"
Deleted vertex shader "5"
Created fragment shader "6"
Dumpped fragment shader "045D796EC4C8EF36F8E6BA573F93E8AE"
Source fragment shader "045D796EC4C8EF36F8E6BA573F93E8AE"
Deleted fragment shader "6"
Forwarding: dlclose(0x7f0178209470 /* libGLX_nvidia.so.0 */) = 0
[deshade]# cat shaders/*
#version 440
#define tex1D texture
#define tex3D texture
#define LUT_POS(x, lut_size) mix(0.5 / (lut_size), 1.0 - 0.5 / (lut_size), (x))
out vec4 out_color;
in vec2 texcoord;
in vec4 ass_color;
layout(std140, binding=0) uniform UBO {
layout(offset=0) vec3 src_luma;
layout(offset=16) vec3 dst_luma;
};
uniform sampler2D osdtex;
void main() {
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
color = vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);
// color mapping
color.rgb *= vec3(1.000000);
color.rgb *= vec3(1.000000);
out_color = color;
}
#version 440
#define tex1D texture
#define tex3D texture
#define LUT_POS(x, lut_size) mix(0.5 / (lut_size), 1.0 - 0.5 / (lut_size), (x))
in vec2 vertex_position;
in vec2 vertex_texcoord0;
out vec2 texcoord0;
in vec2 vertex_texcoord1;
out vec2 texcoord1;
in vec2 vertex_texcoord2;
out vec2 texcoord2;
void main() {
gl_Position = vec4(vertex_position, 1.0, 1.0);
texcoord0 = vertex_texcoord0;
texcoord1 = vertex_texcoord1;
texcoord2 = vertex_texcoord2;
}
#version 440
#define tex1D texture
#define tex3D texture
#define LUT_POS(x, lut_size) mix(0.5 / (lut_size), 1.0 - 0.5 / (lut_size), (x))
in vec2 vertex_position;
in vec2 vertex_texcoord;
out vec2 texcoord;
in vec4 vertex_ass_color;
out vec4 ass_color;
void main() {
gl_Position = vec4(vertex_position, 1.0, 1.0);
texcoord = vertex_texcoord;
ass_color = vertex_ass_color;
}
#version 440
#define tex1D texture
#define tex3D texture
#define LUT_POS(x, lut_size) mix(0.5 / (lut_size), 1.0 - 0.5 / (lut_size), (x))
out vec4 out_color;
in vec2 texcoord0;
in vec2 texcoord1;
in vec2 texcoord2;
layout(std140, binding=0) uniform UBO {
layout(offset=0) mat3 colormatrix;
layout(offset=48) vec3 colormatrix_c;
layout(offset=64) vec3 src_luma;
layout(offset=80) vec3 dst_luma;
layout(offset=96) vec2 texture_size0;
layout(offset=112) mat2 texture_rot0;
layout(offset=144) vec2 texture_off0;
layout(offset=152) vec2 pixel_size0;
layout(offset=160) vec2 texture_size1;
layout(offset=176) mat2 texture_rot1;
layout(offset=208) vec2 texture_off1;
layout(offset=216) vec2 pixel_size1;
layout(offset=224) vec2 texture_size2;
layout(offset=240) mat2 texture_rot2;
layout(offset=272) vec2 texture_off2;
layout(offset=280) vec2 pixel_size2;
};
uniform sampler2D texture0;
uniform sampler2D texture1;
uniform sampler2D texture2;
void main() {
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
color.r = 1.000000 * vec4(texture(texture0, texcoord0)).r;
color.g = 1.000000 * vec4(texture(texture1, texcoord1)).r;
color.b = 1.000000 * vec4(texture(texture2, texcoord2)).r;
color = color.rgbr;
color.rgb = mat3(colormatrix) * color.rgb + colormatrix_c;
color.a = 1.0;
// color mapping
color.rgb *= vec3(1.000000);
color.rgb *= vec3(1.000000);
out_color = color;
}