1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2016-2017 - Hans-Kristian Arntzen
3  *
4  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
5  *  of the GNU General Public License as published by the Free Software Found-
6  *  ation, either version 3 of the License, or (at your option) any later version.
7  *
8  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10  *  PURPOSE.  See the GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License along with RetroArch.
13  *  If not, see <http://www.gnu.org/licenses/>.
14  */
15 
16 #include <string.h>
17 
18 #include <encodings/utf.h>
19 #include <compat/strl.h>
20 
21 #include "../common/vulkan_common.h"
22 
23 #include "../font_driver.h"
24 
25 #include "../../configuration.h"
26 
27 typedef struct
28 {
29    vk_t *vk;
30    void *font_data;
31    struct font_atlas *atlas;
32    const font_renderer_driver_t *font_driver;
33    struct vk_vertex *pv;
34    struct vk_texture texture;
35    struct vk_texture texture_optimal;
36    struct vk_buffer_range range;
37    unsigned vertices;
38 
39    bool needs_update;
40 } vulkan_raster_t;
41 
vulkan_raster_font_update_glyph(vulkan_raster_t * font,const struct font_glyph * glyph)42 static INLINE void vulkan_raster_font_update_glyph(
43       vulkan_raster_t *font, const struct font_glyph *glyph)
44 {
45    if(font->atlas->dirty)
46    {
47       unsigned row;
48       for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++)
49       {
50          uint8_t *src = font->atlas->buffer + row * font->atlas->width + glyph->atlas_offset_x;
51          uint8_t *dst = (uint8_t*)font->texture.mapped + row * font->texture.stride + glyph->atlas_offset_x;
52          memcpy(dst, src, glyph->width);
53       }
54 
55       font->atlas->dirty = false;
56       font->needs_update = true;
57    }
58 }
59 
60 
vulkan_raster_font_free_font(void * data,bool is_threaded)61 static void vulkan_raster_font_free_font(void *data, bool is_threaded)
62 {
63    vulkan_raster_t *font = (vulkan_raster_t*)data;
64    if (!font)
65       return;
66 
67    if (font->font_driver && font->font_data)
68       font->font_driver->free(font->font_data);
69 
70    vkQueueWaitIdle(font->vk->context->queue);
71    vulkan_destroy_texture(
72          font->vk->context->device, &font->texture);
73    vulkan_destroy_texture(
74          font->vk->context->device, &font->texture_optimal);
75 
76    free(font);
77 }
78 
vulkan_raster_font_init_font(void * data,const char * font_path,float font_size,bool is_threaded)79 static void *vulkan_raster_font_init_font(void *data,
80       const char *font_path, float font_size,
81       bool is_threaded)
82 {
83    vulkan_raster_t *font          =
84       (vulkan_raster_t*)calloc(1, sizeof(*font));
85 
86 #if 0
87    VkComponentMapping swizzle = {
88       VK_COMPONENT_SWIZZLE_ONE,
89       VK_COMPONENT_SWIZZLE_ONE,
90       VK_COMPONENT_SWIZZLE_ONE,
91       VK_COMPONENT_SWIZZLE_R,
92    };
93 #endif
94 
95    if (!font)
96       return NULL;
97 
98    font->vk = (vk_t*)data;
99 
100    if (!font_renderer_create_default(
101             &font->font_driver,
102             &font->font_data, font_path, font_size))
103    {
104       RARCH_WARN("Couldn't initialize font renderer.\n");
105       free(font);
106       return NULL;
107    }
108 
109    font->atlas   = font->font_driver->get_atlas(font->font_data);
110    font->texture = vulkan_create_texture(font->vk, NULL,
111          font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, font->atlas->buffer,
112          NULL /*&swizzle*/, VULKAN_TEXTURE_STAGING);
113 
114    {
115       struct vk_texture *texture = &font->texture;
116       VK_MAP_PERSISTENT_TEXTURE(font->vk->context->device, texture);
117    }
118 
119    font->texture_optimal = vulkan_create_texture(font->vk, NULL,
120          font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, NULL,
121          NULL /*&swizzle*/, VULKAN_TEXTURE_DYNAMIC);
122 
123    font->needs_update = true;
124 
125    return font;
126 }
127 
vulkan_get_message_width(void * data,const char * msg,unsigned msg_len,float scale)128 static int vulkan_get_message_width(void *data, const char *msg,
129       unsigned msg_len, float scale)
130 {
131    vulkan_raster_t *font = (vulkan_raster_t*)data;
132    const char* msg_end   = msg + msg_len;
133    int delta_x           = 0;
134 
135    if (     !font
136          || !font->font_driver
137          || !font->font_driver->get_glyph
138          || !font->font_data )
139       return 0;
140 
141    while (msg < msg_end)
142    {
143       uint32_t code                  = utf8_walk(&msg);
144       const struct font_glyph *glyph = font->font_driver->get_glyph(
145             font->font_data, code);
146 
147       if (!glyph) /* Do something smarter here ... */
148          glyph = font->font_driver->get_glyph(font->font_data, '?');
149 
150       if (glyph)
151       {
152          vulkan_raster_font_update_glyph(font, glyph);
153          delta_x += glyph->advance_x;
154       }
155    }
156 
157    return delta_x * scale;
158 }
159 
vulkan_raster_font_render_line(vulkan_raster_t * font,const char * msg,unsigned msg_len,float scale,const float color[4],float pos_x,float pos_y,unsigned text_align)160 static void vulkan_raster_font_render_line(
161       vulkan_raster_t *font, const char *msg, unsigned msg_len,
162       float scale, const float color[4], float pos_x,
163       float pos_y, unsigned text_align)
164 {
165    struct vk_color vk_color;
166    vk_t *vk             = font->vk;
167    const char* msg_end  = msg + msg_len;
168    int x                = roundf(pos_x * vk->vp.width);
169    int y                = roundf((1.0f - pos_y) * vk->vp.height);
170    int delta_x          = 0;
171    int delta_y          = 0;
172    float inv_tex_size_x = 1.0f / font->texture.width;
173    float inv_tex_size_y = 1.0f / font->texture.height;
174    float inv_win_width  = 1.0f / font->vk->vp.width;
175    float inv_win_height = 1.0f / font->vk->vp.height;
176 
177    vk_color.r           = color[0];
178    vk_color.g           = color[1];
179    vk_color.b           = color[2];
180    vk_color.a           = color[3];
181 
182    switch (text_align)
183    {
184       case TEXT_ALIGN_RIGHT:
185          x -= vulkan_get_message_width(font, msg, msg_len, scale);
186          break;
187       case TEXT_ALIGN_CENTER:
188          x -= vulkan_get_message_width(font, msg, msg_len, scale) / 2;
189          break;
190    }
191 
192    while (msg < msg_end)
193    {
194       int off_x, off_y, tex_x, tex_y, width, height;
195       unsigned code                  = utf8_walk(&msg);
196       const struct font_glyph *glyph =
197          font->font_driver->get_glyph(font->font_data, code);
198 
199       if (!glyph) /* Do something smarter here ... */
200          glyph = font->font_driver->get_glyph(font->font_data, '?');
201       if (!glyph)
202          continue;
203 
204       vulkan_raster_font_update_glyph(font, glyph);
205 
206       off_x  = glyph->draw_offset_x;
207       off_y  = glyph->draw_offset_y;
208       tex_x  = glyph->atlas_offset_x;
209       tex_y  = glyph->atlas_offset_y;
210       width  = glyph->width;
211       height = glyph->height;
212 
213       {
214          struct vk_vertex *pv          = font->pv + font->vertices;
215          float _x                      = (x + (off_x + delta_x) * scale)
216             * inv_win_width;
217          float _y                      = (y + (off_y + delta_y) * scale)
218             * inv_win_height;
219          float _width                  = width  * scale * inv_win_width;
220          float _height                 = height * scale * inv_win_height;
221          float _tex_x                  = tex_x * inv_tex_size_x;
222          float _tex_y                  = tex_y * inv_tex_size_y;
223          float _tex_width              = width * inv_tex_size_x;
224          float _tex_height             = height * inv_tex_size_y;
225          const struct vk_color *_color = &vk_color;
226 
227          VULKAN_WRITE_QUAD_VBO(pv, _x, _y, _width, _height, _tex_x, _tex_y, _tex_width, _tex_height, _color);
228       }
229 
230       font->vertices += 6;
231 
232       delta_x        += glyph->advance_x;
233       delta_y        += glyph->advance_y;
234    }
235 }
236 
vulkan_raster_font_render_message(vulkan_raster_t * font,const char * msg,float scale,const float color[4],float pos_x,float pos_y,unsigned text_align)237 static void vulkan_raster_font_render_message(
238       vulkan_raster_t *font, const char *msg, float scale,
239       const float color[4], float pos_x, float pos_y,
240       unsigned text_align)
241 {
242    struct font_line_metrics *line_metrics = NULL;
243    int lines                              = 0;
244    float line_height;
245 
246    if (!msg || !*msg || !font->vk)
247       return;
248 
249    /* If font line metrics are not supported just draw as usual */
250    if (!font->font_driver->get_line_metrics ||
251        !font->font_driver->get_line_metrics(font->font_data, &line_metrics))
252    {
253       vulkan_raster_font_render_line(font, msg, strlen(msg),
254             scale, color, pos_x, pos_y, text_align);
255       return;
256    }
257 
258    line_height = line_metrics->height * scale / font->vk->vp.height;
259 
260    for (;;)
261    {
262       const char *delim = strchr(msg, '\n');
263       unsigned msg_len  = delim
264          ? (unsigned)(delim - msg) : (unsigned)strlen(msg);
265 
266       /* Draw the line */
267       vulkan_raster_font_render_line(font, msg, msg_len,
268             scale, color, pos_x, pos_y - (float)lines * line_height,
269             text_align);
270 
271       if (!delim)
272          break;
273 
274       msg += msg_len + 1;
275       lines++;
276    }
277 }
278 
vulkan_raster_font_flush(vulkan_raster_t * font)279 static void vulkan_raster_font_flush(vulkan_raster_t *font)
280 {
281    struct vk_draw_triangles call;
282 
283    call.pipeline     = font->vk->pipelines.font;
284    call.texture      = &font->texture_optimal;
285    call.sampler      = font->vk->samplers.mipmap_linear;
286    call.uniform      = &font->vk->mvp;
287    call.uniform_size = sizeof(font->vk->mvp);
288    call.vbo          = &font->range;
289    call.vertices     = font->vertices;
290 
291    if(font->needs_update)
292    {
293       VkCommandBuffer staging;
294       VkSubmitInfo submit_info;
295       VkCommandBufferAllocateInfo cmd_info;
296       VkCommandBufferBeginInfo begin_info;
297       struct vk_texture *dynamic_tex  = NULL;
298       struct vk_texture *staging_tex  = NULL;
299 
300       cmd_info.sType              = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
301       cmd_info.pNext              = NULL;
302       cmd_info.commandPool        = font->vk->staging_pool;
303       cmd_info.level              = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
304       cmd_info.commandBufferCount = 1;
305       vkAllocateCommandBuffers(font->vk->context->device, &cmd_info, &staging);
306 
307       begin_info.sType            = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
308       begin_info.pNext            = NULL;
309       begin_info.flags            = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
310       begin_info.pInheritanceInfo = NULL;
311       vkBeginCommandBuffer(staging, &begin_info);
312 
313       VULKAN_SYNC_TEXTURE_TO_GPU_COND_OBJ(font->vk, font->texture);
314 
315       dynamic_tex                 = &font->texture_optimal;
316       staging_tex                 = &font->texture;
317 
318       VULKAN_COPY_STAGING_TO_DYNAMIC(font->vk, staging,
319             dynamic_tex, staging_tex);
320 
321       vkEndCommandBuffer(staging);
322 
323 #ifdef HAVE_THREADS
324       slock_lock(font->vk->context->queue_lock);
325 #endif
326 
327       submit_info.sType                = VK_STRUCTURE_TYPE_SUBMIT_INFO;
328       submit_info.pNext                = NULL;
329       submit_info.waitSemaphoreCount   = 0;
330       submit_info.pWaitSemaphores      = NULL;
331       submit_info.pWaitDstStageMask    = NULL;
332       submit_info.commandBufferCount   = 1;
333       submit_info.pCommandBuffers      = &staging;
334       submit_info.signalSemaphoreCount = 0;
335       submit_info.pSignalSemaphores    = NULL;
336       vkQueueSubmit(font->vk->context->queue,
337             1, &submit_info, VK_NULL_HANDLE);
338 
339       vkQueueWaitIdle(font->vk->context->queue);
340 
341 #ifdef HAVE_THREADS
342       slock_unlock(font->vk->context->queue_lock);
343 #endif
344 
345       vkFreeCommandBuffers(font->vk->context->device,
346             font->vk->staging_pool, 1, &staging);
347 
348       font->needs_update = false;
349    }
350 
351    vulkan_draw_triangles(font->vk, &call);
352 }
353 
vulkan_raster_font_render_msg(void * userdata,void * data,const char * msg,const struct font_params * params)354 static void vulkan_raster_font_render_msg(
355       void *userdata,
356       void *data,
357       const char *msg,
358       const struct font_params *params)
359 {
360    float color[4], color_dark[4];
361    int drop_x, drop_y;
362    bool full_screen;
363    unsigned max_glyphs;
364    unsigned width, height;
365    enum text_alignment text_align;
366    float x, y, scale, drop_mod, drop_alpha;
367    vk_t *vk                         = NULL;
368    vulkan_raster_t *font            = (vulkan_raster_t*)data;
369    settings_t *settings             = config_get_ptr();
370    float video_msg_pos_x            = settings->floats.video_msg_pos_x;
371    float video_msg_pos_y            = settings->floats.video_msg_pos_y;
372    float video_msg_color_r          = settings->floats.video_msg_color_r;
373    float video_msg_color_g          = settings->floats.video_msg_color_g;
374    float video_msg_color_b          = settings->floats.video_msg_color_b;
375 
376    if (!font || !msg || !*msg)
377       return;
378 
379    vk             = font->vk;
380 
381    width          = vk->video_width;
382    height         = vk->video_height;
383 
384    if (params)
385    {
386       x           = params->x;
387       y           = params->y;
388       scale       = params->scale;
389       full_screen = params->full_screen;
390       text_align  = params->text_align;
391       drop_x      = params->drop_x;
392       drop_y      = params->drop_y;
393       drop_mod    = params->drop_mod;
394       drop_alpha  = params->drop_alpha;
395 
396       color[0]    = FONT_COLOR_GET_RED(params->color)   / 255.0f;
397       color[1]    = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
398       color[2]    = FONT_COLOR_GET_BLUE(params->color)  / 255.0f;
399       color[3]    = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;
400 
401       /* If alpha is 0.0f, turn it into default 1.0f */
402       if (color[3] <= 0.0f)
403          color[3] = 1.0f;
404    }
405    else
406    {
407       x           = video_msg_pos_x;
408       y           = video_msg_pos_y;
409       scale       = 1.0f;
410       full_screen = true;
411       text_align  = TEXT_ALIGN_LEFT;
412       drop_x      = -2;
413       drop_y      = -2;
414       drop_mod    = 0.3f;
415       drop_alpha  = 1.0f;
416 
417       color[0]    = video_msg_color_r;
418       color[1]    = video_msg_color_g;
419       color[2]    = video_msg_color_b;
420       color[3]    = 1.0f;
421    }
422 
423    video_driver_set_viewport(width, height, full_screen, false);
424 
425    max_glyphs = strlen(msg);
426    if (drop_x || drop_y)
427       max_glyphs *= 2;
428 
429    if (!vulkan_buffer_chain_alloc(font->vk->context, &font->vk->chain->vbo,
430          6 * sizeof(struct vk_vertex) * max_glyphs, &font->range))
431       return;
432 
433    font->vertices   = 0;
434    font->pv         = (struct vk_vertex*)font->range.data;
435 
436    if (drop_x || drop_y)
437    {
438       color_dark[0] = color[0] * drop_mod;
439       color_dark[1] = color[1] * drop_mod;
440       color_dark[2] = color[2] * drop_mod;
441       color_dark[3] = color[3] * drop_alpha;
442 
443       vulkan_raster_font_render_message(font, msg, scale, color_dark,
444             x + scale * drop_x / vk->vp.width, y +
445             scale * drop_y / vk->vp.height, text_align);
446    }
447 
448    vulkan_raster_font_render_message(font, msg, scale,
449          color, x, y, text_align);
450    vulkan_raster_font_flush(font);
451 }
452 
vulkan_raster_font_get_glyph(void * data,uint32_t code)453 static const struct font_glyph *vulkan_raster_font_get_glyph(
454       void *data, uint32_t code)
455 {
456    const struct font_glyph* glyph;
457    vulkan_raster_t *font = (vulkan_raster_t*)data;
458 
459    if (!font || !font->font_driver)
460       return NULL;
461    if (!font->font_driver->ident)
462        return NULL;
463 
464    glyph = font->font_driver->get_glyph((void*)font->font_driver, code);
465 
466    if(glyph)
467       vulkan_raster_font_update_glyph(font, glyph);
468 
469    return glyph;
470 }
471 
vulkan_get_line_metrics(void * data,struct font_line_metrics ** metrics)472 static bool vulkan_get_line_metrics(void* data,
473       struct font_line_metrics **metrics)
474 {
475    vulkan_raster_t *font = (vulkan_raster_t*)data;
476 
477    if (!font || !font->font_driver || !font->font_data)
478       return -1;
479 
480    return font->font_driver->get_line_metrics(font->font_data, metrics);
481 }
482 
483 font_renderer_t vulkan_raster_font = {
484    vulkan_raster_font_init_font,
485    vulkan_raster_font_free_font,
486    vulkan_raster_font_render_msg,
487    "Vulkan raster",
488    vulkan_raster_font_get_glyph,
489    NULL,                            /* bind_block */
490    NULL,                            /* flush_block */
491    vulkan_get_message_width,
492    vulkan_get_line_metrics
493 };
494