diff --git a/kitty/develop_gl.py b/kitty/develop_gl.py index 48241c9f5..e5fbcf856 100644 --- a/kitty/develop_gl.py +++ b/kitty/develop_gl.py @@ -6,29 +6,9 @@ import glfw import OpenGL.GL as gl import sys -from kitty.shaders import ShaderProgram, array +from kitty.shaders import ShaderProgram, array, GL_VERSION from kitty.fonts import set_font_family, render_cell, cell_size -vertex_shader = """ -# version 410 -in vec2 vertex; - -void main() { - gl_Position = vec4(vertex, 0, 1); -} -""" - - -fragment = """ -# version 410 -out vec4 final_color; - -void main(void) -{ - final_color = vec4(1.0); -} -""" - def key_callback(key, action): """ Sample keyboard callback function """ @@ -45,16 +25,37 @@ def gl_get_unicode(k): return ans -def on_resize(window, w, h): - gl.glViewport(0, 0, w, h) +class Renderer: + + def __init__(self, w, h): + self.w, self.h = w, h + self.do_layout() + self.program = rectangle_texture() + print(gl.glGetIntegerv(gl.GL_MAX_VERTEX_UNIFORM_COMPONENTS)) + print(gl.glGetIntegerv(gl.GL_MAX_UNIFORM_BLOCK_SIZE)) + print(gl.glGetIntegerv(gl.GL_MAX_ARRAY_TEXTURE_LAYERS)) + + def on_resize(self, window, w, h): + gl.glViewport(0, 0, w, h) + self.w, self.h = w, h + self.do_layout() + + def do_layout(self): + # Divide into cells + cell_width, cell_height = cell_size() + self.cells_per_line = self.w // cell_width + self.lines_per_screen = self.h // cell_height + + def render(self): + rectangle_texture(self.program) def _main(): # These Window hints are used to specify # which opengl version to use and other details # for the opengl context that will be created - glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MAJOR, 4) - glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MINOR, 1) + glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION[0]) + glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MINOR, GL_VERSION[1]) glfw.glfwWindowHint(glfw.GLFW_OPENGL_PROFILE, glfw.GLFW_OPENGL_CORE_PROFILE) glfw.glfwWindowHint(glfw.GLFW_OPENGL_FORWARD_COMPAT, True) @@ -65,7 +66,6 @@ def _main(): raise SystemExit("glfwCreateWindow failed") glfw.glfwMakeContextCurrent(window) glfw.glfwSwapInterval(1) - glfw.glfwSetFramebufferSizeCallback(window, on_resize) # If everything went well the following calls # will display the version of opengl being used @@ -74,21 +74,19 @@ def _main(): print('GLSL Version: %s' % (gl_get_unicode(gl.GL_SHADING_LANGUAGE_VERSION))) print('Renderer: %s' % (gl_get_unicode(gl.GL_RENDERER))) + r = Renderer(1024, 1024) + glfw.glfwSetFramebufferSizeCallback(window, r.on_resize) try: gl.glClearColor(0.5, 0.5, 0.5, 0) - rectangle_texture(window) + while not glfw.glfwWindowShouldClose(window): + gl.glClear(gl.GL_COLOR_BUFFER_BIT) + r.render() + glfw.glfwSwapBuffers(window) + glfw.glfwWaitEvents() finally: glfw.glfwDestroyWindow(window) -def triangle_vertices(width=0.8, height=0.8): - return array( - 0.0, height, - -width, -height, - width, -height, - ) - - def rectangle_vertices(left=-0.8, top=0.8, right=0.8, bottom=-0.8): vertex_data = array( right, top, @@ -109,37 +107,8 @@ def rectangle_vertices(left=-0.8, top=0.8, right=0.8, bottom=-0.8): return vertex_data, texture_coords -def rectangle(window): - program = ShaderProgram(vertex_shader, fragment) - - with program: - program.set_attribute_data('vertex', rectangle_vertices()[0]) - - while not glfw.glfwWindowShouldClose(window): - gl.glClear(gl.GL_COLOR_BUFFER_BIT) - with program: - gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6) - - glfw.glfwSwapBuffers(window) - glfw.glfwWaitEvents() - - -def triangle(window): - program = ShaderProgram(vertex_shader, fragment) - with program: - program.set_attribute_data('vertex', triangle_vertices()) - - while not glfw.glfwWindowShouldClose(window): - gl.glClear(gl.GL_COLOR_BUFFER_BIT) - with program: - gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3) - - glfw.glfwSwapBuffers(window) - glfw.glfwWaitEvents() - textured_shaders = ( - ''' -#version 410 + '''\ in vec2 vertex; in vec2 texture_position; out vec2 texture_position_for_fs; @@ -150,8 +119,7 @@ void main() { } ''', - ''' -#version 410 + '''\ uniform sampler2D tex; in vec2 texture_position_for_fs; out vec4 final_color; @@ -172,23 +140,20 @@ def texture_data(): return cell, w, h -def rectangle_texture(window): - program = ShaderProgram(*textured_shaders) - img_data, w, h = texture_data() - with program: - program.set_2d_texture('tex', img_data, w, h) - rv, texc = rectangle_vertices() - program.set_attribute_data('vertex', rv) - program.set_attribute_data('texture_position', texc) - - while not glfw.glfwWindowShouldClose(window): - gl.glClear(gl.GL_COLOR_BUFFER_BIT) +def rectangle_texture(program=None): + if program is None: + program = ShaderProgram(*textured_shaders) + img_data, w, h = texture_data() + with program: + program.set_2d_texture('tex', img_data, w, h) + rv, texc = rectangle_vertices() + program.set_attribute_data('vertex', rv) + program.set_attribute_data('texture_position', texc) + return program + else: with program: gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6) - glfw.glfwSwapBuffers(window) - glfw.glfwWaitEvents() - def on_error(code, msg): if isinstance(msg, bytes): diff --git a/kitty/main.py b/kitty/main.py index 887d7db33..4e1a28984 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -13,6 +13,7 @@ from .config import load_config from .constants import appname, str_version, config_dir from .boss import Boss from .utils import fork_child, hangup +from .shaders import GL_VERSION import glfw @@ -34,8 +35,8 @@ def option_parser(): def setup_opengl(): - glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MAJOR, 3) - glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MINOR, 2) + glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION[0]) + glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MINOR, GL_VERSION[1]) glfw.glfwWindowHint(glfw.GLFW_OPENGL_PROFILE, glfw.GLFW_OPENGL_CORE_PROFILE) glfw.glfwWindowHint(glfw.GLFW_OPENGL_FORWARD_COMPAT, True) glfw.glfwWindowHint(glfw.GLFW_SAMPLES, 0) diff --git a/kitty/shaders.py b/kitty/shaders.py index e433666e0..1400de049 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -7,28 +7,28 @@ from functools import lru_cache from OpenGL.arrays import ArrayDatatype import OpenGL.GL as gl +GL_VERSION = (4, 1) +VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10 + class ShaderProgram: """ Helper class for using GLSL shader programs """ - def __init__(self, vertex: str, fragment: str): + def __init__(self, vertex, fragment): """ Create a shader program. - :param vertex: The vertex shader - :param fragment: The fragment shader - """ self.program_id = gl.glCreateProgram() self.texture_id = None self.is_active = False vs_id = self.add_shader(vertex, gl.GL_VERTEX_SHADER) - frag_id = self.add_shader(fragment, gl.GL_FRAGMENT_SHADER) - gl.glAttachShader(self.program_id, vs_id) - gl.glAttachShader(self.program_id, frag_id) - gl.glLinkProgram(self.program_id) + frag_id = self.add_shader(fragment, gl.GL_FRAGMENT_SHADER) + gl.glAttachShader(self.program_id, frag_id) + + gl.glLinkProgram(self.program_id) if gl.glGetProgramiv(self.program_id, gl.GL_LINK_STATUS) != gl.GL_TRUE: info = gl.glGetProgramInfoLog(self.program_id) gl.glDeleteProgram(self.program_id) @@ -52,6 +52,7 @@ class ShaderProgram: def add_shader(self, source: str, shader_type: int) -> int: ' Compile a shader and return its id, or raise an exception if compilation fails ' shader_id = gl.glCreateShader(shader_type) + source = '#version {}\n{}'.format(VERSION, source) try: gl.glShaderSource(shader_id, source) gl.glCompileShader(shader_id) @@ -90,6 +91,10 @@ class ShaderProgram: def set_2d_texture(self, var_name, data, width, height, data_type='red', min_filter=gl.GL_LINEAR, mag_filter=gl.GL_LINEAR, swrap=gl.GL_CLAMP_TO_EDGE, twrap=gl.GL_CLAMP_TO_EDGE): + if not self.is_active: + raise RuntimeError('The program must be active before you can add buffers') + if self.texture_id is not None: + gl.glDeleteTextures([self.texture_id]) texture_id = self.texture_id = gl.glGenTextures(1) self.texture_var = self.uniform_location(var_name) gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1 if data_type == 'red' else 4) @@ -107,14 +112,16 @@ class ShaderProgram: 0, external_format, gl.GL_UNSIGNED_BYTE, data) return texture_id - def set_attribute_data(self, attribute_name, data, items_per_attribute_value=2, volatile=False, normalize=False): + def set_attribute_data(self, attribute_name, data, items_per_attribute_value=2, normalize=False): if not self.is_active: raise RuntimeError('The program must be active before you can add buffers') if len(data) % items_per_attribute_value != 0: raise ValueError('The length of the data buffer is not a multiple of the items_per_attribute_value') - buf_id = self.attribute_buffers[attribute_name] = gl.glGenBuffers(1) + buf_id = self.attribute_buffers.get(attribute_name) # glBufferData auto-deletes previous data + if buf_id is None: + buf_id = self.attribute_buffers[attribute_name] = gl.glGenBuffers(1) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf_id) - gl.glBufferData(gl.GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(data), data, gl.GL_STREAM_DRAW if volatile else gl.GL_STATIC_DRAW) + gl.glBufferData(gl.GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(data), data, gl.GL_STATIC_DRAW) loc = self.attribute_location(attribute_name) gl.glEnableVertexAttribArray(loc) typ = {