mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-10 22:24:24 +02:00
Migrate the cell program
This commit is contained in:
234
kitty/shaders.py
234
kitty/shaders.py
@@ -2,114 +2,25 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import os
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from contextlib import contextmanager
|
||||
from ctypes import addressof, c_char, sizeof
|
||||
from functools import lru_cache
|
||||
from ctypes import addressof
|
||||
|
||||
from .fast_data_types import (
|
||||
GL_ARRAY_BUFFER, GL_CLAMP_TO_EDGE, GL_FLOAT, GL_MAX_ARRAY_TEXTURE_LAYERS,
|
||||
GL_MAX_TEXTURE_SIZE, GL_NEAREST, GL_R8, GL_RED, GL_STREAM_DRAW,
|
||||
GL_TEXTURE0, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER,
|
||||
GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T,
|
||||
GL_UNIFORM_BUFFER, GL_UNPACK_ALIGNMENT, GL_UNSIGNED_BYTE, GL_WRITE_ONLY,
|
||||
GLSL_VERSION, compile_program, copy_image_sub_data,
|
||||
get_uniform_block_offsets, get_uniform_block_size, glActiveTexture,
|
||||
glBindBuffer, glBindBufferBase, glBindTexture, glBindVertexArray,
|
||||
glCopyImageSubData, glDeleteBuffer, glDeleteTexture, glDeleteVertexArray,
|
||||
glEnableVertexAttribArray, glGenBuffers, glGenTextures, glGenVertexArrays,
|
||||
glGetAttribLocation, glGetBufferSubData, glGetIntegerv,
|
||||
glGetUniformBlockIndex, glGetUniformLocation, glMapBuffer, glPixelStorei,
|
||||
glTexParameteri, glTexStorage3D, glTexSubImage3D, glUnmapBuffer,
|
||||
glUseProgram, glVertexAttribDivisor, glVertexAttribPointer,
|
||||
render_dirty_sprites, replace_or_create_buffer, sprite_map_current_layout,
|
||||
sprite_map_free, sprite_map_increment, sprite_map_set_layout,
|
||||
sprite_map_set_limits
|
||||
GL_CLAMP_TO_EDGE, GL_MAX_ARRAY_TEXTURE_LAYERS, GL_MAX_TEXTURE_SIZE,
|
||||
GL_NEAREST, GL_R8, GL_RED, GL_TEXTURE0, GL_TEXTURE_2D_ARRAY,
|
||||
GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S,
|
||||
GL_TEXTURE_WRAP_T, GL_UNPACK_ALIGNMENT, GL_UNSIGNED_BYTE,
|
||||
copy_image_sub_data, glActiveTexture, glBindTexture, glCopyImageSubData,
|
||||
glDeleteTexture, glGenTextures, glGetIntegerv, glPixelStorei,
|
||||
glTexParameteri, glTexStorage3D, glTexSubImage3D, render_dirty_sprites,
|
||||
sprite_map_current_layout, sprite_map_free, sprite_map_increment,
|
||||
sprite_map_set_layout, sprite_map_set_limits
|
||||
)
|
||||
from .fonts.render import render_cell
|
||||
from .utils import safe_print
|
||||
|
||||
UBO = namedtuple('UBO', 'size index offsets buf_id')
|
||||
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def load_shaders(name):
|
||||
vert = open(os.path.join(BASE, '{}_vertex.glsl'.format(name))).read().replace('GLSL_VERSION', str(GLSL_VERSION), 1)
|
||||
frag = open(os.path.join(BASE, '{}_fragment.glsl'.format(name))).read().replace('GLSL_VERSION', str(GLSL_VERSION), 1)
|
||||
return vert, frag
|
||||
|
||||
|
||||
class BufferManager: # {{{
|
||||
|
||||
def __init__(self):
|
||||
self.sizes = {}
|
||||
self.types = {}
|
||||
self.ctypes_types = {}
|
||||
self.name_count = 0
|
||||
|
||||
def create(self, for_use=GL_ARRAY_BUFFER):
|
||||
buf_id = glGenBuffers(1)
|
||||
self.types[buf_id] = for_use
|
||||
self.sizes.pop(buf_id, None)
|
||||
self.ctypes_types.pop(buf_id, None)
|
||||
return buf_id
|
||||
|
||||
def delete(self, buf_id):
|
||||
if buf_id in self.types:
|
||||
glDeleteBuffer(buf_id)
|
||||
self.sizes.pop(buf_id, None)
|
||||
self.types.pop(buf_id)
|
||||
self.ctypes_types.pop(buf_id, None)
|
||||
|
||||
def set_data(self, buf_id, data, usage=GL_STREAM_DRAW, verify=False):
|
||||
prev_sz = self.sizes.get(buf_id, 0)
|
||||
new_sz = sizeof(data)
|
||||
replace_or_create_buffer(buf_id, new_sz, prev_sz, addressof(data), usage, self.types[buf_id])
|
||||
self.sizes[buf_id] = new_sz
|
||||
self.ctypes_types[buf_id] = type(data)
|
||||
if verify:
|
||||
verify_data = self.get_data(buf_id)
|
||||
if list(data) != list(verify_data):
|
||||
raise RuntimeError('OpenGL failed to upload to buffer')
|
||||
|
||||
def get_data(self, buf_id):
|
||||
verify_data = self.ctypes_types[buf_id]()
|
||||
glGetBufferSubData(self.types[buf_id], buf_id, self.sizes[buf_id], 0, addressof(verify_data))
|
||||
return verify_data
|
||||
|
||||
def bind(self, buf_id):
|
||||
glBindBuffer(self.types[buf_id], buf_id)
|
||||
|
||||
def unbind(self, buf_id):
|
||||
glBindBuffer(self.types[buf_id], 0)
|
||||
|
||||
@contextmanager
|
||||
def bound_buffer(self, buf_id):
|
||||
self.bind(buf_id)
|
||||
yield
|
||||
self.unbind(buf_id)
|
||||
|
||||
@contextmanager
|
||||
def mapped_buffer(self, buf_id, buf_sz, usage=GL_STREAM_DRAW, access=GL_WRITE_ONLY):
|
||||
prev_sz = self.sizes.get(buf_id, 0)
|
||||
buf_type = self.types[buf_id]
|
||||
if prev_sz != buf_sz:
|
||||
replace_or_create_buffer(buf_id, buf_sz, 0, 0, usage, buf_type)
|
||||
self.sizes[buf_id] = buf_sz
|
||||
self.ctypes_types[buf_id] = c_char
|
||||
with self.bound_buffer(buf_id):
|
||||
address = glMapBuffer(buf_type, access)
|
||||
yield address
|
||||
glUnmapBuffer(buf_type)
|
||||
|
||||
|
||||
buffer_manager = BufferManager()
|
||||
# }}}
|
||||
|
||||
|
||||
class Sprites: # {{{
|
||||
class Sprites:
|
||||
''' Maintain sprite sheets of all rendered characters on the GPU as a texture
|
||||
array with each texture being a sprite sheet. '''
|
||||
|
||||
@@ -149,11 +60,6 @@ class Sprites: # {{{
|
||||
if send(strikethrough=True) != 3:
|
||||
raise RuntimeError('Available OpenGL texture size is too small')
|
||||
|
||||
@property
|
||||
def layout(self):
|
||||
xnum, ynum, znum = sprite_map_current_layout()
|
||||
return 1 / xnum, 1 / ynum
|
||||
|
||||
def render_cell(self, text, bold, italic, is_second):
|
||||
first, second = render_cell(text, bold, italic)
|
||||
ans = second if is_second else first
|
||||
@@ -231,121 +137,3 @@ class Sprites: # {{{
|
||||
|
||||
def __exit__(self, *a):
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0)
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class ShaderProgram: # {{{
|
||||
""" Helper class for using GLSL shader programs """
|
||||
|
||||
def __init__(self, which, vertex, fragment):
|
||||
"""
|
||||
Create a shader program.
|
||||
|
||||
"""
|
||||
self.program_id = compile_program(which, vertex, fragment)
|
||||
self.vertex_arrays = {}
|
||||
|
||||
@contextmanager
|
||||
def array_object_creator(self):
|
||||
vao_id = glGenVertexArrays(1)
|
||||
self.vertex_arrays[vao_id] = buffers = []
|
||||
buf_id = None
|
||||
|
||||
def newbuf():
|
||||
nonlocal buf_id
|
||||
buf_id = buffer_manager.create(for_use=GL_ARRAY_BUFFER)
|
||||
buffers.append(buf_id)
|
||||
return buf_id
|
||||
|
||||
def add_attribute(name, size=3, dtype=GL_FLOAT, normalized=False, stride=0, offset=0, divisor=0):
|
||||
nonlocal buf_id
|
||||
aid = self.attribute_location(name)
|
||||
if aid > -1:
|
||||
if buf_id is None:
|
||||
buf_id = newbuf()
|
||||
with buffer_manager.bound_buffer(buf_id):
|
||||
glEnableVertexAttribArray(aid)
|
||||
glVertexAttribPointer(aid, size, dtype, normalized, stride, offset)
|
||||
if divisor > 0:
|
||||
glVertexAttribDivisor(aid, divisor)
|
||||
|
||||
add_attribute.newbuf = newbuf
|
||||
add_attribute.vao_id = vao_id
|
||||
with self.bound_vertex_array(vao_id):
|
||||
yield add_attribute
|
||||
|
||||
@contextmanager
|
||||
def bound_vertex_array(self, vao_id):
|
||||
glBindVertexArray(vao_id)
|
||||
yield
|
||||
glBindVertexArray(0)
|
||||
|
||||
def remove_vertex_array(self, vao_id):
|
||||
buffers = self.vertex_arrays.pop(vao_id, None)
|
||||
if buffers is not None:
|
||||
glDeleteVertexArray(vao_id)
|
||||
for buf_id in buffers:
|
||||
buffer_manager.delete(buf_id)
|
||||
|
||||
def send_vertex_data(self, vao_id, data, usage=GL_STREAM_DRAW, bufnum=0):
|
||||
bufid = self.vertex_arrays[vao_id][bufnum]
|
||||
buffer_manager.set_data(bufid, data, usage=usage)
|
||||
|
||||
def mapped_vertex_data(self, vao_id, buf_sz, usage=GL_STREAM_DRAW, bufnum=0, access=GL_WRITE_ONLY):
|
||||
bufid = self.vertex_arrays[vao_id][bufnum]
|
||||
return buffer_manager.mapped_buffer(bufid, buf_sz, usage=usage, access=access)
|
||||
|
||||
def get_vertex_data(self, vao_id, bufnum=0):
|
||||
bufid = self.vertex_arrays[vao_id][bufnum]
|
||||
return buffer_manager.get_data(bufid)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return self.program_id
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
return isinstance(other, ShaderProgram) and other.program_id == self.program_id
|
||||
|
||||
def __ne__(self, other) -> bool:
|
||||
return not self.__eq__(other)
|
||||
|
||||
@lru_cache(maxsize=2**6)
|
||||
def uniform_location(self, name: str) -> int:
|
||||
' Return the id for the uniform variable `name` or -1 if not found. '
|
||||
return glGetUniformLocation(self.program_id, name)
|
||||
|
||||
@lru_cache(maxsize=2**6)
|
||||
def uniform_block_index(self, name: str) -> int:
|
||||
' Return the block index for the specified uniform block raises ValueError if not found. '
|
||||
return glGetUniformBlockIndex(self.program_id, name)
|
||||
|
||||
def uniform_block_size(self, block_index):
|
||||
return get_uniform_block_size(self.program_id, block_index)
|
||||
|
||||
def init_uniform_block(self, name, *variables):
|
||||
idx = self.uniform_block_index(name)
|
||||
size = self.uniform_block_size(idx)
|
||||
offsets = get_uniform_block_offsets(self.program_id, variables)
|
||||
offsets = dict(zip(variables, offsets))
|
||||
buf_id = buffer_manager.create(GL_UNIFORM_BUFFER)
|
||||
return UBO(size=size, index=idx, offsets=offsets, buf_id=buf_id)
|
||||
|
||||
def mapped_uniform_data(self, ubo, usage=GL_STREAM_DRAW, access=GL_WRITE_ONLY):
|
||||
return buffer_manager.mapped_buffer(ubo.buf_id, ubo.size, usage=usage, access=access)
|
||||
|
||||
@contextmanager
|
||||
def bound_uniform_buffer(self, ubo):
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, ubo.index, ubo.buf_id)
|
||||
yield
|
||||
|
||||
@lru_cache(maxsize=2**6)
|
||||
def attribute_location(self, name: str) -> int:
|
||||
' Return the id for the attribute variable `name` or -1 if not found. '
|
||||
return glGetAttribLocation(self.program_id, name)
|
||||
|
||||
def __enter__(self):
|
||||
glUseProgram(self.program_id)
|
||||
|
||||
def __exit__(self, *args):
|
||||
glUseProgram(0)
|
||||
# }}}
|
||||
|
||||
Reference in New Issue
Block a user