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