Skip to content

Commit

Permalink
Fixes + tests + docs
Browse files Browse the repository at this point in the history
  • Loading branch information
einarf committed Nov 12, 2024
1 parent 735cbb7 commit 69a51fd
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 18 deletions.
47 changes: 30 additions & 17 deletions arcade/gl/texture_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,9 @@ def dtype(self) -> str:
return self._dtype

@property
def size(self) -> tuple[int, int]:
def size(self) -> tuple[int, int, int]:
"""The size of the texture as a tuple"""
return self._width, self._height
return self._width, self._height, self._layers

@property
def samples(self) -> int:
Expand Down Expand Up @@ -633,9 +633,10 @@ def read(self, level: int = 0, alignment: int = 1) -> bytes:
gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, alignment)

buffer = (
gl.GLubyte * (self.width * self.height * self._component_size * self._components)
gl.GLubyte
* (self.width * self.height * self.layers * self._component_size * self._components)
)()
gl.glGetTexImage(gl.GL_TEXTURE_2D, level, self._format, self._type, buffer)
gl.glGetTexImage(self._target, level, self._format, self._type, buffer)
return string_at(buffer, len(buffer))
elif self._ctx.gl_api == "gles":
# FIXME: Check if we can attach a layer to the framebuffer. See Texture2D.read()
Expand All @@ -644,7 +645,7 @@ def read(self, level: int = 0, alignment: int = 1) -> bytes:
raise ValueError("Unknown gl_api: '{self._ctx.gl_api}'")

def write(self, data: BufferOrBufferProtocol, level: int = 0, viewport=None) -> None:
"""Write byte data from the passed source to the texture.
"""Write byte data into layers of the texture.
The ``data`` value can be either an
:py:class:`arcade.gl.Buffer` or anything that implements the
Expand All @@ -660,45 +661,57 @@ def write(self, data: BufferOrBufferProtocol, level: int = 0, viewport=None) ->
data:
:class:`~arcade.gl.Buffer` or buffer protocol object with data to write.
level:
The texture level to write
viewport:
The area of the texture to write. 2 or 4 component tuple.
The texture level to write (LoD level, now layer)
viewport (optional):
The area of the texture to write. Should be a 3 or 5-component tuple
`(x, y, layer, width, height)` writes to an area of a single layer.
If not provided the entire texture is written to.
"""
# TODO: Support writing to layers using viewport + alignment
if self._samples > 0:
raise ValueError("Writing to multisampled textures not supported")

x, y, w, h = 0, 0, self._width, self._height
x, y, l, w, h = (
0,
0,
0,
self._width,
self._height,
)
if viewport:
if len(viewport) == 2:
w, h = viewport
elif len(viewport) == 4:
x, y, w, h = viewport
# TODO: Add more options here. For now we support writing to a single layer
# (width, hight, num_layers) is a suggestion from moderngl
# if len(viewport) == 3:
# w, h, l = viewport
if len(viewport) == 5:
x, y, l, w, h = viewport
else:
raise ValueError("Viewport must be of length 2 or 4")
raise ValueError("Viewport must be of length 5")

if isinstance(data, Buffer):
gl.glBindBuffer(gl.GL_PIXEL_UNPACK_BUFFER, data.glo)
gl.glActiveTexture(gl.GL_TEXTURE0 + self._ctx.default_texture_unit)
gl.glBindTexture(self._target, self._glo)
gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 1)
gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
gl.glTexSubImage2D(self._target, level, x, y, w, h, self._format, self._type, 0)
gl.glTexSubImage3D(self._target, level, x, y, w, h, l, self._format, self._type, 0)
gl.glBindBuffer(gl.GL_PIXEL_UNPACK_BUFFER, 0)
else:
byte_size, data = data_to_ctypes(data)
self._validate_data_size(data, byte_size, w, h, self._layers)
self._validate_data_size(data, byte_size, w, h, 1) # Single layer
gl.glActiveTexture(gl.GL_TEXTURE0 + self._ctx.default_texture_unit)
gl.glBindTexture(self._target, self._glo)
gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 1)
gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
gl.glTexSubImage2D(
gl.glTexSubImage3D(
self._target, # target
level, # level
x, # x offset
y, # y offset
l, # layer
w, # width
h, # height
1, # depth (one layer)
self._format, # format
self._type, # type
data, # pixel data
Expand Down
2 changes: 1 addition & 1 deletion doc/api_docs/gl/sampler.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

.. py:currentmodule:: arcade
Texture
Sampler
=======

.. autoclass:: arcade.gl.Sampler
Expand Down
11 changes: 11 additions & 0 deletions doc/api_docs/gl/texture_array.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

.. py:currentmodule:: arcade
TextureArray
============

.. autoclass:: arcade.gl.TextureArray
:members:
:undoc-members:
:show-inheritance:
:member-order: bysource
47 changes: 47 additions & 0 deletions tests/unit/gl/test_gl_texture_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from array import array

import arcade


def test_create(ctx: arcade.ArcadeContext):
"""Create empty texture array."""
ta = ctx.texture_array((4, 8, 16), components=1, dtype="f1")
assert ta.size == (4, 8, 16)
assert ta.width == 4
assert ta.height == 8
assert ta.layers == 16


def test_create_with_data(ctx: arcade.ArcadeContext):
"""Create texture array with initial data."""
data = array("B")
data.extend([1] * 16)
data.extend([2] * 16)
data.extend([3] * 16)
data.extend([4] * 16)

ta = ctx.texture_array((4, 4, 4), components=1, dtype="f1", data=data)
assert ta.size == (4, 4, 4)
assert ta.read() == data.tobytes()


def test_create_individual_layers(ctx: arcade.ArcadeContext):
"""Create texture array with individual layers."""
layer_1 = array("B", [1] * 16)
layer_2 = array("B", [2] * 16)
layer_3 = array("B", [3] * 16)
layer_4 = array("B", [4] * 16)

layers = array("B")
layers.extend(layer_1)
layers.extend(layer_2)
layers.extend(layer_3)
layers.extend(layer_4)

ta = ctx.texture_array((4, 4, 4), components=1, dtype="f1")
ta.write(layer_1, viewport=(0, 0, 0, 4, 4))
ta.write(layer_2, viewport=(0, 0, 1, 4, 4))
ta.write(layer_3, viewport=(0, 0, 2, 4, 4))
ta.write(layer_4, viewport=(0, 0, 3, 4, 4))

assert ta.read() == layers.tobytes()

0 comments on commit 69a51fd

Please sign in to comment.