Omg, I just deleted all the crap I wrote 12.02. I made static resource transfer SO MUCH EASIER. But in exchange for simplicity, the rules for when you can acreate and ddelete meshes and font faces are ultra-convoluted. Because I removed all the deletions&copying queues. Everything is super-low-level now

This commit is contained in:
Андреев Григорий 2026-02-14 02:02:04 +03:00
parent b05c64a131
commit 248b81f2ec
8 changed files with 201 additions and 408 deletions

View File

@ -26,4 +26,7 @@ void generate_margaret_eve_for_vulkan_utils() {
generate_eve_span_company_for_non_primitive_non_clonable(l, ns, cstr("MargaretImgAllocatorOneBlock"), true, false);
generate_List_templ_inst_eve_header(l, ns, (list_instantiation_op){.T = cstr("MargaretBufAllocatorOneBlock")}, true);
generate_guarded_span_company_for_primitive(l, ns, cstr("MargaretSubbuf"),
cstr("#include \"../../../src/l2/margaret/vulkan_utils.h\"\n"), true, false);
}

View File

@ -30,7 +30,7 @@ NODISCARD VecU8 generate_List_template_instantiation(list_instantiation_op op, b
"} List%s;\n\n", /* op.T */
op.T, op.T);
VecU8_append_fmt(&res,
"#define List%s_new() {0}\n\n" /* op.T */
"#define List%s_new() ((List%s){0})\n\n" /* op.T, op.T */
"void List%s_drop(List%s self) {\n" /* op.T, op.T */
SPACE "ListNode%s* cur = self.first;\n" /* op.T */
SPACE "while (cur){\n"
@ -40,7 +40,7 @@ NODISCARD VecU8 generate_List_template_instantiation(list_instantiation_op op, b
SPACE SPACE "cur = next;\n"
SPACE "}\n"
"}\n\n",
op.T, op.T, op.T, op.T, op.T,
op.T, 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_fmt(&res,
"ListNode%s* List%s_insert(List%s* self, %s el) {\n" /* op.T, op.T, op.T, op.T */

View File

@ -6,12 +6,6 @@
#include "transfer_in_mainloop.h"
#include "../lucy/glyph_render.h"
typedef struct {
U64 count;
MargaretSubbuf staging;
MargaretSubbuf device_local;
} PatriciaBuf;
typedef struct {
/* Each generic model has its own descriptor set
* It's because it has its own textures. But it also has copies of references to light UBO
@ -27,30 +21,25 @@ typedef struct {
TextureDataR8G8B8A8 pixels_normal;
TextureDataR8 pixels_specular;
AliceBufferUplOnce* vbo;
AliceBufferUplOnce* ebo;
MargaretSubbuf vbo;
MargaretSubbuf ebo;
AliceTextureUplOnce* diffuse_texture;
AliceTextureUplOnce* normal_texture;
AliceTextureUplOnce* specular_texture;
MargaretTexture diffuse_texture;
MargaretTexture normal_texture;
MargaretTexture specular_texture;
PatriciaBuf instance_attr;
AliceGenericMeshMemDependantVkObj mem_dependant_vk_obj;
bool scheduled_for_deletion;
} AliceGenericMeshHand;
#include "../../../gen/l1/eve/alice/ListAliceGenericMeshHand.h"
typedef struct {
size_t indexes;
AliceBufferUplOnce* vbo;
AliceBufferUplOnce* ebo;
MargaretSubbuf vbo;
MargaretSubbuf ebo;
PatriciaBuf instance_attr;
bool scheduled_for_deletion;
} AliceShinyMeshHand;
#include "../../../gen/l1/eve/alice/ListAliceShinyMeshHand.h"
@ -712,34 +701,9 @@ float AliceWaylandApp_get_elapsed_time(const AliceWaylandApp* self){
typedef struct ListNodeAliceGenericMeshHand* RefListNodeAliceGenericMeshHand;
#include "../../../gen/l1/eve/alice/VecRefListNodeAliceGenericMeshHand.h"
typedef struct {
ListAliceGenericMeshHand hands;
VecRefListNodeAliceGenericMeshHand to_be_deleted;
} AliceAllMeshesGeneric;
AliceAllMeshesGeneric AliceAllMeshesGeneric_new(){
return (AliceAllMeshesGeneric){
.hands = ListAliceGenericMeshHand_new(),
.to_be_deleted = VecRefListNodeAliceGenericMeshHand_new(),
};
}
typedef struct ListNodeAliceShinyMeshHand* RefListNodeAliceShinyMeshHand;
#include "../../../gen/l1/eve/alice/VecRefListNodeAliceShinyMeshHand.h"
typedef struct {
ListAliceShinyMeshHand hands;
/* Ah */
VecRefListNodeAliceShinyMeshHand to_be_deleted;
} AliceAllMeshesShiny;
AliceAllMeshesShiny AliceAllMeshesShiny_new(){
return (AliceAllMeshesShiny){
.hands = ListAliceShinyMeshHand_new(),
.to_be_deleted = VecRefListNodeAliceShinyMeshHand_new(),
};
}
struct Alice {
AliceCallbacks callbacks;
@ -780,12 +744,11 @@ struct Alice {
Jane_alice jane; // todo: figure out my own design
MargaretSwapchainBundle swfb;
AliceAllSingleUploadBuffers single_upload_buffers;
AliceAllSingleUploadTextures single_upload_textures;
Abigail abigail;
AlicePipline0LightConf pipeline0_light_conf;
AliceAllMeshesGeneric generic_models;
AliceAllMeshesShiny shiny_models;
ListAliceGenericMeshHand generic_models;
ListAliceShinyMeshHand shiny_models;
AliceCamVerticalControl cam_info;
/* stuff like background color, hdr factor and
* postprocessing kernel. Mostly related to renderpass 1 */
@ -803,6 +766,13 @@ struct Alice {
VkFramebuffer IT1_framebuffer;
VkDescriptorSet descriptor_set_for_pipeline_0;
VkDescriptorSet descriptor_set_for_pipeline_1;
/* You see, there are two places when user can write to transfer_command_buffer: When Alice is initialized, but
* the very first frame was not sent in flight, and when we are in a gap between two frames in flight.
* transfer_command_buffer is immediately opened for writing on input, and won't be re-began on the first frame.
* But on the subsequent frames it will be reste and bagan-for-writing after we done waiting for in-flight fence
*/
bool transfer_command_buf_already_reset;
};
ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, const GenericMeshTopology* topology,
@ -824,36 +794,43 @@ ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, const Generic
VecU8_drop(t_paths.normal_texture_path);
VecU8_drop(t_paths.specular_texture_path);
mm->vbo = AliceAllSingleUploadBuffers_register_new(&alice->single_upload_buffers,
GenericMeshVertex* staging_vbo;
mm->vbo = Abigail_register_new_buffer(&alice->abigail,
topology->vertices.len * sizeof(GenericMeshVertex),
&alice->staging_buffers, &alice->dev_local_buffers);
mm->ebo = AliceAllSingleUploadBuffers_register_new(&alice->single_upload_buffers,
topology->indexes.len * sizeof(U32),
&alice->staging_buffers, &alice->dev_local_buffers);
alice->transfer_command_buf, &alice->staging_buffers, &alice->dev_local_buffers,
(void**)&staging_vbo /* We return values here */);
mm->diffuse_texture = AliceAllSingleUploadTextures_register_new(&alice->single_upload_textures,
U32* staging_ebo;
mm->ebo = Abigail_register_new_buffer(&alice->abigail,
topology->indexes.len * sizeof(U32),
alice->transfer_command_buf, &alice->staging_buffers, &alice->dev_local_buffers,
(void**)&staging_ebo /* We return values here */);
void* diffuse_tex_staging;
mm->diffuse_texture = Abigail_register_new_texture(&alice->abigail,
mm->pixels_diffuse.width, mm->pixels_diffuse.height,
sizeof(cvec4), VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
&alice->staging_buffers, &alice->dev_local_images);
mm->normal_texture = AliceAllSingleUploadTextures_register_new(&alice->single_upload_textures,
alice->transfer_command_buf, &alice->staging_buffers, &alice->dev_local_images,
&diffuse_tex_staging);
void *normal_tex_staging;
mm->normal_texture = Abigail_register_new_texture(&alice->abigail,
mm->pixels_normal.width, mm->pixels_normal.height,
sizeof(cvec4), VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
&alice->staging_buffers, &alice->dev_local_images);
mm->specular_texture = AliceAllSingleUploadTextures_register_new(&alice->single_upload_textures,
alice->transfer_command_buf, &alice->staging_buffers, &alice->dev_local_images,
&normal_tex_staging);
void* specular_tex_staging;
mm->specular_texture = Abigail_register_new_texture(&alice->abigail,
mm->pixels_specular.width, mm->pixels_specular.height,
sizeof(U8), VK_FORMAT_R8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
&alice->staging_buffers, &alice->dev_local_images);
mm->scheduled_for_deletion = false;
alice->transfer_command_buf, &alice->staging_buffers, &alice->dev_local_images,
&specular_tex_staging);
assert(mm->vbo.len >= topology->vertices.len * sizeof(GenericMeshVertex));
/* We allocated enough memory, but now it's time to actually fill staging buffers */
/* Filling staging VBO */
assert(mm->vbo->staging.len >= topology->vertices.len * sizeof(GenericMeshVertex));
assert(mm->vbo->device_buf.len >= topology->vertices.len * sizeof(GenericMeshVertex));
GenericMeshVertex* staging_vbo = (GenericMeshVertex*)AliceBufferUplOnce_get_mapped_staging(mm->vbo);
for (U64 i = 0; i < topology->vertices.len; i++) {
staging_vbo[i].base = topology->vertices.buf[i];
}
@ -883,16 +860,14 @@ ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, const Generic
/* Filling EBO is easy */
assert(topology->indexes.len == mm->indexes);
size_t ebo_len = topology->indexes.len * sizeof(U32);
assert(mm->ebo->staging.len >= ebo_len);
U32* staging_ebo = (U32*)AliceBufferUplOnce_get_mapped_staging(mm->ebo);
memcpy(staging_ebo, topology->indexes.buf, ebo_len);
/* Filling staging textures from memory pixel data */
/* todo: do it immediately ON THE READ */
memcpy(AliceTextureUplOnce_get_mapped_staging(mm->diffuse_texture), mm->pixels_diffuse.pixels.buf,
TextureDataR8G8B8A8_get_size_in_bytes(&mm->pixels_diffuse));
memcpy(AliceTextureUplOnce_get_mapped_staging(mm->normal_texture), mm->pixels_normal.pixels.buf,
memcpy(diffuse_tex_staging, mm->pixels_diffuse.pixels.buf,
TextureDataR8G8B8A8_get_size_in_bytes(&mm->pixels_diffuse));
memcpy(normal_tex_staging, mm->pixels_normal.pixels.buf,
TextureDataR8G8B8A8_get_size_in_bytes(&mm->pixels_normal));
memcpy(AliceTextureUplOnce_get_mapped_staging(mm->specular_texture), mm->pixels_specular.pixels.buf,
memcpy(specular_tex_staging, mm->pixels_specular.pixels.buf,
TextureDataR8_get_size_in_bytes(&mm->pixels_specular));
// todo: that is about time when we can delete mm->pixels_* buffers. They are still wasting space out there
// todo: BUt I won't do it, because I chose inplace texture reading (from png)
@ -913,7 +888,7 @@ ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, const Generic
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &(VkDescriptorImageInfo){
.sampler = alice->linear_sampler, .imageView = mm->diffuse_texture->view,
.sampler = alice->linear_sampler, .imageView = mm->diffuse_texture.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
},
},
@ -925,7 +900,7 @@ ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, const Generic
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &(VkDescriptorImageInfo){
.sampler = alice->nearest_sampler, .imageView = mm->normal_texture->view,
.sampler = alice->nearest_sampler, .imageView = mm->normal_texture.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
},
},
@ -937,14 +912,14 @@ ListNodeAliceGenericMeshHand* Alice_add_generic_mesh(Alice* alice, const Generic
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &(VkDescriptorImageInfo){
.sampler = alice->nearest_sampler, .imageView = mm->specular_texture->view,
.sampler = alice->nearest_sampler, .imageView = mm->specular_texture.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
},
},
};
vkUpdateDescriptorSets(alice->device, ARRAY_SIZE(writes_in_descriptor_set), writes_in_descriptor_set, 0, NULL);
ListAliceGenericMeshHand_insert_node(&alice->generic_models.hands, mm_node);
ListAliceGenericMeshHand_insert_node(&alice->generic_models, mm_node);
return mm_node;
}
@ -958,18 +933,21 @@ ListNodeAliceShinyMeshHand* Alice_add_shiny_mesh(Alice* alice, const ShinyMeshTo
mm->instance_attr.staging = MargaretBufAllocator_alloc(&alice->staging_buffers, 128);
mm->instance_attr.device_local = MargaretBufAllocator_alloc(&alice->dev_local_buffers, 128);
mm->vbo = AliceAllSingleUploadBuffers_register_new(&alice->single_upload_buffers,
ShinyMeshVertex* staging_vbo;
mm->vbo = Abigail_register_new_buffer(&alice->abigail,
topology->vertices.len * sizeof(ShinyMeshVertex),
&alice->staging_buffers, &alice->dev_local_buffers);
mm->ebo = AliceAllSingleUploadBuffers_register_new(&alice->single_upload_buffers,
topology->indexes.len * sizeof(U32),
&alice->staging_buffers, &alice->dev_local_buffers);
alice->transfer_command_buf, &alice->staging_buffers, &alice->dev_local_buffers,
(void**)&staging_vbo);
U32* staging_ebo;
mm->ebo = Abigail_register_new_buffer(&alice->abigail,
topology->indexes.len * sizeof(U32),
alice->transfer_command_buf, &alice->staging_buffers, &alice->dev_local_buffers,
(void**)&staging_ebo);
assert(mm->vbo.len >= topology->vertices.len * sizeof(ShinyMeshVertex));
/* We allocated enough memory, now it's time to actually fill staging buffers */
/* And we start by filling staging VBO */
assert(mm->vbo->staging.len >= topology->vertices.len * sizeof(ShinyMeshVertex));
assert(mm->vbo->device_buf.len >= topology->vertices.len * sizeof(ShinyMeshVertex));
ShinyMeshVertex* staging_vbo = (ShinyMeshVertex*)AliceBufferUplOnce_get_mapped_staging(mm->vbo);
for (U64 i = 0; i < topology->vertices.len; i++) {
staging_vbo[i].base = topology->vertices.buf[i];
}
@ -988,31 +966,38 @@ ListNodeAliceShinyMeshHand* Alice_add_shiny_mesh(Alice* alice, const ShinyMeshTo
/* Filling staging EBO is super-duper easy */
assert(topology->indexes.len == mm->indexes);
size_t ebo_len = topology->indexes.len * sizeof(U32);
assert(mm->ebo->staging.len >= ebo_len);
U32* staging_ebo = (U32*)AliceBufferUplOnce_get_mapped_staging(mm->ebo);
memcpy(staging_ebo, topology->indexes.buf, ebo_len);
ListAliceShinyMeshHand_insert_node(&alice->shiny_models.hands, mm_node);
ListAliceShinyMeshHand_insert_node(&alice->shiny_models, mm_node);
return mm_node;
}
// todo: write deletion after I separate textures from Meshes
void Alice_delete_generic_mesh(Alice* alice, ListNodeAliceGenericMeshHand* hand) {
AliceAllSingleUploadBuffers_delete(&alice->single_upload_buffers, hand->el.vbo);
AliceAllSingleUploadBuffers_delete(&alice->single_upload_buffers, hand->el.ebo);
AliceAllSingleUploadTextures_delete(&alice->single_upload_textures, hand->el.diffuse_texture);
AliceAllSingleUploadTextures_delete(&alice->single_upload_textures, hand->el.normal_texture);
AliceAllSingleUploadTextures_delete(&alice->single_upload_textures, hand->el.specular_texture);
hand->el.normal_texture = hand->el.normal_texture = hand->el.specular_texture = NULL;
hand->el.vbo = hand->el.ebo = NULL;
VecRefListNodeAliceGenericMeshHand_append(&alice->generic_models.to_be_deleted, hand);
AliceGenericMeshHand* mm = &hand->el;
MargaretBufAllocator_free(&alice->dev_local_buffers, mm->vbo);
MargaretBufAllocator_free(&alice->dev_local_buffers, mm->ebo);
MargaretImgAllocator_free(&alice->dev_local_images, mm->diffuse_texture.img.a);
MargaretImgAllocator_free(&alice->dev_local_images, mm->normal_texture.img.a);
MargaretImgAllocator_free(&alice->dev_local_images, mm->specular_texture.img.a);
MargaretBufAllocator_free(&alice->staging_buffers, mm->instance_attr.staging);
MargaretBufAllocator_free(&alice->dev_local_buffers, mm->instance_attr.device_local);
// todo: the problem is, here we don't free some memory stored in AliceGenericMeshHand to store texture.
// todo: but the truth is: I am gonna get rid of pixels_{,,} fields very soon, so AliceGenericMeshHand would
// todo: be primitive again, without stupid crap
ListAliceGenericMeshHand_erase_by_it(&alice->generic_models, hand);
}
/* Be careful to only delete meshes when you actually allowed to do so */
void Alice_delete_shiny_mesh(Alice* alice, ListNodeAliceShinyMeshHand* hand) {
AliceAllSingleUploadBuffers_delete(&alice->single_upload_buffers, hand->el.vbo);
AliceAllSingleUploadBuffers_delete(&alice->single_upload_buffers, hand->el.ebo);
hand->el.vbo = hand->el.ebo = NULL;
VecRefListNodeAliceShinyMeshHand_append(&alice->shiny_models.to_be_deleted, hand);
AliceShinyMeshHand* mm = &hand->el;
MargaretBufAllocator_free(&alice->dev_local_buffers, hand->el.vbo);
MargaretBufAllocator_free(&alice->dev_local_buffers, hand->el.ebo);
MargaretBufAllocator_free(&alice->staging_buffers, mm->instance_attr.staging);
MargaretBufAllocator_free(&alice->dev_local_buffers, mm->instance_attr.device_local);
ListAliceShinyMeshHand_erase_by_it(&alice->shiny_models, hand);
}
@ -1058,37 +1043,6 @@ void AliceShinyMeshHand_set_inst(AliceShinyMeshHand* self, size_t instance, Shin
tr_inv.x.z, tr_inv.y.z, tr_inv.z.z );
}
void AliceAllMeshesGeneric__resolve_on_another_frame(Alice* alice, AliceAllMeshesGeneric* self){
for (size_t i = 0; i < self->to_be_deleted.len; i++) {
ListNodeAliceGenericMeshHand* mm_node = self->to_be_deleted.buf[i];
AliceGenericMeshHand* mm = &mm_node->el;
assert(mm->scheduled_for_deletion);
assert(mm->vbo == NULL);
assert(mm->ebo == NULL);
MargaretBufAllocator_free(&alice->staging_buffers, mm->instance_attr.staging);
MargaretBufAllocator_free(&alice->dev_local_buffers, mm->instance_attr.device_local);
// todo: the problem is, here we don't free some memory stored in AliceGenericMeshHand to store texture.
// todo: but the truth is: I am gonna get rid of pixels_{,,} fields very soon, so AliceGenericMeshHand would
// todo: be primitive again, without stupid crap
ListAliceGenericMeshHand_erase_by_it(&self->hands, mm_node);
}
self->to_be_deleted.len = 0;
}
void AliceAllMeshesShiny__resolve_on_another_frame(Alice* alice, AliceAllMeshesShiny* self){
for (size_t i = 0; i < self->to_be_deleted.len; i++) {
ListNodeAliceShinyMeshHand* mm_node = self->to_be_deleted.buf[i];
AliceShinyMeshHand* mm = &mm_node->el;
assert(mm->scheduled_for_deletion);
assert(mm->vbo == NULL);
assert(mm->ebo == NULL);
MargaretBufAllocator_free(&alice->staging_buffers, mm->instance_attr.staging);
MargaretBufAllocator_free(&alice->dev_local_buffers, mm->instance_attr.device_local);
ListAliceShinyMeshHand_erase_by_it(&self->hands, mm_node);
}
self->to_be_deleted.len = 0;
}
/* No buffer rerecording, no buffer beginning, no buffer ending,
* 1) It copies initial generic model and shiny model topology. Textures of generic models included
* 2) For all models and for all light sources it copies EVERYTHING.
@ -1096,10 +1050,8 @@ void AliceAllMeshesShiny__resolve_on_another_frame(Alice* alice, AliceAllMeshesS
* 3) As mentioned before, Pipeline0UBO also gets copied
*/
void AliceScene__another_frame(Alice* alice) {
AliceAllMeshesGeneric__resolve_on_another_frame(alice, &alice->generic_models);
AliceAllMeshesShiny__resolve_on_another_frame(alice, &alice->shiny_models);
for (ListNodeAliceGenericMeshHand* mm_node = alice->generic_models.hands.first; mm_node; mm_node = mm_node->next) {
for (ListNodeAliceGenericMeshHand* mm_node = alice->generic_models.first; mm_node; mm_node = mm_node->next) {
AliceGenericMeshHand* mm = &mm_node->el;
assert(mm->instance_attr.count * sizeof(GenericMeshInstance) <= mm->instance_attr.staging.len);
if (mm->instance_attr.count * sizeof(GenericMeshInstance) > mm->instance_attr.device_local.len) {
@ -1113,7 +1065,7 @@ void AliceScene__another_frame(Alice* alice) {
mm->instance_attr.count * sizeof(GenericMeshInstance));
}
}
for (ListNodeAliceShinyMeshHand* mm_node = alice->shiny_models.hands.first; mm_node; mm_node = mm_node->next) {
for (ListNodeAliceShinyMeshHand* mm_node = alice->shiny_models.first; mm_node; mm_node = mm_node->next) {
AliceShinyMeshHand* mm = &mm_node->el;
assert(mm->instance_attr.count * sizeof(ShinyMeshInstance) <= mm->instance_attr.staging.len);
if (mm->instance_attr.count * sizeof(ShinyMeshInstance) > mm->instance_attr.device_local.len) {
@ -1187,21 +1139,19 @@ void alice_reset_and_record_command_buffer_0(Alice* alice, mat4 proj_cam_t) {
alice->rendering_command_buf_0, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_0a.pipeline_layout,
0, 1, &alice->descriptor_set_for_pipeline_0, 0, NULL);
for (ListNodeAliceGenericMeshHand* mm_node = alice->generic_models.hands.first; mm_node; mm_node = mm_node->next) {
for (ListNodeAliceGenericMeshHand* mm_node = alice->generic_models.first; mm_node; mm_node = mm_node->next) {
AliceGenericMeshHand* model = &mm_node->el;
VkDescriptorSet descriptor_set1_for_textures = model->mem_dependant_vk_obj.p_0a_set_1;
const MargaretSubbuf* dev_local_vbo = &model->vbo->device_buf;
const MargaretSubbuf* dev_local_inst_attr = &model->instance_attr.device_local;
const MargaretSubbuf* dev_local_ebo = &model->ebo->device_buf;
// const
vkCmdBindVertexBuffers(alice->rendering_command_buf_0, 0,
2, (VkBuffer[]){
MargaretSubbuf_get_buffer(dev_local_vbo),
MargaretSubbuf_get_buffer(&model->vbo),
MargaretSubbuf_get_buffer(dev_local_inst_attr),
}, (VkDeviceSize[]){ dev_local_vbo->start, dev_local_inst_attr->start });
vkCmdBindIndexBuffer(alice->rendering_command_buf_0, MargaretSubbuf_get_buffer(dev_local_ebo), dev_local_ebo->start,
VK_INDEX_TYPE_UINT32);
}, (VkDeviceSize[]){ model->vbo.start, dev_local_inst_attr->start });
vkCmdBindIndexBuffer(alice->rendering_command_buf_0,
MargaretSubbuf_get_buffer(&model->ebo), model->ebo.start, VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(
alice->rendering_command_buf_0, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_0a.pipeline_layout,
1, 1, &descriptor_set1_for_textures, 0, NULL);
@ -1218,16 +1168,14 @@ void alice_reset_and_record_command_buffer_0(Alice* alice, mat4 proj_cam_t) {
alice->rendering_command_buf_0, VK_PIPELINE_BIND_POINT_GRAPHICS, alice->pipeline_hands_0b.pipeline_layout,
0, 1, &alice->descriptor_set_for_pipeline_0, 0, NULL);
for (ListNodeAliceShinyMeshHand* mm_node = alice->shiny_models.hands.first; mm_node; mm_node = mm_node->next) {
for (ListNodeAliceShinyMeshHand* mm_node = alice->shiny_models.first; mm_node; mm_node = mm_node->next) {
const AliceShinyMeshHand* model = &mm_node->el;
const MargaretSubbuf* dev_local_vbo = &model->vbo->device_buf;
const MargaretSubbuf* dev_local_inst_attr = &model->instance_attr.device_local;
const MargaretSubbuf* dev_local_ebo = &model->ebo->device_buf;
vkCmdBindVertexBuffers(alice->rendering_command_buf_0, 0, 2, (VkBuffer[]){
MargaretSubbuf_get_buffer(dev_local_vbo), MargaretSubbuf_get_buffer(dev_local_inst_attr)
}, (VkDeviceSize[]){ dev_local_vbo->start, dev_local_inst_attr->start });
vkCmdBindIndexBuffer(alice->rendering_command_buf_0, MargaretSubbuf_get_buffer(dev_local_ebo), dev_local_ebo->start,
VK_INDEX_TYPE_UINT32);
MargaretSubbuf_get_buffer(&model->vbo), MargaretSubbuf_get_buffer(dev_local_inst_attr)
}, (VkDeviceSize[]){ model->vbo.start, dev_local_inst_attr->start });
vkCmdBindIndexBuffer(alice->rendering_command_buf_0,
MargaretSubbuf_get_buffer(&model->ebo), model->ebo.start, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(alice->rendering_command_buf_0, model->indexes, model->instance_attr.count, 0, 0, 0);
}
@ -1398,20 +1346,19 @@ void alice_frame_drawing(Alice* alice) {
mat4 camera_translation_matrix = marie_translation_mat4(vec3_minus(alice->cam_info.cam.pos));
mat4 t_mat = mat4_mul_mat4(projection_matrix, mat4_mul_mat4(camera_rotation_matrix, camera_translation_matrix));
if (!alice->transfer_command_buf_already_reset) {
Abigail_wipe_old_staging(&alice->abigail, alice->device, &alice->staging_buffers);
margaret_reset_and_begin_command_buffer(alice->transfer_command_buf);
} else {
alice->transfer_command_buf_already_reset = false;
}
alice->callbacks.on_another_frame(alice->guest, (float)(alice->wl.cur_frame_time - alice->wl.last_frame_time) / 1000);
margaret_reset_and_begin_command_buffer(alice->transfer_command_buf);
/* Literally everything, including lucy cache, depends on these two steps */
AliceAllSingleUploadBuffers__resolve_on_another_frame(&alice->single_upload_buffers,
&alice->staging_buffers, &alice->dev_local_buffers, alice->transfer_command_buf);
AliceAllSingleUploadTextures__resolve_on_another_frame(&alice->single_upload_textures,
&alice->staging_buffers, &alice->dev_local_images, alice->transfer_command_buf);
AliceScene__another_frame(alice);
LucyGlyphCache_another_frame(&alice->lucy_cache);
// LucyGlyphCache_another_frame(&alice->lucy_cache); lucy cache has no business here
LucyRenderer_another_frame(&alice->lucy_renderer);
margaret_end_command_buffer(alice->transfer_command_buf);
alice_reset_and_record_command_buffer_0(alice, t_mat);
alice_reset_and_record_command_buffer_1(alice, *VecVkFramebuffer_at(&alice->swfb.framebuffers, ij));
@ -1906,11 +1853,10 @@ Alice* Alice_new(){
alice->device, alice->queue_fam, swapchain_details_res.ok,
alice->surface, alice->render_pass_1, NULL);
alice->single_upload_buffers = AliceAllSingleUploadBuffers_new();
alice->single_upload_textures = AliceAllSingleUploadTextures_new();
alice->abigail = (Abigail){ .to_del = VecMargaretSubbuf_new() };
alice->generic_models = AliceAllMeshesGeneric_new();
alice->shiny_models = AliceAllMeshesShiny_new();
alice->generic_models = ListAliceGenericMeshHand_new();
alice->shiny_models = ListAliceShinyMeshHand_new();
alice->pipeline0_light_conf.num_ubo_staging = MargaretBufAllocator_alloc(&alice->staging_buffers, sizeof(Pipeline0UBO));
alice->pipeline0_light_conf.num_ubo_dev_local = MargaretBufAllocator_alloc(&alice->dev_local_buffers, sizeof(Pipeline0UBO));
@ -1939,7 +1885,7 @@ Alice* Alice_new(){
if (ft_init_err)
abortf("Can't init free type library\n");
alice->lucy_cache = LucyGlyphCache_new(engine_reference, &alice->single_upload_textures);
alice->lucy_cache = LucyGlyphCache_new(engine_reference, &alice->abigail);
alice->lucy_renderer = LucyRenderer_new(engine_reference, &alice->lucy_cache, root_dir, alice->render_pass_1, 0);
@ -1947,6 +1893,9 @@ Alice* Alice_new(){
// Creating descriptor set for pipeline 0b and for pipeline 1 (containing IT1 sampler)
alice_create_mem_dependant_vk_obj(alice);
alice->transfer_command_buf_already_reset = true;
margaret_reset_and_begin_command_buffer(alice->transfer_command_buf);
alice->wl.prev_key_frame_time = margaret_clock_gettime_monotonic_raw();
alice->wl.frame_count_since_key = 0;
/* Alice initialization complete */

View File

@ -2,208 +2,72 @@
/* I sometimes call this sub-namespace of Alice namespace Abigail. Though I hadn't renamed it to Abigail yet.
* Abigail does not depend on Alice Engine. It only depends on margaret. So systems like Lucy can use it
* without creating any sus circular dependepncies.
* without creating any sus circular dependencies.
*/
#include "../margaret/vulkan_utils.h"
#include "../../../gen/l1/margaret/VecMargaretSubbuf.h"
/* Handler of a read-only buffer that can be uploaded and deleted at any time with it's staging buffer automatically
* deleted (Similar to what AliceTextureUploadedOnce does to textures) */
typedef struct {
U64 count;
MargaretSubbuf staging;
MargaretSubbuf device_buf;
bool scheduled_for_deletion;
} AliceBufferUplOnce;
typedef AliceBufferUplOnce* RefAliceBufferUplOnce;
#include "../../../gen/l1/eve/alice/VecRefAliceBufferUplOnce.h"
/* Handler of a texture that can be uploaded and deleted at any time with it's staging buffer automatically deleted.
* Stores VkImageView as a bonus */
typedef struct {
MargaretSubbuf staging;
MargaretImg img;
VkImageView view;
bool scheduled_for_deletion;
} AliceTextureUplOnce;
typedef AliceTextureUplOnce* RefAliceTextureUplOnce;
#include "../../../gen/l1/eve/alice/VecRefAliceTextureUplOnce.h"
MargaretSubbuf device_local;
} PatriciaBuf;
typedef struct {
VecRefAliceBufferUplOnce to_be_freed_of_old_staging_next_cycle;
VecRefAliceBufferUplOnce to_be_copied_to_device_next_cycle;
VecRefAliceBufferUplOnce to_be_deleted;
} AliceAllSingleUploadBuffers;
VecMargaretSubbuf to_del;
} Abigail;
AliceAllSingleUploadBuffers AliceAllSingleUploadBuffers_new() {
return (AliceAllSingleUploadBuffers){
.to_be_freed_of_old_staging_next_cycle = VecRefAliceBufferUplOnce_new(),
.to_be_copied_to_device_next_cycle = VecRefAliceBufferUplOnce_new(),
.to_be_deleted = VecRefAliceBufferUplOnce_new(),
};
}
typedef struct {
VecRefAliceTextureUplOnce to_be_freed_of_old_staging_next_cycle;
VecRefAliceTextureUplOnce to_be_copied_to_device_next_cycle;
VecRefAliceTextureUplOnce to_be_deleted;
} AliceAllSingleUploadTextures;
AliceAllSingleUploadTextures AliceAllSingleUploadTextures_new(){
return (AliceAllSingleUploadTextures){
.to_be_freed_of_old_staging_next_cycle = VecRefAliceTextureUplOnce_new(),
.to_be_copied_to_device_next_cycle = VecRefAliceTextureUplOnce_new(),
.to_be_deleted = VecRefAliceTextureUplOnce_new(),
};
}
void AliceAllSingleUploadBuffers__resolve_on_another_frame(AliceAllSingleUploadBuffers* self,
MargaretBufAllocator* staging_buffers, MargaretBufAllocator* dev_local_buffers, VkCommandBuffer transfer_cmd_buffer
/* You know the deal, the buffer, returned in ret_dev_local_buf, cannot be deleted in the same `_another_frame`
* callback as this function was called. And it should be obvious that this function can only be called when the
* frame is not in flight. And ret_mapped_staging region is guaranteed to be len bytes long, also, it cannot be
* used when current init/another_frame phase is over
*/
MargaretSubbuf /* ret_dev_local_buf */ Abigail_register_new_buffer(Abigail* self, U64 len,
VkCommandBuffer transfer_cmd_buffer, MargaretBufAllocator* staging_buffers, MargaretBufAllocator* dev_local_buffers,
void** ret_mapped_staging
) {
for (size_t i = 0; i < self->to_be_freed_of_old_staging_next_cycle.len; i++) {
AliceBufferUplOnce* mm = self->to_be_freed_of_old_staging_next_cycle.buf[i];
assert(mm->staging.len != 0);
MargaretBufAllocator_free(staging_buffers, mm->staging);
mm->staging.len = 0;
}
for (size_t i = 0; i < self->to_be_copied_to_device_next_cycle.len; i++) {
AliceBufferUplOnce* mm = self->to_be_copied_to_device_next_cycle.buf[i];
assert(mm->staging.len != 0);
if (mm->scheduled_for_deletion)
continue;
margaret_rec_cmd_copy_buffer_one_to_one(transfer_cmd_buffer, &mm->staging, &mm->device_buf);
VecRefAliceBufferUplOnce_append(&self->to_be_freed_of_old_staging_next_cycle, mm);
}
for (size_t i = 0; i < self->to_be_deleted.len; i++) {
AliceBufferUplOnce* mm = self->to_be_deleted.buf[i];
assert(mm->scheduled_for_deletion);
if (mm->staging.len != 0)
MargaretBufAllocator_free(staging_buffers, mm->staging);
MargaretBufAllocator_free(dev_local_buffers, mm->device_buf);
free(mm);
}
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;
MargaretSubbuf staging = MargaretBufAllocator_alloc(staging_buffers, len);
MargaretSubbuf dev_local = MargaretBufAllocator_alloc(dev_local_buffers, len);
margaret_rec_cmd_copy_buffer_one_to_one(transfer_cmd_buffer, &staging, &dev_local);
*ret_mapped_staging = MargaretSubbuf_get_mapped(&staging);
VecMargaretSubbuf_append(&self->to_del, staging);
return dev_local;
}
void AliceAllSingleUploadTextures__resolve_on_another_frame(AliceAllSingleUploadTextures* self,
MargaretBufAllocator* staging_buffers, MargaretImgAllocator* dev_local_images, VkCommandBuffer transfer_cmd_buffer
/* Same deal as Abigail_register_new_buffer, but for textures. I say textures, because returned object also includes
* VkImageView, created for this image.
* Returned device localimage cannot be deleted in the same 'no-frame-in-flight' stage. We have to wait for it
* to undergo copying, only then, on another frame, can you delete it. ret_mapped_staging points to staging buffer,
* it is a memory region of width * height * pixed_sz bytes long. Here pixel_sz is a size of pixel in image of format
* `foramt`. This memory can only be edited in the same `on-another-frame` phrase where it was given. But not later
*/
MargaretTexture /* ret_dev_local_texture */ Abigail_register_new_texture(Abigail* self, U64 width, U64 height, U64 pixel_sz,
VkFormat format, VkImageUsageFlags usage,
VkCommandBuffer transfer_cmd_buffer, MargaretBufAllocator* staging_buffers, MargaretImgAllocator* dev_local_images,
void** ret_mapped_staging
) {
for (size_t i = 0; i < self->to_be_freed_of_old_staging_next_cycle.len; i++) {
AliceTextureUplOnce* mm = self->to_be_freed_of_old_staging_next_cycle.buf[i];
assert(mm->staging.len != 0);
MargaretBufAllocator_free(staging_buffers, mm->staging);
mm->staging.len = 0;
}
for (size_t i = 0; i < self->to_be_copied_to_device_next_cycle.len; i++) {
AliceTextureUplOnce* mm = self->to_be_copied_to_device_next_cycle.buf[i];
assert(mm->staging.len != 0);
if (mm->scheduled_for_deletion)
continue;
margaret_rec_cmd_copy_buffer_to_image_one_to_one_color_aspect(transfer_cmd_buffer,
&mm->staging, &mm->img, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
MargaretSubbuf staging = MargaretBufAllocator_alloc(staging_buffers, width * height * pixel_sz);
MargaretImg dev_local = MargaretImgAllocator_alloc(dev_local_images, width, height, format, usage);
margaret_rec_cmd_copy_buffer_to_image_one_to_one_color_aspect(transfer_cmd_buffer,
&staging, &dev_local, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
VecRefAliceTextureUplOnce_append(&self->to_be_freed_of_old_staging_next_cycle, mm);
*ret_mapped_staging = MargaretSubbuf_get_mapped(&staging);
VecMargaretSubbuf_append(&self->to_del, staging);
return (MargaretTexture){.img = dev_local, .view = margaret_create_view_for_image(
dev_local_images->device, dev_local.a.image, format, VK_IMAGE_ASPECT_COLOR_BIT)};
}
void Abigail_wipe_old_staging(Abigail* self, VkDevice device, MargaretBufAllocator* staging_buffers) {
for (U64 i = 0; i < self->to_del.len; i++) {
MargaretSubbuf staging = self->to_del.buf[i];
MargaretBufAllocator_free(staging_buffers, staging);
}
for (size_t i = 0; i < self->to_be_deleted.len; i++) {
AliceTextureUplOnce* mm = self->to_be_deleted.buf[i];
assert(mm->scheduled_for_deletion);
if (mm->staging.len != 0)
MargaretBufAllocator_free(staging_buffers, mm->staging);
MargaretImgAllocator_free(dev_local_images, mm->img.a);
vkDestroyImageView(dev_local_images->device, mm->view, NULL);
free(mm);
}
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;
self->to_del.len = 0;
}
/* Doesn't get automatically added to `to_be_copied_to_device_next_cycle` */
AliceBufferUplOnce* AliceBufferUplOnce_new(size_t len,
MargaretBufAllocator* staging_buffers, MargaretBufAllocator* dev_local_buffers
) {
AliceBufferUplOnce* res = (AliceBufferUplOnce*)safe_malloc(sizeof(AliceBufferUplOnce));
res->staging = MargaretBufAllocator_alloc(staging_buffers, len);
res->device_buf = MargaretBufAllocator_alloc(dev_local_buffers, len);
res->scheduled_for_deletion = false;
return res;
void Abigail_drop(Abigail self) {
assert(self.to_del.len == 0);
VecMargaretSubbuf_drop(self.to_del);
}
void* AliceBufferUplOnce_get_mapped_staging(AliceBufferUplOnce* self) {
return MargaretSubbuf_get_mapped(&self->staging);
}
void AliceAllSingleUploadBuffers_register(AliceAllSingleUploadBuffers* self, AliceBufferUplOnce* obj) {
assert(obj->staging.len > 0);
assert(!obj->scheduled_for_deletion);
VecRefAliceBufferUplOnce_append(&self->to_be_copied_to_device_next_cycle, obj);
}
AliceBufferUplOnce* AliceAllSingleUploadBuffers_register_new(AliceAllSingleUploadBuffers* self, size_t len,
MargaretBufAllocator* staging_buffers, MargaretBufAllocator* dev_local_buffers
) {
AliceBufferUplOnce* res = AliceBufferUplOnce_new(len, staging_buffers, dev_local_buffers);
AliceAllSingleUploadBuffers_register(self, res);
return res;
}
void AliceAllSingleUploadBuffers_delete(AliceAllSingleUploadBuffers* self, AliceBufferUplOnce* obj) {
assert(obj);
obj->scheduled_for_deletion = true;
VecRefAliceBufferUplOnce_append(&self->to_be_deleted, obj);
}
/* Doesn't get automatically added to `to_be_copied_to_device_next_cycle`
* Only works for VK_IMAGE_ASPECT_COLOR_BIT images */
AliceTextureUplOnce* AliceTextureUplOnce_new(U64 width, U64 height, U64 pixel_sz, VkFormat format,
VkImageUsageFlags usage, MargaretBufAllocator* staging_buffers, MargaretImgAllocator* dev_local_images
) {
assert(pixel_sz == margaret_singleplane_format_to_sizeof_type(format));
AliceTextureUplOnce* res = (AliceTextureUplOnce*)safe_malloc(sizeof(AliceTextureUplOnce));
res->staging = MargaretBufAllocator_alloc(staging_buffers, width * height * pixel_sz);
res->img = MargaretImgAllocator_alloc(dev_local_images, width, height, format, usage);
res->view = margaret_create_view_for_image(dev_local_images->device, res->img.a.image,
format, VK_IMAGE_ASPECT_COLOR_BIT);
res->scheduled_for_deletion = false;
return res;
}
void* AliceTextureUplOnce_get_mapped_staging(AliceTextureUplOnce* self) {
return MargaretSubbuf_get_mapped(&self->staging);
}
void AliceAllSingleUploadTextures_register(AliceAllSingleUploadTextures* self, AliceTextureUplOnce* obj) {
assert(obj->staging.len > 0);
assert(!obj->scheduled_for_deletion);
VecRefAliceTextureUplOnce_append(&self->to_be_copied_to_device_next_cycle, obj);
}
AliceTextureUplOnce* AliceAllSingleUploadTextures_register_new(AliceAllSingleUploadTextures* self,
U64 width, U64 height, U64 pixel_sz, VkFormat format,
VkImageUsageFlags usage, MargaretBufAllocator* staging_buffers, MargaretImgAllocator* dev_local_images
) {
AliceTextureUplOnce* res = AliceTextureUplOnce_new(width, height, pixel_sz, format, usage,
staging_buffers, dev_local_images);
AliceAllSingleUploadTextures_register(self, res);
return res;
}
void AliceAllSingleUploadTextures_delete(AliceAllSingleUploadTextures* self, AliceTextureUplOnce* obj) {
assert(obj);
obj->scheduled_for_deletion = true;
VecRefAliceTextureUplOnce_append(&self->to_be_deleted, obj);
}

View File

@ -14,10 +14,10 @@
#define LUCY_MAX_DESCRIPTOR_COUNT 100
typedef struct {
AliceTextureUplOnce* tex;
MargaretTexture tex;
U64 usage;
/* Is this image scheduled for deletion on th next cycle. */
bool scheduled_for_deletion;
/* I just could not avoid storing this here. But yeah, after the copying is done, this field stops being used */
void* staging;
} LucyImage;
#include "../../../gen/l1/eve/lucy/OptionLucyImage.h"
@ -64,7 +64,7 @@ struct LucyFace {
struct LucyGlyphCache {
MargaretEngineReference ve;
AliceAllSingleUploadTextures* single_upload_textures;
Abigail* abigail;
VecOptionLucyImage image_slots;
VkDescriptorSetLayout descriptor_set_layout;
@ -76,7 +76,7 @@ struct LucyGlyphCache {
};
LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, AliceAllSingleUploadTextures* single_upload_textures){
LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, Abigail* abigail){
VkDescriptorSetLayout my_desc_set_layout;
VkDescriptorSetLayoutBindingFlagsCreateInfo set_layout_crinfo_flags = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
@ -101,7 +101,7 @@ LucyGlyphCache LucyGlyphCache_new(MargaretEngineReference ve, AliceAllSingleUplo
image_slots.buf[i].variant = Option_None;
}
return (LucyGlyphCache){
.ve = ve, .single_upload_textures = single_upload_textures, .image_slots = image_slots,
.ve = ve, .abigail = abigail, .image_slots = image_slots,
.descriptor_set_layout = my_desc_set_layout, .descriptor_set = descriptor_set,
.to_be_written_to_descriptor_set = VecU32_new(),
.to_be_deleted = VecU32_new()};
@ -117,11 +117,8 @@ void LucyFaceFixedSize_get_rid_of_myself(LucyFaceFixedSize* self){
LucyImage* img = &img_slot->some;
assert(img->usage > 0);
if (--img->usage) {
assert(!img->scheduled_for_deletion);
img->scheduled_for_deletion = true;
AliceAllSingleUploadTextures_delete(cache->single_upload_textures, img->tex);
img->tex = NULL;
VecU32_append(&cache->to_be_deleted, slot_id);
/* Nothing is written to descriptor set. And luckily, we don't have to */
img_slot->variant = Option_None;
}
}
BufRBTree_MapU32ToLucyStoredGlyph_sink(glyphs);
@ -166,7 +163,6 @@ U32 LucyGlyphCache_add_glyphs__find_image_slot(LucyGlyphCache* cache){
OptionLucyImage* slot = &cache->image_slots.buf[i];
if (slot->variant == Option_None) {
slot->variant = Option_Some;
slot->some.scheduled_for_deletion = 0;
slot->some.usage = 0;
return i;
}
@ -183,15 +179,27 @@ void LucyGlyphCache_add_glyphs__close_img(
assert(img_slot->variant == Option_Some);
LucyImage* img = &img_slot->some;
assert(img->usage > 0);
assert(!img->scheduled_for_deletion);
img_width = MAX_U32(img_width, 10); // Just a precaution. empty buffers aren't supported by Margaret
img_height = MAX_U32(img_height, 10);
img->tex = AliceAllSingleUploadTextures_register_new(cache->single_upload_textures,
img->tex = Abigail_register_new_texture(cache->abigail,
img_width, img_height, sizeof(U8), VK_FORMAT_R8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
cache->ve.staging_buffers, cache->ve.dev_local_images);
VecU32_append(&cache->to_be_written_to_descriptor_set, img_slot_id);
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, cache->ve.transfer_cmd_buffer,
cache->ve.staging_buffers, cache->ve.dev_local_images, &(img->staging));
assert(img->staging);
/* We are writing to descriptor set RIGHT NOW. That is why this function, and as such
* LucyGlyphCache_add_glyphs too, must be called when no frame is in flight */
vkUpdateDescriptorSets(cache->ve.device, 1, &(VkWriteDescriptorSet){
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = cache->descriptor_set, .dstBinding = 0, .dstArrayElement = img_slot_id,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &(VkDescriptorImageInfo){
.sampler = cache->ve.nearest_sampler, .imageView = img->tex.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
}, 0, NULL);
}
void LucyGlyphCache_add_glyphs(VecLucyGlyphCachingRequest requests_for_faces){
@ -312,8 +320,8 @@ void LucyGlyphCache_add_glyphs(VecLucyGlyphCachingRequest requests_for_faces){
for (size_t j = 0; j < ready.len; j++) {
LucyPositionedStagingGlyph* p_glyph = &ready.buf[j];
LucyImage* image = &VecOptionLucyImage_mat(&cache->image_slots, p_glyph->img_slot_id)->some;
U64 staging_width = image->tex->img.width;
U8* staging = (U8*)AliceTextureUplOnce_get_mapped_staging(image->tex);
U64 staging_width = image->tex.img.width;
U8* staging = (U8*)image->staging;
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++) {
@ -336,42 +344,6 @@ void LucyGlyphCache_drop(LucyGlyphCache self){
VecU32_drop(self.to_be_deleted);
}
void LucyGlyphCache_another_frame(LucyGlyphCache* self){
for (size_t i = 0; i < self->to_be_written_to_descriptor_set.len; i++) {
U32 slot_id = self->to_be_written_to_descriptor_set.buf[i];
OptionLucyImage* img_slot = &self->image_slots.buf[slot_id];
assert(img_slot->variant == Option_Some);
LucyImage* img = &img_slot->some;
if (img->scheduled_for_deletion)
continue;
vkUpdateDescriptorSets(self->ve.device, 1, &(VkWriteDescriptorSet){
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = self->descriptor_set, .dstBinding = 0, .dstArrayElement = slot_id,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &(VkDescriptorImageInfo){
.sampler = self->ve.nearest_sampler, .imageView = img->tex->view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
}, 0, NULL);
}
/* 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++) {
U32 slot_id = self->to_be_deleted.buf[i];
OptionLucyImage* img_slot = &self->image_slots.buf[slot_id];
assert(img_slot->variant == Option_Some);
LucyImage* img = &img_slot->some;
assert(img->scheduled_for_deletion);
assert(img->usage == 0);
assert(img->tex == NULL);
img_slot->variant = Option_None;
}
self->to_be_written_to_descriptor_set.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

View File

@ -97,8 +97,8 @@ void LucyRenderer_draw_char_glyph(LucyRenderer* self, vec4 color, ivec2 pos, Luc
OptionLucyImage* img_slot = VecOptionLucyImage_mat(&self->cache->image_slots, glyph->img_slot_id);
assert(img_slot->variant == Option_Some);
LucyImage* img = &img_slot->some;
float atlas_w = (float)img->tex->img.width;
float atlas_h = (float)img->tex->img.height;
float atlas_w = (float)img->tex.img.width;
float atlas_h = (float)img->tex.img.height;
ivec2 positioned = ivec2_add_ivec2(pos, glyph->bearing);
U64 needed_vbo_length = (self->glyphs_count + 1) * sizeof(LucyRenderInstance);

View File

@ -361,4 +361,4 @@ void MargaretBufAllocator_expand_or_free_old(
MargaretBufAllocator_free(self, *allocation);
*allocation = maybe_bigger;
}
}
}

View File

@ -1287,3 +1287,8 @@ U64 margaret_singleplane_format_to_sizeof_type(VkFormat type){
abortf("Jokes on you\n");
}
}
typedef struct {
MargaretImg img;
VkImageView view;
} MargaretTexture;