static void menu_display_vk_draw(void *data)
{
   unsigned i;
   struct vk_buffer_range range;
   struct vk_texture *texture    = NULL;
   const float *vertex           = NULL;
   const float *tex_coord        = NULL;
   const float *color            = NULL;
   struct vk_vertex *pv          = NULL;
   menu_display_ctx_draw_t *draw = (menu_display_ctx_draw_t*)data;
   vk_t *vk                      = (vk_t*)video_driver_get_ptr(false);

   if (!vk || !draw)
      return;

   texture            = (struct vk_texture*)draw->texture;
   vertex             = draw->coords->vertex;
   tex_coord          = draw->coords->tex_coord;
   color              = draw->coords->color;

   if (!vertex)
      vertex          = menu_display_vk_get_default_vertices();
   if (!tex_coord)
      tex_coord       = menu_display_vk_get_default_tex_coords();
   if (!draw->coords->lut_tex_coord)
      draw->coords->lut_tex_coord = menu_display_vk_get_default_tex_coords();
   if (!texture)
      texture         = &vk->display.blank_texture;

   menu_display_vk_viewport(draw);
   vk->tracker.dirty |= VULKAN_DIRTY_DYNAMIC_BIT;

   /* Bake interleaved VBO. Kinda ugly, we should probably try to move to
    * an interleaved model to begin with ... */
   if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->vbo,
         draw->coords->vertices * sizeof(struct vk_vertex), &range))
      return;

   pv = (struct vk_vertex*)range.data;
   for (i = 0; i < draw->coords->vertices; i++, pv++)
   {
      pv->x       = *vertex++;
      pv->y       = 1.0f - (*vertex++); /* Y-flip. Vulkan is top-left clip space */
      pv->tex_x   = *tex_coord++;
      pv->tex_y   = *tex_coord++;
      pv->color.r = *color++;
      pv->color.g = *color++;
      pv->color.b = *color++;
      pv->color.a = *color++;
   }

   switch (draw->pipeline.id)
   {
#ifdef HAVE_SHADERPIPELINE
      case VIDEO_SHADER_MENU:
      case VIDEO_SHADER_MENU_SEC:
      {
         const struct vk_draw_triangles call = {
            vk->display.pipelines[
               to_menu_pipeline(draw->prim_type, draw->pipeline.id)],
               NULL,
               VK_NULL_HANDLE,
               draw->pipeline.backend_data,
               sizeof(float),
               &range,
               draw->coords->vertices,
         };
         vulkan_draw_triangles(vk, &call);
         break;
      }
#endif

      default:
      {
         const struct vk_draw_triangles call = {
            vk->display.pipelines[
               to_display_pipeline(draw->prim_type, vk->display.blend)],
               texture,
               texture->mipmap ?
                  vk->samplers.mipmap_linear :
                  (texture->default_smooth ? vk->samplers.linear : vk->samplers.nearest),
               draw->matrix_data
                  ? draw->matrix_data : menu_display_vk_get_default_mvp(),
               sizeof(math_matrix_4x4),
               &range,
               draw->coords->vertices,
         };
         vulkan_draw_triangles(vk, &call);
         break;
      }
   }
}
static void vulkan_raster_font_render_msg(
      video_frame_info_t *video_info,
      void *data, const char *msg,
      const void *userdata)
{
   float color[4], color_dark[4];
   int drop_x, drop_y;
   bool full_screen;
   unsigned max_glyphs;
   enum text_alignment text_align;
   float x, y, scale, drop_mod, drop_alpha;
   vk_t *vk                         = NULL;
   vulkan_raster_t *font            = (vulkan_raster_t*)data;
   unsigned width                   = video_info->width;
   unsigned height                  = video_info->height;
   const struct font_params *params = (const struct font_params*)userdata;

   if (!font || !msg || !*msg)
      return;

   vk             = font->vk;

   if (params)
   {
      x           = params->x;
      y           = params->y;
      scale       = params->scale;
      full_screen = params->full_screen;
      text_align  = params->text_align;
      drop_x      = params->drop_x;
      drop_y      = params->drop_y;
      drop_mod    = params->drop_mod;
      drop_alpha  = params->drop_alpha;

      color[0]    = FONT_COLOR_GET_RED(params->color)   / 255.0f;
      color[1]    = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
      color[2]    = FONT_COLOR_GET_BLUE(params->color)  / 255.0f;
      color[3]    = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;

      /* If alpha is 0.0f, turn it into default 1.0f */
      if (color[3] <= 0.0f)
         color[3] = 1.0f;
   }
   else
   {
      x           = video_info->font_msg_pos_x;
      y           = video_info->font_msg_pos_y;
      scale       = 1.0f;
      full_screen = true;
      text_align  = TEXT_ALIGN_LEFT;
      drop_x      = -2;
      drop_y      = -2;
      drop_mod    = 0.3f;
      drop_alpha  = 1.0f;

      color[0]    = video_info->font_msg_color_r;
      color[1]    = video_info->font_msg_color_g;
      color[2]    = video_info->font_msg_color_b;
      color[3]    = 1.0f;
   }

   video_driver_set_viewport(width, height, full_screen, false);

   max_glyphs = strlen(msg);
   if (drop_x || drop_y)
      max_glyphs *= 2;

   if (!vulkan_buffer_chain_alloc(font->vk->context, &font->vk->chain->vbo,
         6 * sizeof(struct vk_vertex) * max_glyphs, &font->range))
      return;

   font->vertices   = 0;
   font->pv         = (struct vk_vertex*)font->range.data;

   if (drop_x || drop_y)
   {
      color_dark[0] = color[0] * drop_mod;
      color_dark[1] = color[1] * drop_mod;
      color_dark[2] = color[2] * drop_mod;
      color_dark[3] = color[3] * drop_alpha;

      vulkan_raster_font_render_message(font, msg, scale, color_dark,
            x + scale * drop_x / vk->vp.width, y + 
            scale * drop_y / vk->vp.height, text_align);
   }

   vulkan_raster_font_render_message(font, msg, scale,
         color, x, y, text_align);
   vulkan_raster_font_flush(font);
}
static void menu_display_vk_draw(menu_display_ctx_draw_t *draw,
      video_frame_info_t *video_info)
{
   unsigned i;
   struct vk_buffer_range range;
   struct vk_texture *texture    = NULL;
   const float *vertex           = NULL;
   const float *tex_coord        = NULL;
   const float *color            = NULL;
   struct vk_vertex *pv          = NULL;
   vk_t *vk                      = video_info ? 
      (vk_t*)video_info->userdata : NULL;

   if (!vk || !draw)
      return;

   texture            = (struct vk_texture*)draw->texture;
   vertex             = draw->coords->vertex;
   tex_coord          = draw->coords->tex_coord;
   color              = draw->coords->color;

   if (!vertex)
      vertex          = menu_display_vk_get_default_vertices();
   if (!tex_coord)
      tex_coord       = menu_display_vk_get_default_tex_coords();
   if (!draw->coords->lut_tex_coord)
      draw->coords->lut_tex_coord = menu_display_vk_get_default_tex_coords();
   if (!texture)
      texture         = &vk->display.blank_texture;
   if (!color)
      color           = menu_display_vk_get_default_color();

   menu_display_vk_viewport(draw, video_info);

   vk->tracker.dirty |= VULKAN_DIRTY_DYNAMIC_BIT;

   /* Bake interleaved VBO. Kinda ugly, we should probably try to move to
    * an interleaved model to begin with ... */
   if (!vulkan_buffer_chain_alloc(vk->context, &vk->chain->vbo,
         draw->coords->vertices * sizeof(struct vk_vertex), &range))
      return;

   pv = (struct vk_vertex*)range.data;
   for (i = 0; i < draw->coords->vertices; i++, pv++)
   {
      pv->x       = *vertex++;
      /* Y-flip. Vulkan is top-left clip space */
      pv->y       = 1.0f - (*vertex++);
      pv->tex_x   = *tex_coord++;
      pv->tex_y   = *tex_coord++;
      pv->color.r = *color++;
      pv->color.g = *color++;
      pv->color.b = *color++;
      pv->color.a = *color++;
   }

   switch (draw->pipeline.id)
   {
#ifdef HAVE_SHADERPIPELINE
      case VIDEO_SHADER_MENU:
      case VIDEO_SHADER_MENU_2:
      case VIDEO_SHADER_MENU_3:
      case VIDEO_SHADER_MENU_4:
      case VIDEO_SHADER_MENU_5:
      {
         struct vk_draw_triangles call;

         call.pipeline     = vk->display.pipelines[
               to_menu_pipeline(draw->prim_type, draw->pipeline.id)];
         call.texture      = NULL;
         call.sampler      = VK_NULL_HANDLE;
         call.uniform      = draw->pipeline.backend_data;
         call.uniform_size = draw->pipeline.backend_data_size;
         call.vbo          = &range;
         call.vertices     = draw->coords->vertices;

         vulkan_draw_triangles(vk, &call);
         break;
      }

         break;
#endif

      default:
      {
         struct vk_draw_triangles call;

         call.pipeline     = vk->display.pipelines[
               to_display_pipeline(draw->prim_type, vk->display.blend)];
         call.texture      = texture;
         call.sampler      = texture->mipmap ?
            vk->samplers.mipmap_linear :
            (texture->default_smooth ? vk->samplers.linear
             : vk->samplers.nearest);
         call.uniform      = draw->matrix_data
            ? draw->matrix_data : menu_display_vk_get_default_mvp(video_info);
         call.uniform_size = sizeof(math_matrix_4x4);
         call.vbo          = &range;
         call.vertices     = draw->coords->vertices;

         vulkan_draw_triangles(vk, &call);
         break;
      }
   }
}