From 5b8ecd80207b59eabe831b0a07fed4dbd9685e3f Mon Sep 17 00:00:00 2001 From: Andreew Gregory Date: Thu, 12 Feb 2026 20:58:23 +0300 Subject: [PATCH] Little refactoring of alice: Added Abigail urility (transfer_in_mainloop.h). Buffers/textures that get copied to device local memory and then never modified occured many times in alice and lucy and was generally worth moving into a separate class. --- src/l2/alice/engine.h | 40 +++++++------- src/l2/alice/transfer_in_mainloop.h | 23 ++++++++ src/l2/lucy/glyph_cache.h | 83 +++++++++++------------------ src/l2/lucy/glyph_render.h | 4 +- 4 files changed, 76 insertions(+), 74 deletions(-) diff --git a/src/l2/alice/engine.h b/src/l2/alice/engine.h index 0432d5b..db3714d 100644 --- a/src/l2/alice/engine.h +++ b/src/l2/alice/engine.h @@ -829,29 +829,29 @@ ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, const Generic VecU8_drop(t_paths.normal_texture_path); VecU8_drop(t_paths.specular_texture_path); - mm->vbo = AliceBufferUplOnce_new(topology->vertices.len * sizeof(GenericMeshVertex), + mm->vbo = AliceAllSingleUploadBuffers_register_new(&alice->single_upload_buffers, + topology->vertices.len * sizeof(GenericMeshVertex), &alice->staging_buffers, &alice->dev_local_buffers); - mm->ebo = AliceBufferUplOnce_new(topology->indexes.len * sizeof(U32), + mm->ebo = AliceAllSingleUploadBuffers_register_new(&alice->single_upload_buffers, + topology->indexes.len * sizeof(U32), &alice->staging_buffers, &alice->dev_local_buffers); - mm->diffuse_texture = AliceTextureUplOnce_new(mm->pixels_diffuse.width, mm->pixels_diffuse.height, - VK_FORMAT_R8G8B8A8_SRGB, sizeof(cvec4), + mm->diffuse_texture = AliceAllSingleUploadTextures_register_new(&alice->single_upload_textures, + mm->pixels_diffuse.width, mm->pixels_diffuse.height, + sizeof(cvec4), VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, &alice->staging_buffers, &alice->dev_local_images); - mm->normal_texture = AliceTextureUplOnce_new(mm->pixels_normal.width, mm->pixels_normal.height, - VK_FORMAT_R8G8B8A8_SRGB, sizeof(cvec4), + mm->normal_texture = AliceAllSingleUploadTextures_register_new(&alice->single_upload_textures, + mm->pixels_normal.width, mm->pixels_normal.height, + sizeof(cvec4), VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, &alice->staging_buffers, &alice->dev_local_images); - mm->specular_texture = AliceTextureUplOnce_new(mm->pixels_specular.width, mm->pixels_specular.height, - VK_FORMAT_R8_SRGB, sizeof(U8), + mm->specular_texture = AliceAllSingleUploadTextures_register_new(&alice->single_upload_textures, + mm->pixels_specular.width, mm->pixels_specular.height, + sizeof(U8), VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, &alice->staging_buffers, &alice->dev_local_images); - AliceAllSingleUploadBuffers_register(&alice->single_upload_buffers, mm->vbo); - AliceAllSingleUploadBuffers_register(&alice->single_upload_buffers, mm->ebo); - AliceAllSingleUploadTextures_register(&alice->single_upload_textures, mm->diffuse_texture); - AliceAllSingleUploadTextures_register(&alice->single_upload_textures, mm->normal_texture); - AliceAllSingleUploadTextures_register(&alice->single_upload_textures, mm->specular_texture); mm->scheduled_for_deletion = false; /* We allocated enough memory, but now it's time to actually fill staging buffers */ @@ -979,15 +979,13 @@ ListNodeAliceShinyMeshHand* Alice_add_shiny_mesh(Alice* alice, const ShinyMeshTo mm->instance_attr.staging = MargaretBufAllocator_alloc(&alice->staging_buffers, 128); mm->instance_attr.device_local = MargaretBufAllocator_alloc(&alice->dev_local_buffers, 128); - mm->vbo = AliceBufferUplOnce_new(topology->vertices.len * sizeof(ShinyMeshVertex), + mm->vbo = AliceAllSingleUploadBuffers_register_new(&alice->single_upload_buffers, + topology->vertices.len * sizeof(ShinyMeshVertex), &alice->staging_buffers, &alice->dev_local_buffers); - mm->ebo = AliceBufferUplOnce_new(topology->indexes.len * sizeof(U32), + mm->ebo = AliceAllSingleUploadBuffers_register_new(&alice->single_upload_buffers, + topology->indexes.len * sizeof(U32), &alice->staging_buffers, &alice->dev_local_buffers); - ListAliceShinyMeshHand_insert_node(&alice->shiny_models.hands, mm_node); - AliceAllSingleUploadBuffers_register(&alice->single_upload_buffers, mm->vbo); - AliceAllSingleUploadBuffers_register(&alice->single_upload_buffers, mm->ebo); - /* We allocated enough memory, now it's time to actually fill staging buffers */ /* And we start by filling staging VBO */ assert(mm->vbo->staging.len >= topology->vertices.len * sizeof(ShinyMeshVertex)); @@ -1014,6 +1012,8 @@ ListNodeAliceShinyMeshHand* Alice_add_shiny_mesh(Alice* alice, const ShinyMeshTo assert(mm->ebo->staging.len >= ebo_len); U32* staging_ebo = (U32*)AliceBufferUplOnce_get_mapped_staging(mm->ebo); memcpy(staging_ebo, topology->indexes.buf, ebo_len); + + ListAliceShinyMeshHand_insert_node(&alice->shiny_models.hands, mm_node); return mm_node; } @@ -1921,7 +1921,7 @@ Alice* Alice_new(){ if (ft_init_err) abortf("Can't init free type library\n"); - alice->lucy_cache = LucyGlyphCache_new(engine_reference); + alice->lucy_cache = LucyGlyphCache_new(engine_reference, &alice->single_upload_textures); alice->lucy_renderer = LucyRenderer_new(engine_reference, &alice->lucy_cache, root_dir, alice->render_pass_1, 0); diff --git a/src/l2/alice/transfer_in_mainloop.h b/src/l2/alice/transfer_in_mainloop.h index fac1fc6..80fbc29 100644 --- a/src/l2/alice/transfer_in_mainloop.h +++ b/src/l2/alice/transfer_in_mainloop.h @@ -1,5 +1,10 @@ #pragma once +/* I sometimes call this sub-namespace of Alice namespace Abigail. Though I hadn't renamed it to Abigail yet. + * Abigail does not depend on Alice Engine. It only depends on margaret. So systems like Lucy can use it + * without creating any sus circular dependepncies. + */ + #include "../margaret/vulkan_utils.h" /* Handler of a read-only buffer that can be uploaded and deleted at any time with it's staging buffer automatically @@ -146,6 +151,14 @@ void AliceAllSingleUploadBuffers_register(AliceAllSingleUploadBuffers* self, Ali VecRefAliceBufferUplOnce_append(&self->to_be_copied_to_device_next_cycle, obj); } +AliceBufferUplOnce* AliceAllSingleUploadBuffers_register_new(AliceAllSingleUploadBuffers* self, size_t len, + MargaretBufAllocator* staging_buffers, MargaretBufAllocator* dev_local_buffers + ) { + AliceBufferUplOnce* res = AliceBufferUplOnce_new(len, staging_buffers, dev_local_buffers); + AliceAllSingleUploadBuffers_register(self, res); + return res; +} + void AliceAllSingleUploadBuffers_delete(AliceAllSingleUploadBuffers* self, AliceBufferUplOnce* obj) { assert(obj); obj->scheduled_for_deletion = true; @@ -179,6 +192,16 @@ void AliceAllSingleUploadTextures_register(AliceAllSingleUploadTextures* self, A VecRefAliceTextureUplOnce_append(&self->to_be_copied_to_device_next_cycle, obj); } +AliceTextureUplOnce* AliceAllSingleUploadTextures_register_new(AliceAllSingleUploadTextures* self, + U64 width, U64 height, U64 pixel_sz, VkFormat format, + VkImageUsageFlags usage, MargaretBufAllocator* staging_buffers, MargaretImgAllocator* dev_local_images + ) { + AliceTextureUplOnce* res = AliceTextureUplOnce_new(width, height, pixel_sz, format, usage, + staging_buffers, dev_local_images); + AliceAllSingleUploadTextures_register(self, res); + return res; +} + void AliceAllSingleUploadTextures_delete(AliceAllSingleUploadTextures* self, AliceTextureUplOnce* obj) { assert(obj); obj->scheduled_for_deletion = true; diff --git a/src/l2/lucy/glyph_cache.h b/src/l2/lucy/glyph_cache.h index fdb1570..9eb1683 100644 --- a/src/l2/lucy/glyph_cache.h +++ b/src/l2/lucy/glyph_cache.h @@ -1,28 +1,23 @@ #pragma once #include "../margaret/vulkan_utils.h" -#include -#include FT_FREETYPE_H #include "../../../gen/l1/VecAndSpan_U32Segment.h" #include "../../../gen/l1/vulkan/VecVkDescriptorImageInfo.h" #include "../../../gen/l1/pixel_masses.h" #include "../../../gen/l1/VecAndSpan_U32.h" - #include "../../l1_5/core/buff_rb_tree_node.h" #include "../../l1_5/core/rb_tree_node.h" +#include "../alice/transfer_in_mainloop.h" +#include +#include FT_FREETYPE_H #define LUCY_MAX_DESCRIPTOR_COUNT 100 typedef struct { - /* This value is actually Option. If staging_buffer is already deleted (after it is no longer used), - * staging_buffer.len will be 0 */ - MargaretSubbuf staging_buffer; - MargaretImg img; - VkImageView img_view; + AliceTextureUplOnce* tex; U64 usage; - /* 0 if this image isn't scheduled for deletion on th next cycle. - * 1 if it is */ - int scheduled_for_deletion; + /* Is this image scheduled for deletion on th next cycle. */ + bool scheduled_for_deletion; } LucyImage; #include "../../../gen/l1/eve/lucy/OptionLucyImage.h" @@ -69,19 +64,19 @@ struct LucyFace { struct LucyGlyphCache { MargaretEngineReference ve; + AliceAllSingleUploadTextures* single_upload_textures; + VecOptionLucyImage image_slots; VkDescriptorSetLayout descriptor_set_layout; VkDescriptorSet descriptor_set; - /* to_be_freed_of_old_staging_next_cycle never intersect with to_be_copied_to_device_next_cycle */ - VecU32 to_be_freed_of_old_staging_next_cycle; - VecU32 to_be_copied_to_device_next_cycle; - /* deletion will be performed last */ + /* We can delete images and link images to descriptor set only when frame isn't in flight */ + VecU32 to_be_written_to_descriptor_set; VecU32 to_be_deleted; }; -LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve){ +LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, AliceAllSingleUploadTextures* single_upload_textures){ VkDescriptorSetLayout my_desc_set_layout; VkDescriptorSetLayoutBindingFlagsCreateInfo set_layout_crinfo_flags = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, @@ -106,10 +101,9 @@ LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve){ image_slots.buf[i].variant = Option_None; } return (LucyGlyphCache){ - .ve = ve, .image_slots = image_slots, + .ve = ve, .single_upload_textures = single_upload_textures, .image_slots = image_slots, .descriptor_set_layout = my_desc_set_layout, .descriptor_set = descriptor_set, - .to_be_freed_of_old_staging_next_cycle = VecU32_new(), - .to_be_copied_to_device_next_cycle = VecU32_new(), + .to_be_written_to_descriptor_set = VecU32_new(), .to_be_deleted = VecU32_new()}; } @@ -124,7 +118,9 @@ void LucyFaceFixedSize_get_rid_of_myself(LucyFaceFixedSize* self){ assert(img->usage > 0); if (--img->usage) { assert(!img->scheduled_for_deletion); - img->scheduled_for_deletion = 1; + img->scheduled_for_deletion = true; + AliceAllSingleUploadTextures_delete(cache->single_upload_textures, img->tex); + img->tex = NULL; VecU32_append(&cache->to_be_deleted, slot_id); } } @@ -190,12 +186,12 @@ void LucyGlyphCache_add_glyphs__close_img( assert(!img->scheduled_for_deletion); img_width = MAX_U32(img_width, 10); // Just a precaution. empty buffers aren't supported by Margaret img_height = MAX_U32(img_height, 10); - VecU32_append(&cache->to_be_copied_to_device_next_cycle, img_slot_id); - img->staging_buffer = MargaretBufAllocator_alloc(cache->ve.staging_buffers, img_width * img_height * 1); - img->img = MargaretImgAllocator_alloc(cache->ve.dev_local_images, img_width, img_height, VK_FORMAT_R8_UNORM, - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); - img->img_view = margaret_create_view_for_image(cache->ve.device, img->img.a.image, - VK_FORMAT_R8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); + + img->tex = AliceAllSingleUploadTextures_register_new(cache->single_upload_textures, + img_width, img_height, sizeof(U8), VK_FORMAT_R8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + cache->ve.staging_buffers, cache->ve.dev_local_images); + VecU32_append(&cache->to_be_written_to_descriptor_set, img_slot_id); } void LucyGlyphCache_add_glyphs(VecLucyGlyphCachingRequest requests_for_faces){ @@ -267,7 +263,6 @@ void LucyGlyphCache_add_glyphs(VecLucyGlyphCachingRequest requests_for_faces){ LucyPositionedStagingGlyph* p_glyph; one_more_chance: {} - int s = 23123; p_glyph = &ready.buf[j]; LucyImage* img = &VecOptionLucyImage_mat(&cache->image_slots, img_slot_id)->some; U64 new_width_required = p_glyph->bitmap.width + starting_x; @@ -317,8 +312,8 @@ void LucyGlyphCache_add_glyphs(VecLucyGlyphCachingRequest requests_for_faces){ for (size_t j = 0; j < ready.len; j++) { LucyPositionedStagingGlyph* p_glyph = &ready.buf[j]; LucyImage* image = &VecOptionLucyImage_mat(&cache->image_slots, p_glyph->img_slot_id)->some; - U64 staging_width = image->img.width; - U8* staging = (U8*)MargaretSubbuf_get_mapped(&image->staging_buffer); + U64 staging_width = image->tex->img.width; + U8* staging = (U8*)AliceTextureUplOnce_get_mapped_staging(image->tex); for (U64 y = 0; y < p_glyph->bitmap.height; y++) { U64 Y = y + p_glyph->pos.y; for (U64 x = 0; x < p_glyph->bitmap.width; x++) { @@ -337,31 +332,18 @@ void LucyGlyphCache_drop(LucyGlyphCache self){ for (size_t i = 0; i < self.image_slots.len; i++) { assert(self.image_slots.buf[i].variant == Option_None); } - VecU32_drop(self.to_be_freed_of_old_staging_next_cycle); - VecU32_drop(self.to_be_copied_to_device_next_cycle); + VecU32_drop(self.to_be_written_to_descriptor_set); VecU32_drop(self.to_be_deleted); } void LucyGlyphCache_another_frame(LucyGlyphCache* self){ - for (size_t i = 0; i < self->to_be_freed_of_old_staging_next_cycle.len; i++) { - U32 slot_id = self->to_be_freed_of_old_staging_next_cycle.buf[i]; - LucyImage* img = &self->image_slots.buf[slot_id].some; - assert(img->staging_buffer.len != 0); - MargaretBufAllocator_free(self->ve.staging_buffers, img->staging_buffer); - img->staging_buffer.len = 0; - } - for (size_t i = 0; i < self->to_be_copied_to_device_next_cycle.len; i++) { - U32 slot_id = self->to_be_copied_to_device_next_cycle.buf[i]; + for (size_t i = 0; i < self->to_be_written_to_descriptor_set.len; i++) { + U32 slot_id = self->to_be_written_to_descriptor_set.buf[i]; OptionLucyImage* img_slot = &self->image_slots.buf[slot_id]; assert(img_slot->variant == Option_Some); LucyImage* img = &img_slot->some; - assert(img->staging_buffer.len != 0); if (img->scheduled_for_deletion) continue; - margaret_rec_cmd_copy_buffer_to_image_one_to_one_color_aspect(self->ve.transfer_cmd_buffer, - &img->staging_buffer, &img->img, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); - VecU32_append(&self->to_be_freed_of_old_staging_next_cycle, slot_id); vkUpdateDescriptorSets(self->ve.device, 1, &(VkWriteDescriptorSet){ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, @@ -369,7 +351,7 @@ void LucyGlyphCache_another_frame(LucyGlyphCache* self){ .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .pImageInfo = &(VkDescriptorImageInfo){ - .sampler = self->ve.nearest_sampler, .imageView = img->img_view, + .sampler = self->ve.nearest_sampler, .imageView = img->tex->view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } }, 0, NULL); @@ -377,19 +359,16 @@ void LucyGlyphCache_another_frame(LucyGlyphCache* self){ /* We technically could carry out each deletion request in O(1) and each img creation request in O(1), * but who cares, it's no problem going over the entire descriptor set when something get's added or deleted */ for (size_t i = 0; i < self->to_be_deleted.len; i++) { - U32 slot_id = self->to_be_copied_to_device_next_cycle.buf[i]; + U32 slot_id = self->to_be_deleted.buf[i]; OptionLucyImage* img_slot = &self->image_slots.buf[slot_id]; assert(img_slot->variant == Option_Some); LucyImage* img = &img_slot->some; assert(img->scheduled_for_deletion); assert(img->usage == 0); - if (img->staging_buffer.len != 0) - MargaretBufAllocator_free(self->ve.staging_buffers, img->staging_buffer); - MargaretImgAllocator_free(self->ve.dev_local_images, img->img.a); + assert(img->tex == NULL); img_slot->variant = Option_None; } - self->to_be_freed_of_old_staging_next_cycle.len = 0; - self->to_be_copied_to_device_next_cycle.len = 0; + self->to_be_written_to_descriptor_set.len = 0; self->to_be_deleted.len = 0; } diff --git a/src/l2/lucy/glyph_render.h b/src/l2/lucy/glyph_render.h index c6493ed..e5234bd 100644 --- a/src/l2/lucy/glyph_render.h +++ b/src/l2/lucy/glyph_render.h @@ -97,8 +97,8 @@ void LucyRenderer_draw_char_glyph(LucyRenderer* self, vec4 color, ivec2 pos, Luc OptionLucyImage* img_slot = VecOptionLucyImage_mat(&self->cache->image_slots, glyph->img_slot_id); assert(img_slot->variant == Option_Some); LucyImage* img = &img_slot->some; - float atlas_w = (float)img->img.width; - float atlas_h = (float)img->img.height; + float atlas_w = (float)img->tex->img.width; + float atlas_h = (float)img->tex->img.height; ivec2 positioned = ivec2_add_ivec2(pos, glyph->bearing); U64 needed_vbo_length = (self->glyphs_count + 1) * sizeof(LucyRenderInstance);