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:
parent
516fab6ff6
commit
19f92d9207
@ -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
|
||||
@ -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 } */
|
||||
|
||||
@ -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
|
||||
@ -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"
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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(
|
||||
|
||||
BIN
src/l2/tests/r0/fonts/DMSerifText-Regular.ttf
Normal file
BIN
src/l2/tests/r0/fonts/DMSerifText-Regular.ttf
Normal file
Binary file not shown.
@ -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);
|
||||
|
||||
@ -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(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user