diff --git a/src/l1/anne/lucy.h b/src/l1/anne/lucy.h index de57d28..120fe3d 100644 --- a/src/l1/anne/lucy.h +++ b/src/l1/anne/lucy.h @@ -11,7 +11,14 @@ void generate_l1_lucy_headers(){ .T = cstr("LucyImage"), .t_primitive = true}, true); generate_eve_span_company_for_primitive(l, ns, cstr("KVPU32ToLucyStoredGlyph"), true, false); generate_eve_span_company_for_primitive(l, ns, cstr("KVPU32ToLucyFaceFixedSize"), true, false); + generate_eve_span_company_for_non_primitive_non_clonable(l, ns, cstr("LucyGlyphCachingRequest"), true, true); + /* Vector of iterators */ + generate_eve_span_company_for_primitive(l, ns, cstr("RefListNodeLucyImage"), true, false); + + generate_util_templ_inst_eve_header(l, ns, (util_templates_instantiation_options){ + .T = cstr("LucyPositionedStagingGlyph"), .vec = true, .sort = true, + }); } #endif \ No newline at end of file diff --git a/src/l1/anne/margaret/margaret_misc.h b/src/l1/anne/margaret/margaret_misc.h index 30efee4..67fe12d 100644 --- a/src/l1/anne/margaret/margaret_misc.h +++ b/src/l1/anne/margaret/margaret_misc.h @@ -9,8 +9,7 @@ void generate_margaret_eve_for_vulkan_utils() { SpanU8 ns = cstr("margaret"); mkdir_nofail("l1/eve/margaret"); generate_util_templ_inst_eve_header(l, ns, (util_templates_instantiation_options){ - .T = cstr("MargaretScoredPhysicalDevice"), .t_primitive = true, .vec = true, .span = true, - .mut_span = true, .collab_vec_span = true, .span_sort = true + .T = cstr("MargaretScoredPhysicalDevice"), .t_primitive = true, .vec = true, .sort = true }); /* For l2/margaret/{ vulkan_img_claire.h , vulkan_buffer_claire.h } */ diff --git a/src/l1/anne/util_temp_vulkan.h b/src/l1/anne/util_temp_vulkan.h index c9a5a20..310b450 100644 --- a/src/l1/anne/util_temp_vulkan.h +++ b/src/l1/anne/util_temp_vulkan.h @@ -41,6 +41,7 @@ void generate_util_templ_inst_for_vulkan_headers() { generate_guarded_span_company_for_primitive(l, ns, cstr("VkSemaphore"), vulkan_dep, true, false); generate_guarded_span_company_for_primitive(l, ns, cstr("VkBufferCopy"), vulkan_dep, true, false); generate_guarded_span_company_for_primitive(l, ns, cstr("VkImageMemoryBarrier"), vulkan_dep, true, false); + generate_guarded_span_company_for_primitive(l, ns, cstr("VkDescriptorImageInfo"), vulkan_dep, true, false); } #endif \ No newline at end of file diff --git a/src/l1/codegen/list_template_inst.h b/src/l1/codegen/list_template_inst.h index 7e4495b..991d3d2 100644 --- a/src/l1/codegen/list_template_inst.h +++ b/src/l1/codegen/list_template_inst.h @@ -44,7 +44,7 @@ NODISCARD VecU8 generate_List_template_instantiation(list_instantiation_op op, b op.T, op.T, op.T, op.T, op.T, op.t_primitive ? vcstr("") : VecU8_fmt(SPACE SPACE "%s_drop(cur->el);\n", op.T))); VecU8_append_vec(&res, VecU8_fmt( - "void List%s_insert(List%s* self, %s el) {\n" /* op.T, op.T, op.T */ + "ListNode%s* List%s_insert(List%s* self, %s el) {\n" /* op.T, op.T, op.T, op.T */ SPACE "ListNode%s* new_node = safe_malloc(sizeof(ListNode%s));\n" /* op.T, op.T */ SPACE "new_node->prev = NULL;\n" SPACE "new_node->next = self->first;\n" @@ -52,7 +52,8 @@ NODISCARD VecU8 generate_List_template_instantiation(list_instantiation_op op, b SPACE "if (self->first)\n" SPACE SPACE "self->first->prev = new_node;\n" SPACE "self->first = new_node;\n" - "}\n\n", op.T, op.T, op.T, op.T, op.T)); + SPACE "return new_node;\n" + "}\n\n", op.T, op.T, op.T, op.T, op.T, op.T)); VecU8_append_vec(&res, VecU8_fmt( "void List%s_insert_node(List%s* self, ListNode%s* new_node) {\n" /* op.T, op.T, op.T */ SPACE "new_node->prev = NULL;\n" diff --git a/src/l1/codegen/util_template_inst.h b/src/l1/codegen/util_template_inst.h index 720b6bb..8c6eac8 100644 --- a/src/l1/codegen/util_template_inst.h +++ b/src/l1/codegen/util_template_inst.h @@ -260,9 +260,9 @@ void codegen_append_some_span_span_method(VecU8* res, SpanU8 SpanT) { /* T must be sized. Option `add_sort` requires option `add_mutable` and method T_less * add_mutable option generates MutSpanT. * add_equal option generates equal method. add_extended option generated extended methods - * add_sort option generates T_qcompare and MutSpanT_sort methods */ + */ NODISCARD VecU8 generate_SpanT_struct_and_methods( - SpanU8 T, bool integer, bool add_mutable, bool add_equal, bool add_extended, bool add_sort + SpanU8 T, bool integer, bool add_mutable, bool add_equal, bool add_extended ) { VecU8 g_SpanT = VecU8_fmt("Span%s", T); VecU8 g_MutSpanT = VecU8_fmt("MutSpan%s", T); @@ -293,31 +293,38 @@ NODISCARD VecU8 generate_SpanT_struct_and_methods( codegen_append_some_span_span_method(&res, MutSpanT); } - if (add_sort) { - assert(add_mutable); - VecU8_append_vec(&res, VecU8_fmt( - "int %s_qcompare(const void* a, const void* b) {\n" - SPACE "const %s* A = a;\n" - SPACE "const %s* B = b;\n", T, T, T)); - if (integer) { - VecU8_append_span(&res, cstr(SPACE "return (int)(B < A) - (int)(A < B);\n")); - } else { - VecU8_append_vec(&res, VecU8_fmt( - SPACE "return (int)%s_less_%s(B, A) - (int)%s_less_%s(A, B);\n", T, T, T, T)); - } - VecU8_append_vec(&res, VecU8_fmt( - "}\n\n" - "void %s_sort(%s self) {\n" - SPACE "qsort(self.data, self.len, sizeof(%s), %s_qcompare);\n" - "}\n\n", MutSpanT, MutSpanT, T, T)); - } - VecU8_drop(g_MutSpanT); VecU8_drop(g_SpanT); return res; } -// void codegen_append_vec_some_span_method(VecU8* res, SpanU8 mod, SpanU8 ) +NODISCARD VecU8 generate_span_company_sort_methods(SpanU8 T, bool t_integer, bool mut_span, bool vec){ + VecU8 res = VecU8_new(); + + VecU8_append_vec(&res, VecU8_fmt( + "int %s_qcompare(const void* a, const void* b) {\n" /* T */ + SPACE "const %s* A = a;\n" /* T */ + SPACE "const %s* B = b;\n" /* T */ + SPACE "return %v;\n" /* we return stuff */ + "}\n\n", + T, T, T, t_integer ? vcstr("(int)(*B < *A) - (int)(*A < *B)") : + VecU8_fmt("(int)%s_less_%s(B, A) - (int)%s_less_%s(A, B)", T, T, T, T))); + + if (mut_span) { + VecU8_append_vec(&res, VecU8_fmt( + "void MutSpan%s_sort(MutSpan%s self) {\n" + SPACE "qsort(self.data, self.len, sizeof(%s), %s_qcompare);\n" + "}\n\n", T, T, T, T)); + } + if (vec) { + VecU8_append_vec(&res, VecU8_fmt( + "void Vec%s_sort(Vec%s* self) {\n" + SPACE "qsort(self->buf, self->len, sizeof(%s), %s_qcompare);\n" + "}\n\n", T, T, T, T)); + } + + return res; +} /* T must be trivially movable. If !primitive, requires methods T_drop (implicitly) and, if clonable, requires T_clone */ NODISCARD VecU8 generate_SpanT_VecT_trivmove_collab(SpanU8 T, bool primitive, bool clonable, bool add_mutable, bool add_extended) { @@ -416,7 +423,7 @@ typedef struct { bool span; bool mut_span; bool span_extended; - bool span_sort; + bool sort; bool collab_vec_span; bool collab_vec_span_extended; } util_templates_instantiation_options; @@ -433,8 +440,6 @@ void util_templates_instantiation_options_fix(util_templates_instantiation_optio op->vec = true; if (op->span_extended) op->span = true; - if (op->span_sort) - op->span = true; if (op->mut_span) op->span = true; if (op->collab_vec_span_extended) @@ -467,7 +472,10 @@ NODISCARD VecU8 generate_util_templates_instantiation(util_templates_instantiati VecU8_append_vec(&res, generate_VecT_new_of_size_method(op.T)); } if (op.span) { - VecU8_append_vec(&res, generate_SpanT_struct_and_methods(op.T, op.t_integer, op.mut_span, false, op.span_extended, op.span_sort)); + VecU8_append_vec(&res, generate_SpanT_struct_and_methods(op.T, op.t_integer, op.mut_span, false, op.span_extended)); + } + if (op.sort) { + VecU8_append_vec(&res, generate_span_company_sort_methods(op.T, op.t_integer, op.mut_span, op.vec)); } if (op.collab_vec_span) { assert(op.vec && op.span); diff --git a/src/l1/core/VecU8_as_str.h b/src/l1/core/VecU8_as_str.h index ba7b0a0..90ac0ee 100644 --- a/src/l1/core/VecU8_as_str.h +++ b/src/l1/core/VecU8_as_str.h @@ -219,5 +219,37 @@ bool strings_in_spans_equal(SpanU8 a, SpanU8 b) { return true; } +/* 0 means error */ +U32 SpanU8_decode_as_utf8(SpanU8* rem){ + assert(rem->len > 0); + U8 first = rem->data[0]; + rem->data++; + rem->len--; + if (!(first & 0b10000000)) + return first; + uint8_t a = 0b11000000; + uint8_t b = 0b00100000; + for (int sz = 1; sz <= 3; sz++){ + if ((first & (a | b)) == a) { + /* sz is the character size in bytes */ + if (rem->len < (size_t)sz) + return 0; + U32 res = first & (b - 1); + for (int i = 1; i < sz; i++) { + U8 th = rem->data[0]; + if ((th & 0b11000000) != 0b10000000) + return 0; + res <<= 6; + res |= (th & 0b00111111); + rem->data++; + } + rem->len -= sz; + return res; + } + a |= b; + b >>= 1; + } + return 0; +} #endif diff --git a/src/l1_5/anne/lucy.h b/src/l1_5/anne/lucy.h index 7504aca..ebd85ce 100644 --- a/src/l1_5/anne/lucy.h +++ b/src/l1_5/anne/lucy.h @@ -9,7 +9,7 @@ void generate_l1_5_lucy_headers(){ generate_buf_rbtree_Map_templ_inst_eve_header(l, ns, (map_instantiation_op){ .K = cstr("U32"), .k_integer = true, .V = cstr("LucyStoredGlyph"), .v_primitive = true}); generate_rbtree_Map_templ_inst_eve_header(l, ns, (map_instantiation_op){ - .K = cstr("U32"), .k_integer = true, .V = cstr("LucyFaceFixedSize"), .v_primitive = true}, true); + .K = cstr("U32"), .k_integer = true, .V = cstr("LucyFaceFixedSize")}, true); } diff --git a/src/l2/lucy/glyph_cache.h b/src/l2/lucy/glyph_cache.h index 1dd6017..f579162 100644 --- a/src/l2/lucy/glyph_cache.h +++ b/src/l2/lucy/glyph_cache.h @@ -5,20 +5,36 @@ #include #include FT_FREETYPE_H #include "../../../gen/l1/VecAndSpan_U32Segment.h" +#include "../../../gen/l1/vulkan/VecVkDescriptorImageInfo.h" #include "../../l1_5/core/buff_rb_tree_node.h" #include "../../l1_5/core/rb_tree_node.h" +#define LUCY_MAX_DESCRIPTOR_COUNT 100 + typedef struct { + /* This value is actually Option. If staging_buffer is already deleted (after it is no longer used), + * staging_buffer.len will be 0 */ + MargaretSubbuf staging_buffer; MargaretImg img; + VkImageView img_view; U64 usage; U64 pos_in_desc_array; + /* 0 if this image isn't scheduled for deletion on th next cycle. + * 1 if it is */ + int scheduled_for_deletion; } LucyImage; #include "../../../gen/l1/eve/lucy/ListLucyImage.h" +typedef ListNodeLucyImage* RefListNodeLucyImage; +#include "../../../gen/l1/eve/lucy/VecRefListNodeLucyImage.h" + typedef struct { ListNodeLucyImage* img; - U32 x, y, w, h; + U32 w, h; + U32 advance_x; + ivec2 bearing; + uvec2 pos_on_atlas; } LucyStoredGlyph; typedef struct { @@ -28,35 +44,79 @@ typedef struct { #include "../../../gen/l1/eve/lucy/VecKVPU32ToLucyStoredGlyph.h" #include "../../../gen/l1_5/eve/lucy/BufRBTree_MapU32ToLucyStoredGlyph.h" -typedef struct { - U64 usage; +typedef struct LucyFace LucyFace; + +typedef struct{ + LucyFace* p; BufRBTree_MapU32ToLucyStoredGlyph glyphs; } LucyFaceFixedSize; +void LucyFaceFixedSize_drop(LucyFaceFixedSize self){ + BufRBTree_MapU32ToLucyStoredGlyph_drop(self.glyphs); +} + #include "../../../gen/l1_5/eve/lucy/RBTree_MapU32ToLucyFaceFixedSize.h" -typedef struct{ +// This is a very useful alias +typedef RBTreeNode_KVPU32ToLucyFaceFixedSize RBTreeNodeLucyFaceFixedSize; + +typedef struct LucyGlyphCache LucyGlyphCache; + +struct LucyFace { + LucyGlyphCache* p; FT_Face ft_face; RBTree_MapU32ToLucyFaceFixedSize sizes; -} LucyFace; +}; -typedef struct { +struct LucyGlyphCache { MargaretEngineReference ve; ListLucyImage images; VkDescriptorSetLayout descriptor_set_layout; VkDescriptorSet descriptor_set; -} LucyGlyphCache; + + /* to_be_freed_of_old_staging_next_cycle never intersect with to_be_copied_to_device_next_cycle */ + VecRefListNodeLucyImage to_be_freed_of_old_staging_next_cycle; + VecRefListNodeLucyImage to_be_copied_to_device_next_cycle; + /* deletion will be performed last */ + VecRefListNodeLucyImage to_be_deleted; +}; -// todo: write -LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, VkDescriptorSetLayout descriptor_set_layout){ - VkDescriptorSet descriptor_set = margaret_allocate_descriptor_set(ve.device, ve.descriptor_pool, descriptor_set_layout); - return (LucyGlyphCache){.ve = ve, - .images = ListLucyImage_new(), .descriptor_set_layout = descriptor_set_layout, .descriptor_set = descriptor_set}; +LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve){ + VkDescriptorSetLayout my_desc_set_layout; + check(vkCreateDescriptorSetLayout(ve.device, &(VkDescriptorSetLayoutCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = 1, + .pBindings = (VkDescriptorSetLayoutBinding[]){{ + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = LUCY_MAX_DESCRIPTOR_COUNT, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }}, + }, NULL, &my_desc_set_layout) == VK_SUCCESS); + VkDescriptorSet descriptor_set = margaret_allocate_descriptor_set(ve.device, ve.descriptor_pool, + my_desc_set_layout); + return (LucyGlyphCache){.ve = ve, .images = ListLucyImage_new(), + .descriptor_set_layout = my_desc_set_layout, .descriptor_set = descriptor_set}; +} + +void LucyFaceFixedSize_get_rid_of_myself(LucyFaceFixedSize* self){ + LucyGlyphCache* cache = self->p->p; + BufRBTree_MapU32ToLucyStoredGlyph* glyphs = &self->glyphs; + for (size_t gid = 0; gid < glyphs->el.len; gid++) { + ListNodeLucyImage* img = glyphs->el.buf[gid].value.img; + assert(img->el.usage > 0); + if (--img->el.usage) { + assert(!img->el.scheduled_for_deletion); + img->el.scheduled_for_deletion = 1; + VecRefListNodeLucyImage_append(&cache->to_be_deleted, img); + } + } + BufRBTree_MapU32ToLucyStoredGlyph_sink(glyphs); } typedef struct { - RBTreeNode_KVPU32ToLucyFaceFixedSize* sized_face; + RBTreeNodeLucyFaceFixedSize* sized_face; VecU32Segment codepoint_ranges; } LucyGlyphCachingRequest; @@ -66,28 +126,259 @@ void LucyGlyphCachingRequest_drop(LucyGlyphCachingRequest self){ #include "../../../gen/l1/eve/lucy/VecAndSpan_LucyGlyphCachingRequest.h" -void LucyGlyphCache_add_glyphs(SpanLucyGlyphCachingRequest requests_for_faces){ - // todo: write it all +/* Helper structure */ +typedef struct { + LucyFaceFixedSize* sized_face; + U32 codepoint; + TextureDataR8 bitmap; + /* Will be determined in the next phase */ + uvec2 pos; + ListNodeLucyImage* img; +} LucyPositionedStagingGlyph; + +bool LucyPositionedStagingGlyph_less_LucyPositionedStagingGlyph( + const LucyPositionedStagingGlyph* A, const LucyPositionedStagingGlyph* B){ + return A->bitmap.height < B->bitmap.height; +} + +void LucyPositionedStagingGlyph_drop(LucyPositionedStagingGlyph self){ + TextureDataR8_drop(self.bitmap); +} + +/* Instantiation for helper type */ +#include "../../../gen/l1/eve/lucy/VecLucyPositionedStagingGlyph.h" + +/* Helper function */ +void LucyGlyphCache_add_glyphs__close_img( + LucyGlyphCache* cache, ListNodeLucyImage* img, U32 img_width, U32 img_height + ){ + assert(img->el.usage > 0); + img_width = MAX_U32(img_width, 10); // Just a precaution. empty buffers aren't supported by Margaret + img_height = MAX_U32(img_height, 10); + VecRefListNodeLucyImage_append(&cache->to_be_copied_to_device_next_cycle, img); + img->el.staging_buffer = MargaretBufAllocator_alloc(cache->ve.staging_buffers, img_width * img_height * 1); + img->el.img = MargaretImgAllocator_alloc(cache->ve.dev_local_images, img_width, img_height, VK_FORMAT_R8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + img->el.img_view = margaret_create_view_for_image(cache->ve.device, img->el.img.a.image, + VK_FORMAT_R8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); +} + +void LucyGlyphCache_add_glyphs(VecLucyGlyphCachingRequest requests_for_faces){ + if (requests_for_faces.len == 0) + return; + LucyGlyphCache* cache = requests_for_faces.buf[0].sized_face->value.p->p; for (size_t fi = 0; fi < requests_for_faces.len; fi++) { - LucyGlyphCachingRequest req = requests_for_faces.data[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++) { + LucyGlyphCachingRequest req = requests_for_faces.buf[fi]; + FT_Face ft_face = req.sized_face->value.p->ft_face; U32 font_height = req.sized_face->key; + check(FT_Set_Pixel_Sizes(ft_face, 0, font_height) == 0); BufRBTree_MapU32ToLucyStoredGlyph* glyph_set = &req.sized_face->value.glyphs; + /* Phase 1, where we add some elements to glyph_set, but we don't + * know how many image we will have and we don't know where new glyphs will be placed */ for (size_t ri = 0; ri < req.codepoint_ranges.len; ri++) { U32 range_start = req.codepoint_ranges.buf[ri].start; U32 range_end = range_start + req.codepoint_ranges.buf[ri].len; for (U32 codepoint = range_start; codepoint < range_end; codepoint++) { - // todo: render it all + 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); + } + /* 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 */ + }); + 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 */ + }); + } + } + /* 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; + ListNodeLucyImage* img = ListLucyImage_insert(&cache->images, (LucyImage){0}); + for (size_t j = 0; j < ready.len; j++) { + LucyPositionedStagingGlyph* p_glyph; + one_more_chance: + p_glyph = &ready.buf[j]; + 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, img_width, img_height); + starting_x = 0; + landscape.len = 0; + img_width = 0; + img_height = 0; + img = ListLucyImage_insert(&cache->images, (LucyImage){0}); + 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 = img; + p_glyph->pos = (uvec2){starting_x, height_here}; + img->el.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 = img; + starting_x += p_glyph->bitmap.width; + } + LucyGlyphCache_add_glyphs__close_img(cache, img, 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]; + U64 staging_width = p_glyph->img->el.img.width; + U8* staging = (U8*)MargaretSubbuf_get_mapped(&p_glyph->img->el.staging_buffer); + 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++) { + U64 X = x + p_glyph->pos.x; + staging[Y * staging_width + X] = *TextureDataR8_at(&p_glyph->bitmap, x, y); + } } } } + VecLucyPositionedStagingGlyph_drop(ready); + VecLucyGlyphCachingRequest_drop(requests_for_faces); } -void LucyGlyphCache_delete(LucyGlyphCache* self){ - -} - +/* This must not happen before all the LucyFaceFixedSizes are destroyed */ void LucyGlyphCache_drop(LucyGlyphCache self){ - ListLucyImage_drop(self.images); + assert(self.images.first == NULL); + VecRefListNodeLucyImage_drop(self.to_be_freed_of_old_staging_next_cycle); + VecRefListNodeLucyImage_drop(self.to_be_copied_to_device_next_cycle); + VecRefListNodeLucyImage_drop(self.to_be_deleted); } -#endif \ No newline at end of file +void LucyGlyphCache_another_frame(LucyGlyphCache* self){ + for (size_t i = 0; i < self->to_be_freed_of_old_staging_next_cycle.len; i++) { + LucyImage* img = &self->to_be_freed_of_old_staging_next_cycle.buf[i]->el; + assert(img->staging_buffer.len != 0); + MargaretBufAllocator_free(self->ve.staging_buffers, img->staging_buffer); + img->staging_buffer.len = 0; + } + for (size_t i = 0; i < self->to_be_copied_to_device_next_cycle.len; i++) { + ListNodeLucyImage* img_node = self->to_be_copied_to_device_next_cycle.buf[i]; + LucyImage* img = &img_node->el; + assert(img->staging_buffer.len != 0); + if (img->scheduled_for_deletion) + continue; + margaret_rec_cmd_copy_buffer_to_image_one_to_one_color_aspect(self->ve.transfer_cmd_buffer, + &img->staging_buffer, &img->img, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); + VecRefListNodeLucyImage_append(&self->to_be_freed_of_old_staging_next_cycle, img_node); + } + /* We technically could carry out each deletion request in O(1) and each img creation request in O(1), + * but who cares, it's no problem going over the entire descriptor set when something get's added or deleted */ + for (size_t i = 0; i < self->to_be_deleted.len; i++) { + ListNodeLucyImage* img_node = self->to_be_deleted.buf[i]; + LucyImage* img = &img_node->el; + assert(img->scheduled_for_deletion); + assert(img->usage == 0); + if (img->staging_buffer.len != 0) + MargaretBufAllocator_free(self->ve.staging_buffers, img->staging_buffer); + MargaretImgAllocator_free(self->ve.dev_local_images, img->img.a); + ListLucyImage_erase_by_it(&self->images, img_node); + } + if ((self->to_be_copied_to_device_next_cycle.len > 0) || (self->to_be_deleted.len > 0)) { + U32 descriptor_i = 0; + VecVkDescriptorImageInfo desc_elements = VecVkDescriptorImageInfo_new(); + for (ListNodeLucyImage* list_node = self->images.first; list_node; list_node = list_node->next) { + if (descriptor_i == LUCY_MAX_DESCRIPTOR_COUNT) { + abortf("Today you are out of luck\n"); + } + LucyImage* img = &list_node->el; + img->pos_in_desc_array = descriptor_i; + VecVkDescriptorImageInfo_append(&desc_elements, (VkDescriptorImageInfo){ + .sampler = self->ve.nearest_sampler, .imageView = img->img_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }); + descriptor_i++; + } + vkUpdateDescriptorSets(self->ve.device, 1, &(VkWriteDescriptorSet){ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = self->descriptor_set, .dstBinding = 0, .dstArrayElement = 0, + .descriptorCount = desc_elements.len, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = desc_elements.buf + }, 0, NULL); + VecVkDescriptorImageInfo_drop(desc_elements); + } + self->to_be_freed_of_old_staging_next_cycle.len = 0; + self->to_be_copied_to_device_next_cycle.len = 0; + self->to_be_deleted.len = 0; +} + +/* This function does not check font file for correctness, use only with trusted fonts */ +LucyFace LucyFace_new(FT_Library lib, LucyGlyphCache* cache, VecU8 path){ + VecU8_append(&path, 0); // Making it null-terminated + FT_Face face; + FT_Error ret = FT_New_Face(lib, (const char*)path.buf, 0, &face); + check(ret == 0); + VecU8_drop(path); + return (LucyFace){.p = cache, .ft_face = face, .sizes = RBTree_MapU32ToLucyFaceFixedSize_new()}; +} + +RBTreeNodeLucyFaceFixedSize* LucyFace_of_size(LucyFace* self, U32 size){ + RBTreeNodeLucyFaceFixedSize* nahodka = RBTree_MapU32ToLucyFaceFixedSize_find(&self->sizes, size); + if (nahodka) + return nahodka; + RBTree_MapU32ToLucyFaceFixedSize_insert(&self->sizes, size, (LucyFaceFixedSize){ + .p = self, .glyphs = BufRBTree_MapU32ToLucyStoredGlyph_new() + }); + // todo: add a method to RBTree for proper node insertion. This is just pure crap + return RBTree_MapU32ToLucyFaceFixedSize_find(&self->sizes, size); +} + +#endif diff --git a/src/l2/lucy/glyph_render.h b/src/l2/lucy/glyph_render.h index 7a16daa..002b096 100644 --- a/src/l2/lucy/glyph_render.h +++ b/src/l2/lucy/glyph_render.h @@ -5,6 +5,7 @@ #include "../../../gen/l1/pixel_masses.h" #include "../../../gen/l1/geom.h" +// todo: rewrite this shit crap using instances typedef struct{ vec4 color; vec2 pos; @@ -12,44 +13,38 @@ typedef struct{ U32 tex_ind; } LucyVertex; -typedef struct{ - LucyGlyphCache cache; +typedef struct { + MargaretEngineReference ve; + LucyGlyphCache* cache; VkPipelineLayout pipeline_layout; VkPipeline pipeline; -} LucyGlyphRenderer; -#define LUCY_MAX_DESCRIPTOR_COUNT 10 + U64 vertex_count; + MargaretSubbuf staging_vbo; + MargaretSubbuf vbo; + bool need_to_transfer; +} LucyRenderer; -LucyGlyphRenderer LucyGlyphRenderer_new( - MargaretEngineReference engine_reference, SpanU8 root_dir, +typedef struct { + float width, height; +} LucyRendererPushConstants; + +LucyRenderer LucyRenderer_new( + MargaretEngineReference ve, LucyGlyphCache* cache, SpanU8 root_dir, VkRenderPass render_pass, U32 renderpass_subpass){ - VkDescriptorSetLayout descriptor_set_layout; - check(vkCreateDescriptorSetLayout(engine_reference.device, &(VkDescriptorSetLayoutCreateInfo){ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .bindingCount = 1, - .pBindings = (VkDescriptorSetLayoutBinding[]){{ - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = LUCY_MAX_DESCRIPTOR_COUNT, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - }}, - }, NULL, &descriptor_set_layout) == VK_SUCCESS); - - - LucyGlyphCache cache = LucyGlyphCache_new(engine_reference, descriptor_set_layout); VkPipelineLayout pipeline_layout; - check(vkCreatePipelineLayout(engine_reference.device, &(VkPipelineLayoutCreateInfo){ + check(vkCreatePipelineLayout(ve.device, &(VkPipelineLayoutCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, - .pSetLayouts = &descriptor_set_layout, + .pSetLayouts = &cache->descriptor_set_layout, .pushConstantRangeCount = 1, .pPushConstantRanges = (VkPushConstantRange[]){{ - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .offset = 0, .size = sizeof(float) * 2 + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .offset = 0, .size = sizeof(LucyRendererPushConstants) }}, }, NULL, &pipeline_layout) == VK_SUCCESS); - VkPipeline pipeline = margaret_create_triangle_pipeline_one_attachment(engine_reference.device, + VkPipeline pipeline = margaret_create_triangle_pipeline_one_attachment(ve.device, render_pass, renderpass_subpass, (MargaretMostImportantPipelineOptions){ .pipeline_layout = pipeline_layout, .vertex_shader_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/lucy/vert.spv", root_dir)), @@ -71,7 +66,130 @@ LucyGlyphRenderer LucyGlyphRenderer_new( .depthTestEnable = false, .depthWriteEnable = false, .blendEnable = true, }); - return (LucyGlyphRenderer){.cache = cache, .pipeline_layout = pipeline_layout, .pipeline = pipeline}; + return (LucyRenderer){.ve = ve, .cache = cache, .pipeline_layout = pipeline_layout, .pipeline = pipeline, + .vertex_count = 0, .staging_vbo = MargaretBufAllocator_alloc(ve.staging_buffers, 67), + .vbo = MargaretBufAllocator_alloc(ve.dev_local_buffers, 67) + }; +} + +/* When another_frame starts, you are safe to call this function, but you also have to call it + * before LucyRenderer_another_frame + */ +void LucyRenderer_clear(LucyRenderer* self){ + self->vertex_count = 0; +} + +void LucyRenderer__append_vertex_to_vao(LucyRenderer* self, LucyVertex vert_data){ + self->vertex_count++; + assert(self->vertex_count * sizeof(LucyVertex) <= self->staging_vbo.len); + LucyVertex* staging = (LucyVertex*)MargaretSubbuf_get_mapped(&self->staging_vbo); + staging[self->vertex_count - 1] = vert_data; +} + +/* When another_frame starts, you are safe to call this function, but you also have to call it + * before LucyRenderer_another_frame, because _another_frame method records transfer of data + */ +void LucyRenderer_add_text( + LucyRenderer* self, RBTreeNodeLucyFaceFixedSize* ffs, vec4 color, + U32 additional_y_advance, SpanU8 text, ivec2 start_pos + ){ + self->need_to_transfer = true; + U32 font_height = ffs->key; + ivec2 pos = (ivec2){start_pos.x, start_pos.y + (S32)font_height}; + /* `text` variable will be modified during decoding */ + while (text.len > 0) { + U32 codepoint = SpanU8_decode_as_utf8(&text); + if (codepoint == (U32)'\n') { + pos = (ivec2){start_pos.x, pos.y + (S32)font_height + (S32)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; + if (glyph->w > 0 && glyph->h > 0) { + float atlas_w = (float)glyph->img->el.img.width; + float atlas_h = (float)glyph->img->el.img.height; + U32 desc_elem_id = glyph->img->el.pos_in_desc_array; + ivec2 positioned = ivec2_add_ivec2(pos, glyph->bearing); + LucyVertex v0 = { + .color = color, .pos = (vec2){(float)positioned.x, (float)positioned.y}, + .tex_cord = (vec2){ + (float)(glyph->pos_on_atlas.x) / atlas_w, + (float)(glyph->pos_on_atlas.y) / atlas_h + }, .tex_ind = desc_elem_id + }; + LucyVertex v1 = { + .color = color, .pos = (vec2){(float)(positioned.x + glyph->w), (float)positioned.y}, + .tex_cord = (vec2){ + (float)(glyph->pos_on_atlas.x + glyph->w) / atlas_w, + (float)(glyph->pos_on_atlas.y) / atlas_h + }, .tex_ind = desc_elem_id + }; + LucyVertex v2 = { + .color = color, .pos = (vec2){(float)positioned.x, (float)(positioned.y + glyph->h)}, + .tex_cord = (vec2){ + (float)(glyph->pos_on_atlas.x) / atlas_w, + (float)(glyph->pos_on_atlas.y + glyph->h) / atlas_h + }, .tex_ind = desc_elem_id + }; + LucyVertex v3 = { + .color = color, .pos = (vec2){(float)(positioned.x + glyph->w), (float)(positioned.y + glyph->h)}, + .tex_cord = (vec2){ + (float)(glyph->pos_on_atlas.x + glyph->w) / atlas_w, + (float)(glyph->pos_on_atlas.y + glyph->h) / atlas_h + }, .tex_ind = desc_elem_id + }; + /* What if we run out of space? */ + U64 needed_vbo_length = (self->vertex_count + 6) * sizeof(LucyVertex); + if (self->staging_vbo.len < needed_vbo_length) { + printf("LucyRenderer Staging Buffer: Gotta replace %lu with %lu\n", + self->staging_vbo.len, needed_vbo_length); + MargaretBufAllocator_expand_or_move_old_host_visible( + self->ve.staging_buffers, &self->staging_vbo, needed_vbo_length); + } + LucyRenderer__append_vertex_to_vao(self, v1); + LucyRenderer__append_vertex_to_vao(self, v0); + LucyRenderer__append_vertex_to_vao(self, v2); + LucyRenderer__append_vertex_to_vao(self, v1); + LucyRenderer__append_vertex_to_vao(self, v2); + LucyRenderer__append_vertex_to_vao(self, v3); + + } + pos.x += (S32)glyph->advance_x; + } +} + +/* It only records transfer commands (transfer command buffer is passed in MargaretEngineReference object) */ +void LucyRenderer_another_frame(LucyRenderer* self){ + if (self->vbo.len < self->vertex_count * sizeof(LucyVertex)) { + MargaretBufAllocator_expand_or_free_old(self->ve.dev_local_buffers, &self->vbo, + self->vertex_count * sizeof(LucyVertex)); + } + if (self->need_to_transfer && self->vertex_count > 0) { + self->need_to_transfer = false; + margaret_rec_cmd_copy_buffer_one_to_one_part(self->ve.transfer_cmd_buffer, + &self->staging_vbo, &self->vbo, 0, self->vertex_count * sizeof(LucyVertex)); + } +} + +// todo: merge with the function above. In the future all the shit will be recorded simultaneously +void LucyRenderer_another_frame_rec_drawing( + LucyRenderer* self, VkCommandBuffer drawing_cmd_buf, VkExtent2D image_extent){ + vkCmdBindPipeline(drawing_cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, self->pipeline); + vkCmdPushConstants(drawing_cmd_buf, self->pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, + 0, sizeof(LucyRendererPushConstants), + &(LucyRendererPushConstants){ .width = (float)image_extent.width, .height = (float)image_extent.height }); + vkCmdBindVertexBuffers(drawing_cmd_buf, 0, 1, + (VkBuffer[]){MargaretSubbuf_get_buffer(&self->vbo)}, (VkDeviceSize[]){self->vbo.start}); + vkCmdBindDescriptorSets(drawing_cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, self->pipeline_layout, 0, + 1, (VkDescriptorSet[]){self->cache->descriptor_set}, 0, NULL); + vkCmdDraw(drawing_cmd_buf, self->vertex_count, 1, 0, 0); } #endif \ No newline at end of file diff --git a/src/l2/margaret/vulkan_buffer_claire.h b/src/l2/margaret/vulkan_buffer_claire.h index ad2447e..c7b0f4f 100644 --- a/src/l2/margaret/vulkan_buffer_claire.h +++ b/src/l2/margaret/vulkan_buffer_claire.h @@ -288,7 +288,8 @@ void MargaretBufAllocator_shrink(MargaretBufAllocator* self, MargaretSubbuf* all * But if ret value .len field is non-zero it means a valid MargaretSubbuf object was returned and the * `allocation` argument was untouched. It remains a valid object, you need to deallocate it yourself */ -NODISCARD MargaretSubbuf MargaretBufAllocator_expand(MargaretBufAllocator* self, MargaretSubbuf* allocation, U64 bigger_size){ +NODISCARD MargaretSubbuf MargaretBufAllocator_expand( + MargaretBufAllocator* self, MargaretSubbuf* allocation, U64 bigger_size){ bigger_size = margaret_bump_buffer_size_to_alignment(bigger_size, self->alignment_exp); U64Segment right_free_space = MargaretBufAllocator__get_right_free_space(self, allocation); @@ -314,3 +315,31 @@ VkBuffer MargaretSubbuf_get_buffer(const MargaretSubbuf* allocation){ assert(allocation->start + allocation->len <= allocation->block->capacity); return allocation->block->buf_hand; } + +/* It tries to expand buffer, but if it fails, it creates a freshly-new buffer. It copies all + * the data from old buffer to new one and frees the old buffer, while replacing + * info in `allocation` variable with info about new allocation. + */ +void MargaretBufAllocator_expand_or_move_old_host_visible( + MargaretBufAllocator* self, MargaretSubbuf* allocation, U64 bigger_size){ + assert(self->host_visible); + MargaretSubbuf maybe_bigger = MargaretBufAllocator_expand(self, allocation, bigger_size); + if (maybe_bigger.len > 0) { + memcpy(MargaretSubbuf_get_mapped(&maybe_bigger), MargaretSubbuf_get_mapped(allocation), allocation->len); + MargaretBufAllocator_free(self, *allocation); + *allocation = maybe_bigger; + } +} + +/* It tries to expand buffer, but if it fails, it creates a freshly-new buffer. It + * frees the old buffer, while replacing + * info in `allocation` variable with info about new allocation. Old data gets lost + */ +void MargaretBufAllocator_expand_or_free_old( + MargaretBufAllocator* self, MargaretSubbuf* allocation, U64 bigger_size){ + MargaretSubbuf maybe_bigger = MargaretBufAllocator_expand(self, allocation, bigger_size); + if (maybe_bigger.len > 0) { + MargaretBufAllocator_free(self, *allocation); + *allocation = maybe_bigger; + } +} \ No newline at end of file diff --git a/src/l2/margaret/vulkan_utils.h b/src/l2/margaret/vulkan_utils.h index 9624070..132f7e3 100644 --- a/src/l2/margaret/vulkan_utils.h +++ b/src/l2/margaret/vulkan_utils.h @@ -302,7 +302,7 @@ NODISCARD VecU8 margaret_stringify_device_memory_properties_2(VkPhysicalDevice p return VecU8_fmt( "maxMemoryAllocationsCount: %u\n" "maxMemoryAllocationSize: %u\n" - "maxBufferSize: %u!!!!!!!!!\n", + "maxBufferSize: %u\n", maxMemoryAllocationCount, maxMemoryAllocationSize, maxBufferSize); } @@ -320,14 +320,20 @@ VkDevice margaret_create_logical_device(VkPhysicalDevice physical_device, Margar logical_device_queue_crinfo[0].queueFamilyIndex = queue_fam.for_graphics; logical_device_queue_crinfo[1].queueFamilyIndex = queue_fam.for_presentation; + VkPhysicalDeviceVulkan12Features used_vk12_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .runtimeDescriptorArray = true, + .shaderSampledImageArrayNonUniformIndexing = true, + }; // We DEMAND synchronization2 VkPhysicalDeviceSynchronization2Features used_synchronization2_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + .pNext = &used_vk12_features, .synchronization2 = VK_TRUE, }; VkPhysicalDeviceFeatures2 used_features2 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, - .pNext = (void*)&used_synchronization2_features, + .pNext = &used_synchronization2_features, .features = (VkPhysicalDeviceFeatures) { .geometryShader = true, .samplerAnisotropy = physical_features.samplerAnisotropy, @@ -547,12 +553,16 @@ MargaretScoredPhysicalDevice margaret_score_physical_device( SpanU8_print(VecU8_to_span(&txt)); VecU8_drop(txt); } + VkPhysicalDeviceVulkan12Features vk12_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES + }; VkPhysicalDeviceSynchronization2Features synchronization2_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + .pNext = &vk12_features }; VkPhysicalDeviceFeatures2 features2 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, - .pNext = (void*)&synchronization2_features, + .pNext = &synchronization2_features, }; vkGetPhysicalDeviceFeatures2(dev, &features2); // printf("Device %s\nmaxBoundDescriptorSets: %" PRIu32 " \nmaxPerStageDescriptorUniformBuffers: %" PRIu32 "\n" @@ -569,8 +579,10 @@ MargaretScoredPhysicalDevice margaret_score_physical_device( return (MargaretScoredPhysicalDevice){dev, -1, cstr("No geometry shaders")}; if (!synchronization2_features.synchronization2) return (MargaretScoredPhysicalDevice){dev, -1, cstr("No synchronization2")}; - if (features2.features.samplerAnisotropy) - score += 2; + if (!vk12_features.shaderSampledImageArrayNonUniformIndexing) + return (MargaretScoredPhysicalDevice){dev, -1, cstr("No shaderSampledImageArrayNonUniformIndexing")}; + if (!vk12_features.runtimeDescriptorArray) + return (MargaretScoredPhysicalDevice){dev, -1, cstr("No runtimeDescriptorArray")}; ResultMargaretChosenQueueFamiliesOrSpanU8 queue_families = margaret_choose_good_queue_families(dev, surface); if (queue_families.variant == Result_Err) return (MargaretScoredPhysicalDevice){dev, -1, queue_families.err}; @@ -602,7 +614,7 @@ MargaretScoredPhysicalDevice margaret_score_physical_device( #define MargaretScoredPhysicalDevice_less_MargaretScoredPhysicalDevice(cap, cbp) ((cap)->score < (cbp)->score) -#include "../../../gen/l1/eve/margaret/VecAndSpan_MargaretScoredPhysicalDevice.h" +#include "../../../gen/l1/eve/margaret/VecMargaretScoredPhysicalDevice.h" VecMargaretScoredPhysicalDevice margaret_get_physical_devices_scored( VkInstance instance, VkSurfaceKHR surface, @@ -621,7 +633,7 @@ VecMargaretScoredPhysicalDevice margaret_get_physical_devices_scored( favourite_word, forbidden_word, sane_image_extent_limit ); } - MutSpanMargaretScoredPhysicalDevice_sort(VecMargaretScoredPhysicalDevice_to_mspan(&scored_devices)); + VecMargaretScoredPhysicalDevice_sort(&scored_devices); VecVkPhysicalDevice_drop(physical_devices); return scored_devices; } @@ -1065,8 +1077,8 @@ VkPipeline margaret_create_triangle_pipeline_one_attachment( .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .depthClampEnable = VK_FALSE, .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = VK_CULL_MODE_BACK_BIT, - // .cullMode = VK_CULL_MODE_NONE, + // .cullMode = VK_CULL_MODE_BACK_BIT, + .cullMode = VK_CULL_MODE_NONE, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .depthBiasEnable = VK_FALSE, .depthBiasConstantFactor = 0.0f, @@ -1138,6 +1150,8 @@ typedef struct { MargaretBufAllocator* dev_local_buffers; MargaretBufAllocator* staging_buffers; VkDescriptorPool descriptor_pool; + VkSampler linear_sampler; + VkSampler nearest_sampler; } MargaretEngineReference; void margaret_rec_cmd_copy_buffer( diff --git a/src/l2/tests/r0/fonts/DMSerifText-Regular.ttf b/src/l2/tests/r0/fonts/DMSerifText-Regular.ttf new file mode 100644 index 0000000..e3f6248 Binary files /dev/null and b/src/l2/tests/r0/fonts/DMSerifText-Regular.ttf differ diff --git a/src/l2/tests/r0/r0.c b/src/l2/tests/r0/r0.c index 3b0f620..9a85865 100644 --- a/src/l2/tests/r0/r0.c +++ b/src/l2/tests/r0/r0.c @@ -624,7 +624,7 @@ void reset_and_record_command_buffer_1( const PipelineHands* pipeline_and_layout_1, VkFramebuffer swapchain_image_framebuffer, VkExtent2D image_extent, VkExtent2D max_win_size, const Scene* scene, - VkDescriptorSet descriptor_set_for_pipeline_1 + VkDescriptorSet descriptor_set_for_pipeline_1, LucyRenderer* lucy_renderer ) { margaret_reset_and_begin_command_buffer(command_buffer); @@ -658,15 +658,16 @@ void reset_and_record_command_buffer_1( sizeof(Pipeline1PushRangeVertex), sizeof(Pipeline1PushRangeFragment), ®_and_params_fragment); vkCmdDraw(command_buffer, 3, 1, 0, 0); + LucyRenderer_another_frame_rec_drawing(lucy_renderer, command_buffer, image_extent); + vkCmdEndRenderPass(command_buffer); margaret_end_command_buffer(command_buffer); } /* This function records a command buffer that copies EVERYTHING. You won't normally need to copy every single thing, - * but here you have the most bold solution. Resets + starts + ends command buffer + * but here you have the most bold solution. Does not 'begin', does not 'end' comamnd buffer * */ void record_copying_entire_scene_from_staging_to_device_local(VkCommandBuffer command_buffer, const Scene* scene) { - margaret_reset_and_begin_command_buffer(command_buffer); for (size_t mi = 0; mi < scene->generic_models.len; mi++) { const GenericModelOnSceneMem* model = VecGenericModelOnSceneMem_at(&scene->generic_models, mi); assert(model->instance_attr.count * sizeof(GenericMeshInstance) <= model->instance_attr.staging_busy.len); @@ -710,7 +711,6 @@ void record_copying_entire_scene_from_staging_to_device_local(VkCommandBuffer co margaret_rec_cmd_copy_buffer_one_to_one_part(command_buffer, ubo_staging, ubo_device_local, offsetof(Pipeline0UBO, spotlight_arr), sizeof(Pipeline0Spotlight) * mem->spotlight_count); } - margaret_end_command_buffer(command_buffer); } } @@ -753,6 +753,12 @@ typedef struct { MargaretImg IT1_image; MargaretImg zbuffer_image; + FT_Library ft_library; + LucyGlyphCache lucy_cache; + LucyFace font_face; + RBTreeNodeLucyFaceFixedSize* font_face_of_size_40; + LucyRenderer lucy_renderer; + VecGenericModelTexVulkPointers generic_model_tex_vulk_pointers; VkImageView zbuffer_view; VkImageView IT1_view; @@ -1097,14 +1103,14 @@ void vulkano_frame_drawing(state_r0* state) { mat4 camera_translation_matrix = marie_translation_mat4(vec3_minus(state->vk.scene.cam.pos)); mat4 t_mat = mat4_mul_mat4(projection_matrix, mat4_mul_mat4(camera_rotation_matrix, camera_translation_matrix)); + margaret_reset_and_begin_command_buffer(state->vk.transfer_command_buf); record_copying_entire_scene_from_staging_to_device_local(state->vk.transfer_command_buf, &state->vk.scene); - check(vkQueueSubmit(state->vk.queues.graphics_queue, 1, &(VkSubmitInfo){ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .commandBufferCount = 1, - .pCommandBuffers = (VkCommandBuffer[]){ state->vk.transfer_command_buf }, - .signalSemaphoreCount = 1, - .pSignalSemaphores = (VkSemaphore[]){ state->vk.jane.in_frame_transfer_complete }, - }, NULL) == VK_SUCCESS); + LucyGlyphCache_another_frame(&state->vk.lucy_cache); + LucyRenderer_clear(&state->vk.lucy_renderer); + LucyRenderer_add_text(&state->vk.lucy_renderer, state->vk.font_face_of_size_40, (vec4){1, 0, 0, 1}, 0, + VecU8_to_span(&state->vk.scene.text_on_screen), (ivec2){10, 10}); + LucyRenderer_another_frame(&state->vk.lucy_renderer); + margaret_end_command_buffer(state->vk.transfer_command_buf); reset_and_record_command_buffer_0( state->vk.rendering_command_buf_0, state->vk.render_pass_0, @@ -1119,7 +1125,16 @@ void vulkano_frame_drawing(state_r0* state) { &state->vk.pipeline_hands_1, *VecVkFramebuffer_at(&state->vk.swfb.framebuffers, ij), state->vk.swfb.extent, - state->sane_image_extent_limit, &state->vk.scene, state->vk.descriptor_set_for_pipeline_1); + state->sane_image_extent_limit, &state->vk.scene, state->vk.descriptor_set_for_pipeline_1, + &state->vk.lucy_renderer); + + check(vkQueueSubmit(state->vk.queues.graphics_queue, 1, &(VkSubmitInfo){ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = (VkCommandBuffer[]){ state->vk.transfer_command_buf }, + .signalSemaphoreCount = 1, + .pSignalSemaphores = (VkSemaphore[]){ state->vk.jane.in_frame_transfer_complete }, + }, NULL) == VK_SUCCESS); check(vkQueueSubmit(state->vk.queues.graphics_queue, 1, &(VkSubmitInfo){ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, @@ -1306,6 +1321,10 @@ static void main_h_wl_keyboard_key( } else if (keysym == XKB_KEY_3) { state->vk.scene.hdr_factor *= 1.05f; printf("hdr factor increased to %f\n", state->vk.scene.hdr_factor); + } else if (keysym == XKB_KEY_7) { + VecU8_append(&state->vk.scene.text_on_screen, '7'); + } else if (keysym == XKB_KEY_Return) { + VecU8_append(&state->vk.scene.text_on_screen, '\n'); } } } @@ -1484,6 +1503,7 @@ int main() { compile_shader_dir(cstr("0b"), false); compile_shader_dir(cstr("1"), false); + SpanU8 root_dir = cstr("../../../.."); SpanU8 GPU = cstr("nvidia"); SpanU8 bugged_GPU = cstr("nothere"); bool ENABLE_VALIDATION_LAYERS = true; @@ -1541,11 +1561,6 @@ int main() { vk->physical_device = margaret_select_one_physical_device( instance, vk->surface, GPU, bugged_GPU, state.sane_image_extent_limit); - { /* Funny vibe check */ - VecU8 txt = margaret_stringify_device_memory_properties_2(vk->physical_device); - SpanU8_print(VecU8_to_span(&txt)); - } - // print_physical_device_available_extensions(physical_device); ResultMargaretChosenQueueFamiliesOrSpanU8 queue_fam_res = margaret_choose_good_queue_families( @@ -1743,6 +1758,32 @@ int main() { vk->zbuffer_image = MargaretImgAllocator_alloc(&vk->dev_local_images, MAX_WIN_WIDTH, MAX_WIN_HEIGHT, vk->zbuffer_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + MargaretEngineReference engine_reference = { + .device = vk->device, .physical_device = vk->physical_device, .transfer_cmd_buffer = vk->transfer_command_buf, + .dev_local_images = &vk->dev_local_images, .dev_local_buffers = &vk->dev_local_buffers, + .staging_buffers = &vk->staging_buffers, .descriptor_pool = vk->descriptor_pool, + .linear_sampler = vk->linear_sampler, .nearest_sampler = vk->nearest_sampler + }; + + FT_Error ft_init_err = FT_Init_FreeType(&vk->ft_library); + if (ft_init_err) + abortf("Can't init free type library\n"); + + vk->lucy_cache = LucyGlyphCache_new(engine_reference); + vk->font_face = LucyFace_new(vk->ft_library, &vk->lucy_cache, vcstr("fonts/DMSerifText-Regular.ttf")); + vk->font_face_of_size_40 = LucyFace_of_size(&vk->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 = vk->font_face_of_size_40, .codepoint_ranges = ranges_needed, + }); + LucyGlyphCache_add_glyphs(lucy_requests); + } + VecU8_append_span(&vk->scene.text_on_screen, cstr("I am late :(\nExam is in 25 hours...")); + vk->lucy_renderer = LucyRenderer_new(engine_reference, &vk->lucy_cache, root_dir, vk->render_pass_1, 0); + { GenericModelOnSceneMem *model_g = VecGenericModelOnSceneMem_mat(&vk->scene.generic_models, 0); assert(model_g->instance_attr.cap >= 100); diff --git a/src/l2/tests/r0/r0_scene.h b/src/l2/tests/r0/r0_scene.h index 597f6ae..95c4e56 100644 --- a/src/l2/tests/r0/r0_scene.h +++ b/src/l2/tests/r0/r0_scene.h @@ -4,13 +4,16 @@ #include "r0_assets.h" #include "../../margaret/vulkan_utils.h" +#include "../../lucy/glyph_render.h" typedef struct { U64 count; MargaretSubbuf staging_busy; MargaretSubbuf staging_updatable; MargaretSubbuf device_local; - U64 cap; /* All 3 buffers are synced to the same capacity */ + U64 cap; + // todo: delete this crap. This crap turned out to be completely useless. It is another one of my very very dumb ideas + // todo: remove updatable buffer, fill staging buffer in main thread } PatriciaBuf; void PatriciaBuf_swap_staging(PatriciaBuf* self){ @@ -164,8 +167,9 @@ typedef struct { CamControlInfo cam; VecObjectInfo smeshnyavka_1; - VecObjectInfo smeshnyavka_2; VecObjectInfo smeshnyavka_3; + + VecU8 text_on_screen; } Scene; ShinyMeshInstanceInc ShinyMeshInstanceInc_from_ObjectInfo(const ObjectInfo* oi){ @@ -186,7 +190,7 @@ void Scene_add_smeshnyavka_3(Scene* self, ObjectInfo oi){ ShinyModelOnSceneMem_set(model_sh, ni, ShinyMeshInstanceInc_from_ObjectInfo(&oi)); } -// todo: remove this shit +// todo: remove this shit (and rewrite everything in haskell) void Scene_update_smeshnyavka_3(Scene* self, size_t sh_id){ assert(sh_id < self->smeshnyavka_3.len); const ObjectInfo* oi = VecObjectInfo_at(&self->smeshnyavka_3, sh_id); @@ -227,8 +231,9 @@ Scene Scene_new(VecGenericModelOnSceneMem generic_models, VecShinyModelOnSceneMe .color = {.float32 = {0, 0, 0, 1}}, .gamma_correction_factor = 2.2f, .hdr_factor = 1, .lsd_factor = 0, .anim_time = 0, .pipeline0_ubo = pipeline0_ubo, .cam = CamControlInfo_new(), - .smeshnyavka_1 = VecObjectInfo_new(), .smeshnyavka_2 = VecObjectInfo_new(), + .smeshnyavka_1 = VecObjectInfo_new(), .smeshnyavka_3 = VecObjectInfo_new(), // todo: remove this shit and rewrite everything in haskell + .text_on_screen = VecU8_new(), }; } diff --git a/src/l_adele/lucy/lucy.frag b/src/l_adele/lucy/lucy.frag index 972aac8..24272f9 100644 --- a/src/l_adele/lucy/lucy.frag +++ b/src/l_adele/lucy/lucy.frag @@ -12,5 +12,5 @@ layout (binding=0) uniform sampler2D images[]; void main(){ float I = texture(images[nonuniformEXT(tex_ind)], tex_cord).r; - fin_color = color * vec4(I, I, I, I); + fin_color = vec4(color.rgb, color.a * I); }