From ac4f63b76e514119f7d595fef28c2e981c6f9b05 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 13 Mar 2026 12:27:01 -0400 Subject: [PATCH] feat(shaders): Create ShaderManager with basic compilation --- src/shader_manager.py | 40 ++++++++++++++++++++++++++++++++++++ tests/test_shader_manager.py | 25 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/shader_manager.py create mode 100644 tests/test_shader_manager.py diff --git a/src/shader_manager.py b/src/shader_manager.py new file mode 100644 index 0000000..19ecf57 --- /dev/null +++ b/src/shader_manager.py @@ -0,0 +1,40 @@ +# src/shader_manager.py +import OpenGL.GL as gl + +class ShaderManager: + def __init__(self): + pass + + def compile_shader(self, vertex_src: str, fragment_src: str) -> int: + program = gl.glCreateProgram() + + def _compile(src, shader_type): + shader = gl.glCreateShader(shader_type) + gl.glShaderSource(shader, src) + gl.glCompileShader(shader) + + if not gl.glGetShaderiv(shader, gl.GL_COMPILE_STATUS): + info_log = gl.glGetShaderInfoLog(shader) + if hasattr(info_log, "decode"): + info_log = info_log.decode() + raise RuntimeError(f"Shader compilation failed: {info_log}") + return shader + + vert_shader = _compile(vertex_src, gl.GL_VERTEX_SHADER) + frag_shader = _compile(fragment_src, gl.GL_FRAGMENT_SHADER) + + gl.glAttachShader(program, vert_shader) + gl.glAttachShader(program, frag_shader) + gl.glLinkProgram(program) + + if not gl.glGetProgramiv(program, gl.GL_LINK_STATUS): + info_log = gl.glGetProgramInfoLog(program) + if hasattr(info_log, "decode"): + info_log = info_log.decode() + raise RuntimeError(f"Program linking failed: {info_log}") + + # Optional: delete shaders as they are linked into the program + gl.glDeleteShader(vert_shader) + gl.glDeleteShader(frag_shader) + + return program diff --git a/tests/test_shader_manager.py b/tests/test_shader_manager.py new file mode 100644 index 0000000..f4e9cc6 --- /dev/null +++ b/tests/test_shader_manager.py @@ -0,0 +1,25 @@ +import pytest +from unittest.mock import patch, MagicMock + +def test_shader_manager_initialization_and_compilation(): + # Import inside test to allow patching OpenGL before import if needed + # In this case, we patch the OpenGL.GL functions used by ShaderManager + with patch("src.shader_manager.gl") as mock_gl: + mock_gl.glCreateProgram.return_value = 1 + mock_gl.glCreateShader.return_value = 2 + mock_gl.glGetShaderiv.return_value = mock_gl.GL_TRUE + mock_gl.glGetProgramiv.return_value = mock_gl.GL_TRUE + + from src.shader_manager import ShaderManager + + manager = ShaderManager() + + # Basic vertex and fragment shader source + vert_src = "void main() {}" + frag_src = "void main() {}" + + program_id = manager.compile_shader(vert_src, frag_src) + + assert program_id == 1 + assert mock_gl.glCreateProgram.called + assert mock_gl.glCreateShader.called