diff --git a/src/l2/alice/abigail.h b/src/l2/alice/abigail.h index 6b46bd7..6532218 100644 --- a/src/l2/alice/abigail.h +++ b/src/l2/alice/abigail.h @@ -11,11 +11,11 @@ typedef struct { VecMargaretSubbuf to_del_buffers; - VecMargaretTexture to_del_images; + VecMargaretTexture to_del_textures; } Abigail; Abigail Abigail_new() { - return (Abigail){.to_del_buffers = VecMargaretSubbuf_new(), .to_del_images = VecMargaretTexture_new(), }; + return (Abigail){.to_del_buffers = VecMargaretSubbuf_new(), .to_del_textures = VecMargaretTexture_new(), }; } /* You know the deal, the buffer, returned in ret_dev_local_buf, cannot be deleted in the same `_another_frame` @@ -63,15 +63,15 @@ void Abigail_wipe(Abigail* self) { MargaretSubbuf_free(self->to_del_buffers.buf[i]); } self->to_del_buffers.len = 0; - for (U64 i = 0; i < self->to_del_images.len; i++) { - MargaretTexture_free(self->to_del_images.buf[i]); + for (U64 i = 0; i < self->to_del_textures.len; i++) { + MargaretTexture_free(self->to_del_textures.buf[i]); } - self->to_del_images.len = 0; + self->to_del_textures.len = 0; } void Abigail_drop(Abigail self) { assert(self.to_del_buffers.len == 0); VecMargaretSubbuf_drop(self.to_del_buffers); - assert(self.to_del_images.len == 0); - VecMargaretTexture_drop(self.to_del_images); + assert(self.to_del_textures.len == 0); + VecMargaretTexture_drop(self.to_del_textures); } diff --git a/src/l2/lucy/glyph_cache.h b/src/l2/lucy/glyph_cache.h index df75dc9..d1ef6f1 100644 --- a/src/l2/lucy/glyph_cache.h +++ b/src/l2/lucy/glyph_cache.h @@ -84,7 +84,7 @@ struct LucyGlyphCache { VkDescriptorSet descriptor_set; MargaretSubbuf dyn_staging; - U32 dyn_stating_usage; + U32 dyn_staging_usage; U32 dynamic_image_slot; VecU32 dynamic_image_landscape; U32 dynamic_landscape_progress_x; @@ -176,7 +176,7 @@ LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, Abigail* abigail){ .ve = ve, .abigail = abigail, .max_dim = max_dim, .image_slots = image_slots, .descriptor_set_layout = my_desc_set_layout, .descriptor_set = descriptor_set, - .dyn_staging = dyn_staging, .dyn_stating_usage = 0, + .dyn_staging = dyn_staging, .dyn_staging_usage = 0, .dynamic_image_slot = 67, /* .dynamic_image_landscape will be initialized later */ .dynamic_landscape_progress_x = 0 @@ -193,15 +193,28 @@ LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, Abigail* abigail){ return cache; } -void LucyGlyphCache__extract_bitmap(FT_Face ft_face, U32 codepoint, U32 max_dim, - TextureDataR8* ret_my_bitmap, FT_GlyphSlot* ret_slot - ) { +/* return true if the image was put in deletion queue (it happens when usage reaches zero) */ +bool LucyGlyphCache__decrease_image_usage(LucyGlyphCache* cache, U32 slot_id) { + OptionLucyImage* img_slot = VecOptionLucyImage_mat(&cache->image_slots, slot_id); + assert(img_slot->variant == Option_Some); + LucyImage* img = &img_slot->some; + assert(img->usage > 0); + if (--img->usage == 0) { + printf("Deleting LucyImage %u\n", slot_id); + /* Nothing is written to descriptor set. And luckily, we don't have to */ + img_slot->variant = Option_None; + VecMargaretTexture_append(&cache->abigail->to_del_textures, img->tex); + return true; + } + return false; +} + +/* Helper for a helper function */ +FT_GlyphSlot LucyGlyphCache__prepare_slot(FT_Face ft_face, U32 codepoint, U32 max_dim) { FT_UInt glyph_index = FT_Get_Char_Index(ft_face, (FT_ULong)codepoint); check(FT_Load_Glyph(ft_face, glyph_index, 0) == 0); FT_GlyphSlot slot = ft_face->glyph; - *ret_slot = slot; FT_Bitmap* bitmap = &slot->bitmap; - *ret_my_bitmap = TextureDataR8_new(bitmap->width, bitmap->rows); check(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY); if (slot->format != FT_GLYPH_FORMAT_BITMAP) { check(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL) == 0); @@ -210,6 +223,16 @@ void LucyGlyphCache__extract_bitmap(FT_Face ft_face, U32 codepoint, U32 max_dim, /* Here we dismiss very big glyphs. This guarantees that each glyph on it's own fits into VkImage */ check(bitmap->width <= max_dim && bitmap->rows <= max_dim); assert(bitmap->rows == 0 || bitmap->width != 0 || bitmap->buffer != NULL); + return slot; +} + +/* Helper function */ +void LucyGlyphCache__extract_bitmap(FT_Face ft_face, U32 codepoint, U32 max_dim, + TextureDataR8* ret_my_bitmap, FT_GlyphSlot* ret_slot + ) { + *ret_slot = LucyGlyphCache__prepare_slot(ft_face, codepoint, max_dim); + FT_Bitmap* bitmap = &((*ret_slot)->bitmap); + *ret_my_bitmap = TextureDataR8_new(bitmap->width, bitmap->rows); for (S64 y = 0; y < bitmap->rows; y++) { for (S64 x = 0; x < bitmap->width; x++) { *TextureDataR8_mat(ret_my_bitmap, x, y) = *(bitmap->buffer + y * bitmap->pitch + x * sizeof(U8)); @@ -218,14 +241,20 @@ void LucyGlyphCache__extract_bitmap(FT_Face ft_face, U32 codepoint, U32 max_dim, } /* pos on atlas may be left undefined */ -void LucyGlyphCache__add_glyph_to_glyph_set(BufRBTree_MapU32ToLucyStoredGlyph* glyph_set, +LucyStoredGlyph* LucyGlyphCache__add_glyph_to_glyph_set(BufRBTree_MapU32ToLucyStoredGlyph* glyph_set, U32 codepoint, FT_GlyphSlot slot, uvec2 pos_on_atlas) { - BufRBTree_MapU32ToLucyStoredGlyph_insert(glyph_set, codepoint, (LucyStoredGlyph){ + bool iret = BufRBTree_MapU32ToLucyStoredGlyph_insert(glyph_set, codepoint, (LucyStoredGlyph){ .w = slot->bitmap.width, .h = slot->bitmap.rows, .advance_x = slot->advance.x >> 6, .bearing = (ivec2){ slot->bitmap_left, -slot->bitmap_top }, .pos_on_atlas = pos_on_atlas, }); + assert(iret); + U64 it = BufRBTree_MapU32ToLucyStoredGlyph_find(glyph_set, codepoint); + assert(it != 0); + const LucyStoredGlyph* stored_glyph; + BufRBTree_MapU32ToLucyStoredGlyph_at_iter(glyph_set, it, &codepoint, &stored_glyph); + return (LucyStoredGlyph*)stored_glyph; } U32 LucyGlyphCache__pick_size_next_dyn_img(U32 max_dim, U32 cur_dyn_img_dim) { @@ -233,7 +262,7 @@ U32 LucyGlyphCache__pick_size_next_dyn_img(U32 max_dim, U32 cur_dyn_img_dim) { } /* Returned pointer will be invalidated after you modify the set of codepoints for this size of this face */ -LucyStoredGlyph* LucyCache_add_glyph_on_the_fly(RBTreeNodeLucyFaceFixedSize* ffs_node, U32 codepoint) { +LucyStoredGlyph* LucyGlyphCache_get_glyph_on_the_fly(RBTreeNodeLucyFaceFixedSize* ffs_node, U32 codepoint) { LucyFaceFixedSize* ffs = &ffs_node->value; BufRBTree_MapU32ToLucyStoredGlyph* glyph_set = &ffs->glyphs; U64 map_it = BufRBTree_MapU32ToLucyStoredGlyph_find(glyph_set, codepoint); @@ -246,10 +275,9 @@ LucyStoredGlyph* LucyCache_add_glyph_on_the_fly(RBTreeNodeLucyFaceFixedSize* ffs U32 font_height = ffs_node->key; check(FT_Set_Pixel_Sizes(ft_face, 0, font_height) == 0); - TextureDataR8 my_bitmap; - FT_GlyphSlot slot; - LucyGlyphCache__extract_bitmap(ft_face, codepoint, cache->max_dim, &my_bitmap, &slot); - if (my_bitmap.width > max_dim || my_bitmap.height > max_dim) { + FT_GlyphSlot ft_slot = LucyGlyphCache__prepare_slot(ft_face, codepoint, cache->max_dim); + FT_Bitmap* bitmap = &ft_slot->bitmap; + if (bitmap->width > max_dim || bitmap->rows > max_dim) { abortf("LucyCache_add_glyph_on_the_fly there is no way to fit this monstrosity\n"); } another_attempt: @@ -258,21 +286,25 @@ LucyStoredGlyph* LucyCache_add_glyph_on_the_fly(RBTreeNodeLucyFaceFixedSize* ffs LucyImage* dyn_img = &cache->image_slots.buf[cache->dynamic_image_slot].some; U32 cur_dyn_img_dim = dyn_img->tex.img.width; assert(cur_dyn_img_dim == dyn_img->tex.img.height); - U32 required_atlas_width = cache->dynamic_landscape_progress_x + my_bitmap.width; + U32 required_atlas_width = cache->dynamic_landscape_progress_x + ft_slot->bitmap.width; if (required_atlas_width > cur_dyn_img_dim) { cache->dynamic_landscape_progress_x = 0; goto another_attempt; } U32 start_y = 0; - for (size_t gx = 0; gx < my_bitmap.width; gx++) { + for (size_t gx = 0; gx < ft_slot->bitmap.width; gx++) { start_y = MAX_U32(start_y, cache->dynamic_image_landscape.buf[gx]); } - U32 ceiling = start_y + my_bitmap.height; + U32 ceiling = start_y + ft_slot->bitmap.rows; if (ceiling > cur_dyn_img_dim) { /* This is serious. We need to issue a new image */ - // todo: decrease usage on the previous dyn_image. If we see that usage is zero, we want to pop the last img - // todo: from accumulated_syn_images and send it to deleter (for images) - // todo: it is the second place where we need a deleter for images. I REALLY REALLY NEED A DELETER, other than Abigail + + // If we see that usage is zero, we want to pop the last img + // from accumulated_syn_images and send it to deleter (for images) + if (LucyGlyphCache__decrease_image_usage(cache, cache->dynamic_image_slot)) { + assert(cache->accumulated_dyn_images.buf[cache->accumulated_dyn_images.len - 1].glyphs.capacity == 0); + cache->accumulated_dyn_images.len--; + } U32 new_dyn_img_dim = LucyGlyphCache__pick_size_next_dyn_img(max_dim, cur_dyn_img_dim); LucyGlyphCache__create_dyn_texture(cache, new_dyn_img_dim); @@ -289,9 +321,31 @@ LucyStoredGlyph* LucyCache_add_glyph_on_the_fly(RBTreeNodeLucyFaceFixedSize* ffs cache->dynamic_landscape_progress_x = 0; goto another_attempt; } - // todo: but we forgot to even write LucyAccumDynTransferImage, that would contain LucyPositionedDynGlyph - // todo: write swap, that would change the LucyAccumDynTransferImage we are writing to. - return NULL; // todo: fix + + VecVkBufferImageCopy_append(&cache->accumulated_dyn_images.buf[cache->accumulated_dyn_images.len - 1].glyphs, + (VkBufferImageCopy){ .bufferOffset = cache->dyn_staging_usage, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = (VkImageSubresourceLayers){ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, + }, + .imageOffset = {(S32)cache->dynamic_landscape_progress_x, (S32)start_y, 0}, + .imageExtent = { .width = ft_slot->bitmap.width, .height = ft_slot->bitmap.rows, .depth = 1 }, + }); + + U8* staging_pixels = (U8*)MargaretSubbuf_get_mapped(&cache->dyn_staging) + cache->dyn_staging_usage; + for (S64 y = 0; y < bitmap->rows; y++) { + for (S64 x = 0; x < bitmap->width; x++) { + *(staging_pixels + (y * bitmap->width + x) * sizeof(U8)) = + *(bitmap->buffer + (y * bitmap->pitch + x) * sizeof(U8)); + } + + } + cache->dyn_staging_usage += bitmap->rows * bitmap->width * sizeof(U8); + + LucyStoredGlyph* res = LucyGlyphCache__add_glyph_to_glyph_set(glyph_set, codepoint, ft_slot, + (uvec2){cache->dynamic_landscape_progress_x, start_y}); + return res; } /* This function must be called at the end of another_frame callback, after every single operation with glyph cache @@ -310,7 +364,7 @@ void LucyGlyphCache__flush_dyn_images_transfer(LucyGlyphCache* cache) { VecLucyAccumDynTransferImage_append(&cache->accumulated_dyn_images, (LucyAccumDynTransferImage){ .img_slot_id = cache->dynamic_image_slot, .glyphs = VecVkBufferImageCopy_new(), }); - cache->dyn_stating_usage = 0; + cache->dyn_staging_usage = 0; } void LucyFaceFixedSize_get_rid_of_myself(LucyFaceFixedSize* self){ @@ -318,15 +372,7 @@ void LucyFaceFixedSize_get_rid_of_myself(LucyFaceFixedSize* self){ BufRBTree_MapU32ToLucyStoredGlyph* glyphs = &self->glyphs; for (size_t gid = 0; gid < glyphs->el.len; gid++) { U32 slot_id = glyphs->el.buf[gid].value.img_slot_id; - OptionLucyImage* img_slot = VecOptionLucyImage_mat(&cache->image_slots, slot_id); - assert(img_slot->variant == Option_Some); - LucyImage* img = &img_slot->some; - assert(img->usage > 0); - if (--img->usage == 0) { - /* Nothing is written to descriptor set. And luckily, we don't have to */ - img_slot->variant = Option_None; - // todo: add image to deletion queue. I NEED IMAGE DELETER SO SO BADLY. - } + LucyGlyphCache__decrease_image_usage(cache, slot_id); } BufRBTree_MapU32ToLucyStoredGlyph_sink(glyphs); } diff --git a/src/l2/lucy/glyph_render.h b/src/l2/lucy/glyph_render.h index eedb1f8..9812337 100644 --- a/src/l2/lucy/glyph_render.h +++ b/src/l2/lucy/glyph_render.h @@ -4,15 +4,6 @@ #include "../../../gen/l1/pixel_masses.h" #include "../../../gen/l1/geom.h" -// todo: rewrite this crrp crap using instances -// typedef struct{ -// vec4 color; -// vec2 pos; -// vec2 tex_cord; -// U32 tex_ind; -// } LucyVertex; - - typedef struct { vec4 color; vec2 lt_pos; @@ -140,15 +131,7 @@ void LucyRenderer_add_simple_label( pos = (ivec2){start_pos.x, pos.y + (S32)font_height + additional_y_advance}; continue; } - BufRBTree_MapU32ToLucyStoredGlyph *glyphs = &ffs->value.glyphs; - U64 map_it = BufRBTree_MapU32ToLucyStoredGlyph_find(glyphs, codepoint); - if (map_it == 0) { - /* We probably should have requested LucyCache to load more glyphs or draw 'unknown character' - * character, but we just skip things. We will force someone else to do that job */ - continue; - } - assert(map_it > 0 && map_it < glyphs->tree.len); - LucyStoredGlyph* glyph = &glyphs->el.buf[map_it - 1].value; + LucyStoredGlyph* glyph = LucyGlyphCache_get_glyph_on_the_fly(ffs, codepoint); LucyRenderer_draw_char_glyph(self, color, pos, glyph); pos.x += (S32)glyph->advance_x; } diff --git a/src/l2/margaret/vulkan_utils.h b/src/l2/margaret/vulkan_utils.h index e27ffb4..97b72b8 100644 --- a/src/l2/margaret/vulkan_utils.h +++ b/src/l2/margaret/vulkan_utils.h @@ -1325,12 +1325,17 @@ void margaret_rec_cmd_copy_buffer_to_image_one_to_one_color_aspect( /* (destination_stage_mask, destination_access_mask) are probably * (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT) * - * aspect_mask is probably VK_IMAGE_ASPECT_COLOR_BIT. Don't forget that each region also specifies aspect mask */ + * aspect_mask is probably VK_IMAGE_ASPECT_COLOR_BIT. Don't forget that each region also specifies aspect mask + * + * If `regions` is empty, you are allowed to pass incorrect buffer, incorrect image, incorrect everything + */ void margaret_rec_cmd_copy_buffer_to_image(VkCommandBuffer cmd_buf, const MargaretSubbuf* src, MargaretImg* dst, VkImageLayout dst_new_layout, VkPipelineStageFlags destination_stage_mask, VkAccessFlags destination_access_mask, VkImageAspectFlags aspect_mask, SpanVkBufferImageCopy regions ) { + if (regions.len == 0) + return; margaret_rec_pipeline_barrier__begin_copy_to_image(cmd_buf, dst, aspect_mask); vkCmdCopyBufferToImage(cmd_buf, MargaretSubbuf_get_buffer(src), dst->a.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,