From 9c2078ad78024a9c788312af594203d2db145208 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 13 Mar 2026 20:38:03 -0400 Subject: [PATCH] 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 --- .../tracks/frosted_glass_20260313/plan.md | 4 +- config.toml | 14 ++-- manualslop_layout.ini | 65 +++++++++---------- src/shader_manager.py | 30 +++++++-- tests/test_shader_manager.py | 41 ++++++++++++ 5 files changed, 104 insertions(+), 50 deletions(-) diff --git a/conductor/tracks/frosted_glass_20260313/plan.md b/conductor/tracks/frosted_glass_20260313/plan.md index 53a0483..1610b53 100644 --- a/conductor/tracks/frosted_glass_20260313/plan.md +++ b/conductor/tracks/frosted_glass_20260313/plan.md @@ -4,10 +4,10 @@ - [x] Task: Implement: Create `ShaderManager` methods for downsampled FBO setup (scene, temp, blur). [d9148ac] - [x] Task: Implement: Develop the "Deep Sea" background shader and integrate it as the FBO source. [d85dc3a] - [x] Task: Implement: Develop the 2-pass Gaussian blur shaders with a wide tap distribution. [c8b7fca] -- [ ] Task: Conductor - User Manual Verification 'Phase 1: Robust Foundation' (Protocol in workflow.md) +- [x] Task: Conductor - User Manual Verification 'Phase 1: Robust Foundation' (Protocol in workflow.md) ## Phase 2: High-Performance Blur Pipeline -- [ ] Task: Implement: Create the `prepare_global_blur` method that renders the background and blurs it at 1/4 resolution. +- [~] Task: Implement: Create the `prepare_global_blur` method that renders the background and blurs it at 1/4 resolution. - [ ] Task: Implement: Ensure the pipeline correctly handles high-DPI scaling (`fb_scale`) for internal FBO dimensions. - [ ] Task: Conductor - User Manual Verification 'Phase 2: High-Performance Pipeline' (Protocol in workflow.md) diff --git a/config.toml b/config.toml index 696f0a3..44e667b 100644 --- a/config.toml +++ b/config.toml @@ -23,15 +23,15 @@ active = "C:/projects/gencpp/gencpp_sloppy.toml" separate_message_panel = false separate_response_panel = false separate_tool_calls_panel = false -bg_shader_enabled = true +bg_shader_enabled = false crt_filter_enabled = false separate_task_dag = false -separate_usage_analytics = false +separate_usage_analytics = true separate_tier1 = false separate_tier2 = false separate_tier3 = false separate_tier4 = false -separate_external_tools = false +separate_external_tools = true [gui.show_windows] "Context Hub" = true @@ -39,7 +39,7 @@ separate_external_tools = false "AI Settings" = true "MMA Dashboard" = true "Task DAG" = false -"Usage Analytics" = false +"Usage Analytics" = true "Tier 1" = false "Tier 2" = false "Tier 3" = false @@ -56,8 +56,8 @@ Response = true Theme = true "Log Management" = true Diagnostics = false -"External Tools" = false -"Shader Editor" = false +"External Tools" = true +"Shader Editor" = true [theme] palette = "Nord Dark" @@ -65,7 +65,7 @@ font_path = "C:/projects/manual_slop/assets/fonts/MapleMono-Regular.ttf" font_size = 18.0 scale = 1.0 transparency = 0.5400000214576721 -child_transparency = 0.5899999737739563 +child_transparency = 1.0 [mma] max_workers = 4 diff --git a/manualslop_layout.ini b/manualslop_layout.ini index 358c364..bc54c46 100644 --- a/manualslop_layout.ini +++ b/manualslop_layout.ini @@ -74,8 +74,8 @@ Collapsed=0 DockId=0xAFC85805,2 [Window][Theme] -Pos=0,703 -Size=630,737 +Pos=0,899 +Size=484,737 Collapsed=0 DockId=0x00000002,2 @@ -91,8 +91,8 @@ Collapsed=0 DockId=0x00000010,2 [Window][Context Hub] -Pos=0,703 -Size=630,737 +Pos=0,899 +Size=484,737 Collapsed=0 DockId=0x00000002,1 @@ -103,26 +103,26 @@ Collapsed=0 DockId=0x0000000D,0 [Window][Discussion Hub] -Pos=1263,22 -Size=709,1418 +Pos=1030,26 +Size=950,1610 Collapsed=0 DockId=0x00000013,0 [Window][Operations Hub] -Pos=632,22 -Size=629,1418 +Pos=486,26 +Size=542,1610 Collapsed=0 DockId=0x00000005,0 [Window][Files & Media] -Pos=0,703 -Size=630,737 +Pos=0,899 +Size=484,737 Collapsed=0 DockId=0x00000002,0 [Window][AI Settings] -Pos=0,22 -Size=630,679 +Pos=0,26 +Size=484,871 Collapsed=0 DockId=0x00000001,0 @@ -132,14 +132,14 @@ Size=416,325 Collapsed=0 [Window][MMA Dashboard] -Pos=1974,22 -Size=586,1418 +Pos=1982,26 +Size=653,1610 Collapsed=0 DockId=0x00000010,0 [Window][Log Management] -Pos=1974,22 -Size=586,1418 +Pos=1982,26 +Size=653,1610 Collapsed=0 DockId=0x00000010,1 @@ -167,7 +167,7 @@ Collapsed=0 Pos=2822,1717 Size=1018,420 Collapsed=0 -DockId=0x0000000C,0 +DockId=0x00000011,0 [Window][Approve PowerShell Command] Pos=649,435 @@ -330,10 +330,9 @@ Size=967,499 Collapsed=0 [Window][Usage Analytics] -Pos=1739,1107 -Size=586,269 +Pos=1439,259 +Size=480,343 Collapsed=0 -DockId=0x0000000F,0 [Window][Tool Preset Manager] Pos=1301,302 @@ -351,7 +350,7 @@ Size=1000,800 Collapsed=0 [Window][External Tools] -Pos=531,376 +Pos=1968,516 Size=616,409 Collapsed=0 @@ -381,8 +380,8 @@ Size=900,700 Collapsed=0 [Window][Shader Editor] -Pos=457,710 -Size=493,252 +Pos=923,623 +Size=493,369 Collapsed=0 [Table][0xFB6E3870,4] @@ -498,23 +497,21 @@ Column 1 Weight=1.0000 DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 -DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,22 Size=2560,1418 Split=X - DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=1640,1183 Split=X +DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,26 Size=2635,1610 Split=X + DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=1980,1183 Split=X DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 - DockNode ID=0x00000007 Parent=0x0000000B SizeRef=630,858 Split=Y Selected=0x8CA2375C + DockNode ID=0x00000007 Parent=0x0000000B SizeRef=484,858 Split=Y Selected=0x8CA2375C DockNode ID=0x00000001 Parent=0x00000007 SizeRef=824,525 CentralNode=1 Selected=0x7BD57D6A DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,737 Selected=0x8CA2375C - DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1340,858 Split=X Selected=0x418C7449 - DockNode ID=0x00000012 Parent=0x0000000E SizeRef=629,402 Split=Y Selected=0x418C7449 + DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1494,858 Split=X Selected=0x418C7449 + DockNode ID=0x00000012 Parent=0x0000000E SizeRef=542,402 Split=Y Selected=0x418C7449 DockNode ID=0x00000005 Parent=0x00000012 SizeRef=876,1749 Selected=0x418C7449 DockNode ID=0x00000006 Parent=0x00000012 SizeRef=876,362 Selected=0x1D56B311 - DockNode ID=0x00000013 Parent=0x0000000E SizeRef=709,402 Selected=0x6F2B5B04 + DockNode ID=0x00000013 Parent=0x0000000E SizeRef=950,402 Selected=0x6F2B5B04 DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 - DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=586,1183 Split=Y Selected=0x3AEC3498 - DockNode ID=0x00000010 Parent=0x00000004 SizeRef=1199,1689 Selected=0x2C0206CE - DockNode ID=0x00000011 Parent=0x00000004 SizeRef=1199,420 Split=X Selected=0xDEB547B6 - DockNode ID=0x0000000C Parent=0x00000011 SizeRef=916,380 Selected=0x655BC6E9 - DockNode ID=0x0000000F Parent=0x00000011 SizeRef=281,380 Selected=0xDEB547B6 + DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=653,1183 Split=Y Selected=0x3AEC3498 + DockNode ID=0x00000010 Parent=0x00000004 SizeRef=1199,1689 Selected=0x3AEC3498 + DockNode ID=0x00000011 Parent=0x00000004 SizeRef=1199,420 Selected=0xDEB547B6 ;;;<<>>;;; ;;;<<>>;;; diff --git a/src/shader_manager.py b/src/shader_manager.py index d5d11df..2ed1f67 100644 --- a/src/shader_manager.py +++ b/src/shader_manager.py @@ -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 diff --git a/tests/test_shader_manager.py b/tests/test_shader_manager.py index c9ffca1..b73ba35 100644 --- a/tests/test_shader_manager.py +++ b/tests/test_shader_manager.py @@ -109,6 +109,47 @@ def test_blur_pipeline_prepare_blur(): assert mock_gl.glBindFramebuffer.called assert mock_gl.glUseProgram.called +def test_blur_pipeline_prepare_global_blur(): + with patch("src.shader_manager.gl") as mock_gl: + tex_counter = iter([10, 20, 30]) + fbo_counter = iter([1, 2, 3]) + mock_gl.glGenTextures.side_effect = lambda n: next(tex_counter) + mock_gl.glGenFramebuffers.side_effect = lambda n: next(fbo_counter) + mock_gl.glCreateProgram.return_value = 100 + mock_gl.glCreateShader.return_value = 200 + mock_gl.glGetShaderiv.return_value = mock_gl.GL_TRUE + mock_gl.glGetProgramiv.return_value = mock_gl.GL_TRUE + from src.shader_manager import BlurPipeline + pipeline = BlurPipeline() + pipeline.setup_fbos(800, 600) + pipeline.compile_deepsea_shader() + pipeline.compile_blur_shaders() + pipeline.prepare_global_blur(800, 600, 0.0) + assert mock_gl.glBindFramebuffer.called + assert mock_gl.glUseProgram.called + assert mock_gl.glViewport.called + blur_tex = pipeline.get_blur_texture() + assert blur_tex is not None + assert blur_tex == 30 + +def test_blur_pipeline_high_dpi_scaling(): + with patch("src.shader_manager.gl") as mock_gl: + tex_counter = iter([10, 20, 30]) + fbo_counter = iter([1, 2, 3]) + mock_gl.glGenTextures.side_effect = lambda n: next(tex_counter) + mock_gl.glGenFramebuffers.side_effect = lambda n: next(fbo_counter) + mock_gl.glCreateProgram.return_value = 100 + mock_gl.glCreateShader.return_value = 200 + mock_gl.glGetShaderiv.return_value = mock_gl.GL_TRUE + mock_gl.glGetProgramiv.return_value = mock_gl.GL_TRUE + from src.shader_manager import BlurPipeline + pipeline = BlurPipeline() + fb_scale = 2.0 + pipeline.setup_fbos(800, 600, fb_scale) + assert pipeline._fb_width == (800 * int(fb_scale)) // 4 + assert pipeline._fb_height == (600 * int(fb_scale)) // 4 + assert pipeline._fb_scale == int(fb_scale) + def test_blur_pipeline_cleanup(): with patch("src.shader_manager.gl") as mock_gl: from src.shader_manager import BlurPipeline