diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asset/model.c | 111 | ||||
-rw-r--r-- | src/asset/texture.c | 32 | ||||
-rw-r--r-- | src/core/texture.c | 10 | ||||
-rw-r--r-- | src/scene/scene_graph.h | 42 | ||||
-rw-r--r-- | src/scene/scene_memory.c | 59 |
5 files changed, 161 insertions, 93 deletions
diff --git a/src/asset/model.c b/src/asset/model.c index 25f2780..402b2e1 100644 --- a/src/asset/model.c +++ b/src/asset/model.c | |||
@@ -578,11 +578,10 @@ static bool load_buffers( | |||
578 | const cgltf_buffer* buffer = &data->buffers[i]; | 578 | const cgltf_buffer* buffer = &data->buffers[i]; |
579 | assert(buffer->data); | 579 | assert(buffer->data); |
580 | buffers[i] = gfx_make_buffer( | 580 | buffers[i] = gfx_make_buffer( |
581 | gfxcore, &(BufferDesc){ | 581 | gfxcore, &(BufferDesc){.usage = BufferStatic, |
582 | .usage = BufferStatic, | 582 | .type = BufferUntyped, |
583 | .type = BufferUntyped, | 583 | .data.data = buffer->data, |
584 | .data.data = buffer->data, | 584 | .data.count = buffer->size}); |
585 | .data.count = buffer->size}); | ||
586 | if (!buffers[i]) { | 585 | if (!buffers[i]) { |
587 | return false; | 586 | return false; |
588 | } | 587 | } |
@@ -604,11 +603,10 @@ static bool load_tangent_buffers( | |||
604 | const cgltfTangentBuffer* buffer = &cgltf_tangent_buffers[i]; | 603 | const cgltfTangentBuffer* buffer = &cgltf_tangent_buffers[i]; |
605 | assert(buffer->data); | 604 | assert(buffer->data); |
606 | tangent_buffers[i] = gfx_make_buffer( | 605 | tangent_buffers[i] = gfx_make_buffer( |
607 | gfxcore, &(BufferDesc){ | 606 | gfxcore, &(BufferDesc){.usage = BufferStatic, |
608 | .usage = BufferStatic, | 607 | .type = BufferUntyped, |
609 | .type = BufferUntyped, | 608 | .data.data = buffer->data, |
610 | .data.data = buffer->data, | 609 | .data.count = buffer->size_bytes}); |
611 | .data.count = buffer->size_bytes}); | ||
612 | if (!tangent_buffers[i]) { | 610 | if (!tangent_buffers[i]) { |
613 | return false; | 611 | return false; |
614 | } | 612 | } |
@@ -682,14 +680,13 @@ static void load_textures_lazy( | |||
682 | mstring fullpath = | 680 | mstring fullpath = |
683 | mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); | 681 | mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); |
684 | 682 | ||
685 | load_texture_cmds[i] = (LoadTextureCmd){ | 683 | load_texture_cmds[i] = (LoadTextureCmd){.origin = AssetFromFile, |
686 | .origin = AssetFromFile, | 684 | .type = LoadTexture, |
687 | .type = LoadTexture, | 685 | .colour_space = LinearColourSpace, |
688 | .colour_space = sRGB, | 686 | .filtering = filtering, |
689 | .filtering = filtering, | 687 | .wrap = wrap, |
690 | .wrap = wrap, | 688 | .mipmaps = mipmaps, |
691 | .mipmaps = mipmaps, | 689 | .data.texture.filepath = fullpath}; |
692 | .data.texture.filepath = fullpath}; | ||
693 | } | 690 | } |
694 | } | 691 | } |
695 | 692 | ||
@@ -717,8 +714,12 @@ static bool load_texture_and_uniform( | |||
717 | // not be used as albedo and vice versa. | 714 | // not be used as albedo and vice versa. |
718 | if (!textures[texture_index]) { | 715 | if (!textures[texture_index]) { |
719 | LoadTextureCmd* cmd = &load_texture_cmds[texture_index]; | 716 | LoadTextureCmd* cmd = &load_texture_cmds[texture_index]; |
720 | // TODO: Check for colour textures and default to LinearColourSpace instead. | 717 | // Albedo and emissive use sRGB. Other textures use a linear colour space. |
721 | if (texture_type == NormalMap) { | 718 | // See the notes on the spec. |
719 | if ((texture_type == BaseColorTexture) || | ||
720 | (texture_type == EmissiveTexture)) { | ||
721 | cmd->colour_space = sRGB; | ||
722 | } else { | ||
722 | cmd->colour_space = LinearColourSpace; | 723 | cmd->colour_space = LinearColourSpace; |
723 | } | 724 | } |
724 | 725 | ||
@@ -778,22 +779,22 @@ static bool load_materials( | |||
778 | .value.vec4 = vec4_from_array(pbr->base_color_factor)}; | 779 | .value.vec4 = vec4_from_array(pbr->base_color_factor)}; |
779 | 780 | ||
780 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 781 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
781 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 782 | desc.uniforms[next_uniform++] = |
782 | .name = sstring_make(UNIFORM_METALLIC_FACTOR), | 783 | (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), |
783 | .type = UniformFloat, | 784 | .type = UniformFloat, |
784 | .value.scalar = pbr->metallic_factor}; | 785 | .value.scalar = pbr->metallic_factor}; |
785 | 786 | ||
786 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 787 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
787 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 788 | desc.uniforms[next_uniform++] = |
788 | .name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), | 789 | (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), |
789 | .type = UniformFloat, | 790 | .type = UniformFloat, |
790 | .value.scalar = pbr->roughness_factor}; | 791 | .value.scalar = pbr->roughness_factor}; |
791 | 792 | ||
792 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); | 793 | assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); |
793 | desc.uniforms[next_uniform++] = (ShaderUniform){ | 794 | desc.uniforms[next_uniform++] = |
794 | .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), | 795 | (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), |
795 | .type = UniformVec3, | 796 | .type = UniformVec3, |
796 | .value.vec3 = vec3_from_array(mat->emissive_factor)}; | 797 | .value.vec3 = vec3_from_array(mat->emissive_factor)}; |
797 | 798 | ||
798 | if (pbr->base_color_texture.texture) { | 799 | if (pbr->base_color_texture.texture) { |
799 | if (!load_texture_and_uniform( | 800 | if (!load_texture_and_uniform( |
@@ -854,28 +855,28 @@ static Material* make_default_material() { | |||
854 | MaterialDesc desc = (MaterialDesc){0}; | 855 | MaterialDesc desc = (MaterialDesc){0}; |
855 | 856 | ||
856 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 857 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
857 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 858 | desc.uniforms[desc.num_uniforms++] = |
858 | .name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), | 859 | (ShaderUniform){.name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), |
859 | .type = UniformVec4, | 860 | .type = UniformVec4, |
860 | .value.vec4 = vec4_make(1, 1, 1, 1)}; | 861 | .value.vec4 = vec4_make(1, 1, 1, 1)}; |
861 | 862 | ||
862 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 863 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
863 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 864 | desc.uniforms[desc.num_uniforms++] = |
864 | .name = sstring_make(UNIFORM_METALLIC_FACTOR), | 865 | (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), |
865 | .type = UniformFloat, | 866 | .type = UniformFloat, |
866 | .value.scalar = 0}; | 867 | .value.scalar = 0}; |
867 | 868 | ||
868 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 869 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
869 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 870 | desc.uniforms[desc.num_uniforms++] = |
870 | .name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), | 871 | (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), |
871 | .type = UniformFloat, | 872 | .type = UniformFloat, |
872 | .value.scalar = 1}; | 873 | .value.scalar = 1}; |
873 | 874 | ||
874 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); | 875 | assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); |
875 | desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ | 876 | desc.uniforms[desc.num_uniforms++] = |
876 | .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), | 877 | (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), |
877 | .type = UniformVec3, | 878 | .type = UniformVec3, |
878 | .value.vec3 = vec3_make(0, 0, 0)}; | 879 | .value.vec3 = vec3_make(0, 0, 0)}; |
879 | 880 | ||
880 | return gfx_make_material(&desc); | 881 | return gfx_make_material(&desc); |
881 | } | 882 | } |
@@ -1192,10 +1193,10 @@ static bool load_meshes( | |||
1192 | shader ? shader : make_shader_permutation(gfxcore, perm); | 1193 | shader ? shader : make_shader_permutation(gfxcore, perm); |
1193 | assert(mesh_shader); | 1194 | assert(mesh_shader); |
1194 | 1195 | ||
1195 | meshes[next_mesh] = gfx_make_mesh(&(MeshDesc){ | 1196 | meshes[next_mesh] = |
1196 | .geometry = geometries[next_mesh], | 1197 | gfx_make_mesh(&(MeshDesc){.geometry = geometries[next_mesh], |
1197 | .material = material, | 1198 | .material = material, |
1198 | .shader = mesh_shader}); | 1199 | .shader = mesh_shader}); |
1199 | 1200 | ||
1200 | if (!meshes[next_mesh]) { | 1201 | if (!meshes[next_mesh]) { |
1201 | return false; | 1202 | return false; |
@@ -1432,9 +1433,9 @@ static void load_animations( | |||
1432 | const cgltf_animation* animation = &data->animations[a]; | 1433 | const cgltf_animation* animation = &data->animations[a]; |
1433 | AnimationDesc* animation_desc = &anima_desc->animations[a]; | 1434 | AnimationDesc* animation_desc = &anima_desc->animations[a]; |
1434 | 1435 | ||
1435 | *animation_desc = (AnimationDesc){ | 1436 | *animation_desc = |
1436 | .name = sstring_make(animation->name), | 1437 | (AnimationDesc){.name = sstring_make(animation->name), |
1437 | .num_channels = animation->channels_count}; | 1438 | .num_channels = animation->channels_count}; |
1438 | 1439 | ||
1439 | assert(animation->channels_count <= GFX_MAX_NUM_CHANNELS); | 1440 | assert(animation->channels_count <= GFX_MAX_NUM_CHANNELS); |
1440 | for (cgltf_size c = 0; c < animation->channels_count; ++c) { | 1441 | for (cgltf_size c = 0; c < animation->channels_count; ++c) { |
diff --git a/src/asset/texture.c b/src/asset/texture.c index c790394..fb423cc 100644 --- a/src/asset/texture.c +++ b/src/asset/texture.c | |||
@@ -49,7 +49,7 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
49 | assert(cmd->origin == AssetFromFile || cmd->origin == AssetFromMemory); | 49 | assert(cmd->origin == AssetFromFile || cmd->origin == AssetFromMemory); |
50 | assert(cmd->type == LoadTexture || cmd->type == LoadCubemap); | 50 | assert(cmd->type == LoadTexture || cmd->type == LoadCubemap); |
51 | 51 | ||
52 | int width, height, components, old_components; | 52 | int width, height, components; |
53 | unsigned char* pixels[6] = {0}; | 53 | unsigned char* pixels[6] = {0}; |
54 | 54 | ||
55 | switch (cmd->origin) { | 55 | switch (cmd->origin) { |
@@ -64,7 +64,8 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
64 | } | 64 | } |
65 | break; | 65 | break; |
66 | } | 66 | } |
67 | case LoadCubemap: | 67 | case LoadCubemap: { |
68 | int old_components = 0; | ||
68 | for (int i = 0; i < 6; ++i) { | 69 | for (int i = 0; i < 6; ++i) { |
69 | // Flip +Y and -Y textures vertically. | 70 | // Flip +Y and -Y textures vertically. |
70 | stbi_set_flip_vertically_on_load(((i == 2) || (i == 3)) ? 1 : 0); | 71 | stbi_set_flip_vertically_on_load(((i == 2) || (i == 3)) ? 1 : 0); |
@@ -76,9 +77,10 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
76 | log_error("Failed to load texture file: %s", filepath); | 77 | log_error("Failed to load texture file: %s", filepath); |
77 | break; | 78 | break; |
78 | } | 79 | } |
79 | if (i > 0 && components != old_components) { | 80 | if ((i > 0) && (components != old_components)) { |
80 | log_error("All textures in a cubemap must have the same number of " | 81 | log_error( |
81 | "components"); | 82 | "All textures in a cubemap must have the same number of " |
83 | "components"); | ||
82 | break; | 84 | break; |
83 | } | 85 | } |
84 | if ((i != 2) && (i != 3)) { | 86 | if ((i != 2) && (i != 3)) { |
@@ -89,6 +91,7 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
89 | } | 91 | } |
90 | break; | 92 | break; |
91 | } | 93 | } |
94 | } | ||
92 | break; | 95 | break; |
93 | case AssetFromMemory: | 96 | case AssetFromMemory: |
94 | // TODO: Load textures from memory. | 97 | // TODO: Load textures from memory. |
@@ -122,6 +125,25 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { | |||
122 | } | 125 | } |
123 | 126 | ||
124 | switch (components) { | 127 | switch (components) { |
128 | case 1: | ||
129 | switch (cmd->colour_space) { | ||
130 | case LinearColourSpace: | ||
131 | desc.format = TextureR8; | ||
132 | break; | ||
133 | case sRGB: | ||
134 | // TODO: Gamma single-channel textures are not implemented yet. | ||
135 | // The caller should convert the single-channel to RGB and pass it down | ||
136 | // as sRGB. This is why the ChronographWatch currently appears red on the | ||
137 | // back. | ||
138 | log_error("Gamma colour space is not supported for 1-channel textures"); | ||
139 | // return 0; | ||
140 | desc.format = TextureR8; | ||
141 | break; | ||
142 | default: | ||
143 | log_error("Unsupported texture colour space: %d", cmd->colour_space); | ||
144 | return 0; | ||
145 | } | ||
146 | break; | ||
125 | case 3: | 147 | case 3: |
126 | switch (cmd->colour_space) { | 148 | switch (cmd->colour_space) { |
127 | case LinearColourSpace: | 149 | case LinearColourSpace: |
diff --git a/src/core/texture.c b/src/core/texture.c index 89f7ec0..372f9e6 100644 --- a/src/core/texture.c +++ b/src/core/texture.c | |||
@@ -37,6 +37,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | |||
37 | gfx_del_texture(texture); | 37 | gfx_del_texture(texture); |
38 | return false; | 38 | return false; |
39 | } | 39 | } |
40 | ASSERT_GL; | ||
40 | 41 | ||
41 | texture->format = to_GL_format(desc->format); | 42 | texture->format = to_GL_format(desc->format); |
42 | texture->type = to_GL_type(desc->format); | 43 | texture->type = to_GL_type(desc->format); |
@@ -50,6 +51,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | |||
50 | // Mipmaps. | 51 | // Mipmaps. |
51 | if (desc->mipmaps) { | 52 | if (desc->mipmaps) { |
52 | glGenerateMipmap(texture->target); | 53 | glGenerateMipmap(texture->target); |
54 | ASSERT_GL; | ||
53 | } | 55 | } |
54 | 56 | ||
55 | // Texture filtering. | 57 | // Texture filtering. |
@@ -60,6 +62,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | |||
60 | GLenum mag = linear ? GL_LINEAR : GL_NEAREST; | 62 | GLenum mag = linear ? GL_LINEAR : GL_NEAREST; |
61 | glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); | 63 | glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); |
62 | glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); | 64 | glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); |
65 | ASSERT_GL; | ||
63 | 66 | ||
64 | // Texture wrapping. | 67 | // Texture wrapping. |
65 | GLenum wrap = GL_INVALID_ENUM; | 68 | GLenum wrap = GL_INVALID_ENUM; |
@@ -74,6 +77,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { | |||
74 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); | 77 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); |
75 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); | 78 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); |
76 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); | 79 | glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); |
80 | ASSERT_GL; | ||
77 | 81 | ||
78 | glBindTexture(texture->target, 0); | 82 | glBindTexture(texture->target, 0); |
79 | return true; | 83 | return true; |
@@ -119,6 +123,7 @@ void gfx_update_texture(Texture* texture, const TextureDataDesc* desc) { | |||
119 | FAIL("Unhandled texture dimension"); | 123 | FAIL("Unhandled texture dimension"); |
120 | break; | 124 | break; |
121 | } | 125 | } |
126 | ASSERT_GL; | ||
122 | 127 | ||
123 | glBindTexture(texture->target, 0); | 128 | glBindTexture(texture->target, 0); |
124 | } | 129 | } |
@@ -139,6 +144,8 @@ GLenum to_GL_internal_format(TextureFormat format) { | |||
139 | switch (format) { | 144 | switch (format) { |
140 | case TextureDepth: | 145 | case TextureDepth: |
141 | return GL_DEPTH_COMPONENT; | 146 | return GL_DEPTH_COMPONENT; |
147 | case TextureR8: | ||
148 | return GL_R8; | ||
142 | case TextureRG16: | 149 | case TextureRG16: |
143 | return GL_RG16; | 150 | return GL_RG16; |
144 | case TextureRG16F: | 151 | case TextureRG16F: |
@@ -163,6 +170,8 @@ GLenum to_GL_format(TextureFormat format) { | |||
163 | switch (format) { | 170 | switch (format) { |
164 | case TextureDepth: | 171 | case TextureDepth: |
165 | return GL_DEPTH_COMPONENT; | 172 | return GL_DEPTH_COMPONENT; |
173 | case TextureR8: | ||
174 | return GL_RED; | ||
166 | case TextureRG16: | 175 | case TextureRG16: |
167 | case TextureRG16F: | 176 | case TextureRG16F: |
168 | return GL_RG; | 177 | return GL_RG; |
@@ -185,6 +194,7 @@ GLenum to_GL_type(TextureFormat format) { | |||
185 | case TextureRG16F: | 194 | case TextureRG16F: |
186 | case TextureR11G11B10F: | 195 | case TextureR11G11B10F: |
187 | return GL_FLOAT; | 196 | return GL_FLOAT; |
197 | case TextureR8: | ||
188 | case TextureRG16: | 198 | case TextureRG16: |
189 | case TextureRGB8: | 199 | case TextureRGB8: |
190 | case TextureRGBA8: | 200 | case TextureRGBA8: |
diff --git a/src/scene/scene_graph.h b/src/scene/scene_graph.h index a26f828..0b1f7d0 100644 --- a/src/scene/scene_graph.h +++ b/src/scene/scene_graph.h | |||
@@ -6,35 +6,41 @@ | |||
6 | // NOTE: SceneMemory guarantees that index 0 can be regarded as an invalid | 6 | // NOTE: SceneMemory guarantees that index 0 can be regarded as an invalid |
7 | // index. | 7 | // index. |
8 | 8 | ||
9 | #define MEM_GET(INDEX) \ | 9 | #define MEM_GET(INDEX) \ |
10 | _Generic((INDEX), camera_idx \ | 10 | _Generic( \ |
11 | : mem_get_camera, material_idx \ | 11 | (INDEX), \ |
12 | : mem_get_material, mesh_idx \ | 12 | camera_idx: mem_get_camera, \ |
13 | : mem_get_mesh, mesh_link_idx \ | 13 | material_idx: mem_get_material, \ |
14 | : mem_get_mesh_link, node_idx \ | 14 | mesh_idx: mem_get_mesh, \ |
15 | : mem_get_node, object_idx \ | 15 | mesh_link_idx: mem_get_mesh_link, \ |
16 | : mem_get_object, scene_idx \ | 16 | node_idx: mem_get_node, \ |
17 | : mem_get_scene)(INDEX) | 17 | object_idx: mem_get_object, \ |
18 | scene_idx: mem_get_scene)(INDEX) | ||
18 | 19 | ||
19 | #define MEM_GET_INDEX(ITEM) \ | 20 | #define MEM_GET_INDEX(ITEM) \ |
20 | _Generic((ITEM), SceneCamera * \ | 21 | _Generic( \ |
21 | : mem_get_camera_index, Material * \ | 22 | (ITEM), \ |
22 | : mem_get_material_index, Mesh * \ | 23 | SceneCamera *: mem_get_camera_index, \ |
23 | : mem_get_mesh_index, MeshLink * \ | 24 | Material *: mem_get_material_index, \ |
24 | : mem_get_mesh_link_index, SceneNode * \ | 25 | Mesh *: mem_get_mesh_index, \ |
25 | : mem_get_node_index, SceneObject * \ | 26 | MeshLink *: mem_get_mesh_link_index, \ |
26 | : mem_get_object_index, Scene * \ | 27 | SceneNode *: mem_get_node_index, \ |
27 | : mem_get_scene_index)(ITEM) | 28 | SceneObject *: mem_get_object_index, \ |
29 | Scene *: mem_get_scene_index)(ITEM) | ||
28 | 30 | ||
29 | /// Assert the list node invariant. | 31 | /// Assert the list node invariant. |
30 | /// | 32 | /// |
31 | /// - A node does not point to itself. | 33 | /// - A node does not point to itself. |
34 | #if NDEBUG | ||
35 | #define ASSERT_LIST_NODE_INVARIANT(ITEM) | ||
36 | #else | ||
32 | #define ASSERT_LIST_NODE_INVARIANT(ITEM) \ | 37 | #define ASSERT_LIST_NODE_INVARIANT(ITEM) \ |
33 | { \ | 38 | { \ |
34 | const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \ | 39 | const gfx_idx item_idx = MEM_GET_INDEX(ITEM).val; \ |
35 | assert((ITEM)->prev.val != item_idx); \ | 40 | assert((ITEM)->prev.val != item_idx); \ |
36 | assert((ITEM)->next.val != item_idx); \ | 41 | assert((ITEM)->next.val != item_idx); \ |
37 | } | 42 | } |
43 | #endif | ||
38 | 44 | ||
39 | /// Assert the tree node invariant. | 45 | /// Assert the tree node invariant. |
40 | /// | 46 | /// |
diff --git a/src/scene/scene_memory.c b/src/scene/scene_memory.c index 85c27e7..d1d81a9 100644 --- a/src/scene/scene_memory.c +++ b/src/scene/scene_memory.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "object_impl.h" | 12 | #include "object_impl.h" |
13 | #include "scene_impl.h" | 13 | #include "scene_impl.h" |
14 | 14 | ||
15 | #include <log/log.h> | ||
15 | #include <mempool.h> | 16 | #include <mempool.h> |
16 | 17 | ||
17 | DEF_MEMPOOL(anima_pool, Anima, GFX_MAX_NUM_ANIMAS) | 18 | DEF_MEMPOOL(anima_pool, Anima, GFX_MAX_NUM_ANIMAS) |
@@ -47,10 +48,11 @@ typedef struct SceneMemory { | |||
47 | 48 | ||
48 | static SceneMemory mem; | 49 | static SceneMemory mem; |
49 | 50 | ||
50 | #define ALLOC_DUMMY(POOL) \ | 51 | #define ALLOC_DUMMY(POOL) \ |
51 | { \ | 52 | { \ |
52 | const void* object = mempool_alloc(POOL); \ | 53 | const void* object = mempool_alloc(POOL); \ |
53 | assert(mempool_get_block_index(POOL, object) == 0); \ | 54 | (void)object; /* Silence warning in release builds. */ \ |
55 | assert(mempool_get_block_index(POOL, object) == 0); \ | ||
54 | } | 56 | } |
55 | 57 | ||
56 | #define PLURAL(name) name##s | 58 | #define PLURAL(name) name##s |
@@ -90,13 +92,40 @@ void scene_mem_destroy() { | |||
90 | // NOTE: the dummy objects are not constructed, so the destruction code below | 92 | // NOTE: the dummy objects are not constructed, so the destruction code below |
91 | // always skips index 0. (I don't really like the conditional inside the loop, | 93 | // always skips index 0. (I don't really like the conditional inside the loop, |
92 | // but this gets the job done without having to specialize the loop macro.) | 94 | // but this gets the job done without having to specialize the loop macro.) |
93 | #define DESTROY(name) \ | 95 | #define DESTROY(NAME) \ |
94 | mempool_foreach(&MEM_FIELD(name), obj, { \ | 96 | mempool_foreach(&MEM_FIELD(NAME), obj, { \ |
95 | if (i > 0) { \ | 97 | if (i > 0) { \ |
96 | gfx_destroy_##name(&obj); \ | 98 | gfx_destroy_##NAME(&obj); \ |
97 | } \ | 99 | } \ |
98 | }) | 100 | }) |
99 | 101 | ||
102 | // Print memory diagnostics. | ||
103 | #define PRINT_POOL(POOL_NAME, POOL) \ | ||
104 | { \ | ||
105 | const size_t capacity = mempool_capacity(POOL); \ | ||
106 | const size_t size = mempool_size(POOL); \ | ||
107 | const size_t block_size_bytes = mempool_block_size_bytes(POOL); \ | ||
108 | const size_t size_bytes = size * block_size_bytes; \ | ||
109 | const size_t capacity_bytes = capacity * block_size_bytes; \ | ||
110 | LOGI( \ | ||
111 | "%s pool: %lu/%lu (%lu/%lu bytes)", POOL_NAME, size, capacity, \ | ||
112 | size_bytes, capacity_bytes); \ | ||
113 | } | ||
114 | |||
115 | LOGI("Pool diagnostics:"); | ||
116 | PRINT_POOL("Animas", &mem.animas); | ||
117 | PRINT_POOL("Animations", &mem.animations); | ||
118 | PRINT_POOL("Cameras", &mem.cameras); | ||
119 | PRINT_POOL("Lights", &mem.lights); | ||
120 | PRINT_POOL("Materials", &mem.materials); | ||
121 | PRINT_POOL("Meshes", &mem.meshs); | ||
122 | PRINT_POOL("Mesh links", &mem.mesh_links); | ||
123 | PRINT_POOL("Models", &mem.models); | ||
124 | PRINT_POOL("Nodes", &mem.nodes); | ||
125 | PRINT_POOL("Objects", &mem.objects); | ||
126 | PRINT_POOL("Scenes", &mem.scenes); | ||
127 | PRINT_POOL("Skeletons", &mem.skeletons); | ||
128 | |||
100 | // Models contain scene elements. Destruction is handled by the remainder of | 129 | // Models contain scene elements. Destruction is handled by the remainder of |
101 | // scene destructionb elow. | 130 | // scene destructionb elow. |
102 | // | 131 | // |
@@ -119,20 +148,20 @@ void scene_mem_destroy() { | |||
119 | // Skeletons are owned by animas and do not have a destructor. | 148 | // Skeletons are owned by animas and do not have a destructor. |
120 | } | 149 | } |
121 | 150 | ||
122 | #define DEF_MEMORY(name, type) \ | 151 | #define DEF_MEMORY(NAME, TYPE) \ |
123 | /* xyz* mem_alloc_xyz(); */ \ | 152 | /* xyz* mem_alloc_xyz(); */ \ |
124 | type* mem_alloc_##name() { return mempool_alloc(&MEM_FIELD(name)); } \ | 153 | TYPE* mem_alloc_##NAME() { return mempool_alloc(&MEM_FIELD(NAME)); } \ |
125 | /* void mem_free_xyz(xyz**); */ \ | 154 | /* void mem_free_xyz(xyz**); */ \ |
126 | void mem_free_##name(type** obj) { mempool_free(&MEM_FIELD(name), obj); } \ | 155 | void mem_free_##NAME(TYPE** obj) { mempool_free(&MEM_FIELD(NAME), obj); } \ |
127 | /* xyz* mem_get_xyz(xyz_idx); */ \ | 156 | /* xyz* mem_get_xyz(xyz_idx); */ \ |
128 | type* mem_get_##name(NAMED_INDEX(name) index) { \ | 157 | TYPE* mem_get_##NAME(NAMED_INDEX(NAME) index) { \ |
129 | assert(index.val != 0); /* 0 is the dummy allocation. */ \ | 158 | assert(index.val != 0); /* 0 is the dummy allocation. */ \ |
130 | return mempool_get_block(&MEM_FIELD(name), index.val); \ | 159 | return mempool_get_block(&MEM_FIELD(NAME), index.val); \ |
131 | } \ | 160 | } \ |
132 | /* xyz_idx mem_get_xyz_index(const xyz*); */ \ | 161 | /* xyz_idx mem_get_xyz_index(const xyz*); */ \ |
133 | NAMED_INDEX(name) mem_get_##name##_index(const type* obj) { \ | 162 | NAMED_INDEX(NAME) mem_get_##NAME##_index(const TYPE* obj) { \ |
134 | return (NAMED_INDEX(name)){ \ | 163 | return (NAMED_INDEX(NAME)){ \ |
135 | .val = mempool_get_block_index(&MEM_FIELD(name), obj)}; \ | 164 | .val = mempool_get_block_index(&MEM_FIELD(NAME), obj)}; \ |
136 | } | 165 | } |
137 | 166 | ||
138 | DEF_MEMORY(anima, Anima) | 167 | DEF_MEMORY(anima, Anima) |