Skip to content

Commit

Permalink
Split shaders module into a new pacckage called gl
Browse files Browse the repository at this point in the history
  • Loading branch information
einarf committed Apr 17, 2020
1 parent 6fdd408 commit c5459e3
Show file tree
Hide file tree
Showing 19 changed files with 2,312 additions and 2,200 deletions.
14 changes: 7 additions & 7 deletions arcade/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from arcade import get_viewport
from arcade import set_viewport
from arcade import set_window
from arcade import shader
from arcade.gl import Context

if TYPE_CHECKING:
from arcade import TextBox
Expand Down Expand Up @@ -80,10 +80,6 @@ def __init__(self, width: int = 800, height: int = 600,
except pyglet.gl.GLException:
print("Warning: Anti-aliasing not supported on this computer.")

# Required for transparency
gl.glEnable(gl.GL_BLEND)
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)

if update_rate:
from pyglet import compat_platform
if compat_platform == 'darwin' or compat_platform == 'linux':
Expand All @@ -109,7 +105,11 @@ def __init__(self, width: int = 800, height: int = 600,
self.key: Optional[int] = None

# Representation of the OpenGL context for this window
self._ctx = shader.Context(self)
self._ctx = Context(self)

# Required for transparency
self.ctx.enable(self.ctx.BLEND)
self.ctx.blend_func = self.ctx.BLEND_DEFAULT

def close(self):
""" Close the Window. """
Expand All @@ -118,7 +118,7 @@ def close(self):
pyglet.clock.unschedule(self.on_update)

@property
def ctx(self) -> shader.Context:
def ctx(self) -> Context:
"""The OpenGL context for this window"""
return self._ctx

Expand Down
6 changes: 3 additions & 3 deletions arcade/buffered_draw_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from arcade import get_projection
from arcade import get_window
from arcade import get_points_for_thick_line
from arcade import shader
from arcade.gl import BufferDescription


class Shape:
Expand Down Expand Up @@ -105,7 +105,7 @@ def create_line_generic_with_colors(point_list: PointList,

vbo = ctx.buffer(data=data.tobytes())
vao_content = [
shader.BufferDescription(
BufferDescription(
vbo,
'2f 4f1',
('in_vert', 'in_color'),
Expand Down Expand Up @@ -626,7 +626,7 @@ def _refresh_shape(self, group):
ibo = self.ctx.buffer(data=indices.astype('i4').tobytes())

vao_content = [
shader.BufferDescription(
BufferDescription(
vbo,
'2f 4f1',
('in_vert', 'in_color'),
Expand Down
6 changes: 3 additions & 3 deletions arcade/experimental/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
import array
from arcade import shader, get_window

from arcade.gl import BufferDescription

def quad_fs(size=(1.0, 1.0), pos=(0.0, 0.0)):
width, height = size
Expand All @@ -22,7 +22,7 @@ def quad_fs(size=(1.0, 1.0), pos=(0.0, 0.0)):
ctx = get_window().ctx
vbo = ctx.buffer(data=data.tobytes())
vao_content = [
shader.BufferDescription(
BufferDescription(
vbo,
'2f 2f',
('in_vert', 'in_uv'),
Expand Down Expand Up @@ -66,7 +66,7 @@ def screen_rectangle(rectangle_size=None,
ctx = get_window().ctx
vbo = ctx.buffer(data=data.tobytes())
vao_content = [
shader.BufferDescription(
BufferDescription(
vbo,
'2f 2f',
('in_vert', 'in_uv'),
Expand Down
13 changes: 7 additions & 6 deletions arcade/experimental/postprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from pathlib import Path
from pyglet import gl
from typing import Tuple
from arcade import shader
from arcade.gl import Context
from arcade.gl.texture import Texture
from arcade import get_window
from arcade.experimental import geometry
from arcade.experimental.gaussian_kernel import gaussian_kernel
Expand Down Expand Up @@ -34,7 +35,7 @@ def __init__(self, size: Tuple[int, int], *args, **kwargs):
self._ctx = get_window().ctx

@property
def ctx(self) -> shader.Context:
def ctx(self) -> Context:
""" Get the shader context. """
return self._ctx

Expand All @@ -53,7 +54,7 @@ def size(self):
""" Get the size of the buffer. """
return self._size

def render(self, *args, **kwargs) -> shader.Texture:
def render(self, *args, **kwargs) -> Texture:
""" Render. Should be over-loaded by the child class. """
pass

Expand Down Expand Up @@ -81,7 +82,7 @@ def __init__(self, size: Tuple[int, int], defines):
)
self._quad_fs = geometry.quad_fs(size=(2.0, 2.0))

def render(self, source: shader.Texture) -> shader.Texture:
def render(self, source: Texture) -> Texture:
""" Render """
self._fbo.use()
source.use(0)
Expand All @@ -107,7 +108,7 @@ def __init__(self, size: Tuple[int, int], defines):
)
self._quad_fs = geometry.quad_fs(size=(2.0, 2.0))

def render(self, source: shader.Texture) -> shader.Texture:
def render(self, source: Texture) -> Texture:
""" Render """
self._fbo.use()
source.use(0)
Expand All @@ -123,7 +124,7 @@ def __init__(self, size, defines):
self._blur_x = GaussianBlurHorizontal(size, defines=defines)
self._blur_y = GaussianBlurVertical(size, defines=defines)

def render(self, source: shader.Texture) -> shader.Texture:
def render(self, source: Texture) -> Texture:
""" Render """
blurred_x = self._blur_x.render(source)
return self._blur_y.render(blurred_x)
Expand Down
8 changes: 8 additions & 0 deletions arcade/gl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

# Thank you Benjamin Moran for writing part of this code!
# https://bitbucket.org/HigashiNoKaze/pyglet/src/shaders/pyglet/graphics/shader.py


from .context import Context
from .types import BufferDescription
from .exceptions import ShaderException
170 changes: 170 additions & 0 deletions arcade/gl/buffer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
from ctypes import byref, string_at
import weakref
from typing import TYPE_CHECKING

from pyglet import gl

from .exceptions import ShaderException

if TYPE_CHECKING: # handle import cycle caused by type hinting
from arcade.gl import Context


class Buffer:
"""OpenGL Buffer object. Buffers store byte data and upload it
to graphics memory. They are used for storage og vertex data,
element data (vertex indexing), uniform buffer data etc.
Common bind targets are: ``GL_ARRAY_BUFFER``, ``GL_ELEMENT_ARRAY_BUFFER``,
``GL_UNIFORM_BUFFER``, ``GL_SHADER_STORAGE_BUFFER``
It doesn't matter what bind target the buffer has on creation. What
matters is how we bind it in rendering calls.
"""
__slots__ = '_ctx', '_glo', '_size', '_usage', '__weakref__'
usages = {
'static': gl.GL_STATIC_DRAW,
'dynamic': gl.GL_DYNAMIC_DRAW,
'stream': gl.GL_STREAM_DRAW
}

def __init__(self, ctx, data: bytes = None, reserve: int = 0, usage: str = 'static'):
self._ctx = ctx
self._glo = glo = gl.GLuint()
self._size = -1
self._usage = Buffer.usages[usage]

gl.glGenBuffers(1, byref(self._glo))
# print(f"glGenBuffers() -> {self._glo.value}")
if self._glo.value == 0:
raise ShaderException("Cannot create Buffer object.")

# print(f"glBindBuffer({self._glo.value})")
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._glo)
# print(f"glBufferData(gl.GL_ARRAY_BUFFER, {self._size}, data, {self._usage})")

if data and len(data) > 0:
self._size = len(data)
gl.glBufferData(gl.GL_ARRAY_BUFFER, self._size, data, self._usage)
elif reserve > 0:
self._size = reserve
gl.glBufferData(gl.GL_ARRAY_BUFFER, self._size, None, self._usage)
else:
raise ValueError("Buffer takes byte data or number of reserved bytes")

self.ctx.stats.incr('buffer')
weakref.finalize(self, Buffer.release, self.ctx, glo)

@property
def size(self) -> int:
"""The byte size of the buffer"""
return self._size

@property
def ctx(self) -> 'Context':
"""The context this resource belongs to"""
return self._ctx

@property
def glo(self) -> gl.GLuint:
"""The OpenGL resource id"""
return self._glo

@staticmethod
def release(ctx: 'Context', glo: gl.GLuint):
""" Release/delete open gl buffer. """
# print(f"*** Buffer {glo} have no more references. Deleting.")

# If we have no context, then we are shutting down, so skip this
if gl.current_context is None:
return

if glo.value != 0:
gl.glDeleteBuffers(1, byref(glo))
glo.value = 0

ctx.stats.decr('buffer')

def read(self, size=-1, offset=0) -> bytes:
"""Read data from the buffer.
:param int size: The bytes to read. -1 means the entire buffer
:param int offset: Byte read offset
"""
if size == -1:
size = self._size

# Catch this before confusing INVALID_OPERATION is raised
if size < 1:
raise ValueError("Attempting to read 0 or less bytes from buffer")

# Manually detect this so it doesn't raise a confusing INVALID_VALUE error
if size + offset > self._size:
raise ValueError(
(
"Attempting to read outside the buffer. "
f"Buffer size: {self._size} "
f"Reading from {offset} to {size + offset}"
)
)

gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._glo)
ptr = gl.glMapBufferRange(gl.GL_ARRAY_BUFFER, offset, size, gl.GL_MAP_READ_BIT)
data = string_at(ptr, size=size)
gl.glUnmapBuffer(gl.GL_ARRAY_BUFFER)
return data

def write(self, data: bytes, offset: int = 0):
"""Write byte data to the buffer.
:param bytes data: The byte data to write
:param int offset: The byte offset
"""
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._glo)
gl.glBufferSubData(gl.GL_ARRAY_BUFFER, gl.GLintptr(offset), len(data), data)

def copy_from_buffer(self, source: 'Buffer', size=-1, offset=0, source_offset=0):
"""Copy data into this buffer from another buffer
:param Buffer source: The buffer to copy from
:param int size: The amount of bytes to copy
:param int offset: The byte offset to write the data in this buffer
:param int source_offset: The byte offset to read from the source buffer
"""
# Read the entire source buffer into this buffer
if size == -1:
size = source.size

# TODO: Check buffer bounds
if size + source_offset > source.size:
raise ValueError("Attempting to read outside the source buffer")

if size + offset > self._size:
raise ValueError("Attempting to write outside the buffer")

gl.glBindBuffer(gl.GL_COPY_READ_BUFFER, source.glo)
gl.glBindBuffer(gl.GL_COPY_WRITE_BUFFER, self._glo)
gl.glCopyBufferSubData(
gl.GL_COPY_READ_BUFFER,
gl.GL_COPY_WRITE_BUFFER,
gl.GLintptr(source_offset), # readOffset
gl.GLintptr(offset), # writeOffset
size # size (number of bytes to copy)
)

def orphan(self, size=-1):
"""
Re-allocate the entire buffer memory.
If the current buffer is busy in redering operations
it will be deallocated by OpenGL when completed.
:param int size: New size of buffer. -1 will retain the current size.
"""
if size > -1:
self._size = size

gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._glo)
gl.glBufferData(gl.GL_ARRAY_BUFFER, self._size, None, self._usage)

def bind(self):
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._glo)

0 comments on commit c5459e3

Please sign in to comment.