Skip to content

Atlas resize shader without geometry shader #2698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions arcade/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,13 @@ def __init__(
)
# Atlas shaders
self.atlas_resize_program: Program = self.load_program(
vertex_shader=":system:shaders/atlas/resize_vs.glsl",
geometry_shader=":system:shaders/atlas/resize_gs.glsl",
fragment_shader=":system:shaders/atlas/resize_fs.glsl",
# NOTE: This is the geo shader version of the atlas resize program.
# vertex_shader=":system:shaders/atlas/resize_vs.glsl",
# geometry_shader=":system:shaders/atlas/resize_gs.glsl",
# fragment_shader=":system:shaders/atlas/resize_fs.glsl",
# Vertex and fragment shader version
vertex_shader=":system:shaders/atlas/resize_simple_vs.glsl",
fragment_shader=":system:shaders/atlas/resize_simple_fs.glsl",
)
self.atlas_resize_program["atlas_old"] = 0 # Configure texture channels
self.atlas_resize_program["atlas_new"] = 1
Expand Down
3 changes: 2 additions & 1 deletion arcade/resources/system/shaders/atlas/resize_fs.glsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#version 330

// The old atlas texture.
// The old atlas texture. We copy sections to the new atlas texture
// by render into an fbo with the target texture as the color attachment.
uniform sampler2D atlas_old;

out vec4 fragColor;
Expand Down
6 changes: 4 additions & 2 deletions arcade/resources/system/shaders/atlas/resize_gs.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

#include :system:shaders/lib/sprite.glsl

// Old and new texture coordiantes
// Old and new texture coordinates
uniform sampler2D atlas_old;
uniform sampler2D atlas_new;

uniform sampler2D texcoords_old;
uniform sampler2D texcoords_new;

uniform mat4 projection;
uniform float border;

Expand All @@ -33,7 +35,7 @@ void main() {
// absolute value of the diagonal * size + border * 2
vec2 size = abs(new_uv3 - new_uv0) * vec2(size_new) + vec2(border * 2.0);

// We need to offset the old coordiantes by border size
// We need to offset the old coordinates by border size
vec2 pix_offset = vec2(border) / vec2(size_old);
// (
// 0.015625, 0.015625, # minus, minus
Expand Down
13 changes: 13 additions & 0 deletions arcade/resources/system/shaders/atlas/resize_simple_fs.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#version 330
// Atlas resize without geometry shader

// The old atlas texture. We copy sections to the new atlas texture
// by render into an fbo with the target texture as the color attachment.
uniform sampler2D atlas_old;

out vec4 fragColor;
in vec2 uv;

void main() {
fragColor = texture(atlas_old, uv);
}
76 changes: 76 additions & 0 deletions arcade/resources/system/shaders/atlas/resize_simple_vs.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#version 330
// Atlas resize without geometry shader

// The render target for this program is the new
// texture atlas texture

#include :system:shaders/lib/sprite.glsl

// Old and new texture coordinates
uniform sampler2D atlas_old;
uniform sampler2D atlas_new;

uniform sampler2D texcoords_old;
uniform sampler2D texcoords_new;

uniform mat4 projection;
uniform float border;

out vec2 uv;

void main() {
// Get the texture sizes
ivec2 size_old = textureSize(atlas_old, 0).xy;
ivec2 size_new = textureSize(atlas_new, 0).xy;

// Read texture coordinates from UV texture here
int texture_id = gl_VertexID / 6;
vec2 old_uv0, old_uv1, old_uv2, old_uv3;
getSpriteUVs(texcoords_old, texture_id, old_uv0, old_uv1, old_uv2, old_uv3);
vec2 new_uv0, new_uv1, new_uv2, new_uv3;
getSpriteUVs(texcoords_new, texture_id, new_uv0, new_uv1, new_uv2, new_uv3);

// Lower left corner flipped * size - border
vec2 pos = vec2(new_uv2.x, 1.0 - new_uv2.y) * vec2(size_new) - vec2(border);
// absolute value of the diagonal * size + border * 2
vec2 size = abs(new_uv3 - new_uv0) * vec2(size_new) + vec2(border * 2.0);

// We need to offset the old coordinates by border size
vec2 pix_offset = vec2(border) / vec2(size_old);

// Emit two triangles over 6 vertices
switch (gl_VertexID % 6) {
// First triangle
case 0:
// upper left
uv = old_uv0 - pix_offset;
gl_Position = projection * vec4(pos + vec2(0.0, size.y), 0.0, 1.0);
break;
case 1:
// lower left
uv = old_uv2 + vec2(-pix_offset.x, pix_offset.y);
gl_Position = projection * vec4(pos, 0.0, 1.0);
break;
case 2:
// upper right
uv = old_uv1 + vec2(pix_offset.x, -pix_offset.y);
gl_Position = projection * vec4(pos + vec2(size.x, size.y), 0.0, 1.0);
break;
// Second triangle
case 3:
// lower left
uv = old_uv2 + vec2(-pix_offset.x, pix_offset.y);
gl_Position = projection * vec4(pos, 0.0, 1.0);
break;
case 4:
// upper right
uv = old_uv1 + vec2(pix_offset.x, -pix_offset.y);
gl_Position = projection * vec4(pos + vec2(size.x, size.y), 0.0, 1.0);
break;
case 5:
// lower right
uv = old_uv3 + pix_offset;
gl_Position = projection * vec4(pos + vec2(size.x, 0.0), 0.0, 1.0);
break;
}
}
12 changes: 6 additions & 6 deletions arcade/texture_atlas/atlas_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def __init__(
self,
size: tuple[int, int],
*,
border: int = 1,
border: int = 2,
textures: Sequence[Texture] | None = None,
auto_resize: bool = True,
ctx: ArcadeContext | None = None,
Expand Down Expand Up @@ -667,8 +667,7 @@ def resize(self, size: tuple[int, int], force=False) -> None:
force:
Force a resize even if the size is the same
"""
# LOG.info("[%s] Resizing atlas from %s to %s", id(self), self._size, size)
# print("Resizing atlas from", self._size, "to", size)
print("Resizing atlas from", self._size, "to", size)

# Only resize if the size actually changed
if size == self._size and not force:
Expand Down Expand Up @@ -732,8 +731,9 @@ def resize(self, size: tuple[int, int], force=False) -> None:
with self._ctx.enabled_only():
self._ctx.geometry_empty.render(
self._ctx.atlas_resize_program,
mode=self._ctx.POINTS,
vertices=self.max_width,
mode=self._ctx.TRIANGLES,
# Two triangles per texture
vertices=UV_TEXTURE_WIDTH * self._capacity * 6,
)

# duration = time.perf_counter() - resize_start
Expand All @@ -746,7 +746,7 @@ def rebuild(self) -> None:
This method also tries to organize the textures more efficiently ordering them by size.
The texture ids will persist so the sprite list doesn't need to be rebuilt.
"""
# LOG.info("Rebuilding atlas")
print("Rebuilding atlas")

# Hold a reference to the old textures
textures = self.textures
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/atlas/test_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def test_create(ctx, common):
assert atlas.width == 100
assert atlas.height == 200
assert atlas.size == (100, 200)
assert atlas.border == 1
assert atlas.border == 2
assert atlas.auto_resize is True
assert isinstance(atlas.max_size, tuple)
assert atlas.max_size > (0, 0)
Expand Down
Loading