So far nothing broke. Key word so far. Can't wait to run _LucyGlyphCache_get_glyph_on_the_fly and enter infinite loop of sigsegvs

This commit is contained in:
Андреев Григорий 2026-04-25 22:55:44 +03:00
parent 74c2b8007e
commit 76a3469e4e
4 changed files with 93 additions and 59 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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,