automatic detenction of texture mode and batching based on it

This commit is contained in:
veclav talica 2024-07-28 23:59:23 +03:00
parent 945b1d21fe
commit 3edd692771
9 changed files with 89 additions and 30 deletions

View File

@ -11,38 +11,31 @@ static void ingame_tick(struct state *state) {
world_drawdef(scn->world);
player_calc(scn->player);
for (size_t i = 100000; --i;)
push_sprite_ex((t_frect){ .x = 32, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){
.path = "/assets/light.png",
.color = (t_color){255, 0, 0, 255},
.blend = false });
.color = (t_color){255, 0, 0, 255}, });
push_sprite_ex((t_frect){ .x = 48, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){
.path = "/assets/light.png",
.color = (t_color){0, 255, 0, 255},
.blend = true });
.color = (t_color){0, 255, 0, 255}, });
push_sprite_ex((t_frect){ .x = 64, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){
.path = "/assets/light.png",
.color = (t_color){0, 0, 255, 255},
.blend = true });
.color = (t_color){0, 0, 255, 255}, });
push_sprite_ex((t_frect){ .x = 32, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){
.path = "/assets/player/baron-walk.png",
.color = (t_color){255, 255, 255, 255},
.rotation = (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64,
.blend = true });
.rotation = (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64, });
push_sprite_ex((t_frect){ .x = 64, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){
.path = "/assets/player/baron-walk.png",
.color = (t_color){255, 255, 255, 255},
.rotation = (float)M_PI / 64,
.blend = true });
.rotation = (float)M_PI / 64, });
push_sprite_ex((t_frect){ .x = 96, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){
.path = "/assets/player/baron-walk.png",
.color = (t_color){255, 255, 255, 255},
.blend = true });
.color = (t_color){255, 255, 255, 255}, });
unfurl_triangle("/assets/big-violet.png",
(t_fvec3){ -1, -1, 0 },

View File

@ -17,7 +17,6 @@ struct sprite_primitive {
t_texture_key texture_key;
bool flip_x;
bool flip_y;
bool blend; /* must be explicitly stated, textures having alpha channel isn't enough */
};
struct rect_primitive {

View File

@ -2,6 +2,7 @@
#define PRIVATE_TEXTURES_H
#include "../util.h"
#include "../textures/modes.h"
#include <SDL2/SDL.h>
#include <stb_rect_pack.h>
@ -14,6 +15,7 @@ struct texture {
SDL_Surface *data; /* original image data */
int atlas_index;
GLuint loner_texture; /* stored directly for loners, == 0 means atlas_index should be used */
enum texture_mode mode;
};

View File

@ -71,7 +71,6 @@ static void render_2d(void) {
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glDisable(GL_CULL_FACE);
glDisable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST);
const size_t render_queue_len = arrlenu(ctx.render_queue_2d);
@ -107,11 +106,12 @@ static void render_2d(void) {
static void render_space(void) {
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glDisable(GL_ALPHA_TEST);
glActiveTexture(GL_TEXTURE0);
/* solid white, no modulation */

View File

@ -13,7 +13,6 @@ typedef struct push_sprite_args {
float rotation;
bool flip_x;
bool flip_y;
bool blend;
} t_push_sprite_args;
/* clears all render queues */

View File

@ -68,7 +68,6 @@ void push_sprite(char *path, t_frect rect) {
.texture_key = textures_get_key(&ctx.texture_cache, path),
.flip_x = false,
.flip_y = false,
.blend = true,
};
struct primitive_2d primitive = {
@ -88,7 +87,6 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) {
.texture_key = textures_get_key(&ctx.texture_cache, args.path),
.flip_x = args.flip_x,
.flip_y = args.flip_y,
.blend = args.blend,
};
struct primitive_2d primitive = {
@ -101,16 +99,16 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) {
static struct sprite_batch {
int atlas_id;
size_t size; /* how many primitives are in current batch */
bool blend; /* whether it's blended or not */
int atlas_id;
enum texture_mode mode;
bool constant_colored; /* whether colored batch is uniformly colored */
} collect_sprite_batch(const struct primitive_2d *primitives, size_t len) {
/* assumes that first primitive is already a sprite */
struct sprite_batch batch = {
.atlas_id =
textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key),
.blend = primitives[0].sprite.blend,
.mode = textures_get_mode(&ctx.texture_cache, primitives[0].sprite.texture_key),
.constant_colored = true,
};
@ -128,7 +126,8 @@ static struct sprite_batch {
break;
/* only collect the same blend modes */
if (current->sprite.blend != batch.blend)
const enum texture_mode mode = textures_get_mode(&ctx.texture_cache, current->sprite.texture_key);
if (mode != batch.mode)
break;
/* only collect the same texture atlases */
@ -156,13 +155,23 @@ static void render_sprites(const struct primitive_2d primitives[],
if (vertex_array == 0)
glGenBuffers(1, &vertex_array);
if (batch.blend) {
if (batch.mode == TEXTURE_MODE_GHOSTLY) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_ALWAYS);
glDepthMask(GL_TRUE);
glDisable(GL_ALPHA_TEST);
} else if (batch.mode == TEXTURE_MODE_SEETHROUGH) {
glDisable(GL_BLEND);
glDepthFunc(GL_ALWAYS);
glDepthMask(GL_TRUE);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_EQUAL, 1.0f);
} else {
glDisable(GL_BLEND);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glDisable(GL_ALPHA_TEST);
}
size_t payload_size;
@ -186,7 +195,8 @@ static void render_sprites(const struct primitive_2d primitives[],
void *const payload = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
for (size_t i = 0; i < batch.size; ++i) {
const size_t cur = !batch.blend ? batch.size - i - 1: i; /* render opaques front to back */
/* render opaques front to back */
const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1;
const struct sprite_primitive sprite = primitives[cur].sprite;
const t_rect srcrect =

View File

@ -293,6 +293,34 @@ void textures_dump_atlases(struct texture_cache *cache) {
}
static enum texture_mode infer_texture_mode(SDL_Surface *surface) {
const uint32_t amask = surface->format->Amask;
if (amask == 0)
return TEXTURE_MODE_OPAQUE;
enum texture_mode result = TEXTURE_MODE_OPAQUE;
SDL_LockSurface(surface);
for (int i = 0; i < surface->w * surface->h; ++i) {
/* TODO: don't assume 32 bit depth ? */
t_color color;
SDL_GetRGBA(((uint32_t *)surface->pixels)[i], surface->format, &color.r, &color.g, &color.b, &color.a);
if (color.a == 0)
result = TEXTURE_MODE_SEETHROUGH;
else if (color.a != 255) {
result = TEXTURE_MODE_GHOSTLY;
break;
}
}
SDL_UnlockSurface(surface);
return result;
}
static t_texture_key textures_load(struct texture_cache *cache, const char *path) {
/* no need to do anything if it was loaded already */
const ptrdiff_t i = shgeti(cache->hash, path);
@ -300,8 +328,10 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path
return (t_texture_key){ (uint16_t)i };
SDL_Surface *surface = image_to_surface(path);
struct texture new_texture = {0};
new_texture.data = surface;
struct texture new_texture = {
.data = surface,
.mode = infer_texture_mode(surface),
};
/* it's a "loner texture," it doesn't fit in an atlas so it's not in one */
if (surface->w >= TEXTURE_ATLAS_SIZE || surface->h >= TEXTURE_ATLAS_SIZE) {
@ -472,6 +502,17 @@ void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum
}
enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key) {
if (m_texture_key_is_valid(key)) {
return cache->hash[key.id].value.mode;
} else {
CRY("Texture binding failed.",
"Tried to get texture that isn't loaded.");
return TEXTURE_MODE_GHOSTLY;
}
}
size_t textures_get_num_atlases(const struct texture_cache *cache) {
return cache->atlas_index + 1;
}

View File

@ -2,6 +2,7 @@
#define TEXTURES_H
#include "private/textures.h"
#include "textures/modes.h"
#include "util.h"
#include <SDL2/SDL.h>
@ -45,6 +46,9 @@ int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key k
/* binds atlas texture in opengl state */
void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target);
/* returns helpful information about contents of alpha channel in given texture */
enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key);
/* returns the number of atlases in the cache */
size_t textures_get_num_atlases(const struct texture_cache *cache);

11
src/textures/modes.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef TEXTURES_MODES_H
#define TEXTURES_MODES_H
/* alpha channel information */
enum texture_mode {
TEXTURE_MODE_OPAQUE, /* all pixels are solid */
TEXTURE_MODE_SEETHROUGH, /* some pixels are alpha zero */
TEXTURE_MODE_GHOSTLY, /* arbitrary alpha values */
};
#endif