feat(shader): Add prepare_global_blur and high-DPI scaling support

- Add prepare_global_blur() method combining Deep Sea render + blur
- Add fb_scale parameter to setup_fbos() for high-DPI display support
- FBOs now created at scaled resolution (width * fb_scale, height * fb_scale)
- prepare_global_blur() auto-initializes FBOs if scale changes
- Add test_blur_pipeline_prepare_global_blur
- Add test_blur_pipeline_high_dpi_scaling

Task: Phase 2, Tasks 1-2 of frosted_glass_20260313 track
This commit is contained in:
2026-03-13 20:38:03 -04:00
parent ab44102bad
commit 9c2078ad78
5 changed files with 104 additions and 50 deletions

View File

@@ -164,6 +164,7 @@ class BlurPipeline:
self.deepsea_program: int | None = None
self._fb_width: int = 0
self._fb_height: int = 0
self._fb_scale: int = 1
def _compile_shader(self, vertex_src: str, fragment_src: str) -> int:
program = gl.glCreateProgram()
@@ -206,12 +207,16 @@ class BlurPipeline:
gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
return fbo, tex
def setup_fbos(self, width: int, height: int):
blur_w = max(1, width // 4)
blur_h = max(1, height // 4)
def setup_fbos(self, width: int, height: int, fb_scale: float = 1.0):
scale = max(1, int(fb_scale))
blur_w = max(1, (width * scale) // 4)
blur_h = max(1, (height * scale) // 4)
self._fb_width = blur_w
self._fb_height = blur_h
self.scene_fbo, self.scene_tex = self._create_fbo(width, height)
self._fb_scale = scale
scene_w = width * scale
scene_h = height * scale
self.scene_fbo, self.scene_tex = self._create_fbo(scene_w, scene_h)
self.blur_fbo_a, self.blur_tex_a = self._create_fbo(blur_w, blur_h)
self.blur_fbo_b, self.blur_tex_b = self._create_fbo(blur_w, blur_h)
@@ -355,8 +360,10 @@ void main() {
def render_deepsea_to_fbo(self, width: int, height: int, time: float):
if not self.deepsea_program or not self.scene_fbo:
return
scene_w = width * self._fb_scale
scene_h = height * self._fb_scale
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.scene_fbo)
gl.glViewport(0, 0, width, height)
gl.glViewport(0, 0, scene_w, scene_h)
gl.glClearColor(0.01, 0.05, 0.12, 1.0)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
gl.glUseProgram(self.deepsea_program)
@@ -365,7 +372,7 @@ void main() {
gl.glUniform1f(u_time, time)
u_res = gl.glGetUniformLocation(self.deepsea_program, "u_resolution")
if u_res != -1:
gl.glUniform2f(u_res, float(width), float(height))
gl.glUniform2f(u_res, float(scene_w), float(scene_h))
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
gl.glUseProgram(0)
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
@@ -402,7 +409,16 @@ void main() {
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
self._render_quad(self.v_blur_program, self.blur_tex_a, (texel_x, texel_y))
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
gl.glViewport(0, 0, width, height)
restore_w = width * self._fb_scale
restore_h = height * self._fb_scale
gl.glViewport(0, 0, restore_w, restore_h)
def prepare_global_blur(self, width: int, height: int, time: float, fb_scale: float = 1.0):
if not self.scene_fbo:
if self._fb_scale != int(fb_scale):
self.setup_fbos(width, height, fb_scale)
self.render_deepsea_to_fbo(width, height, time)
self.prepare_blur(width, height, time)
def get_blur_texture(self) -> int | None:
return self.blur_tex_b