Wrote shit + changed README = it was a good day
This commit is contained in:
parent
306536c214
commit
5e3aa054d3
@ -15,6 +15,8 @@
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#define LUCY_MAX_DESCRIPTOR_COUNT 100
|
||||
#define LUCY_INITIAL_DYN_IMAGE_DIM 300
|
||||
#define LUCY_INITIAL_DYN_STAGING_BUF_SIZE 30000
|
||||
|
||||
typedef struct {
|
||||
MargaretTexture tex;
|
||||
@ -63,20 +65,76 @@ struct LucyFace {
|
||||
RBTree_MapU32ToLucyFaceFixedSize sizes;
|
||||
};
|
||||
|
||||
struct {
|
||||
U64 pos_in_staging;
|
||||
U32 img_slot_id;
|
||||
uvec2 pos_in_atlas;
|
||||
/* The rest is determined on the fly */
|
||||
} LucyPositionedDynGlyph;
|
||||
|
||||
struct LucyGlyphCache {
|
||||
MargaretEngineReference ve;
|
||||
Abigail* abigail;
|
||||
U32 max_dim;
|
||||
|
||||
VecOptionLucyImage image_slots;
|
||||
VkDescriptorSetLayout descriptor_set_layout;
|
||||
VkDescriptorSet descriptor_set;
|
||||
|
||||
/* 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;
|
||||
MargaretSubbuf dyn_staging;
|
||||
U32 dynamic_image_slot;
|
||||
VecU32 dynamic_image_landscape;
|
||||
U32 dynamic_landscape_progress_x;
|
||||
};
|
||||
|
||||
|
||||
/* Helper function */
|
||||
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) {
|
||||
slot->variant = Option_Some;
|
||||
slot->some.usage = 0;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
abortf("LucyCache run out of image descriptor in a descriptor set (dictated by layout).\n"
|
||||
"You better add up on them\n");
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
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);
|
||||
OptionLucyImage* img_slot = VecOptionLucyImage_mat(&cache->image_slots, image_slot_id);
|
||||
assert(img_slot->variant == Option_None);
|
||||
img_slot->some.usage = 1;
|
||||
img_slot->some.tex = (MargaretTexture){};
|
||||
|
||||
|
||||
vkUpdateDescriptorSets(cache->ve.device, 1, &(VkWriteDescriptorSet){
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = cache->descriptor_set, .dstBinding = 0, .dstArrayElement = image_slot_id,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = &(VkDescriptorImageInfo){
|
||||
.sampler = cache->ve.nearest_sampler, .imageView = view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
|
||||
}
|
||||
}, 0, NULL);
|
||||
|
||||
// for (size_t i = 0; i < cache->dynamic_image_landscape.len; i++) {
|
||||
// cache->dynamic_image_landscape.buf[i] = 0;
|
||||
// }
|
||||
// VecU32_expand(&cache->dynamic_image_landscape, new_atlas_dim, 0);
|
||||
}
|
||||
|
||||
|
||||
LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, Abigail* abigail){
|
||||
VkDescriptorSetLayout my_desc_set_layout;
|
||||
VkDescriptorSetLayoutBindingFlagsCreateInfo set_layout_crinfo_flags = {
|
||||
@ -101,11 +159,107 @@ LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, Abigail* abigail){
|
||||
for (size_t i = 0; i < LUCY_MAX_DESCRIPTOR_COUNT; i++) {
|
||||
image_slots.buf[i].variant = Option_None;
|
||||
}
|
||||
return (LucyGlyphCache){
|
||||
.ve = ve, .abigail = abigail, .image_slots = image_slots,
|
||||
|
||||
/* Dynamic image stuff */
|
||||
MargaretSubbuf dyn_staging = MargaretBufAllocator_alloc(ve.staging_buffers, LUCY_INITIAL_DYN_STAGING_BUF_SIZE);
|
||||
|
||||
VkPhysicalDeviceProperties properties;
|
||||
vkGetPhysicalDeviceProperties(ve.physical_device, &properties);
|
||||
U32 max_dim = properties.limits.maxImageDimension2D;
|
||||
check(max_dim >= 10);
|
||||
|
||||
LucyGlyphCache cache = (LucyGlyphCache){
|
||||
.ve = ve, .abigail = abigail, .max_dim = max_dim,
|
||||
.image_slots = image_slots,
|
||||
.descriptor_set_layout = my_desc_set_layout, .descriptor_set = descriptor_set,
|
||||
.to_be_written_to_descriptor_set = VecU32_new(),
|
||||
.to_be_deleted = VecU32_new()};
|
||||
.dyn_staging = dyn_staging, .dynamic_image_slot = -1,
|
||||
/* .dynamic_image_landscape = VecU32_new(), */
|
||||
.dynamic_landscape_progress_x = 0
|
||||
};
|
||||
|
||||
LucyGlyphCache__create_dyn_texture(&cache, LUCY_INITIAL_DYN_IMAGE_DIM);
|
||||
cache.dynamic_image_landscape = VecU32_new_zeroinit(LUCY_INITIAL_DYN_IMAGE_DIM);
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
void LucyGlyphCache__extract_bitmap(FT_Face ft_face, U32 codepoint, U32 max_dim,
|
||||
TextureDataR8* ret_my_bitmap, FT_GlyphSlot* ret_slot
|
||||
) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* pos on atlas may be left undefined */
|
||||
void 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){
|
||||
.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,
|
||||
});
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
LucyFaceFixedSize* ffs = &ffs_node->value;
|
||||
BufRBTree_MapU32ToLucyStoredGlyph* glyph_set = &ffs->glyphs;
|
||||
U64 map_it = BufRBTree_MapU32ToLucyStoredGlyph_find(glyph_set, codepoint);
|
||||
if (map_it != 0)
|
||||
return &glyph_set->el.buf[map_it - 1].value;
|
||||
LucyGlyphCache* cache = ffs->p->p;
|
||||
|
||||
U32 max_dim = cache->max_dim;
|
||||
FT_Face ft_face = ffs->p->ft_face;
|
||||
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) {
|
||||
abortf("LucyCache_add_glyph_on_the_fly there is no way to fit this monstrosity\n");
|
||||
}
|
||||
another_attempt:
|
||||
{}
|
||||
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);
|
||||
U32 required_atlas_width = cache->dynamic_landscape_progress_x + my_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++) {
|
||||
start_y = MAX_U32(start_y, cache->dynamic_image_landscape.buf[gx]);
|
||||
}
|
||||
U32 ceiling = start_y + my_bitmap.height;
|
||||
if (ceiling > cur_dyn_img_dim) {
|
||||
/* This is serious. We need to swap */
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
void LucyFaceFixedSize_get_rid_of_myself(LucyFaceFixedSize* self){
|
||||
@ -117,9 +271,10 @@ void LucyFaceFixedSize_get_rid_of_myself(LucyFaceFixedSize* self){
|
||||
assert(img_slot->variant == Option_Some);
|
||||
LucyImage* img = &img_slot->some;
|
||||
assert(img->usage > 0);
|
||||
if (--img->usage) {
|
||||
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
|
||||
}
|
||||
}
|
||||
BufRBTree_MapU32ToLucyStoredGlyph_sink(glyphs);
|
||||
@ -171,20 +326,6 @@ void LucyPrepMassTransferImage_drop(LucyPrepMassTransferImage self) {
|
||||
|
||||
#include "../../../gen/l1/eve/lucy/VecLucyPrepMassTransferImage.h"
|
||||
|
||||
/* Helper function */
|
||||
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) {
|
||||
slot->variant = Option_Some;
|
||||
slot->some.usage = 0;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
abortf("LucyCache run out of image descriptor in a descriptor set (dictated by layout).\n"
|
||||
"You better add up on them\n");
|
||||
}
|
||||
|
||||
/* Helper function */
|
||||
LucyPrepMassTransferImage LucyGlyphCache_add_glyphs__another_image(LucyGlyphCache* cache) {
|
||||
return (LucyPrepMassTransferImage){
|
||||
@ -232,11 +373,6 @@ void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest
|
||||
for (size_t fi = 0; fi < requests_for_faces.len; fi++) {
|
||||
assert(cache == requests_for_faces.buf[fi].sized_face->value.p->p);
|
||||
}
|
||||
VkPhysicalDeviceProperties properties;
|
||||
vkGetPhysicalDeviceProperties(cache->ve.physical_device, &properties);
|
||||
U32 max_dim = properties.limits.maxImageDimension2D;
|
||||
check(max_dim >= 10);
|
||||
|
||||
|
||||
VecLucyPositionedStagingGlyph ready = VecLucyPositionedStagingGlyph_new();
|
||||
for (size_t fi = 0; fi < requests_for_faces.len; fi++) {
|
||||
@ -253,30 +389,11 @@ void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest
|
||||
for (U32 codepoint = range_start; codepoint < range_end; codepoint++) {
|
||||
if (BufRBTree_MapU32ToLucyStoredGlyph_find(glyph_set, codepoint) != 0)
|
||||
continue;
|
||||
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;
|
||||
FT_Bitmap* bitmap = &slot->bitmap;
|
||||
TextureDataR8 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);
|
||||
}
|
||||
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);
|
||||
for (S64 y = 0; y < bitmap->rows; y++) {
|
||||
for (S64 x = 0; x < bitmap->width; x++) {
|
||||
*TextureDataR8_mat(&my_bitmap, x, y) = *(bitmap->buffer + y * bitmap->pitch + x * sizeof(U8));
|
||||
}
|
||||
}
|
||||
BufRBTree_MapU32ToLucyStoredGlyph_insert(glyph_set, codepoint, (LucyStoredGlyph){
|
||||
.w = bitmap->width, .h = bitmap->rows,
|
||||
.advance_x = slot->advance.x >> 6,
|
||||
.bearing = (ivec2){ slot->bitmap_left, -slot->bitmap_top },
|
||||
/* x_on_atlas, y_on_atlas and img will be set later, when `ready` vector is ready */
|
||||
});
|
||||
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});
|
||||
VecLucyPositionedStagingGlyph_append(&ready, (LucyPositionedStagingGlyph){
|
||||
.sized_face = &req.sized_face->value, .codepoint = codepoint, .bitmap = my_bitmap,
|
||||
/* pos be filled later by packing algorithm */
|
||||
@ -301,7 +418,7 @@ void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest
|
||||
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) {
|
||||
if (new_width_required > cache->max_dim) {
|
||||
/* Resetting row */
|
||||
starting_x = 0;
|
||||
goto one_more_chance;
|
||||
@ -313,7 +430,7 @@ void LucyGlyphCache_add_glyphs(LucyGlyphCache* cache, VecLucyGlyphCachingRequest
|
||||
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) {
|
||||
if (new_height_required > cache->max_dim) {
|
||||
/* Resetting image */
|
||||
LucyGlyphCache_add_glyphs__close_img(cache, &mass_transfer, unprepared_image, img_width, img_height);
|
||||
starting_x = 0;
|
||||
@ -377,8 +494,6 @@ 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_written_to_descriptor_set);
|
||||
VecU32_drop(self.to_be_deleted);
|
||||
}
|
||||
|
||||
/* This function does not check font file for correctness, use only with trusted fonts */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user