I did it! I added dynamic glyph support. Now Lucy is unstoppable
This commit is contained in:
parent
76a3469e4e
commit
b913d115bf
@ -1077,6 +1077,7 @@ void alice_frame_drawing(Alice* alice) {
|
||||
// MutRefWidget_draw(alice->overlay_ui_root, (ivec2){0}, (uvec2){win_width, win_height}, (BorderS32){.lt = {0},
|
||||
// .rb.x = (S32)win_width, .rb.y = (S32)win_height});
|
||||
Plain2dShapeRenderer_another_frame(&alice->plain_shape_renderer);
|
||||
LucyGlyphCache_another_frame_flush_dyn_images_transfer(&alice->lucy_cache);
|
||||
LucyRenderer_another_frame(&alice->lucy_renderer);
|
||||
margaret_end_command_buffer(alice->transfer_command_buf);
|
||||
margaret_end_command_buffer(alice->compute_command_buf);
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#define LUCY_MAX_DESCRIPTOR_COUNT 100
|
||||
#define LUCY_INITIAL_DYN_IMAGE_DIM 300
|
||||
#define LUCY_INITIAL_DYN_IMAGE_DIM 1
|
||||
#define LUCY_INITIAL_DYN_STAGING_BUF_SIZE 30000
|
||||
|
||||
typedef struct {
|
||||
@ -113,16 +113,15 @@ U32 LucyGlyphCache__find_image_slot(LucyGlyphCache* cache){
|
||||
void LucyGlyphCache__create_dyn_texture(LucyGlyphCache* cache, U32 new_atlas_dim) {
|
||||
assert(new_atlas_dim <= cache->max_dim);
|
||||
U32 image_slot_id = LucyGlyphCache__find_image_slot(cache);
|
||||
MargaretImg dev_local = MargaretImgAllocator_alloc(cache->ve.dev_local_images,
|
||||
MargaretImg dev_local_img = MargaretImgAllocator_alloc(cache->ve.dev_local_images,
|
||||
new_atlas_dim, new_atlas_dim, VK_FORMAT_R8_UNORM,
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
||||
VkImageView view = margaret_create_view_for_image(
|
||||
cache->ve.device, dev_local.a.image, VK_FORMAT_R8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
cache->ve.device, dev_local_img.a.image, VK_FORMAT_R8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
OptionLucyImage* img_slot = VecOptionLucyImage_mat(&cache->image_slots, image_slot_id);
|
||||
assert(img_slot->variant == Option_Some);
|
||||
img_slot->some.usage = 1;
|
||||
img_slot->some.tex = (MargaretTexture){};
|
||||
|
||||
img_slot->some.tex = (MargaretTexture){ .img = dev_local_img, .view = view};
|
||||
|
||||
vkUpdateDescriptorSets(cache->ve.device, 1, &(VkWriteDescriptorSet){
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
@ -240,10 +239,11 @@ void LucyGlyphCache__extract_bitmap(FT_Face ft_face, U32 codepoint, U32 max_dim,
|
||||
}
|
||||
}
|
||||
|
||||
/* pos on atlas may be left undefined */
|
||||
/* pos_on_atlas may be left undefined, img_slot_id may be left undefined */
|
||||
LucyStoredGlyph* LucyGlyphCache__add_glyph_to_glyph_set(BufRBTree_MapU32ToLucyStoredGlyph* glyph_set,
|
||||
U32 codepoint, FT_GlyphSlot slot, uvec2 pos_on_atlas) {
|
||||
U32 codepoint, FT_GlyphSlot slot, uvec2 pos_on_atlas, U32 img_slot_id) {
|
||||
bool iret = BufRBTree_MapU32ToLucyStoredGlyph_insert(glyph_set, codepoint, (LucyStoredGlyph){
|
||||
.img_slot_id = img_slot_id,
|
||||
.w = slot->bitmap.width, .h = slot->bitmap.rows,
|
||||
.advance_x = slot->advance.x >> 6,
|
||||
.bearing = (ivec2){ slot->bitmap_left, -slot->bitmap_top },
|
||||
@ -261,6 +261,27 @@ U32 LucyGlyphCache__pick_size_next_dyn_img(U32 max_dim, U32 cur_dyn_img_dim) {
|
||||
return cur_dyn_img_dim * 2 <= max_dim ? cur_dyn_img_dim * 2 : max_dim;
|
||||
}
|
||||
|
||||
/* Recurring piece of code in LucyGlyphCache_get_glyph_on_the_fly() to reset current atlas */
|
||||
void LucyGlyphCache_get_glyph_on_the_fly__another_dyn_atlas(LucyGlyphCache* cache, U32 cur_dyn_img_dim) {
|
||||
/* Suppose we sent some dynamic atlas to deleter. Ok, so what? We will first perform some meaningless
|
||||
* copying from staging to device local image. */
|
||||
LucyGlyphCache__decrease_image_usage(cache, cache->dynamic_image_slot);
|
||||
|
||||
U32 new_dyn_img_dim = LucyGlyphCache__pick_size_next_dyn_img(cache->max_dim, cur_dyn_img_dim);
|
||||
LucyGlyphCache__create_dyn_texture(cache, new_dyn_img_dim);
|
||||
/* cache->dynamic_img_slot was updated */
|
||||
VecLucyAccumDynTransferImage_append(&cache->accumulated_dyn_images, (LucyAccumDynTransferImage){
|
||||
.img_slot_id = cache->dynamic_image_slot, .glyphs = VecVkBufferImageCopy_new(),
|
||||
});
|
||||
|
||||
for (size_t x = 0; x < cur_dyn_img_dim; x++) {
|
||||
cache->dynamic_image_landscape.buf[x] = 0;
|
||||
}
|
||||
VecU32_expand(&cache->dynamic_image_landscape, new_dyn_img_dim, 0);
|
||||
|
||||
cache->dynamic_landscape_progress_x = 0;
|
||||
}
|
||||
|
||||
/* Returned pointer will be invalidated after you modify the set of codepoints for this size of this face */
|
||||
LucyStoredGlyph* LucyGlyphCache_get_glyph_on_the_fly(RBTreeNodeLucyFaceFixedSize* ffs_node, U32 codepoint) {
|
||||
LucyFaceFixedSize* ffs = &ffs_node->value;
|
||||
@ -282,48 +303,45 @@ LucyStoredGlyph* LucyGlyphCache_get_glyph_on_the_fly(RBTreeNodeLucyFaceFixedSize
|
||||
}
|
||||
another_attempt:
|
||||
{}
|
||||
printf("DEBUG: Attempting to add %u\n", codepoint);
|
||||
assert(cache->image_slots.buf[cache->dynamic_image_slot].variant == Option_Some);
|
||||
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);
|
||||
if (ft_slot->bitmap.width > cur_dyn_img_dim) {
|
||||
LucyGlyphCache_get_glyph_on_the_fly__another_dyn_atlas(cache, cur_dyn_img_dim);
|
||||
printf("Cause 0: %u > %u\n", ft_slot->bitmap.width, cur_dyn_img_dim);
|
||||
goto another_attempt;
|
||||
}
|
||||
|
||||
U32 required_atlas_width = cache->dynamic_landscape_progress_x + ft_slot->bitmap.width;
|
||||
if (required_atlas_width > cur_dyn_img_dim) {
|
||||
printf("Cause 1: %u > %u\n", required_atlas_width, cur_dyn_img_dim);
|
||||
cache->dynamic_landscape_progress_x = 0;
|
||||
goto another_attempt;
|
||||
}
|
||||
assert(cur_dyn_img_dim == cache->dynamic_image_landscape.len);
|
||||
U32 start_y = 0;
|
||||
for (size_t gx = 0; gx < ft_slot->bitmap.width; gx++) {
|
||||
start_y = MAX_U32(start_y, cache->dynamic_image_landscape.buf[gx]);
|
||||
start_y = MAX_U32(start_y, cache->dynamic_image_landscape.buf[cache->dynamic_landscape_progress_x + gx]);
|
||||
}
|
||||
U32 ceiling = start_y + ft_slot->bitmap.rows;
|
||||
if (ceiling > cur_dyn_img_dim) {
|
||||
/* This is serious. We need to issue a new image */
|
||||
|
||||
// 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);
|
||||
/* cache->dynamic_img_slot was updated */
|
||||
VecLucyAccumDynTransferImage_append(&cache->accumulated_dyn_images, (LucyAccumDynTransferImage){
|
||||
.img_slot_id = cache->dynamic_image_slot, .glyphs = VecVkBufferImageCopy_new(),
|
||||
});
|
||||
|
||||
for (size_t x = 0; x < cur_dyn_img_dim; x++) {
|
||||
cache->dynamic_image_landscape.buf[x] = 0;
|
||||
}
|
||||
VecU32_expand(&cache->dynamic_image_landscape, new_dyn_img_dim, 0);
|
||||
|
||||
cache->dynamic_landscape_progress_x = 0;
|
||||
LucyGlyphCache_get_glyph_on_the_fly__another_dyn_atlas(cache, cur_dyn_img_dim);
|
||||
goto another_attempt;
|
||||
}
|
||||
/* Successful placement */
|
||||
|
||||
for (size_t gx = 0; gx < ft_slot->bitmap.width; gx++) {
|
||||
cache->dynamic_image_landscape.buf[cache->dynamic_landscape_progress_x + gx] = ceiling;
|
||||
}
|
||||
dyn_img->usage++;
|
||||
|
||||
/* This is actually annoying, I may todo: make a function that forms VkBufferImageCopy for me */
|
||||
VecVkBufferImageCopy_append(&cache->accumulated_dyn_images.buf[cache->accumulated_dyn_images.len - 1].glyphs,
|
||||
(VkBufferImageCopy){ .bufferOffset = cache->dyn_staging_usage,
|
||||
(VkBufferImageCopy){
|
||||
.bufferOffset = cache->dyn_staging.start + cache->dyn_staging_usage,
|
||||
.bufferRowLength = 0,
|
||||
.bufferImageHeight = 0,
|
||||
.imageSubresource = (VkImageSubresourceLayers){
|
||||
@ -333,25 +351,33 @@ LucyStoredGlyph* LucyGlyphCache_get_glyph_on_the_fly(RBTreeNodeLucyFaceFixedSize
|
||||
.imageExtent = { .width = ft_slot->bitmap.width, .height = ft_slot->bitmap.rows, .depth = 1 },
|
||||
});
|
||||
|
||||
U64 needed_staging_sz = cache->dyn_staging_usage + bitmap->rows * bitmap->width * sizeof(U8);
|
||||
if (cache->dyn_staging.len < needed_staging_sz) {
|
||||
MargaretSubbuf_expand_or_move_old_host_visible(&cache->dyn_staging,
|
||||
Vec_get_new_capacity(cache->dyn_staging.len, needed_staging_sz));
|
||||
}
|
||||
assert(cache->dyn_staging.len >= needed_staging_sz);
|
||||
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);
|
||||
cache->dyn_staging_usage = needed_staging_sz;
|
||||
|
||||
LucyStoredGlyph* res = LucyGlyphCache__add_glyph_to_glyph_set(glyph_set, codepoint, ft_slot,
|
||||
(uvec2){cache->dynamic_landscape_progress_x, start_y});
|
||||
(uvec2){cache->dynamic_landscape_progress_x, start_y}, cache->dynamic_image_slot);
|
||||
|
||||
cache->dynamic_landscape_progress_x += bitmap->width;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* This function must be called at the end of another_frame callback, after every single operation with glyph cache
|
||||
* has been done. It will record copy commands for transferring dynamic images. All the accumulated dyn-glyph
|
||||
* records will be cleared */
|
||||
void LucyGlyphCache__flush_dyn_images_transfer(LucyGlyphCache* cache) {
|
||||
void LucyGlyphCache_another_frame_flush_dyn_images_transfer(LucyGlyphCache* cache) {
|
||||
for (size_t ii = 0; ii < cache->accumulated_dyn_images.len; ii++) {
|
||||
LucyAccumDynTransferImage* img_regions = &cache->accumulated_dyn_images.buf[ii];
|
||||
margaret_rec_cmd_copy_buffer_to_image(cache->ve.transfer_cmd_buffer,
|
||||
@ -489,8 +515,8 @@ void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest
|
||||
TextureDataR8 my_bitmap;
|
||||
FT_GlyphSlot slot;
|
||||
LucyGlyphCache__extract_bitmap(ft_face, codepoint, cache->max_dim, &my_bitmap, &slot);
|
||||
/* x_on_atlas, y_on_atlas and img will be set later, when `ready` vector is ready */
|
||||
LucyGlyphCache__add_glyph_to_glyph_set(glyph_set, codepoint, slot, (uvec2){0});
|
||||
/* x_on_atlas, y_on_atlas and img_slot_id will be set later, when `ready` vector is ready */
|
||||
LucyGlyphCache__add_glyph_to_glyph_set(glyph_set, codepoint, slot, (uvec2){0}, 0);
|
||||
VecLucyPositionedStagingGlyph_append(&ready, (LucyPositionedStagingGlyph){
|
||||
.sized_face = &req.sized_face->value, .codepoint = codepoint, .bitmap = my_bitmap,
|
||||
/* pos be filled later by packing algorithm */
|
||||
|
||||
@ -94,7 +94,8 @@ void LucyRenderer_draw_char_glyph(LucyRenderer* self, vec4 color, ivec2 pos, Luc
|
||||
|
||||
U64 needed_vbo_length = (self->glyphs_count + 1) * sizeof(LucyRenderInstance);
|
||||
if (self->staging_vbo.len < needed_vbo_length) {
|
||||
MargaretSubbuf_expand_or_move_old_host_visible(&self->staging_vbo, needed_vbo_length);
|
||||
MargaretSubbuf_expand_or_move_old_host_visible(&self->staging_vbo,
|
||||
Vec_get_new_capacity(self->staging_vbo.len, needed_vbo_length));
|
||||
}
|
||||
|
||||
LucyRenderInstance* vbo_data = (LucyRenderInstance*)MargaretSubbuf_get_mapped(&self->staging_vbo);
|
||||
|
||||
@ -333,19 +333,19 @@ void run_app(){
|
||||
// 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();
|
||||
VecU32Segment_append(&ranges_needed, (U32Segment){.start = 32, .len = 126 - 32 + 1});
|
||||
VecLucyGlyphCachingRequest_append(&lucy_requests, (LucyGlyphCachingRequest){
|
||||
.sized_face = st.font_face_of_size_40, .codepoint_ranges = ranges_needed,
|
||||
});
|
||||
ranges_needed = VecU32Segment_new();
|
||||
VecU32Segment_append(&ranges_needed, (U32Segment){.start = 0x430, .len = 0x44f - 0x430 + 1});
|
||||
VecU32Segment_append(&ranges_needed, (U32Segment){.start = 0x410, .len = 0x42f - 0x410 + 1});
|
||||
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);
|
||||
// VecLucyGlyphCachingRequest lucy_requests = VecLucyGlyphCachingRequest_new();
|
||||
// VecU32Segment ranges_needed = VecU32Segment_new();
|
||||
// VecU32Segment_append(&ranges_needed, (U32Segment){.start = 32, .len = 126 - 32 + 1});
|
||||
// VecLucyGlyphCachingRequest_append(&lucy_requests, (LucyGlyphCachingRequest){
|
||||
// .sized_face = st.font_face_of_size_40, .codepoint_ranges = ranges_needed,
|
||||
// });
|
||||
// ranges_needed = VecU32Segment_new();
|
||||
// VecU32Segment_append(&ranges_needed, (U32Segment){.start = 0x430, .len = 0x44f - 0x430 + 1});
|
||||
// VecU32Segment_append(&ranges_needed, (U32Segment){.start = 0x410, .len = 0x42f - 0x410 + 1});
|
||||
// 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);
|
||||
|
||||
Alice_lucy_renderer_add_simple_label(alice, st.font_face_of_size_40, (vec4){0, 0, 0, 1}, 0,
|
||||
cstr("..."), (ivec2){10, 10});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user