vulkan_raster_font.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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. #include <string.h>
  16. #include <encodings/utf.h>
  17. #include <compat/strl.h>
  18. #include "../common/vulkan_common.h"
  19. #include "../font_driver.h"
  20. #include "../../configuration.h"
  21. typedef struct
  22. {
  23. vk_t *vk;
  24. void *font_data;
  25. struct font_atlas *atlas;
  26. const font_renderer_driver_t *font_driver;
  27. struct vk_vertex *pv;
  28. struct vk_texture texture;
  29. struct vk_texture texture_optimal;
  30. struct vk_buffer_range range;
  31. unsigned vertices;
  32. bool needs_update;
  33. } vulkan_raster_t;
  34. static INLINE void vulkan_font_update_glyph(
  35. vulkan_raster_t *font, const struct font_glyph *glyph)
  36. {
  37. unsigned row;
  38. for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++)
  39. {
  40. uint8_t *src = font->atlas->buffer + row * font->atlas->width + glyph->atlas_offset_x;
  41. uint8_t *dst = (uint8_t*)font->texture.mapped + row * font->texture.stride + glyph->atlas_offset_x;
  42. memcpy(dst, src, glyph->width);
  43. }
  44. }
  45. static void vulkan_font_free(void *data, bool is_threaded)
  46. {
  47. vulkan_raster_t *font = (vulkan_raster_t*)data;
  48. if (!font)
  49. return;
  50. if (font->font_driver && font->font_data)
  51. font->font_driver->free(font->font_data);
  52. vkQueueWaitIdle(font->vk->context->queue);
  53. vulkan_destroy_texture(
  54. font->vk->context->device, &font->texture);
  55. vulkan_destroy_texture(
  56. font->vk->context->device, &font->texture_optimal);
  57. free(font);
  58. }
  59. static void *vulkan_font_init(void *data,
  60. const char *font_path, float font_size,
  61. bool is_threaded)
  62. {
  63. vulkan_raster_t *font =
  64. (vulkan_raster_t*)calloc(1, sizeof(*font));
  65. if (!font)
  66. return NULL;
  67. font->vk = (vk_t*)data;
  68. if (!font_renderer_create_default(
  69. &font->font_driver,
  70. &font->font_data, font_path, font_size))
  71. {
  72. free(font);
  73. return NULL;
  74. }
  75. font->atlas = font->font_driver->get_atlas(font->font_data);
  76. font->texture = vulkan_create_texture(font->vk, NULL,
  77. font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, font->atlas->buffer,
  78. NULL, VULKAN_TEXTURE_STAGING);
  79. {
  80. struct vk_texture *texture = &font->texture;
  81. VK_MAP_PERSISTENT_TEXTURE(font->vk->context->device, texture);
  82. }
  83. font->texture_optimal = vulkan_create_texture(font->vk, NULL,
  84. font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, NULL,
  85. NULL, VULKAN_TEXTURE_DYNAMIC);
  86. font->needs_update = true;
  87. return font;
  88. }
  89. static int vulkan_get_message_width(void *data, const char *msg,
  90. size_t msg_len, float scale)
  91. {
  92. const struct font_glyph* glyph_q = NULL;
  93. vulkan_raster_t *font = (vulkan_raster_t*)data;
  94. const char* msg_end = msg + msg_len;
  95. int delta_x = 0;
  96. if ( !font
  97. || !font->font_driver
  98. || !font->font_driver->get_glyph
  99. || !font->font_data )
  100. return 0;
  101. glyph_q = font->font_driver->get_glyph(font->font_data, '?');
  102. while (msg < msg_end)
  103. {
  104. const struct font_glyph *glyph;
  105. uint32_t code = utf8_walk(&msg);
  106. /* Do something smarter here ... */
  107. if (!(glyph = font->font_driver->get_glyph(
  108. font->font_data, code)))
  109. if (!(glyph = glyph_q))
  110. continue;
  111. if (font->atlas->dirty)
  112. {
  113. vulkan_font_update_glyph(font, glyph);
  114. font->atlas->dirty = false;
  115. font->needs_update = true;
  116. }
  117. delta_x += glyph->advance_x;
  118. }
  119. return delta_x * scale;
  120. }
  121. static void vulkan_font_render_line(vk_t *vk,
  122. vulkan_raster_t *font, const char *msg, size_t msg_len,
  123. float scale, const float color[4], float pos_x,
  124. float pos_y, unsigned text_align)
  125. {
  126. struct vk_color vk_color;
  127. const struct font_glyph* glyph_q = NULL;
  128. const char* msg_end = msg + msg_len;
  129. int x = roundf(pos_x * vk->vp.width);
  130. int y = roundf((1.0f - pos_y) * vk->vp.height);
  131. int delta_x = 0;
  132. int delta_y = 0;
  133. float inv_tex_size_x = 1.0f / font->texture.width;
  134. float inv_tex_size_y = 1.0f / font->texture.height;
  135. float inv_win_width = 1.0f / font->vk->vp.width;
  136. float inv_win_height = 1.0f / font->vk->vp.height;
  137. vk_color.r = color[0];
  138. vk_color.g = color[1];
  139. vk_color.b = color[2];
  140. vk_color.a = color[3];
  141. switch (text_align)
  142. {
  143. case TEXT_ALIGN_RIGHT:
  144. x -= vulkan_get_message_width(font, msg, msg_len, scale);
  145. break;
  146. case TEXT_ALIGN_CENTER:
  147. x -= vulkan_get_message_width(font, msg, msg_len, scale) / 2;
  148. break;
  149. }
  150. glyph_q = font->font_driver->get_glyph(font->font_data, '?');
  151. while (msg < msg_end)
  152. {
  153. const struct font_glyph *glyph;
  154. int off_x, off_y, tex_x, tex_y, width, height;
  155. unsigned code = utf8_walk(&msg);
  156. /* Do something smarter here ... */
  157. if (!(glyph =
  158. font->font_driver->get_glyph(font->font_data, code)))
  159. if (!(glyph = glyph_q))
  160. continue;
  161. if (font->atlas->dirty)
  162. {
  163. vulkan_font_update_glyph(font, glyph);
  164. font->atlas->dirty = false;
  165. font->needs_update = true;
  166. }
  167. off_x = glyph->draw_offset_x;
  168. off_y = glyph->draw_offset_y;
  169. tex_x = glyph->atlas_offset_x;
  170. tex_y = glyph->atlas_offset_y;
  171. width = glyph->width;
  172. height = glyph->height;
  173. {
  174. struct vk_vertex *pv = font->pv + font->vertices;
  175. float _x = (x + (off_x + delta_x) * scale)
  176. * inv_win_width;
  177. float _y = (y + (off_y + delta_y) * scale)
  178. * inv_win_height;
  179. float _width = width * scale * inv_win_width;
  180. float _height = height * scale * inv_win_height;
  181. float _tex_x = tex_x * inv_tex_size_x;
  182. float _tex_y = tex_y * inv_tex_size_y;
  183. float _tex_width = width * inv_tex_size_x;
  184. float _tex_height = height * inv_tex_size_y;
  185. const struct vk_color *_color = &vk_color;
  186. VULKAN_WRITE_QUAD_VBO(pv, _x, _y, _width, _height, _tex_x, _tex_y, _tex_width, _tex_height, _color);
  187. }
  188. font->vertices += 6;
  189. delta_x += glyph->advance_x;
  190. delta_y += glyph->advance_y;
  191. }
  192. }
  193. static void vulkan_font_render_message(
  194. vulkan_raster_t *font, const char *msg, float scale,
  195. const float color[4], float pos_x, float pos_y,
  196. unsigned text_align)
  197. {
  198. struct font_line_metrics *line_metrics = NULL;
  199. int lines = 0;
  200. float line_height;
  201. if (!msg || !*msg || !font->vk)
  202. return;
  203. /* If font line metrics are not supported just draw as usual */
  204. if (!font->font_driver->get_line_metrics ||
  205. !font->font_driver->get_line_metrics(font->font_data, &line_metrics))
  206. {
  207. vulkan_font_render_line(font->vk, font, msg, strlen(msg),
  208. scale, color, pos_x, pos_y, text_align);
  209. return;
  210. }
  211. line_height = line_metrics->height * scale / font->vk->vp.height;
  212. for (;;)
  213. {
  214. const char *delim = strchr(msg, '\n');
  215. size_t msg_len = delim
  216. ? (delim - msg) : strlen(msg);
  217. /* Draw the line */
  218. vulkan_font_render_line(font->vk, font, msg, msg_len,
  219. scale, color, pos_x, pos_y - (float)lines * line_height,
  220. text_align);
  221. if (!delim)
  222. break;
  223. msg += msg_len + 1;
  224. lines++;
  225. }
  226. }
  227. static void vulkan_font_flush(vulkan_raster_t *font)
  228. {
  229. struct vk_draw_triangles call;
  230. call.pipeline = font->vk->pipelines.font;
  231. call.texture = &font->texture_optimal;
  232. call.sampler = font->vk->samplers.mipmap_linear;
  233. call.uniform = &font->vk->mvp;
  234. call.uniform_size = sizeof(font->vk->mvp);
  235. call.vbo = &font->range;
  236. call.vertices = font->vertices;
  237. if (font->needs_update)
  238. {
  239. VkCommandBuffer staging;
  240. VkSubmitInfo submit_info;
  241. VkCommandBufferAllocateInfo cmd_info;
  242. VkCommandBufferBeginInfo begin_info;
  243. struct vk_texture *dynamic_tex = NULL;
  244. struct vk_texture *staging_tex = NULL;
  245. cmd_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  246. cmd_info.pNext = NULL;
  247. cmd_info.commandPool = font->vk->staging_pool;
  248. cmd_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  249. cmd_info.commandBufferCount = 1;
  250. vkAllocateCommandBuffers(font->vk->context->device, &cmd_info, &staging);
  251. begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  252. begin_info.pNext = NULL;
  253. begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
  254. begin_info.pInheritanceInfo = NULL;
  255. vkBeginCommandBuffer(staging, &begin_info);
  256. VULKAN_SYNC_TEXTURE_TO_GPU_COND_OBJ(font->vk, font->texture);
  257. dynamic_tex = &font->texture_optimal;
  258. staging_tex = &font->texture;
  259. vulkan_copy_staging_to_dynamic(font->vk, staging,
  260. dynamic_tex, staging_tex);
  261. vkEndCommandBuffer(staging);
  262. #ifdef HAVE_THREADS
  263. slock_lock(font->vk->context->queue_lock);
  264. #endif
  265. submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  266. submit_info.pNext = NULL;
  267. submit_info.waitSemaphoreCount = 0;
  268. submit_info.pWaitSemaphores = NULL;
  269. submit_info.pWaitDstStageMask = NULL;
  270. submit_info.commandBufferCount = 1;
  271. submit_info.pCommandBuffers = &staging;
  272. submit_info.signalSemaphoreCount = 0;
  273. submit_info.pSignalSemaphores = NULL;
  274. vkQueueSubmit(font->vk->context->queue,
  275. 1, &submit_info, VK_NULL_HANDLE);
  276. vkQueueWaitIdle(font->vk->context->queue);
  277. #ifdef HAVE_THREADS
  278. slock_unlock(font->vk->context->queue_lock);
  279. #endif
  280. vkFreeCommandBuffers(font->vk->context->device,
  281. font->vk->staging_pool, 1, &staging);
  282. font->needs_update = false;
  283. }
  284. vulkan_draw_triangles(font->vk, &call);
  285. }
  286. static void vulkan_font_render_msg(
  287. void *userdata,
  288. void *data,
  289. const char *msg,
  290. const struct font_params *params)
  291. {
  292. float color[4];
  293. int drop_x, drop_y;
  294. bool full_screen;
  295. size_t max_glyphs;
  296. unsigned width, height;
  297. enum text_alignment text_align;
  298. float x, y, scale, drop_mod, drop_alpha;
  299. vk_t *vk = NULL;
  300. vulkan_raster_t *font = (vulkan_raster_t*)data;
  301. settings_t *settings = config_get_ptr();
  302. float video_msg_pos_x = settings->floats.video_msg_pos_x;
  303. float video_msg_pos_y = settings->floats.video_msg_pos_y;
  304. float video_msg_color_r = settings->floats.video_msg_color_r;
  305. float video_msg_color_g = settings->floats.video_msg_color_g;
  306. float video_msg_color_b = settings->floats.video_msg_color_b;
  307. if (!font || !msg || !*msg)
  308. return;
  309. vk = font->vk;
  310. width = vk->video_width;
  311. height = vk->video_height;
  312. if (params)
  313. {
  314. x = params->x;
  315. y = params->y;
  316. scale = params->scale;
  317. full_screen = params->full_screen;
  318. text_align = params->text_align;
  319. drop_x = params->drop_x;
  320. drop_y = params->drop_y;
  321. drop_mod = params->drop_mod;
  322. drop_alpha = params->drop_alpha;
  323. color[0] = FONT_COLOR_GET_RED(params->color) / 255.0f;
  324. color[1] = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
  325. color[2] = FONT_COLOR_GET_BLUE(params->color) / 255.0f;
  326. color[3] = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;
  327. /* If alpha is 0.0f, turn it into default 1.0f */
  328. if (color[3] <= 0.0f)
  329. color[3] = 1.0f;
  330. }
  331. else
  332. {
  333. x = video_msg_pos_x;
  334. y = video_msg_pos_y;
  335. scale = 1.0f;
  336. full_screen = true;
  337. text_align = TEXT_ALIGN_LEFT;
  338. drop_x = -2;
  339. drop_y = -2;
  340. drop_mod = 0.3f;
  341. drop_alpha = 1.0f;
  342. color[0] = video_msg_color_r;
  343. color[1] = video_msg_color_g;
  344. color[2] = video_msg_color_b;
  345. color[3] = 1.0f;
  346. }
  347. video_driver_set_viewport(width, height, full_screen, false);
  348. max_glyphs = strlen(msg);
  349. if (drop_x || drop_y)
  350. max_glyphs *= 2;
  351. if (!vulkan_buffer_chain_alloc(font->vk->context, &font->vk->chain->vbo,
  352. 6 * sizeof(struct vk_vertex) * max_glyphs, &font->range))
  353. return;
  354. font->vertices = 0;
  355. font->pv = (struct vk_vertex*)font->range.data;
  356. if (drop_x || drop_y)
  357. {
  358. float color_dark[4];
  359. color_dark[0] = color[0] * drop_mod;
  360. color_dark[1] = color[1] * drop_mod;
  361. color_dark[2] = color[2] * drop_mod;
  362. color_dark[3] = color[3] * drop_alpha;
  363. vulkan_font_render_message(font, msg, scale, color_dark,
  364. x + scale * drop_x / vk->vp.width, y +
  365. scale * drop_y / vk->vp.height, text_align);
  366. }
  367. vulkan_font_render_message(font, msg, scale,
  368. color, x, y, text_align);
  369. vulkan_font_flush(font);
  370. }
  371. static const struct font_glyph *vulkan_font_get_glyph(
  372. void *data, uint32_t code)
  373. {
  374. const struct font_glyph* glyph;
  375. vulkan_raster_t *font = (vulkan_raster_t*)data;
  376. if (!font || !font->font_driver || !font->font_driver->ident)
  377. return NULL;
  378. glyph = font->font_driver->get_glyph((void*)font->font_driver, code);
  379. if (glyph && font->atlas->dirty)
  380. {
  381. vulkan_font_update_glyph(font, glyph);
  382. font->atlas->dirty = false;
  383. font->needs_update = true;
  384. }
  385. return glyph;
  386. }
  387. static bool vulkan_get_line_metrics(void* data,
  388. struct font_line_metrics **metrics)
  389. {
  390. vulkan_raster_t *font = (vulkan_raster_t*)data;
  391. if (font && font->font_driver && font->font_data)
  392. return font->font_driver->get_line_metrics(font->font_data, metrics);
  393. return false;
  394. }
  395. font_renderer_t vulkan_raster_font = {
  396. vulkan_font_init,
  397. vulkan_font_free,
  398. vulkan_font_render_msg,
  399. "vulkan_font",
  400. vulkan_font_get_glyph,
  401. NULL, /* bind_block */
  402. NULL, /* flush_block */
  403. vulkan_get_message_width,
  404. vulkan_get_line_metrics
  405. };