I wrote Lucy. It works. I can render text. Example is written in r0. Too bad I am running out of time. Exam is the next day after tomorrow :(

This commit is contained in:
Андреев Григорий 2025-12-23 22:47:15 +03:00
parent 516fab6ff6
commit 19f92d9207
15 changed files with 657 additions and 111 deletions

View File

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

View File

@ -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 } */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,20 +5,36 @@
#include <ft2build.h>
#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<MargaretSubbuf>. 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
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

View File

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

View File

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

View File

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

Binary file not shown.

View File

@ -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), &reg_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);

View File

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

View File

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