Super optimizing, gprof is no longer my friend

This commit is contained in:
2025-06-06 13:51:44 +02:00
parent 6399b355bc
commit c05ab2d3ae
18 changed files with 166 additions and 104 deletions
+3 -3
View File
@@ -5,8 +5,8 @@ SRC=src/main.c src/gl/program.c src/resources.c src/camera.c src/chunk/chunk.c \
src/ui/uiman.c
OBJ=$(SRC:.c=.o)
CFLAGS=-g -pg -Werror -Wextra -Wall `pkg-config --cflags sdl3 sdl3-image cglm gl glib-2.0`
LDFLAGS=-g -pg -lm `pkg-config --libs sdl3 sdl3-image cglm gl glib-2.0`
CFLAGS=-g -Werror -Wextra -Wall `pkg-config --cflags sdl3 sdl3-image cglm gl glib-2.0`
LDFLAGS=-g -lm `pkg-config --libs sdl3 sdl3-image cglm gl glib-2.0`
TARGET=wschod
CC=gcc
LD=gcc
@@ -22,4 +22,4 @@ $(TARGET): $(OBJ)
$(CC) -c $^ -o $@ $(CFLAGS)
clean:
$(RM) gmon.out $(TARGET) $(OBJ)
$(RM) perf.data* $(TARGET) $(OBJ)
+1 -1
View File
@@ -1,2 +1,2 @@
#!/usr/bin/env bash
cpulimit -l 20 -- ./wschod
cpulimit -l $1 -- ./wschod
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.
+2
View File
@@ -16,5 +16,7 @@ pkgs.mkShellNoCC {
pkg-config
valgrind
gdb
cpulimit
linuxKernel.packages.linux_6_12.perf
];
}
+72 -38
View File
@@ -1,8 +1,5 @@
#include "chunkman.h"
/* TODO: perhaps render the chunks in a circle, not a square, around
the player? */
int n_frame_chunks;
static GHashTable *chunks;
@@ -24,17 +21,14 @@ chunkman_ivec2_hash (gconstpointer key)
gboolean
chunkman_ivec2_equal (gconstpointer a, gconstpointer b)
{
/* Much faster than comparing the integer values. */
return memcmp (a, b, sizeof (int) * 2) == 0;
}
ivec2 *
chunkman_make_key(int x, int z)
{
ivec2 *key = malloc(sizeof(ivec2));
(*key)[0] = x;
(*key)[1] = z;
return key;
ivec2 key = { x, z };
return g_memdup2 (&key, sizeof (ivec2));
}
void
@@ -107,43 +101,63 @@ chunkman_init ()
chunk_meshes = g_hash_table_new_full (chunkman_ivec2_hash, chunkman_ivec2_equal, free, (GDestroyNotify) Chunk_free);
}
/* TODO: Update the chunks only if the player has moved into another
chunk, to save frame time. */
void
chunkman_update (vec3 position)
chunkman_tick (vec3 position)
{
int cx, cz;
chunkman_world_to_chunk (position[0], position[2], &cx, &cz);
unsigned int frame_time = SDL_GetTicks ();
for (int z = cz - RENDER_DISTANCE - 1; z <= cz + RENDER_DISTANCE + 1; ++z)
{
for (int x = cx - RENDER_DISTANCE - 1; x <= cx + RENDER_DISTANCE + 1; ++x)
{
if (chunkman_get_Chunk_at (x, z) == NULL)
{
chunkman_generate_Chunk_at (x, z);
}
}
{
if ((x - cx) * (x - cx) + (z - cz) * (z - cz) <= (RENDER_DISTANCE + 1) * (RENDER_DISTANCE + 1))
{
if (chunkman_get_Chunk_at (x, z) == NULL)
{
chunkman_generate_Chunk_at (x, z);
}
if (SDL_GetTicks () - frame_time > FRAME_BUDGET)
{
return;
}
}
}
}
for (int z = cz - RENDER_DISTANCE; z <= cz + RENDER_DISTANCE; ++z)
{
for (int x = cx - RENDER_DISTANCE; x <= cx + RENDER_DISTANCE; ++x)
{
if (chunkman_get_ChunkMesh_at (x, z) == NULL)
{
chunkman_generate_ChunkMesh_at (x, z);
}
}
{
if ((x - cx) * (x - cx) + (z - cz) * (z - cz) <= RENDER_SQR)
{
if (chunkman_get_ChunkMesh_at (x, z) == NULL)
{
chunkman_generate_ChunkMesh_at (x, z);
}
if (SDL_GetTicks () - frame_time > FRAME_BUDGET)
{
return;
}
}
}
}
}
void
chunkman_render (vec3 position)
chunkman_render (vec3 position, vec4 planes[6])
{
#if !FRUSTUM_CULL
UNUSED (planes);
#endif
int cx = 0, cz = 0;
chunkman_world_to_chunk (position[0], position[2], &cx, &cz);
program_use (RESPRG ("chunk"));
glActiveTexture (GL_TEXTURE0);
@@ -163,18 +177,35 @@ chunkman_render (vec3 position)
{
for (int x = cx - RENDER_DISTANCE; x <= cx + RENDER_DISTANCE; ++x)
{
ChunkMesh *mesh = chunkman_get_ChunkMesh_at (x, z);
if (mesh != NULL)
if ((x - cx) * (x - cx) + (z - cz) * (z - cz) <= RENDER_SQR)
{
if (!bound)
ChunkMesh *mesh = chunkman_get_ChunkMesh_at (x, z);
if (mesh != NULL)
{
ChunkMesh_bind (mesh);
bound = true;
}
/* This, compared to the entity AABB, seems to work
completely fine. */
#if FRUSTUM_CULL
vec3 aabb[2] = {
{ x * CHUNK_DIM, 0.0, z * CHUNK_DIM },
{ (x + 1) * CHUNK_DIM, TERRAIN_HEIGHT, (z + 1) * CHUNK_DIM }
};
if (!glm_aabb_frustum (aabb, planes))
{
continue;
}
#endif
if (!bound)
{
ChunkMesh_bind (mesh);
bound = true;
}
program_set_vec3_ind (RESPRG ("chunk"), "chunk_position", x * CHUNK_DIM, 0.0, z * CHUNK_DIM);
ChunkMesh_render (mesh);
n_frame_chunks++;
program_set_vec3_ind (RESPRG ("chunk"), "chunk_position", x * CHUNK_DIM, 0.0, z * CHUNK_DIM);
ChunkMesh_render (mesh);
n_frame_chunks++;
}
}
}
}
@@ -185,7 +216,8 @@ chunkman_render (vec3 position)
Chunk *
chunkman_get_Chunk_at (int x, int z)
{
return (Chunk *) g_hash_table_lookup (chunks, (ivec2) { x, z });
ivec2 key = { x, z };
return (Chunk *) g_hash_table_lookup (chunks, &key);
}
void
@@ -205,12 +237,13 @@ chunkman_generate_Chunk_at (int cx, int cz)
{
for (int x = 0; x < CHUNK_DIM; ++x)
{
chunk->heightmap[INDEX (x, z)] =
(glm_perlin_vec2
((vec2) { (x + xoffset + 0.5) / 10.0, (z + zoffset + 0.5) / 10.0 }) + 0.6) * 2;
((vec2) { (x + xoffset + 0.333) / 10.0, (z + zoffset + 0.333) / 10.0 }) + 0.6) * 2;
chunk->materialmap[INDEX (x, z)] = (int)
(glm_perlin_vec2 ((vec2) { (x + xoffset + 0.5) / 20.0, (z + zoffset + 0.5) / 20.0 }) + 0.6 * 5) % 5;
(glm_perlin_vec2 ((vec2) { (x + xoffset + 0.333) / 20.0, (z + zoffset + 0.333) / 20.0 }) + 0.6 * 5) % 5;
if (rand() % 100 < 2)
{
@@ -250,7 +283,8 @@ chunkman_get_Chunk_neighbors_at (int x, int z, Chunk **nr, Chunk **nd, Chunk **n
ChunkMesh *
chunkman_get_ChunkMesh_at (int x, int z)
{
return (ChunkMesh *) g_hash_table_lookup (chunk_meshes, (ivec2) { x, z });
ivec2 key = { x, z };
return (ChunkMesh *) g_hash_table_lookup (chunk_meshes, key);
}
void
+7 -3
View File
@@ -8,12 +8,16 @@
#include "../gl/program.h"
#include "../entity/entman.h"
#include "../entity/templates.h"
#include "../graphics.h"
#define RENDER_DISTANCE 3
#define RENDER_DISTANCE 5
#define RENDER_SQR (RENDER_DISTANCE*RENDER_DISTANCE)
#define TERRAIN_HEIGHT 8.0
#define FRAME_BUDGET 3
void chunkman_init ();
void chunkman_update (vec3 position);
void chunkman_render (vec3 position);
void chunkman_tick (vec3 position);
void chunkman_render (vec3 position, vec4 planes[6]);
float chunkman_get_height_at (float x, float z);
+3
View File
@@ -12,6 +12,9 @@ typedef struct Entity
enum { RENDER_NORMAL, RENDER_BILLBOARD_Y, RENDER_BILLBOARD } render_type;
bool visible;
bool dont_cull;
float cull_distance; /* 1 means as far as the terrain goes,
less than 1, for example 0.5, means
half as far as the terrain goes. */
bool wavy;
void *userdata;
+38 -35
View File
@@ -1,6 +1,8 @@
#include "entman.h"
/* TODO: Put uniforms into vertex attributes for pseudoinstancing. */
/* TODO: What COULD be done, but not necessarily SHOULD be done, is to
generate a mesh for say, 16 entities at once, then, the number of
drawcalls would drop substantially. */
/* TODO: Instead of having a huge far plane, render the clouds
seperately somehow. */
@@ -9,7 +11,9 @@ extern vec3 camera_position;
Entity *player_entity, *sky_entity;
int n_frame_ents;
static GSequence *entities;
/* The GArray is the fastest of all sequence types. Should work fine
for a billion entities. */
static GArray *entities;
static SimpleMesh mesh_quad1, mesh_quad2, mesh_quad_x;
static unsigned int last_program = -1;
static unsigned int last_type = -1;
@@ -24,7 +28,7 @@ character_on_tick (Entity *entity)
void
entman_init ()
{
entities = g_sequence_new (NULL);
entities = g_array_new (false, false, sizeof (Entity *));
mesh_quad1 = SimpleMesh_make_quad1 ();
mesh_quad2 = SimpleMesh_make_quad2 ();
mesh_quad_x = SimpleMesh_make_quad_x ();
@@ -57,28 +61,22 @@ entman_init ()
}
void
entman_tick_internal (gpointer data, gpointer userdata)
entman_tick ()
{
UNUSED (userdata);
Entity *entity = data;
if (entity->on_tick)
for (guint i = 0; i < entities->len; ++i)
{
entity->on_tick (entity);
Entity *e = g_array_index (entities, Entity*, i);
if (e->on_tick)
{
e->on_tick (e);
}
}
}
void
entman_tick ()
entman_render_entity (Entity *entity, void *userdata)
{
g_sequence_foreach (entities, entman_tick_internal, NULL);
}
void
entman_render_internal (gpointer data, gpointer userdata)
{
Entity *entity = data;
struct {
mat4 *view;
#ifdef FRUSTUM_CULL
@@ -91,8 +89,8 @@ entman_render_internal (gpointer data, gpointer userdata)
return;
}
if (glm_vec3_distance (camera_position, entity->position) > ENTITY_RENDER_DISTANCE
&& !entity->dont_cull)
if (glm_vec3_distance (camera_position, entity->position)
> entity->cull_distance * RENDER_DISTANCE * CHUNK_DIM && !entity->dont_cull)
{
return;
}
@@ -203,36 +201,41 @@ entman_render_internal (gpointer data, gpointer userdata)
}
void
entman_render (mat4 *view, mat4 *proj)
entman_render (mat4 *view, vec4 planes[6])
{
#if !FRUSTUM_CULL
UNUSED (planes);
#endif
last_program = -1;
last_texture = -1;
last_type = -1;
n_frame_ents = 0;
#ifdef FRUSTUM_CULL
vec4 planes[6];
mat4 view_proj;
glm_mat4_mul (*proj, *view, view_proj);
glm_frustum_planes (view_proj, planes);
#endif
struct {
mat4 *view;
#ifdef FRUSTUM_CULL
#if FRUSTUM_CULL
vec4 *frustum_planes;
#endif
} data = { view, planes };
g_sequence_foreach (entities, entman_render_internal, &data);
} data = {
view
#if FRUSTUM_CULL
,planes
#endif
};
for (guint i = 0; i < entities->len; ++i)
{
Entity *e = g_array_index (entities, Entity*, i);
entman_render_entity (e, &data);
}
}
static inline int
entman_compare (gconstpointer a, gconstpointer b, gpointer userdata)
entman_compare (gconstpointer a, gconstpointer b)
/* This function is used to sort Entities by their mesh type, so that
diferrent buffers aren't bound every single frame. */
{
UNUSED (userdata);
const Entity *entity1 = a;
const Entity *entity2 = b;
@@ -242,5 +245,5 @@ entman_compare (gconstpointer a, gconstpointer b, gpointer userdata)
void
entman_add_entity (Entity *entity)
{
g_sequence_insert_sorted (entities, entity, entman_compare, NULL);
g_array_append_val (entities, entity);
}
+1 -3
View File
@@ -9,9 +9,7 @@
#include "../resources.h"
#include "../state.h"
#include "../chunk/chunkman.h"
#define ENTITY_RENDER_DISTANCE (RENDER_DISTANCE*CHUNK_DIM)
#define FRUSTUM_CULL 1
#include "../graphics.h"
#define ENTADD(entity) (entman_add_entity (entity))
+2
View File
@@ -23,6 +23,7 @@ ent_templates_init ()
*tree = (Entity) {
.mesh_type = MESH_QUAD_X,
.visible = true,
.cull_distance = 0.7,
.wavy = true,
.origin = ORIGIN_BOTTOM,
.position = { 0.0 },
@@ -39,6 +40,7 @@ ent_templates_init ()
.mesh_type = MESH_QUAD1,
.render_type = RENDER_BILLBOARD_Y,
.visible = true,
.cull_distance = 0.2,
.wavy = true,
.origin = ORIGIN_BOTTOM,
.position = { 0.0 },
+9 -2
View File
@@ -69,8 +69,15 @@ graphics_render ()
resources_programs_set_mat4 ("view", &view);
resources_programs_set_float ("time", last_time);
chunkman_render (camera_position);
entman_render (view, projection);
vec4 planes[6];
#if FRUSTUM_CULL
mat4 view_proj;
glm_mat4_mul (projection, view, view_proj);
glm_frustum_planes (view_proj, planes);
#endif
chunkman_render (camera_position, planes);
entman_render (view, planes);
/* We don't care about blending elsewhere for now. */
glEnable (GL_BLEND);
+2
View File
@@ -10,6 +10,8 @@
#include "resources.h"
#include "util.h"
#define FRUSTUM_CULL 1
void graphics_init ();
void graphics_render ();
void graphics_free ();
+1 -1
View File
@@ -54,7 +54,7 @@ SDL_AppIterate (void *appstate)
game_state_tick (SDL_GetTicks ());
entman_tick ();
chunkman_update (camera_position);
chunkman_tick (camera_position);
graphics_render ();
return SDL_APP_CONTINUE;
+7 -11
View File
@@ -3,7 +3,7 @@
extern vec3 camera_position;
extern vec3 camera_rotation;
extern Entity *player_entity, *sky_entity;
extern UIElement *player_pos, *n_ents, *n_chunks;
extern UIElement *player_pos, *n_ents, *n_chunks, *frame_time;
extern int n_frame_ents, n_frame_chunks;
float delta_time;
@@ -33,16 +33,12 @@ game_state_tick (int ticks)
sky_entity->position[0] = camera_position[0];
sky_entity->position[2] = camera_position[2];
/* Put the player position into the UI. */
player_pos->text = malloc (sizeof (char) * 32);
snprintf (player_pos->text, 32, "position X:%2.1f Y:%2.1f Z:%2.1f", player_entity->position[0],
player_entity->position[1], player_entity->position[2]);
/* Put the amount of entities rendered into the UI. */
n_ents->text = malloc (sizeof (char) * 32);
snprintf (n_ents->text, 32, "entities rendered: %d", n_frame_ents);
/* Put the amount of chunks rendered into the UI. */
n_chunks->text = malloc (sizeof (char) * 32);
snprintf (n_chunks->text, 32, "chunks rendered: %d", n_frame_chunks);
g_string_printf (player_pos->text, "Position (X:%2.1f, Y:%2.1f, Z:%2.1f)",
player_entity->position[0], player_entity->position[1],
player_entity->position[2]);
g_string_printf (n_ents->text, "Entities rendered: %d", n_frame_ents);
g_string_printf (n_chunks->text, "Chunks rendered: %d", n_frame_chunks);
g_string_printf (frame_time->text, "Frame time: %.3f", delta_time);
if (flags_get ("moving_forward"))
{
+2 -4
View File
@@ -45,8 +45,6 @@ UIElement_render (UIElement *element)
glm_scale (model, element->scale);
program_set_mat4 (RESPRG ("uielement"), "model", model);
/* TODO: rename this function to vec3_individual or
something. */
program_set_vec3 (RESPRG ("uielement"), "color", element->background_color);
SimpleMesh_render (&quad);
@@ -56,7 +54,7 @@ UIElement_render (UIElement *element)
glBindTexture (GL_TEXTURE_2D, RESTEX ("font0"));
program_set_vec3 (RESPRG ("uielement"), "color", element->text_color);
for (size_t i = 0; i < strlen (element->text); ++i)
for (size_t i = 0; i < element->text->len; ++i)
{
vec3 pos;
float x = -element->scale[0] + (element->scale[1] / 2) + (element->scale[1] * i);
@@ -67,7 +65,7 @@ UIElement_render (UIElement *element)
glm_scale (model, (vec3) { element->scale[1] / 2, element->scale[1], 1.0 });
program_set_mat4 (RESPRG ("uielement"), "model", model);
program_set_float (RESPRG ("uielement"), "character", element->text[i]);
program_set_float (RESPRG ("uielement"), "character", element->text->str[i]);
SimpleMesh_render (&quad);
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ typedef struct UIElement
vec3 position;
vec3 scale;
char *text; /* Maybe switch to GString? */
GString *text;
} UIElement;
void ui_element_mesh_init ();
+15 -2
View File
@@ -1,11 +1,11 @@
#include "uiman.h"
UIElement *player_pos, *n_ents, *n_chunks;
UIElement *player_pos, *n_ents, *n_chunks, *frame_time;
static UIElement ui_root;
#define UINEWROOT() ((UIElement) { .parent = NULL, .children = g_sequence_new (NULL), .visible = false })
#define UINEW(pare) ((UIElement) { .parent = pare, .text_color = { 1.0, 1.0, 1.0 }, .visible = true })
#define UINEW(pare) ((UIElement) { .parent = pare, .text_color = { 1.0, 1.0, 1.0 }, .visible = true, .text = g_string_new (NULL) })
#define UIADD(pare, child) (UIElement_add_child (pare, child))
void
@@ -32,6 +32,19 @@ uiman_init ()
glm_vec3_copy ((vec3) { -0.65, 0.70, 0.0 }, n_chunks->position);
glm_vec3_copy ((vec3) { 0.3, 0.03, 1.0 }, n_chunks->scale);
UIADD (&ui_root, n_chunks);
frame_time = malloc (sizeof (UIElement));
*frame_time = UINEW (&ui_root);
glm_vec3_copy ((vec3) { -0.65, 0.59, 0.0 }, frame_time->position);
glm_vec3_copy ((vec3) { 0.3, 0.03, 1.0 }, frame_time->scale);
UIADD (&ui_root, frame_time);
}
void
uiman_tick ()
{
}
void