Fixed an obvious bug in LucyCache. Improved code quality in mass transfer of glyphs

This commit is contained in:
Андреев Григорий 2026-04-21 14:16:53 +03:00
parent 0258b946bb
commit 36396a0a7d
3 changed files with 119 additions and 75 deletions

View File

@ -9,6 +9,7 @@ void generate_l1_lucy_headers(){
generate_Option_templ_inst_eve_header(l, ns, (option_template_instantiation_op){
.T = cstr("LucyImage"), .t_primitive = true});
generate_eve_span_company_for_primitive(l, ns, cstr("OptionLucyImage"), true, false);
generate_eve_span_company_for_non_primitive_non_clonable(l, ns, cstr("LucyPrepMassTransferImage"), true, false);
generate_eve_span_company_for_primitive(l, ns, cstr("KVPU32ToLucyStoredGlyph"), true, false);
generate_eve_span_company_for_primitive(l, ns, cstr("KVPU32ToLucyFaceFixedSize"), true, false);

View File

@ -1,5 +1,8 @@
#pragma once
#define DARIA_ROOT ""
#define _DARIA "asdasd"
#include "../margaret/vulkan_utils.h"
#include "../../../gen/l1/VecAndSpan_U32Segment.h"
#include "../../../gen/l1/vulkan/VecVkDescriptorImageInfo.h"
@ -16,8 +19,6 @@
typedef struct {
MargaretTexture tex;
U64 usage;
/* I just could not avoid storing this here. But yeah, after the copying is done, this field stops being used */
void* staging;
} LucyImage;
#include "../../../gen/l1/eve/lucy/OptionLucyImage.h"
@ -142,7 +143,8 @@ typedef struct {
TextureDataR8 bitmap;
/* Will be determined in the next phase */
uvec2 pos;
U32 img_slot_id;
/* But img_slot_id isn't stored here because structure itself will be relocated to the
* corresponding LucyPrepMassTransfer record for the right image */
} LucyPositionedStagingGlyph;
bool LucyPositionedStagingGlyph_less_LucyPositionedStagingGlyph(
@ -157,8 +159,20 @@ void LucyPositionedStagingGlyph_drop(LucyPositionedStagingGlyph self){
/* Instantiation for helper type */
#include "../../../gen/l1/eve/lucy/VecLucyPositionedStagingGlyph.h"
typedef struct {
U32 img_slot_id;
void* staging;
VecLucyPositionedStagingGlyph glyphs_inside;
} LucyPrepMassTransferImage;
void LucyPrepMassTransferImage_drop(LucyPrepMassTransferImage self) {
VecLucyPositionedStagingGlyph_drop(self.glyphs_inside);
}
#include "../../../gen/l1/eve/lucy/VecLucyPrepMassTransferImage.h"
/* Helper function */
U32 LucyGlyphCache_add_glyphs__find_image_slot(LucyGlyphCache* cache){
U32 LucyGlyphCache__find_image_slot(LucyGlyphCache* cache){
for (U32 i = 0; i < cache->image_slots.len; i++) {
OptionLucyImage* slot = &cache->image_slots.buf[i];
if (slot->variant == Option_None) {
@ -171,10 +185,20 @@ U32 LucyGlyphCache_add_glyphs__find_image_slot(LucyGlyphCache* cache){
"You better add up on them\n");
}
/* Helper function */
LucyPrepMassTransferImage LucyGlyphCache_add_glyphs__another_image(LucyGlyphCache* cache) {
return (LucyPrepMassTransferImage){
.img_slot_id = LucyGlyphCache__find_image_slot(cache),
.glyphs_inside = VecLucyPositionedStagingGlyph_new(),
};
}
/* Helper function */
void LucyGlyphCache_add_glyphs__close_img(
LucyGlyphCache* cache, U32 img_slot_id, U32 img_width, U32 img_height
LucyGlyphCache* cache, VecLucyPrepMassTransferImage* mass_transfer,
LucyPrepMassTransferImage image_to_prep, U32 img_width, U32 img_height
){
U32 img_slot_id = image_to_prep.img_slot_id;
OptionLucyImage* img_slot = VecOptionLucyImage_mat(&cache->image_slots, img_slot_id);
assert(img_slot->variant == Option_Some);
LucyImage* img = &img_slot->some;
@ -185,8 +209,8 @@ void LucyGlyphCache_add_glyphs__close_img(
img->tex = Abigail_register_new_texture(cache->abigail,
img_width, img_height, sizeof(U8), VK_FORMAT_R8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, cache->ve.transfer_cmd_buffer,
cache->ve.staging_buffers, cache->ve.dev_local_images, &(img->staging));
assert(img->staging);
cache->ve.staging_buffers, cache->ve.dev_local_images, &(image_to_prep.staging));
assert(image_to_prep.staging);
/* We are writing to descriptor set RIGHT NOW. That is why this function, and as such
* LucyGlyphCache_add_glyphs too, must be called when no frame is in flight */
@ -200,6 +224,8 @@ void LucyGlyphCache_add_glyphs__close_img(
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
}, 0, NULL);
/* Added with a staging buffer data pointer in it */
VecLucyPrepMassTransferImage_append(mass_transfer, image_to_prep);
}
void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest requests_for_faces){
@ -236,6 +262,7 @@ void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest
if (slot->format != FT_GLYPH_FORMAT_BITMAP) {
check(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL) == 0);
}
check(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY);
/* 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);
@ -252,73 +279,90 @@ void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest
});
VecLucyPositionedStagingGlyph_append(&ready, (LucyPositionedStagingGlyph){
.sized_face = &req.sized_face->value, .codepoint = codepoint, .bitmap = my_bitmap,
/* pos and img will be filled later by packing algorithm */
/* pos be filled later by packing algorithm */
});
}
}
/* Phase 2. Here we determine (in ready vector) where each new glyph sits (atlas image + pos).
* But we won't copy TextureDataR8 to staging buffer before everything is known */
VecLucyPositionedStagingGlyph_sort(&ready);
/* Variables, that have to be reset after each image overflow */
U32 starting_x = 0;
VecU32 landscape = VecU32_new_reserved(200);
U32 img_width = 0, img_height = 0;
U32 img_slot_id = LucyGlyphCache_add_glyphs__find_image_slot(cache);
for (size_t j = 0; j < ready.len; j++) {
LucyPositionedStagingGlyph* p_glyph;
one_more_chance:
{}
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;
if (new_width_required > max_dim) {
/* Resetting row */
starting_x = 0;
goto one_more_chance;
}
for (U32 h = img_width; h < new_width_required; h++) {
VecU32_append(&landscape, 0);
}
img_width = MAX_U64(img_width, new_width_required);
U32 height_here = 0;
for (size_t x = 0; x < p_glyph->bitmap.width; x++) {
height_here = MAX_U32(height_here, *VecU32_at(&landscape, starting_x + x));
}
U64 new_height_required = height_here + p_glyph->bitmap.height;
if (new_height_required > max_dim) {
/* Resetting image */
LucyGlyphCache_add_glyphs__close_img(cache, img_slot_id, img_width, img_height);
starting_x = 0;
landscape.len = 0;
img_width = 0;
img_height = 0;
img_slot_id = LucyGlyphCache_add_glyphs__find_image_slot(cache);
goto one_more_chance;
}
/* Success */
for (size_t x = 0; x < p_glyph->bitmap.width; x++) {
*VecU32_mat(&landscape, starting_x + x) = new_height_required;
}
img_height = MAX_U64(img_height, new_height_required);
p_glyph->img_slot_id = img_slot_id;
p_glyph->pos = (uvec2){starting_x, height_here};
img->usage++; /* p_glyph uses it, that's a rock fact */
BufRBTree_MapU32ToLucyStoredGlyph *glyphs = &p_glyph->sized_face->glyphs;
U64 map_it = BufRBTree_MapU32ToLucyStoredGlyph_find(glyphs, p_glyph->codepoint);
assert(map_it > 0 && map_it < glyphs->tree.len);
LucyStoredGlyph* actual_glyph = &glyphs->el.buf[map_it - 1].value;
actual_glyph->pos_on_atlas = (uvec2){starting_x, height_here};
actual_glyph->img_slot_id = img_slot_id;
starting_x += p_glyph->bitmap.width;
}
/* Phase 2. Here we determine (in ready vector) where each new glyph sits (atlas image + pos).
* But we won't copy TextureDataR8 to staging buffer before everything is known */
VecLucyPositionedStagingGlyph_sort(&ready);
VecLucyPrepMassTransferImage mass_transfer = VecLucyPrepMassTransferImage_new();
/* Variables, that have to be reset after each image overflow */
U32 starting_x = 0;
VecU32 landscape = VecU32_new_reserved(200);
U32 img_width = 0, img_height = 0;
LucyPrepMassTransferImage unprepared_image = LucyGlyphCache_add_glyphs__another_image(cache);
for (size_t j = 0; j < ready.len; j++) {
one_more_chance:
{}
U32 img_slot_id = unprepared_image.img_slot_id;
LucyPositionedStagingGlyph* 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;
if (new_width_required > max_dim) {
/* Resetting row */
starting_x = 0;
goto one_more_chance;
}
LucyGlyphCache_add_glyphs__close_img(cache, img_slot_id, img_width, img_height);
/* Phase 3. We have all the data. Now what?
* Now we fill staging buffers with glyphs bitsets from `ready` vector */
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->tex.img.width;
U8* staging = (U8*)image->staging;
// todo: replace with resize method
// todo: Add expand method to vector
for (U32 h = img_width; h < new_width_required; h++) {
VecU32_append(&landscape, 0);
}
img_width = MAX_U64(img_width, new_width_required);
U32 height_here = 0;
for (size_t x = 0; x < p_glyph->bitmap.width; x++) {
height_here = MAX_U32(height_here, *VecU32_at(&landscape, starting_x + x));
}
U64 new_height_required = height_here + p_glyph->bitmap.height;
if (new_height_required > max_dim) {
/* Resetting image */
LucyGlyphCache_add_glyphs__close_img(cache, &mass_transfer, unprepared_image, img_width, img_height);
starting_x = 0;
landscape.len = 0;
img_width = 0;
img_height = 0;
unprepared_image = LucyGlyphCache_add_glyphs__another_image(cache);
goto one_more_chance;
}
/* Success */
for (size_t x = 0; x < p_glyph->bitmap.width; x++) {
*VecU32_mat(&landscape, starting_x + x) = new_height_required;
}
img_height = MAX_U64(img_height, new_height_required);
img->usage++; /* p_glyph uses it, that's a rock fact */
BufRBTree_MapU32ToLucyStoredGlyph *glyphs = &p_glyph->sized_face->glyphs;
U64 map_it = BufRBTree_MapU32ToLucyStoredGlyph_find(glyphs, p_glyph->codepoint);
assert(map_it > 0 && map_it < glyphs->tree.len);
LucyStoredGlyph* actual_glyph = &glyphs->el.buf[map_it - 1].value;
actual_glyph->pos_on_atlas = (uvec2){starting_x, height_here};
actual_glyph->img_slot_id = img_slot_id;
p_glyph->pos = (uvec2){starting_x, height_here};
starting_x += p_glyph->bitmap.width;
/* p_glyph is made empty because we are MOVING it to unprepared_image */
VecLucyPositionedStagingGlyph_append(&unprepared_image.glyphs_inside, *p_glyph);
/* Can't afford owning one texture in two places */
p_glyph->bitmap = (TextureDataR8){.width = 0, .height = 0, .pixels = (VecU8){.buf = NULL, .len = 0, .capacity = 0}};
}
LucyGlyphCache_add_glyphs__close_img(cache, &mass_transfer, unprepared_image, img_width, img_height);
VecLucyPositionedStagingGlyph_drop(ready);
/* Phase 3. We have all the data. Now what?
* Now we fill staging buffers with glyphs bitsets from `mass_transfer` vector */
for (size_t ii = 0; ii < mass_transfer.len; ii++) {
LucyPrepMassTransferImage* new_img_rec = &mass_transfer.buf[ii];
assert(VecOptionLucyImage_mat(&cache->image_slots, new_img_rec->img_slot_id)->variant == Option_Some);
LucyImage* image = &VecOptionLucyImage_mat(&cache->image_slots, new_img_rec->img_slot_id)->some;
U64 staging_width = image->tex.img.width;
U8* staging = (U8*)new_img_rec->staging;
for (size_t j = 0; j < new_img_rec->glyphs_inside.len; j++) {
LucyPositionedStagingGlyph* p_glyph = &new_img_rec->glyphs_inside.buf[j];
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++) {
@ -328,7 +372,7 @@ void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest
}
}
}
VecLucyPositionedStagingGlyph_drop(ready);
VecLucyPrepMassTransferImage_drop(mass_transfer);
VecLucyGlyphCachingRequest_drop(requests_for_faces);
}

View File

@ -306,7 +306,7 @@ void main_h_on_another_frame(void* data, float fl){
VecU8 text = VecU8_new();
VecU8_append_fmt(&text, "Bullet mass = %v ; bullet velocity = %v\n",
VecU8_append_fmt(&text, "Масса пули = %v ; Скорость пули = %v\n",
VecU8_format("%.4f", st->bullet_config.mass), VecU8_format("%.4f", st->bullet_config.velocity));
if (st->misses_count == 0 && st->hits_count == 0) {
VecU8_append_cstr(&text, "Press BTN_LEFT to shoot\n");
@ -330,7 +330,8 @@ void run_app(){
Alice* alice = Alice_new();
st.alice = alice;
st.font_face = Alice_new_LucyFace(alice, vcstr("./src/l3/fonts/DMSerifText-Regular.ttf"));
// st.font_face = Alice_new_LucyFace(alice, vcstr("./src/l3/fonts/DMSerifText-Regular.ttf"));
st.font_face = Alice_new_LucyFace(alice, vcstr("./src/l3/fonts/Roboto-VariableFont_wdth,wght.ttf"));
st.font_face_of_size_40 = LucyFace_of_size(st.font_face, 40);
VecLucyGlyphCachingRequest lucy_requests = VecLucyGlyphCachingRequest_new();
VecU32Segment ranges_needed = VecU32Segment_new();
@ -338,8 +339,6 @@ void run_app(){
VecLucyGlyphCachingRequest_append(&lucy_requests, (LucyGlyphCachingRequest){
.sized_face = st.font_face_of_size_40, .codepoint_ranges = ranges_needed,
});
Alice_lucy_cache_add_glyphs(alice, lucy_requests);
lucy_requests = VecLucyGlyphCachingRequest_new();
ranges_needed = VecU32Segment_new();
VecU32Segment_append(&ranges_needed, (U32Segment){.start = 0x430, .len = 0x44f - 0x430 + 1});
VecLucyGlyphCachingRequest_append(&lucy_requests, (LucyGlyphCachingRequest){