feat(shaders): Implement FBO capture lifecycle in ShaderManager
This commit is contained in:
@@ -6,6 +6,38 @@ class ShaderManager:
|
|||||||
self.bg_program = None
|
self.bg_program = None
|
||||||
self.pp_program = None
|
self.pp_program = None
|
||||||
self.blur_program = None
|
self.blur_program = None
|
||||||
|
self.capture_fbo = None
|
||||||
|
self.capture_tex = None
|
||||||
|
self.fbo_width = 0
|
||||||
|
self.fbo_height = 0
|
||||||
|
|
||||||
|
def setup_capture_fbo(self, width, height):
|
||||||
|
if self.capture_fbo is not None:
|
||||||
|
gl.glDeleteFramebuffers(1, [self.capture_fbo])
|
||||||
|
if self.capture_tex is not None:
|
||||||
|
gl.glDeleteTextures(1, [self.capture_tex])
|
||||||
|
self.capture_fbo = gl.glGenFramebuffers(1)
|
||||||
|
self.capture_tex = gl.glGenTextures(1)
|
||||||
|
gl.glBindTexture(gl.GL_TEXTURE_2D, self.capture_tex)
|
||||||
|
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)
|
||||||
|
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
|
||||||
|
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
|
||||||
|
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.capture_fbo)
|
||||||
|
gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.capture_tex, 0)
|
||||||
|
if gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) != gl.GL_FRAMEBUFFER_COMPLETE:
|
||||||
|
raise RuntimeError("Framebuffer not complete")
|
||||||
|
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
|
||||||
|
self.fbo_width = width
|
||||||
|
self.fbo_height = height
|
||||||
|
|
||||||
|
def capture_begin(self, width, height):
|
||||||
|
if self.capture_fbo is None or self.fbo_width != width or self.fbo_height != height:
|
||||||
|
self.setup_capture_fbo(width, height)
|
||||||
|
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.capture_fbo)
|
||||||
|
gl.glViewport(0, 0, width, height)
|
||||||
|
|
||||||
|
def capture_end(self):
|
||||||
|
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
|
||||||
|
|
||||||
def compile_shader(self, vertex_src: str, fragment_src: str) -> int:
|
def compile_shader(self, vertex_src: str, fragment_src: str) -> int:
|
||||||
program = gl.glCreateProgram()
|
program = gl.glCreateProgram()
|
||||||
|
|||||||
37
tests/test_fbo_capture.py
Normal file
37
tests/test_fbo_capture.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import pytest
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
import OpenGL.GL as gl
|
||||||
|
|
||||||
|
def test_shader_manager_fbo_initialization():
|
||||||
|
with patch("src.shader_manager.gl") as mock_gl:
|
||||||
|
mock_gl.glGenFramebuffers.return_value = 1
|
||||||
|
mock_gl.glGenTextures.return_value = 2
|
||||||
|
mock_gl.glCheckFramebufferStatus.return_value = mock_gl.GL_FRAMEBUFFER_COMPLETE
|
||||||
|
|
||||||
|
from src.shader_manager import ShaderManager
|
||||||
|
manager = ShaderManager()
|
||||||
|
|
||||||
|
manager.setup_capture_fbo(800, 600)
|
||||||
|
|
||||||
|
assert manager.capture_fbo == 1
|
||||||
|
assert manager.capture_tex == 2
|
||||||
|
assert mock_gl.glGenFramebuffers.called
|
||||||
|
assert mock_gl.glGenTextures.called
|
||||||
|
assert mock_gl.glCheckFramebufferStatus.called
|
||||||
|
|
||||||
|
def test_shader_manager_capture_lifecycle():
|
||||||
|
with patch("src.shader_manager.gl") as mock_gl:
|
||||||
|
mock_gl.glCheckFramebufferStatus.return_value = mock_gl.GL_FRAMEBUFFER_COMPLETE
|
||||||
|
from src.shader_manager import ShaderManager
|
||||||
|
manager = ShaderManager()
|
||||||
|
|
||||||
|
# Ensure setup is called on first capture
|
||||||
|
manager.capture_begin(1024, 768)
|
||||||
|
assert manager.fbo_width == 1024
|
||||||
|
assert manager.fbo_height == 768
|
||||||
|
assert mock_gl.glBindFramebuffer.called
|
||||||
|
|
||||||
|
mock_gl.glBindFramebuffer.reset_mock()
|
||||||
|
manager.capture_end()
|
||||||
|
# Verify unbind (glBindFramebuffer(..., 0))
|
||||||
|
mock_gl.glBindFramebuffer.assert_called_with(mock_gl.GL_FRAMEBUFFER, 0)
|
||||||
Reference in New Issue
Block a user