Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Text mesh layout handler #95

Merged
merged 2 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion include/renderer/internal/vulkan/vulkan_text_mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
#include <bufferlib/buffer.h>
#include <renderer/dictionary.h>
#include <renderer/internal/vulkan/vulkan_host_buffered_buffer.h>
#include <renderer/internal/vulkan/vulkan_text_types.h>
#include <glslcommon/glsl_types.h>

typedef struct vulkan_text_mesh_glsl_glyph_render_data_t
Expand Down Expand Up @@ -138,6 +139,30 @@ typedef buf_ucount_t vulkan_text_mesh_string_handle_t;
typedef dictionary_t /* (u16, sub_buffer_handle_t) */ vulkan_text_mesh_glyph_render_data_sub_buffer_handle_list_t;
typedef buffer_t vulkan_text_mesh_string_char_buffer_t;

/* <begin> typedefs facilitating text layout */

typedef vulkan_text_glyph_layout_data_t vulkan_text_mesh_glyph_layout_data_t;

typedef vulkan_text_glyph_layout_data_buffer_t vulkan_text_mesh_glyph_layout_data_buffer_t;

typedef vulkan_text_glyph_info_t vulkan_text_mesh_glyph_info_t;

typedef vulkan_text_glyph_layout_handler_input_data_t vulkan_text_mesh_glyph_layout_handler_input_data_t;

typedef vulkan_text_glyph_layout_handler_t vulkan_text_mesh_glyph_layout_handler_t;

typedef vulkan_text_glyph_strikethrough_handler_t vulkan_text_mesh_glyph_strikethrough_handler_t;

typedef vulkan_text_glyph_strikethrough_handler_t vulkan_text_mesh_glyph_strikethrough_handler_t;

typedef vulkan_text_glyph_underline_handler_t vulkan_text_mesh_glyph_underline_handler_t;

typedef vulkan_text_glyph_layout_handler_void_ptr_pair_t vulkan_text_mesh_glyph_layout_handler_void_ptr_pair_t;
typedef vulkan_text_glyph_strikethrough_handler_void_ptr_pair_t vulkan_text_mesh_glyph_strikethrough_handler_void_ptr_pair_t;
typedef vulkan_text_glyph_underline_handler_void_ptr_pair_t vulkan_text_mesh_glyph_underline_handler_void_ptr_pair_t;

/* <end> typedefs facilitating text layout */

typedef struct vulkan_text_mesh_string_t
{
vulkan_text_mesh_string_handle_t next_handle;
Expand All @@ -160,9 +185,23 @@ typedef struct vulkan_text_mesh_t
{
vulkan_renderer_t* renderer;
vulkan_material_t* material;
u32 point_size;
vulkan_text_mesh_render_space_type_t render_space_type;
vulkan_text_mesh_render_surface_type_t render_surface_type;
u32 point_size;

/* <begin> fields facilitating text layout */

/* called whenever vulkan_text_mesh_string_setH is called */
vulkan_text_mesh_glyph_layout_handler_void_ptr_pair_t glyph_layout_handler;
/* called whenever vulkan_text_mesh_glyph_layout_data_t::is_strikethrough is true */
vulkan_text_mesh_glyph_strikethrough_handler_void_ptr_pair_t glyph_strikethrough_handler;
/* called whenever vulkan_text_mesh_glyph_layout_data_t::is_underline is true */
vulkan_text_mesh_glyph_underline_handler_void_ptr_pair_t glyph_underline_handler;

/* holds post processed info for each glyph */
vulkan_text_mesh_glyph_layout_data_buffer_t glyph_layout_data_buffer;

/* <end> fields facilitating text layout */

/* CPU side */
vulkan_glyph_mesh_pool_t* pool; // pool from which the mesh_t should be queried from for a particular glyph
Expand Down Expand Up @@ -192,6 +231,9 @@ RENDERER_API vulkan_text_mesh_string_handle_t vulkan_text_mesh_string_create(vul
RENDERER_API void vulkan_text_mesh_string_destroyH(vulkan_text_mesh_t* text_mesh, vulkan_text_mesh_string_handle_t handle);

// setters
RENDERER_API void vulkan_text_mesh_set_glyph_layout_handler(vulkan_text_mesh_t* text, vulkan_text_mesh_glyph_layout_handler_t handler, void* user_data);
RENDERER_API void vulkan_text_mesh_set_glyph_strikethrough_handler(vulkan_text_mesh_t* text, vulkan_text_mesh_glyph_strikethrough_handler_t handler, void* user_data);
RENDERER_API void vulkan_text_mesh_set_glyph_underline_handler(vulkan_text_mesh_t* text, vulkan_text_mesh_glyph_underline_handler_t handler, void* user_data);
RENDERER_API void vulkan_text_mesh_set_point_size(vulkan_text_mesh_t* text_mesh, u32 point_size);
RENDERER_API void vulkan_text_mesh_set_material(vulkan_text_mesh_t* text_mesh, vulkan_material_t* material);
RENDERER_API void vulkan_text_mesh_set_render_space_type(vulkan_text_mesh_t* text, vulkan_text_mesh_render_space_type_t space_type);
Expand Down
2 changes: 2 additions & 0 deletions include/renderer/internal/vulkan/vulkan_text_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
/* holds the post processed layout data for each glyph */
typedef struct vulkan_text_glyph_layout_data_t
{
/* index of the glyph in the string */
u32 index;
/* offset added to this glyph, in pixel coordinates if space is 2D, otherwise in world coordinates */
vec3_t offset;
/* color of this glyph (8 bit per component)*/
Expand Down
1 change: 1 addition & 0 deletions source/renderer/font.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ RENDERER_API void font_get_glyph_mesh(font_t* font, utf32_t unicode, u8 mesh_qua
RENDERER_API void font_get_glyph_info(font_t* font, utf32_t unicode, font_glyph_info_t* out_info)
{
int index = ttf_find_glyph(font->ttf_handle, unicode);
out_info->is_graph = isgraph(CAST_TO(s32, unicode)) != 0;
if(index < 0)
{
LOG_FETAL_ERR("Font error: couldn't find glyph \"%c\"\n", unicode);
Expand Down
28 changes: 23 additions & 5 deletions source/renderer/vulkan/vulkan_bitmap_text.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,23 @@ static void update_bga_and_gtc_buffer(void* publisher, void* handler_data)

static bool default_glyph_layout_handler(vulkan_bitmap_text_glyph_layout_data_buffer_t* output, const vulkan_bitmap_text_glyph_layout_handler_input_data_t* input, void* user_data)
{
return false;
u32 count = input->glyph_count;
for(u32 i = 0; i < count; i++)
{
vulkan_bitmap_text_glyph_layout_data_t data =
{
.index = i,
.offset = { 0, 0, 0 },
.color = ICOLOR4_GREY,
.is_bold = false,
.is_italic = false,
.is_strikethrough = false,
.is_underline = false,
.idoc = NULL
};
buf_push(output, &data);
}
return true;
}

RENDERER_API void vulkan_bitmap_text_create_no_alloc(vulkan_renderer_t* renderer, vulkan_bitmap_text_create_info_t* create_info, vulkan_bitmap_text_t OUT text)
Expand Down Expand Up @@ -515,17 +531,19 @@ static void text_string_set(vulkan_bitmap_text_t* text, vulkan_bitmap_text_strin

for(u32 i = 0; i < final_count; i++)
{
AUTO glyph_data = is_changed ? buf_get_ptr_at_typeof(&text->glyph_layout_data_buffer, vulkan_bitmap_text_glyph_layout_data_t, i) : NULL;
u32 index = is_changed ? glyph_data->index : i;

/* skip the whitespaces (for which we don't have any physical texture coord information) */
if(texcoord_indices[i] == U32_MAX)
if(texcoord_indices[index] == U32_MAX)
continue;

AUTO glyph_data = is_changed ? buf_get_ptr_at_typeof(&text->glyph_layout_data_buffer, vulkan_bitmap_text_glyph_layout_data_t, i) : NULL;
AUTO offset = vec3_add(2, glyph_infos[i].rect.offset, (is_changed ? glyph_data->offset : vec3_zero()));
AUTO offset = vec3_add(2, glyph_infos[index].rect.offset, (is_changed ? glyph_data->offset : vec3_zero()));
// _debug_assert__(info.bitmap_top == info.bearing_y);
vulkan_bitmap_text_glsl_glyph_render_data_t data =
{
.ofst = { offset.x, offset.y, offset.z },
.indx = texcoord_indices[i],
.indx = texcoord_indices[index],
/* TODO: Remove this as we don't need this level of flexibility */
.rotn = { ((i%3) == 0) ? 1 : 0, ((i%3) == 1) ? 1 : 0, ((i%3) == 2) ? 1 : 0 },
.stid = U64_TO_U32(text_string->handle),
Expand Down
125 changes: 114 additions & 11 deletions source/renderer/vulkan/vulkan_text_mesh.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <renderer/assert.h>
#include <renderer/font.h>
#include <renderer/memory_allocator.h>
#include <renderer/system/display.h> // display_get_dpi()
#include <string.h> // strlen
#include <ctype.h> // isspace

Expand Down Expand Up @@ -65,6 +66,27 @@ RENDERER_API vulkan_text_mesh_t* vulkan_text_mesh_create(vulkan_renderer_t* rend
return text;
}

static bool default_glyph_layout_handler(vulkan_text_mesh_glyph_layout_data_buffer_t* output, const vulkan_text_mesh_glyph_layout_handler_input_data_t* input, void* user_data)
{
u32 count = input->glyph_count;
for(u32 i = 0; i < count; i++)
{
vulkan_text_mesh_glyph_layout_data_t data =
{
.index = i,
.offset = { 0, 0, 0 },
.color = ICOLOR4_GREY,
.is_bold = false,
.is_italic = false,
.is_strikethrough = false,
.is_underline = false,
.idoc = NULL
};
buf_push(output, &data);
}
return true;
}

RENDERER_API void vulkan_text_mesh_create_no_alloc(vulkan_renderer_t* renderer, vulkan_glyph_mesh_pool_t* pool, vulkan_text_mesh_t OUT text)
{
_debug_assert__(pool != NULL);
Expand All @@ -76,9 +98,11 @@ RENDERER_API void vulkan_text_mesh_create_no_alloc(vulkan_renderer_t* renderer,
text->pool = pool;
text->free_list = BUF_INVALID_INDEX; // free list
text->inuse_list = BUF_INVALID_INDEX; // inuse list
text->point_size = font_get_char_size(vulkan_glyph_mesh_pool_get_font(pool));
text->render_space_type = VULKAN_TEXT_MESH_RENDER_SPACE_TYPE_2D;
text->render_surface_type = VULKAN_TEXT_MESH_RENDER_SURFACE_TYPE_CAMERA;
text->point_size = font_get_char_size(vulkan_glyph_mesh_pool_get_font(pool));
text->glyph_layout_data_buffer = buf_create(sizeof(vulkan_text_mesh_glyph_layout_data_t), 128, 0);
text->glyph_layout_handler = (vulkan_text_mesh_glyph_layout_handler_void_ptr_pair_t) { default_glyph_layout_handler, NULL };

_debug_assert__(glsl_sizeof(glsl_mat4_t) == sizeof(glsl_mat4_t));

Expand All @@ -96,6 +120,7 @@ RENDERER_API void vulkan_text_mesh_create_no_alloc(vulkan_renderer_t* renderer,

RENDERER_API void vulkan_text_mesh_destroy(vulkan_text_mesh_t* text_mesh)
{
buf_clear(&text_mesh->glyph_layout_data_buffer, NULL);
dictionary_t* glyph_render_data_buffers = &text_mesh->glyph_render_data_buffers;
BUFFER* strings = &text_mesh->strings;
for(buf_ucount_t i = 0; i < dictionary_get_count(glyph_render_data_buffers); i++)
Expand All @@ -108,6 +133,7 @@ RENDERER_API void vulkan_text_mesh_destroy(vulkan_text_mesh_t* text_mesh)

RENDERER_API void vulkan_text_mesh_release_resources(vulkan_text_mesh_t* text_mesh)
{
buf_free(&text_mesh->glyph_layout_data_buffer);
dictionary_t* glyph_render_data_buffers = &text_mesh->glyph_render_data_buffers;
BUFFER* strings = &text_mesh->strings;
for(buf_ucount_t i = 0; i < dictionary_get_count(glyph_render_data_buffers); i++)
Expand Down Expand Up @@ -344,11 +370,15 @@ static vulkan_text_mesh_string_t* get_text_stringH(vulkan_text_mesh_t* text, vul
return buf_get_ptr_at_typeof(&text->strings, vulkan_text_mesh_string_t, handle);
}

static INLINE_IF_RELEASE_MODE f32 get_world_from_pixels(f32 pixels, f32 point_size, f32 dpi)
{
return pixels / get_pixels_from_point_size(point_size, dpi);
}

RENDERER_API void vulkan_text_mesh_string_setH(vulkan_text_mesh_t* text_mesh, vulkan_text_mesh_string_handle_t handle, const char* str)
{
// TODO: ensure handle isn't in the free-list, meaning it has been already destroyed, this check should be in debug mode only
vulkan_text_mesh_string_t* string = get_text_stringH(text_mesh, handle);
u32 len = strlen(str);
dictionary_t* glyph_render_data_buffers = &text_mesh->glyph_render_data_buffers;

// clear the sub buffers
Expand All @@ -361,35 +391,93 @@ RENDERER_API void vulkan_text_mesh_string_setH(vulkan_text_mesh_t* text_mesh, vu
sub_buffer_clear(buffer, get_sub_buffer_handle(buffer, &string->glyph_sub_buffer_handles, ch));
}

/* calculate the anchor offset to shift the anchor to the top left */
AUTO font = vulkan_glyph_mesh_pool_get_font(text_mesh->pool);
f32 anchor_offset_y = font_get_ascender(font) / font_get_units_per_em(font);
// re-write on the sub buffers
float horizontal_pen = 0.0f;

u32 len = strlen(str);

/* prepare glyph infos to be fed into the layout handler */

vulkan_text_mesh_glyph_info_t glyph_infos[len];

f32 horizontal_pen = 0.0f;
for(u32 i = 0; i < len; i++)
{
u16 ch = str[i];
font_glyph_info_t info;
font_get_glyph_info(vulkan_glyph_mesh_pool_get_font(text_mesh->pool), ch, &info);
if(isspace(ch))
glyph_infos[i] = (vulkan_text_mesh_glyph_info_t)
{
horizontal_pen += info.advance_width;
.unicode = ch,
.rect = { offset3d(horizontal_pen, -anchor_offset_y, 0.0f), extent3d(info.max_x - info.min_x, info.max_y - info.min_y, 0.0f) }
};
horizontal_pen += info.advance_width;
}

/* call the layout handler */

_debug_assert__(text_mesh->glyph_layout_handler.first != NULL);
const vulkan_text_mesh_glyph_layout_handler_input_data_t input_data =
{
.glyphs = glyph_infos,
.glyph_count = len, /* whitespaces are also included */
.anchor_pos = mat4_get_position(string->transform).xyz /* vec4 -> vec3 */
};
/* NOTE: no need to clear the glyph_layout_data_buffer as it is cleared after every set call */
bool is_changed = (text_mesh->glyph_layout_handler.first)(&text_mesh->glyph_layout_data_buffer, &input_data, text_mesh->glyph_layout_handler.second);

/* rewrite on the sub buffers */

/* final (post-processed) glyph counts */
u32 final_count = is_changed ? buf_get_element_count(&text_mesh->glyph_layout_data_buffer) : len;

_debug_assert__(is_changed || ((!is_changed) && (final_count == len)));

for(u32 i = 0; i < final_count; i++)
{
AUTO glyph_data = is_changed ? buf_get_ptr_at_typeof(&text_mesh->glyph_layout_data_buffer, vulkan_text_mesh_glyph_layout_data_t, i) : NULL;
u32 index = is_changed ? glyph_data->index : i;
utf32_t unicode = glyph_infos[index].unicode;
_debug_assert__(unicode != 0);
if(!isgraph(CAST_TO(s32, unicode)))
continue;
}
vulkan_instance_buffer_t* instance_buffer = get_instance_buffer(text_mesh->renderer, glyph_render_data_buffers, ch);

/* get the glyph render data buffer pointer */

vulkan_instance_buffer_t* instance_buffer = get_instance_buffer(text_mesh->renderer, glyph_render_data_buffers, unicode);
multi_buffer_t* buffer = vulkan_instance_buffer_get_host_buffer(instance_buffer);
sub_buffer_handle_t handle = get_sub_buffer_handle(buffer, &string->glyph_sub_buffer_handles, ch);
sub_buffer_handle_t handle = get_sub_buffer_handle(buffer, &string->glyph_sub_buffer_handles, unicode);

/* if the space type is 2D, then convert from pixels to world */
AUTO _offset = vec3_zero();
if(is_changed)
{
_offset = glyph_data->offset;
if(text_mesh->render_space_type == VULKAN_TEXT_MESH_RENDER_SPACE_TYPE_2D)
{
AUTO dpi = display_get_dpi();
_offset = vec3(get_world_from_pixels(_offset.x, string->point_size, dpi.x), get_world_from_pixels(_offset.y, string->point_size, dpi.y), 0.0f);
}
}

/* calculate the final offset */
AUTO offset = vec3_add(2, glyph_infos[index].rect.offset, _offset);

/* add the glyph render data to the render buffer */
vulkan_text_mesh_glsl_glyph_render_data_t data =
{
.ofst = { 0.0f, -anchor_offset_y, horizontal_pen },
.ofst = { offset.z, offset.y, offset.x },
.stid = U64_TO_U32(string->handle)
};
sub_buffer_push(buffer, handle, CAST_TO(void*, &data));
horizontal_pen += info.advance_width;

vulkan_instance_buffer_commit(instance_buffer, NULL);
}

/* clear the glyph layout data buffer for the next set call */
buf_clear(&text_mesh->glyph_layout_data_buffer, NULL);

/* update the device side buffers if not updated in the above loop */

/* sub buffer handle count might have been increased due to new glyphs */
Expand Down Expand Up @@ -439,6 +527,21 @@ RENDERER_API void vulkan_text_mesh_string_set_transformH(vulkan_text_mesh_t* tex
}

// getters
RENDERER_API void vulkan_text_mesh_set_glyph_layout_handler(vulkan_text_mesh_t* text, vulkan_text_mesh_glyph_layout_handler_t handler, void* user_data)
{
text->glyph_layout_handler = (vulkan_text_mesh_glyph_layout_handler_void_ptr_pair_t) { handler, user_data };
}

RENDERER_API void vulkan_text_mesh_set_glyph_strikethrough_handler(vulkan_text_mesh_t* text, vulkan_text_mesh_glyph_strikethrough_handler_t handler, void* user_data)
{
text->glyph_strikethrough_handler = (vulkan_text_mesh_glyph_strikethrough_handler_void_ptr_pair_t) { handler, user_data };
}

RENDERER_API void vulkan_text_mesh_set_glyph_underline_handler(vulkan_text_mesh_t* text, vulkan_text_mesh_glyph_underline_handler_t handler, void* user_data)
{
text->glyph_underline_handler = (vulkan_text_mesh_glyph_underline_handler_void_ptr_pair_t) { handler, user_data };
}

RENDERER_API u32 vulkan_text_mesh_get_point_size(vulkan_text_mesh_t* text_mesh)
{
return text_mesh->point_size;
Expand Down
4 changes: 2 additions & 2 deletions source/tests/bitmap_text.c
Original file line number Diff line number Diff line change
Expand Up @@ -753,9 +753,9 @@ TEST_ON_UPDATE(BITMAP_TEXT)
counter = 0;
char buffer[128] = { };
sprintf(buffer, "%d", counter);
// bitmap_text_string_setH(this->text, this->text_string_handle, buffer);
bitmap_text_string_setH(this->text, this->text_string_handle, buffer);
sprintf(buffer, "%d", counter);
// bitmap_text_string_setH(this->text, this->another_string_handle, buffer);
bitmap_text_string_setH(this->text, this->another_string_handle, buffer);

bitmap_glyph_atlas_texture_commit(this->texture, NULL);
}
Expand Down