Skip to content

Commit

Permalink
#2467 generate separate shaders for each pixel format
Browse files Browse the repository at this point in the history
  • Loading branch information
totaam committed Mar 27, 2024
1 parent b130556 commit 0c6fc0d
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 14 deletions.
21 changes: 13 additions & 8 deletions xpra/client/gl/backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,10 @@ def gl_init_shaders(self) -> None:
# Create and assign fragment programs
from OpenGL.GL import GL_FRAGMENT_SHADER, GL_VERTEX_SHADER
vertex_shader = self.gl_init_shader("vertex", GL_VERTEX_SHADER)
for name in ("YUV_to_RGB", "YUV_to_RGB_FULL", "NV12_to_RGB"):
from xpra.client.gl.shaders import SOURCE
for name, source in SOURCE.items():
if name in ("overlay", "vertex"):
continue
fragment_shader = self.gl_init_shader(name, GL_FRAGMENT_SHADER)
self.gl_init_program(name, vertex_shader, fragment_shader)
overlay_shader = self.gl_init_shader("overlay", GL_FRAGMENT_SHADER)
Expand Down Expand Up @@ -976,7 +979,7 @@ def paint_nvdec(gl_context):
flush = options.intget("flush", 0)
w = img.get_width()
h = img.get_height()
self.idle_add(self.gl_paint_planar, "YUV_to_RGB_FULL", flush, encoding, img,
self.idle_add(self.gl_paint_planar, "YUV420P_to_RGB_FULL", flush, encoding, img,
x, y, w, h, width, height, options, callbacks)
return
if encoding == "jpeg":
Expand Down Expand Up @@ -1126,7 +1129,7 @@ def paint_webp(self, img_data, x: int, y: int, width: int, height: int,
flush = options.intget("flush", 0)
w = img.get_width()
h = img.get_height()
self.idle_add(self.gl_paint_planar, "YUV_to_RGB", flush, "webp", img,
self.idle_add(self.gl_paint_planar, f"{subsampling}_to_RGB", flush, "webp", img,
x, y, w, h, width, height, options, callbacks)
return
super().paint_webp(img_data, x, y, width, height, options, callbacks)
Expand All @@ -1140,7 +1143,7 @@ def paint_avif(self, img_data, x: int, y: int, width: int, height: int,
w = img.get_width()
h = img.get_height()
if pixel_format.startswith("YUV"):
self.idle_add(self.gl_paint_planar, "YUV_to_RGB_FULL", flush, "avif", img,
self.idle_add(self.gl_paint_planar, f"{pixel_format}_to_RGB_FULL", flush, "avif", img,
x, y, w, h, width, height, options, callbacks)
else:
self.idle_add(self.do_paint_rgb, pixel_format, img.get_pixels(), x, y, w, h, width, height,
Expand Down Expand Up @@ -1253,7 +1256,7 @@ def do_video_paint(self, img,
# which will end up calling paint rgb with r210 data
super().do_video_paint(img, x, y, enc_width, enc_height, width, height, options, callbacks)
return
shader = "NV12_to_RGB" if pixel_format == "NV12" else "YUV_to_RGB"
shader = f"{pixel_format}_to_RGB"
self.idle_add(self.gl_paint_planar, shader, options.intget("flush", 0), options.strget("encoding"), img,
x, y, enc_width, enc_height, width, height, options, callbacks)

Expand Down Expand Up @@ -1389,7 +1392,7 @@ def update_planar_textures(self, width: int, height: int, img, pixel_format, sca
# glActiveTexture(GL_TEXTURE0) #redundant, we always call render_planar_update afterwards

def render_planar_update(self, rx: int, ry: int, rw: int, rh: int, width: int, height: int,
shader="YUV_to_RGB") -> None:
shader="YUV420P_to_RGB") -> None:
log("%s.render_planar_update%s pixel_format=%s",
self, (rx, ry, rw, rh, width, height, shader), self.planar_pixel_format)
if self.planar_pixel_format not in ("YUV420P", "YUV422P", "YUV444P", "GBRP", "NV12", "GBRP16", "YUV444P16"):
Expand Down Expand Up @@ -1423,14 +1426,16 @@ def clampy(v: int) -> int:
glViewport(*viewport)
log("viewport: %s for size=%s render_size=%s", viewport, self.size, self.render_size)

program = self.programs[shader]
program = self.programs.get(shader)
if not program:
raise RuntimeError(f"no {shader} found!")
glUseProgram(program)
for texture, tex_index in textures:
glActiveTexture(texture)
glBindTexture(target, self.textures[tex_index])
# TEX_Y is 0, so effectively index==tex_index
index = tex_index-TEX_Y
plane_name = shader[index:index + 1] # ie: "YUV_to_RGB" 0 -> "Y"
plane_name = shader[index:index + 1] # ie: "YUV420P_to_RGB" 0 -> "Y"
tex_loc = glGetUniformLocation(program, plane_name) # ie: "Y" -> 0
glUniform1i(tex_loc, index) # tell the shader where to find the texture: 0 -> TEXTURE_0

Expand Down
35 changes: 29 additions & 6 deletions xpra/client/gl/shaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
# G = Y - (a * e / b) * Cr - (c * d / b) * Cb
# B = Y + d * Cb

from xpra.codecs.constants import get_subsampling_divs

GLSL_VERSION = "330 core"

CS_MULTIPLIERS = {
Expand All @@ -33,17 +35,35 @@
}


def gen_YUV_to_RGB(cs="bt601", full_range=True):
def gen_YUV_to_RGB(divs: tuple[tuple[int, int], ...], cs="bt601", full_range=True):
if cs not in CS_MULTIPLIERS:
raise ValueError(f"unsupported colorspace {cs}")
a, b, c, d, e = CS_MULTIPLIERS[cs]
f = - c * d / b
g = - a * e / b
ymult = "" if full_range else " * 1.1643835616438356"
umult = vmult = "" if full_range else " * 1.1383928571428572"
defines = []

def add_div(name: str, xdiv=1, ydiv=1):
if xdiv == ydiv:
# just divide by the same integer:
# ie: "#define Ydiv 1"
value = xdiv
else:
# store each component in a vector:
# ie: "#define Udiv vec2(2, 1)"
value = f"vec2({xdiv}, {ydiv})"
defines.append(f"{name}div {value}")

for i, div in enumerate(divs):
add_div("YUV"[i], *div)

defines_str = "\n".join(f"#define {define}" for define in defines)
return f"""
#version {GLSL_VERSION}
layout(origin_upper_left) in vec4 gl_FragCoord;
{defines_str}
uniform vec2 viewport_pos;
uniform vec2 scaling;
uniform sampler2DRect Y;
Expand All @@ -54,9 +74,9 @@ def gen_YUV_to_RGB(cs="bt601", full_range=True):
void main()
{{
vec2 pos = (gl_FragCoord.xy-viewport_pos.xy)/scaling;
highp float y = texture(Y, pos).r {ymult};
highp float u = (texture(U, pos/2.0).r - 0.5) {umult};
highp float v = (texture(V, pos/2.0).r - 0.5) {vmult};
highp float y = texture(Y, pos/Ydiv).r {ymult};
highp float u = (texture(U, pos/Udiv).r - 0.5) {umult};
highp float v = (texture(V, pos/Vdiv).r - 0.5) {vmult};
highp float r = y + {e} * v;
highp float g = y + {f} * u + {g} * v;
Expand Down Expand Up @@ -129,6 +149,9 @@ def gen_NV12_to_RGB(cs="bt601"):
"vertex": VERTEX_SHADER,
"overlay": OVERLAY_SHADER,
"NV12_to_RGB": gen_NV12_to_RGB(),
"YUV_to_RGB": gen_YUV_to_RGB(),
"YUV_to_RGB_FULL": gen_YUV_to_RGB(full_range=True),
}

for fmt in ("YUV420P", "YUV422P", "YUV444P"):
divs = get_subsampling_divs(fmt)
SOURCE[f"{fmt}_to_RGB"] = gen_YUV_to_RGB(divs, full_range=False)
SOURCE[f"{fmt}_to_RGB_FULL"] = gen_YUV_to_RGB(divs, full_range=True)

0 comments on commit 0c6fc0d

Please sign in to comment.