# Copyright (c) 2023-2024 Thomas Mathieson.
# Distributed under the terms of the MIT license.
from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Any, Dict, Tuple
from .ssv_vertex_buffer import SSVVertexBuffer
if TYPE_CHECKING:
from .ssv_canvas import SSVCanvas
from .ssv_render_process_client import SSVRenderProcessClient
from .ssv_shader_preprocessor import SSVShaderPreprocessor
[docs]
class SSVRenderBuffer:
"""
A lightweight class representing a native render buffer.
"""
def __init__(self, canvas: SSVCanvas, render_process_client: SSVRenderProcessClient,
preprocessor: SSVShaderPreprocessor,
render_buffer_uid: Optional[int], render_buffer_name: str,
order: int, size: tuple[int, int], dtype: str,
components: int):
"""
*Used Internally*
Note that ``SSVRenderBuffer`` objects should be constructed using the factory method on an ``SSVCanvas``.
:param canvas: the canvas creating this render buffer.
:param render_process_client: the render process connection belonging to the canvas.
:param preprocessor: the preprocessor belonging to the canvas.
:param render_buffer_uid: the UID to give this render buffer. Set to ``None`` to generate one automatically.
:param render_buffer_name: the name of this render buffer. This is the name given to the automatically
generated uniform declaration.
:param order: the render order of the render buffer. Smaller numbers are rendered first.
:param size: the resolution of the render buffer.
:param dtype: the moderngl datatype of the render buffer.
:param components: how many vector components should each pixel have (RGB=3, RGBA=4).
"""
self._canvas = canvas
self._render_process_client = render_process_client
self._preprocessor = preprocessor
self._render_buffer_uid = id(self) if render_buffer_uid is None else render_buffer_uid
self._render_buffer_name = render_buffer_name
self._order = order
self._size = size
self._components = components
self._dtype = dtype
self._vertex_buffers: Dict[int, SSVVertexBuffer] = {}
# Register this frame buffer
render_process_client.update_frame_buffer(self._render_buffer_uid, order, size, render_buffer_name,
components, dtype)
# TODO: Support sampler3D
preprocessor.add_dynamic_uniform(render_buffer_name, "sampler2D")
# Create a default full screen draw call for this render buffer
self._full_screen_vertex_buffer = SSVVertexBuffer(0, self, self._render_process_client,
self._preprocessor)
self._vertex_buffers[self._full_screen_vertex_buffer.draw_call_uid] = self._full_screen_vertex_buffer
def _update_frame_buffer(self):
self._render_process_client.update_frame_buffer(self._render_buffer_uid, self._order, self._size,
self._render_buffer_name, self._components, self._dtype)
@property
def render_buffer_uid(self) -> int:
"""
Gets the internal UID of this render buffer.
"""
return self._render_buffer_uid
@property
def render_buffer_name(self) -> str:
"""
Gets the name of this render buffer.
"""
return self._render_buffer_name
@property
def canvas(self) -> SSVCanvas:
"""
Gets the canvas associated with this render buffer.
"""
return self._canvas
@property
def full_screen_vertex_buffer(self) -> SSVVertexBuffer:
"""
Gets the default full-screen vertex buffer for this render buffer.
"""
return self._full_screen_vertex_buffer
@property
def order(self) -> int:
"""
Gets or sets the render order of this render buffer. This number to hint the renderer as to the order to render
the buffers in. Smaller values are rendered first; the main render buffer has an order of 999999.
"""
return self._order
@order.setter
def order(self, value):
self._order = value
self._update_frame_buffer()
@property
def size(self) -> tuple[int, int]:
"""
Gets or sets the resolution of this render buffer.
"""
return self._size
@size.setter
def size(self, value):
self._size = value
self._update_frame_buffer()
@property
def components(self) -> int:
"""
Gets or sets how many vector components each pixel should have (RGB=3, RGBA=4).
"""
return self._components
@components.setter
def components(self, value):
self._components = value
self._update_frame_buffer()
@property
def dtype(self) -> str:
"""
Gets or sets the data type for each pixel component
(see: https://moderngl.readthedocs.io/en/5.8.2/topics/texture_formats.html).
"""
return self._dtype
@dtype.setter
def dtype(self, value):
self._dtype = value
self._update_frame_buffer()
@property
def vertex_buffers(self) -> Tuple[SSVVertexBuffer, ...]:
"""Gets the tuple of vertex buffers registered to this render buffer."""
return tuple(v for v in self._vertex_buffers.values())
[docs]
def shader(self, shader_source: str, additional_template_directory: Optional[str] = None,
additional_templates: Optional[list[str]] = None,
shader_defines: Optional[dict[str, str]] = None,
compiler_extensions: Optional[list[str]] = None):
"""
Registers, compiles and attaches a full-screen shader to this render buffer.
:param shader_source: the shader source code to preprocess. It should contain the necessary
``#pragma SSV <template_name>`` directive see :ref:`built-in-shader-templates` for more
information.
:param additional_template_directory: a path to a directory containing custom shader templates. See
:ref:`writing-shader-templates` for information about using custom shader
templates.
:param additional_templates: a list of custom shader templates (source code, not paths).See
:ref:`writing-shader-templates` for information about using custom shader
templates.
:param shader_defines: extra preprocessor defines to be enabled globally.
:param compiler_extensions: a list of GLSL extensions required by this shader
(eg: ``GL_EXT_control_flow_attributes``)
"""
self._full_screen_vertex_buffer.shader(shader_source, additional_template_directory, additional_templates,
shader_defines, compiler_extensions)
[docs]
def vertex_buffer(self) -> SSVVertexBuffer:
"""
Creates a new draw call and associated vertex buffer on this render buffer.
:return: A new vertex buffer object.
"""
vb = SSVVertexBuffer(None, self, self._render_process_client, self._preprocessor)
self._vertex_buffers[vb.draw_call_uid] = vb
return vb
[docs]
def delete_vertex_buffer(self, buffer: SSVVertexBuffer):
"""
Removes a vertex buffer from this render buffer, releasing its resources.
:param buffer: the vertex buffer to remove.
"""
buff = self._vertex_buffers.pop(buffer.draw_call_uid)
buff.release()