Files
RetroArch/gfx/drivers/rsx_gfx.c
T
LibretroAdmin efe2158a48 rsx: native RSX context, follow-ups
Give the RSX context and render interface proper IDs in libretro.h
(RETRO_HW_CONTEXT_RSX = 11, RETRO_HW_RENDER_INTERFACE_RSX = 6) and ship
one interface, version 1, with retire_memory in it, instead of two
versions picked through the context's version_major.

Retired memory goes on a ring and is covered by one label write per
frame, with no wait-for-idle and no flush per call.

Font vertex pages are all allocated with the font, and batches bigger
than a page are drawn in page-sized pieces, so nothing is allocated
while drawing.

When the menu shows over a duped frame from a hardware core, the core's
image is drawn behind it rather than the software texture. The frontend
state reset runs only on frames the core rendered.

The host mapping default stays 32 MB; RSX_HOST_SIZE still overrides it.
The PSL1GHT guards around the new code are dropped: the driver only
builds with HAVE_GCM, which only Makefile.psl1ght sets.
2026-10-27 17:52:21 +01:00

3150 lines
102 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2020 Google
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include <retro_inline.h>
#include <retro_math.h>
#include <encodings/utf.h>
#include <formats/image.h>
#include <string/stdstring.h>
#include <gfx/math/matrix_4x4.h>
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#ifdef HAVE_MENU
#include "../../menu/menu_driver.h"
#endif
#ifdef HAVE_GFX_WIDGETS
#include "../gfx_widgets.h"
#endif
#ifdef __PSL1GHT__
#include <rsx/rsx.h>
#include <rsx/nv40.h>
#include <ppu-types.h>
#endif
#include "../../driver.h"
#include "../../retroarch.h"
#include <libretro_rsx.h>
#ifdef HAVE_THREADS
#include "../video_thread_wrapper.h"
#include "../display_servers/dispserv_ps3.h"
#endif
#define RSX_MAX_BUFFERS 2
#define RSX_MAX_MENU_BUFFERS 2
#define RSX_MAX_TEXTURES 4
#define RSX_MAX_SHADERS 2
#define RSX_MAX_VERTICES 4
#define RSX_MAX_TEXTURE_VERTICES 4096 /* Set > 0 for preallocated texture vertices */
#define RSX_MAX_FONT_VERTICES 8192
#define RSX_FONT_VERTEX_PAGES 8
#define RSX_FONT_VERTEX_LABEL 198
/* Largest triangle-list draw that fits a page */
#define RSX_FONT_VERTEX_CHUNK (RSX_MAX_FONT_VERTICES - RSX_MAX_FONT_VERTICES % 3)
#define RSX_SHADER_STOCK_BLEND (RSX_MAX_SHADERS - 1)
#define RSX_SHADER_MENU (RSX_MAX_SHADERS - 2)
/* Shader objects */
extern const u8 modern_opaque_vpo_end[];
extern const u8 modern_opaque_vpo[];
extern const u32 modern_opaque_vpo_size;
extern const u8 modern_opaque_fpo_end[];
extern const u8 modern_opaque_fpo[];
extern const u32 modern_opaque_fpo_size;
extern const u8 modern_alpha_blend_vpo_end[];
extern const u8 modern_alpha_blend_vpo[];
extern const u32 modern_alpha_blend_vpo_size;
extern const u8 modern_alpha_blend_fpo_end[];
extern const u8 modern_alpha_blend_fpo[];
extern const u32 modern_alpha_blend_fpo_size;
typedef struct
{
/* The origin and the drawn area, one word each, in
* VIDEO_POS_PACK's and VIDEO_SCALE_PACK's layouts. Every value
* this driver puts here is a whole pixel count. */
unsigned pos;
unsigned dims;
float min, max;
float scale[4];
float offset[4];
} rsx_viewport_t;
typedef struct __attribute__((aligned(128)))
{
float x, y;
float u, v;
float r, g, b, a;
} rsx_vertex_t;
typedef struct
{
gcmTexture tex;
u32 *data;
u32 size; /* bytes data holds */
u32 offset;
u32 wrap_s;
u32 wrap_t;
u32 min_filter;
u32 mag_filter;
u32 width;
u32 height;
} rsx_texture_t;
#ifdef HAVE_OVERLAY
typedef struct
{
rsx_vertex_t *vertices;
rsx_texture_t texture;
} rsx_overlay_t;
#endif
typedef struct
{
int height;
int width;
int id;
uint32_t *ptr;
/* Internal stuff */
uint32_t offset;
} rsx_buffer;
typedef struct
{
video_viewport_t vp;
rsx_buffer buffers[RSX_MAX_BUFFERS];
#if defined(HAVE_MENU_BUFFER)
rsx_buffer menuBuffers[RSX_MAX_MENU_BUFFERS];
int menuBuffer;
#endif
int currentBuffer, nextBuffer;
gcmContextData* context;
u32* depth_buffer;
#if defined(HAVE_MENU_BUFFER)
gcmSurface surface[RSX_MAX_BUFFERS + RSX_MAX_MENU_BUFFERS];
#else
gcmSurface surface[RSX_MAX_BUFFERS];
#endif
rsx_texture_t texture[RSX_MAX_TEXTURES];
rsx_texture_t menu_texture;
/* Sampled by a quad drawn with no texture: solid colour */
rsx_texture_t *white_texture;
rsx_vertex_t *vertices;
rsx_vertex_t *texture_vertices;
int tex_index;
int vert_idx;
int texture_vert_idx;
int font_vert_idx;
u32 pos_offset[RSX_MAX_SHADERS];
u32 uv_offset[RSX_MAX_SHADERS];
u32 col_offset[RSX_MAX_SHADERS];
rsxProgramConst *proj_matrix[RSX_MAX_SHADERS];
rsxProgramConst *bgcolor[RSX_MAX_SHADERS];
rsxProgramAttrib *pos_index[RSX_MAX_SHADERS];
rsxProgramAttrib *col_index[RSX_MAX_SHADERS];
rsxProgramAttrib *uv_index[RSX_MAX_SHADERS];
rsxProgramAttrib *tex_unit[RSX_MAX_SHADERS];
void *vp_ucode[RSX_MAX_SHADERS];
void *fp_ucode[RSX_MAX_SHADERS];
rsxVertexProgram *vpo[RSX_MAX_SHADERS];
rsxFragmentProgram *fpo[RSX_MAX_SHADERS];
u32 *fp_buffer[RSX_MAX_SHADERS];
u32 fp_offset[RSX_MAX_SHADERS];
math_matrix_4x4 mvp, mvp_no_rot;
const shader_backend_t* shader;
void* shader_data;
void* renderchain_data;
void* ctx_data;
const gfx_ctx_driver_t* ctx_driver;
video_info_t video_info;
float menu_texture_alpha;
#ifdef HAVE_OVERLAY
rsx_overlay_t *overlay;
unsigned overlays;
bool overlay_enable;
bool overlay_full_screen;
/* overlay[].texture are the overlay pack's (load_textures) */
bool overlay_borrowed;
#endif
unsigned rotation;
u16 width;
u16 height;
u16 menu_width;
u16 menu_height;
u32 depth_pitch;
u32 depth_offset;
bool menu_frame_enable;
bool rgb32;
bool vsync;
bool smooth;
bool keep_aspect;
bool should_resize;
bool msg_rendering_enabled;
bool shared_context_use;
struct retro_hw_render_interface_rsx hw_interface;
struct retro_rsx_image hw_image;
bool hw_render;
bool hw_image_valid;
} rsx_t;
#include "../font_driver.h"
#include "../../configuration.h"
#include "../../command.h"
#include "../../driver.h"
#include "../../retroarch.h"
#include "../../runloop.h"
#include "../../verbosity.h"
#ifndef HAVE_THREADS
#include "../../tasks/tasks_internal.h"
#endif
#include <defines/ps3_defines.h>
#define CB_SIZE 0x100000
#ifndef RSX_HOST_SIZE
#define RSX_HOST_SIZE (32*1024*1024)
#endif
#define HOST_SIZE RSX_HOST_SIZE
static void *rsx_host_addr;
static bool rsx_set_hw_image(void *data, const struct retro_rsx_image *image)
{
rsx_t *rsx = (rsx_t*)data;
if (!rsx)
return false;
rsx->hw_image_valid = false;
if (!image)
return true;
if (!rsx->hw_render || rsx->video_info.is_threaded
|| !image->width || !image->height
|| image->width > 4096 || image->height > 4096
|| image->pitch < image->width * 4
|| (uint64_t)image->offset + (uint64_t)(image->height - 1)
* image->pitch + image->width * 4 > 256u * 1024 * 1024)
return false;
rsx->hw_image = *image;
rsx->hw_image_valid = true;
return true;
}
static bool rsx_get_hw_render_interface(void *data,
const struct retro_hw_render_interface **iface)
{
rsx_t *rsx = (rsx_t*)data;
if (!rsx || !iface || !rsx->hw_render
|| rsx->video_info.is_threaded || !rsx_host_addr)
return false;
*iface = (const struct retro_hw_render_interface*)&rsx->hw_interface;
return true;
}
#ifdef __PSL1GHT__
#include <rsx/rsx.h>
#include <rsx/nv40.h>
#include <ppu-types.h>
#include <ppu-lv2.h>
#include <sysutil/video.h>
#include <rsx/gcm_sys.h>
#include <rsx/rsx.h>
#include <io/pad.h>
#endif
#define RSX_RETIRED_MEMORY_COUNT 256 /* power of two */
#define RSX_RETIRED_MEMORY_LABEL 199
static struct
{
void *memory;
uint32_t fence;
} rsx_retired_memory[RSX_RETIRED_MEMORY_COUNT];
static unsigned rsx_retired_memory_head;
static unsigned rsx_retired_memory_tail;
/* Last fence written to the label */
static uint32_t rsx_retired_memory_serial;
static bool rsx_retired_memory_queued;
static bool rsx_labels_initialized;
static void rsx_collect_retired_memory(void)
{
uint32_t done;
if (rsx_retired_memory_head == rsx_retired_memory_tail)
return;
done = *(volatile uint32_t*)gcmGetLabelAddress(RSX_RETIRED_MEMORY_LABEL);
while (rsx_retired_memory_head != rsx_retired_memory_tail)
{
unsigned i = rsx_retired_memory_head & (RSX_RETIRED_MEMORY_COUNT - 1);
if ((int32_t)(done - rsx_retired_memory[i].fence) < 0)
break;
rsxFree(rsx_retired_memory[i].memory);
rsx_retired_memory_head++;
}
}
/* One fence per frame covers everything retired since the last one */
static void rsx_fence_retired_memory(gcmContextData *context)
{
if (!rsx_retired_memory_queued)
return;
rsxSetWriteBackendLabel(context, RSX_RETIRED_MEMORY_LABEL,
++rsx_retired_memory_serial);
rsx_retired_memory_queued = false;
}
static bool rsx_retire_hw_memory(void *data, void *memory)
{
unsigned i;
rsx_t *rsx = (rsx_t*)data;
if (!memory)
return true;
if (!rsx || !rsx->hw_render || rsx->video_info.is_threaded)
return false;
if (rsx_retired_memory_tail - rsx_retired_memory_head
== RSX_RETIRED_MEMORY_COUNT)
{
rsx_collect_retired_memory();
if (rsx_retired_memory_tail - rsx_retired_memory_head
== RSX_RETIRED_MEMORY_COUNT)
return false;
}
i = rsx_retired_memory_tail++ & (RSX_RETIRED_MEMORY_COUNT - 1);
rsx_retired_memory[i].memory = memory;
rsx_retired_memory[i].fence = rsx_retired_memory_serial + 1;
rsx_retired_memory_queued = true;
return true;
}
#define rsx_context_bind_hw_render(rsx, enable) \
if (rsx->shared_context_use) \
rsx->ctx_driver->bind_hw_render(rsx->ctx_data, enable)
#define RSX_FONT_EMIT(c, vx, vy) \
font_vertex[ 2 * (6 * i + c) + 0] = (x + (delta_x + off_x + vx * width) * scale) * inv_win_width; \
font_vertex[ 2 * (6 * i + c) + 1] = (y + (delta_y - off_y - vy * height) * scale) * inv_win_height; \
font_tex_coords[ 2 * (6 * i + c) + 0] = (tex_x + vx * width) * inv_tex_size_x; \
font_tex_coords[ 2 * (6 * i + c) + 1] = (tex_y + vy * height) * inv_tex_size_y
#define MAX_MSG_LEN_CHUNK 64
typedef struct
{
rsx_vertex_t *vertices;
unsigned used;
u32 fence;
} rsx_font_vertex_page_t;
static u32 rsx_font_vertex_serial;
typedef struct
{
rsx_t *rsx;
rsx_vertex_t *vertices;
rsx_font_vertex_page_t vertex_pages[RSX_FONT_VERTEX_PAGES];
unsigned vertex_page;
rsx_texture_t texture;
rsxProgramConst *proj_matrix;
rsxProgramAttrib *pos_index;
rsxProgramAttrib *uv_index;
rsxProgramAttrib *col_index;
rsxProgramAttrib *tex_unit;
rsxVertexProgram* vpo;
rsxFragmentProgram* fpo;
void *vp_ucode;
void *fp_ucode;
const font_renderer_driver_t *font_driver;
void *font_data;
struct font_atlas *atlas;
video_font_raster_block_t *block;
unsigned tex_dims;
/* Texture memory the atlas outgrew: a frame in flight may still
* draw from it, and freeing it means waiting for the RSX, so it goes
* when the font does - the atlas grows at most twice */
u32 *retired[2];
unsigned retired_count;
u32 fp_offset;
u32 pos_offset;
u32 uv_offset;
u32 col_offset;
/* The chunk a line is built into before it is handed over. Here
* rather than on the stack of the function that fills it: three
* arrays of MAX_MSG_LEN_CHUNK glyphs are twelve kilobytes, and a
* frame that size is three times what this tree allows. One font
* renders at a time on the thread that draws, so one is enough. */
float font_vertex[2 * 6 * MAX_MSG_LEN_CHUNK];
float font_tex_coords[2 * 6 * MAX_MSG_LEN_CHUNK];
float font_color[4 * 6 * MAX_MSG_LEN_CHUNK];
} rsx_font_t;
static const float rsx_vertexes[8] = {
0, 0,
1, 0,
0, 1,
1, 1
};
static const float rsx_tex_coords[8] = {
0, 1,
1, 1,
0, 0,
1, 0
};
/*
* FORWARD DECLARATIONS
*/
static void rsx_set_viewport(void *data, unsigned dims,
bool force_full, bool allow_rotate);
/*
* DISPLAY DRIVER
*/
static const float *gfx_display_rsx_get_default_vertices(void)
{
return &rsx_vertexes[0];
}
static const float *gfx_display_rsx_get_default_tex_coords(void)
{
return &rsx_tex_coords[0];
}
static void *gfx_display_rsx_get_default_mvp(void *data)
{
rsx_t *rsx = (rsx_t*)data;
if (!rsx)
return NULL;
return &rsx->mvp_no_rot;
}
static void gfx_display_rsx_draw(gfx_display_ctx_draw_t *draw,
void *data, unsigned video_dims)
{
unsigned i;
rsx_viewport_t vp;
int end_vert_idx;
rsx_vertex_t *vertices = NULL;
rsx_texture_t *texture = NULL;
const float *vertex = NULL;
const float *tex_coord = NULL;
const float *color = NULL;
rsx_t *rsx = (rsx_t*)data;
if (!rsx || !draw)
return;
texture = draw->texture
? (rsx_texture_t *)draw->texture : rsx->white_texture;
vertex = draw->coords->vertex;
tex_coord = draw->coords->tex_coord;
color = draw->coords->color;
if (!vertex)
vertex = &rsx_vertexes[0];
if (!tex_coord)
tex_coord = &rsx_tex_coords[0];
if (!texture)
return;
vp.pos = VIDEO_POS_PACK(abs(VIDEO_POS_X(draw->pos)),
abs(VIDEO_POS_FLIP_Y(rsx->height, draw->pos, draw->dims)));
vp.dims = VIDEO_SCALE_PACK(
MIN(VIDEO_SCALE_W(draw->dims), rsx->width),
MIN(VIDEO_SCALE_H(draw->dims), rsx->height));
vp.min = 0.0f;
vp.max = 1.0f;
vp.scale[0] = VIDEO_SCALE_W(vp.dims) * 0.5f;
vp.scale[1] = VIDEO_SCALE_H(vp.dims) * -0.5f;
vp.scale[2] = (vp.max - vp.min) * 0.5f;
vp.scale[3] = 0.0f;
vp.offset[0] = VIDEO_POS_X(vp.pos)
+ VIDEO_SCALE_W(vp.dims) * 0.5f;
vp.offset[1] = VIDEO_POS_Y(vp.pos)
+ VIDEO_SCALE_H(vp.dims) * 0.5f;
vp.offset[2] = (vp.max + vp.min) * 0.5f;
vp.offset[3] = 0.0f;
rsxSetViewport(rsx->context, VIDEO_POS_X(vp.pos),
VIDEO_POS_Y(vp.pos), VIDEO_SCALE_W(vp.dims),
VIDEO_SCALE_H(vp.dims), vp.min, vp.max,
vp.scale, vp.offset);
rsxInvalidateTextureCache(rsx->context, GCM_INVALIDATE_TEXTURE);
rsxLoadTexture(rsx->context, rsx->tex_unit[RSX_SHADER_STOCK_BLEND]->index, &texture->tex);
rsxTextureControl(rsx->context, rsx->tex_unit[RSX_SHADER_STOCK_BLEND]->index,
GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
rsxTextureFilter(rsx->context, rsx->tex_unit[RSX_SHADER_STOCK_BLEND]->index, 0,
texture->min_filter, texture->mag_filter, GCM_TEXTURE_CONVOLUTION_QUINCUNX);
rsxTextureWrapMode(rsx->context, rsx->tex_unit[RSX_SHADER_STOCK_BLEND]->index, texture->wrap_s,
texture->wrap_t, GCM_TEXTURE_CLAMP_TO_EDGE, 0, GCM_TEXTURE_ZFUNC_LESS, 0);
#if RSX_MAX_TEXTURE_VERTICES > 0
/* Using preallocated texture vertices uses better memory management but may cause more flickering */
end_vert_idx = rsx->texture_vert_idx + draw->coords->vertices;
if (end_vert_idx > RSX_MAX_TEXTURE_VERTICES)
{
rsx->texture_vert_idx = 0;
end_vert_idx = rsx->texture_vert_idx + draw->coords->vertices;
}
vertices = rsx->texture_vertices;
#else
/* Smoother gfx at the cost of unmanaged rsx memory */
rsx->texture_vert_idx = 0;
end_vert_idx = draw->coords->vertices;
vertices = (rsx_vertex_t *)rsxMemalign(128, sizeof(rsx_vertex_t) * draw->coords->vertices);
#endif
for (i = rsx->texture_vert_idx; i < end_vert_idx; i++)
{
vertices[i].x = *vertex++;
vertices[i].y = *vertex++;
vertices[i].u = *tex_coord++;
vertices[i].v = *tex_coord++;
vertices[i].r = *color++;
vertices[i].g = *color++;
vertices[i].b = *color++;
vertices[i].a = *color++;
}
rsxAddressToOffset(&vertices[rsx->texture_vert_idx].x,
&rsx->pos_offset[RSX_SHADER_STOCK_BLEND]);
rsxAddressToOffset(&vertices[rsx->texture_vert_idx].u,
&rsx->uv_offset[RSX_SHADER_STOCK_BLEND]);
rsxAddressToOffset(&vertices[rsx->texture_vert_idx].r,
&rsx->col_offset[RSX_SHADER_STOCK_BLEND]);
rsx->texture_vert_idx = end_vert_idx;
rsxBindVertexArrayAttrib(rsx->context, rsx->pos_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->pos_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 2,
GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, rsx->uv_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->uv_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 2,
GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, rsx->col_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->col_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 4,
GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxLoadVertexProgram(rsx->context, rsx->vpo[RSX_SHADER_STOCK_BLEND],
rsx->vp_ucode[RSX_SHADER_STOCK_BLEND]);
rsxSetVertexProgramParameter(rsx->context,
rsx->vpo[RSX_SHADER_STOCK_BLEND], rsx->proj_matrix[RSX_SHADER_STOCK_BLEND],
(float *)&rsx->mvp_no_rot);
rsxLoadFragmentProgramLocation(rsx->context,
rsx->fpo[RSX_SHADER_STOCK_BLEND], rsx->fp_offset[RSX_SHADER_STOCK_BLEND],
GCM_LOCATION_RSX);
rsxClearSurface(rsx->context, GCM_CLEAR_Z);
rsxInvalidateVertexCache(rsx->context);
rsxDrawVertexArray(rsx->context, GCM_TYPE_TRIANGLE_STRIP, 0, draw->coords->vertices);
}
static void gfx_display_rsx_scissor_begin(void *data, unsigned video_dims,
int x, int y, unsigned dims)
{
unsigned video_height = VIDEO_SCALE_H(video_dims);
unsigned width = VIDEO_SCALE_W(dims);
unsigned height = VIDEO_SCALE_H(dims);
rsx_t *rsx = (rsx_t *)data;
rsxSetScissor(rsx->context, x, video_height - y - height, width, height);
}
static void gfx_display_rsx_scissor_end(void *data, unsigned video_dims)
{
unsigned video_width = VIDEO_SCALE_W(video_dims);
unsigned video_height = VIDEO_SCALE_H(video_dims);
rsx_t *rsx = (rsx_t *)data;
rsxSetScissor(rsx->context, 0, 0, video_width, video_height);
}
static void gfx_display_rsx_blend_begin(void *data)
{
rsx_t *rsx = (rsx_t *)data;
rsxSetBlendEnable(rsx->context, GCM_TRUE);
rsxSetBlendFunc(rsx->context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA);
rsxSetBlendEquation(rsx->context, GCM_FUNC_ADD, GCM_FUNC_ADD);
#if 0
rsxSetBlendEnableMrt(rsx->context, GCM_TRUE, GCM_TRUE, GCM_TRUE);
rsxSetDepthFunc(rsx->context, GCM_LESS);
rsxSetDepthTestEnable(rsx->context, GCM_FALSE);
rsxSetAlphaFunc(rsx->context, GCM_ALWAYS, 0);
rsxSetAlphaTestEnable(rsx->context, GCM_TRUE);
#endif
}
static void gfx_display_rsx_blend_end(void *data)
{
rsx_t *rsx = (rsx_t *)data;
rsxSetBlendEnable(rsx->context, GCM_FALSE);
#if 0
rsxSetBlendEnableMrt(rsx->context, GCM_FALSE, GCM_FALSE, GCM_FALSE);
#endif
}
/*
* FONT DRIVER
*/
static void rsx_font_free(void *data,
bool is_threaded)
{
rsx_font_t *font = (rsx_font_t*)data;
if (!font)
return;
if (font->font_driver && font->font_data)
font->font_driver->free(font->font_data);
if (is_threaded)
{
if (
font->rsx &&
font->rsx->ctx_driver &&
font->rsx->ctx_driver->make_current)
font->rsx->ctx_driver->make_current(true);
}
rsxClearSurface(font->rsx->context, GCM_CLEAR_Z);
gcmSetWaitFlip(font->rsx->context);
/* Drain the RSX command queue before freeing font memory.
* gcmSetWaitFlip only waits for the page flip, not for all
* draw commands to complete. rsxFinish ensures the GPU is
* fully idle so the font texture and vertex buffer can be
* safely freed.
*
* Previously #if 0'd out, leaking font RSX memory on every
* font scale change or context reset. */
rsxFinish(font->rsx->context, 0);
if (font->texture.data)
rsxFree(font->texture.data);
{
unsigned i;
for (i = 0; i < font->retired_count; i++)
rsxFree(font->retired[i]);
}
{
unsigned i;
for (i = 0; i < RSX_FONT_VERTEX_PAGES; i++)
if (font->vertex_pages[i].vertices)
rsxFree(font->vertex_pages[i].vertices);
}
free(font);
}
static bool rsx_font_upload_atlas(rsx_t *rsx, rsx_font_t *font)
{
u8 *texbuffer = (u8 *)font->texture.data;
const u8 *atlas_data = (u8 *)font->atlas->buffer;
/* Texture pitch equals the atlas width, so the dirty row band is
* contiguous in both buffers and one memcpy of just that band
* suffices; the initial upload (dirty rect covering everything
* after the renderer pre-cache) still transfers the full atlas. */
unsigned y0 = VIDEO_SCALE_H(font->atlas->dirty_xy0);
unsigned y1 = VIDEO_SCALE_H(font->atlas->dirty_xy1);
if (y1 > font->atlas->height || y1 <= y0)
{
y0 = 0;
y1 = font->atlas->height;
}
memcpy(texbuffer + (size_t)y0 * font->atlas->width,
atlas_data + (size_t)y0 * font->atlas->width,
(size_t)(y1 - y0) * font->atlas->width);
font->texture.tex.format = GCM_TEXTURE_FORMAT_B8 | GCM_TEXTURE_FORMAT_LIN;
font->texture.tex.mipmap = 1;
font->texture.tex.dimension = GCM_TEXTURE_DIMS_2D;
font->texture.tex.cubemap = GCM_FALSE;
font->texture.tex.remap = (
(GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_G_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_R_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_A_SHIFT));
font->texture.tex.width = VIDEO_SCALE_W(font->tex_dims);
font->texture.tex.height = VIDEO_SCALE_H(font->tex_dims);
font->texture.tex.depth = 1;
font->texture.tex.pitch = VIDEO_SCALE_W(font->tex_dims);
font->texture.tex.location = GCM_LOCATION_RSX;
font->texture.tex.offset = font->texture.offset;
font->texture.wrap_s = GCM_TEXTURE_CLAMP_TO_EDGE;
font->texture.wrap_t = GCM_TEXTURE_CLAMP_TO_EDGE;
font->texture.min_filter = GCM_TEXTURE_LINEAR;
font->texture.mag_filter = GCM_TEXTURE_LINEAR;
rsxInvalidateTextureCache(rsx->context, GCM_INVALIDATE_TEXTURE);
rsxLoadTexture(rsx->context, font->tex_unit->index, &font->texture.tex);
rsxTextureControl(rsx->context, font->tex_unit->index,
GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
rsxTextureFilter(rsx->context, font->tex_unit->index,
0, font->texture.min_filter,
font->texture.mag_filter, GCM_TEXTURE_CONVOLUTION_QUINCUNX);
rsxTextureWrapMode(rsx->context, font->tex_unit->index,
font->texture.wrap_s, font->texture.wrap_t,
GCM_TEXTURE_CLAMP_TO_EDGE, 0, GCM_TEXTURE_ZFUNC_LESS, 0);
return true;
}
static void *rsx_font_init(void *data,
const char *font_path, float font_size,
bool is_threaded)
{
rsx_font_t *font = (rsx_font_t*)calloc(1, sizeof(*font));
if (!font)
return NULL;
font->rsx = (rsx_t *)data;
if (!font_renderer_create_default(
&font->font_driver,
&font->font_data, font_path, font_size, FONT_ATLAS_FORMAT_A8))
{
free(font);
return NULL;
}
if (is_threaded)
if (
font->rsx
&& font->rsx->ctx_driver
&& font->rsx->ctx_driver->make_current)
font->rsx->ctx_driver->make_current(false);
font->atlas = font->font_driver->get_atlas(font->font_data);
font->vpo = font->rsx->vpo[RSX_SHADER_STOCK_BLEND];
font->fpo = font->rsx->fpo[RSX_SHADER_STOCK_BLEND];
font->fp_ucode = font->rsx->fp_ucode[RSX_SHADER_STOCK_BLEND];
font->vp_ucode = font->rsx->vp_ucode[RSX_SHADER_STOCK_BLEND];
font->fp_offset = font->rsx->fp_offset[RSX_SHADER_STOCK_BLEND];
font->proj_matrix = font->rsx->proj_matrix[RSX_SHADER_STOCK_BLEND];
font->pos_index = font->rsx->pos_index[RSX_SHADER_STOCK_BLEND];
font->uv_index = font->rsx->uv_index[RSX_SHADER_STOCK_BLEND];
font->col_index = font->rsx->col_index[RSX_SHADER_STOCK_BLEND];
font->tex_unit = font->rsx->tex_unit[RSX_SHADER_STOCK_BLEND];
{
unsigned i;
for (i = 0; i < RSX_FONT_VERTEX_PAGES; i++)
if (!(font->vertex_pages[i].vertices = (rsx_vertex_t *)rsxMemalign(
128, sizeof(rsx_vertex_t) * RSX_MAX_FONT_VERTICES)))
goto error;
}
font->vertices = font->vertex_pages[0].vertices;
font->rsx->font_vert_idx = 0;
/* The atlas may grow; the texture follows it */
font->atlas->max_dims = VIDEO_SCALE_PACK(2048, 2048);
font->tex_dims = VIDEO_SCALE_PACK(font->atlas->width,
font->atlas->height);
if (!(font->texture.data = (u32*)rsxMemalign(128,
VIDEO_SCALE_AREA(font->tex_dims))))
goto error;
rsxAddressToOffset(font->texture.data, &font->texture.offset);
if (!rsx_font_upload_atlas(font->rsx, font))
goto error;
font->atlas->dirty = false;
rsxTextureControl(font->rsx->context, font->tex_unit->index,
GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
return font;
error:
rsx_font_free(font, is_threaded);
return NULL;
}
static int rsx_font_get_message_width(void *data, const char *msg,
size_t msg_len, float scale)
{
rsx_font_t *font = (rsx_font_t*)data;
if (!font)
return 0;
return font_renderer_get_message_width(font->font_driver,
font->font_data, msg, msg_len, scale);
}
/* Returns how many of count vertices the current page now has room
* for, at most a chunk; 0 while every other page is still on the GPU. */
static unsigned rsx_font_reserve_vertices(rsx_t *rsx, rsx_font_t *font,
unsigned count)
{
rsx_font_vertex_page_t *page = &font->vertex_pages[font->vertex_page];
unsigned i;
u32 done;
if (count > RSX_FONT_VERTEX_CHUNK)
count = RSX_FONT_VERTEX_CHUNK;
if (!page->fence && count <= RSX_MAX_FONT_VERTICES - page->used)
return count;
if (page->used && !page->fence)
{
page->fence = ++rsx_font_vertex_serial;
if (!page->fence)
page->fence = ++rsx_font_vertex_serial;
rsxSetWriteBackendLabel(rsx->context, RSX_FONT_VERTEX_LABEL,
page->fence);
rsxFlushBuffer(rsx->context);
}
done = *(volatile u32*)gcmGetLabelAddress(RSX_FONT_VERTEX_LABEL);
for (i = 0; i < RSX_FONT_VERTEX_PAGES; i++)
{
page = &font->vertex_pages[i];
if (page->fence ? (int32_t)(done - page->fence) < 0 : page->used != 0)
continue;
page->used = 0;
page->fence = 0;
font->vertices = page->vertices;
font->vertex_page = i;
return count;
}
return 0;
}
static void rsx_font_draw_vertices(
rsx_t *rsx,
rsx_font_t *font,
const video_coords_t *coords)
{
unsigned i, count;
unsigned remaining = coords->vertices;
rsx_vertex_t *vertices = NULL;
const float *vertex = coords->vertex;
const float *tex_coord = coords->tex_coord;
const float *color = coords->color;
if (font->atlas->dirty)
{
rsx_font_upload_atlas(rsx, font);
font->atlas->dirty = false;
}
rsxLoadVertexProgram(rsx->context, font->vpo, font->vp_ucode);
rsxSetVertexProgramParameter(rsx->context, font->vpo,
font->proj_matrix, (float *)&rsx->mvp_no_rot);
rsxLoadFragmentProgramLocation(rsx->context, font->fpo,
font->fp_offset, GCM_LOCATION_RSX);
rsxClearSurface(rsx->context, GCM_CLEAR_Z);
while (remaining)
{
rsx_font_vertex_page_t *page;
if (!(count = rsx_font_reserve_vertices(rsx, font, remaining)))
return;
page = &font->vertex_pages[font->vertex_page];
vertices = font->vertices + page->used;
for (i = 0; i < count; i++)
{
vertices[i].x = *vertex++;
vertices[i].y = *vertex++;
vertices[i].u = *tex_coord++;
vertices[i].v = *tex_coord++;
vertices[i].r = *color++;
vertices[i].g = *color++;
vertices[i].b = *color++;
vertices[i].a = *color++;
}
rsxAddressToOffset(&vertices[0].x, &font->pos_offset);
rsxAddressToOffset(&vertices[0].u, &font->uv_offset);
rsxAddressToOffset(&vertices[0].r, &font->col_offset);
page->used += count;
rsx->font_vert_idx = page->used;
remaining -= count;
rsxBindVertexArrayAttrib(rsx->context, font->pos_index->index, 0,
font->pos_offset, sizeof(rsx_vertex_t), 2,
GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, font->uv_index->index, 0,
font->uv_offset, sizeof(rsx_vertex_t), 2,
GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, font->col_index->index, 0,
font->col_offset, sizeof(rsx_vertex_t), 4,
GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxInvalidateVertexCache(rsx->context);
rsxDrawVertexArray(rsx->context, GCM_TYPE_TRIANGLES, 0, count);
}
}
#define RSX_FONT_FLUSH() \
do \
{ \
coords.tex_coord = font_tex_coords; \
coords.vertex = font_vertex; \
coords.color = font_color; \
coords.vertices = i * 6; \
coords.lut_tex_coord = font_tex_coords; \
if (font->block) \
video_coord_array_append( \
&font->block->carr, &coords, coords.vertices); \
else \
rsx_font_draw_vertices(rsx, font, &coords); \
i = 0; \
} while (0)
static void rsx_font_render_message(rsx_t *rsx,
rsx_font_t *font, const char *msg, size_t msg_len, float scale,
const float color[4], float pos_x, float pos_y,
unsigned text_align)
{
struct video_coords coords;
float color_block[4 * 6];
int n;
float line_height;
struct font_line_metrics *line_metrics = NULL;
int i = 0;
int x = 0;
int y = 0;
float *font_tex_coords = font->font_tex_coords;
float *font_vertex = font->font_vertex;
float *font_color = font->font_color;
int pre_x = roundf(pos_x * VIDEO_SCALE_W(rsx->vp.dims));
float inv_tex_size_x = 1.0f / VIDEO_SCALE_W(font->tex_dims);
float inv_tex_size_y = 1.0f / VIDEO_SCALE_H(font->tex_dims);
float inv_win_width = 1.0f / VIDEO_SCALE_W(rsx->vp.dims);
float inv_win_height = 1.0f / VIDEO_SCALE_H(rsx->vp.dims);
const struct font_glyph* (*get_glyph)(void*, uint32_t)
= font->font_driver->get_glyph;
void *font_data = font->font_data;
const struct font_glyph* glyph_q = get_glyph(font_data, '?');
bool aligned = (text_align == TEXT_ALIGN_RIGHT
|| text_align == TEXT_ALIGN_CENTER);
font->font_driver->get_line_metrics(font->font_data, &line_metrics);
line_height = line_metrics->height * scale / VIDEO_SCALE_H(rsx->vp.dims);
for (n = 0; n < 6; n++)
{
color_block[4 * n + 0] = color[0];
color_block[4 * n + 1] = color[1];
color_block[4 * n + 2] = color[2];
color_block[4 * n + 3] = color[3];
}
#define FONT_LAYOUT_ALIGNED aligned
#define FONT_LAYOUT_LINE(line, line_width, count, bytes) \
do \
{ \
(void)(count); \
x = pre_x; \
y = roundf((pos_y - (float)(line) * line_height) \
* VIDEO_SCALE_H(rsx->vp.dims)); \
if (text_align == TEXT_ALIGN_RIGHT) \
x -= (int)((line_width) * scale); \
else if (text_align == TEXT_ALIGN_CENTER) \
x -= (int)((line_width) * scale) / 2; \
} while (0)
#define FONT_LAYOUT_GLYPH(glyph, pen_x, pen_y) \
do \
{ \
int off_x = (glyph)->draw_offset_x; \
int off_y = (glyph)->draw_offset_y; \
int tex_x = VIDEO_SCALE_W((glyph)->atlas_pos); \
int tex_y = VIDEO_SCALE_H((glyph)->atlas_pos); \
int width = VIDEO_SCALE_W((glyph)->dims); \
int height = VIDEO_SCALE_H((glyph)->dims); \
int delta_x = (pen_x); \
int delta_y = -(pen_y); \
RSX_FONT_EMIT(0, 0, 1); /* Bottom-left */ \
RSX_FONT_EMIT(1, 1, 1); /* Bottom-right */ \
RSX_FONT_EMIT(2, 0, 0); /* Top-left */ \
RSX_FONT_EMIT(3, 1, 0); /* Top-right */ \
RSX_FONT_EMIT(4, 0, 0); /* Top-left */ \
RSX_FONT_EMIT(5, 1, 1); /* Bottom-right */ \
memcpy(&font_color[4 * 6 * i], color_block, sizeof(color_block)); \
if (++i == MAX_MSG_LEN_CHUNK) \
RSX_FONT_FLUSH(); \
} while (0)
#define FONT_LAYOUT_LINE_END() \
do \
{ \
if (i) \
RSX_FONT_FLUSH(); \
} while (0)
#include "../font_layout.h"
}
#undef RSX_FONT_FLUSH
static void rsx_font_setup_viewport(
rsx_t *rsx, rsx_font_t *font,
unsigned dims,
bool full_screen)
{
rsx_set_viewport(rsx, dims, full_screen, false);
rsxSetBlendEnable(rsx->context, GCM_TRUE);
rsxSetBlendFunc(rsx->context, GCM_SRC_ALPHA,
GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA);
rsxSetBlendEquation(rsx->context, GCM_FUNC_ADD, GCM_FUNC_ADD);
rsxLoadTexture(rsx->context, font->tex_unit->index, &font->texture.tex);
rsxTextureControl(rsx->context, font->tex_unit->index,
GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
}
static void rsx_font_render_msg(
void *userdata,
void *data,
const char *msg, size_t msg_len,
const struct font_params *params)
{
font_params_resolved_t rp;
float color[4];
int drop_x, drop_y;
unsigned dims;
float x, y, scale, drop_mod, drop_alpha;
enum text_alignment text_align = TEXT_ALIGN_LEFT;
bool full_screen = false;
rsx_font_t *font = (rsx_font_t*)data;
rsx_t *rsx = (rsx_t*)userdata;
if (!font || !msg || !*msg || !rsx)
return;
/* Asked for before anything is laid out: when it has grown, texture
* memory of its size takes the old one's place, all of it to be
* uploaded at the next draw */
if (font->font_driver && font->font_data)
{
font->atlas = font->font_driver->get_atlas(font->font_data);
if ( font->retired_count < 2
&& font->tex_dims != VIDEO_SCALE_PACK(font->atlas->width,
font->atlas->height))
{
u32 *data = (u32*)rsxMemalign(128,
font->atlas->width * font->atlas->height);
if (data)
{
font->retired[font->retired_count++] = font->texture.data;
font->texture.data = data;
rsxAddressToOffset(font->texture.data, &font->texture.offset);
font->tex_dims = VIDEO_SCALE_PACK(font->atlas->width,
font->atlas->height);
font->atlas->dirty_xy0 = 0;
font->atlas->dirty_xy1 = font->tex_dims;
font->atlas->dirty_rects = 0;
font->atlas->dirty = true;
}
}
}
dims = VIDEO_SCALE_PACK(rsx->width,
rsx->height);
font_driver_resolve_params(params, &rp);
x = rp.x;
y = rp.y;
scale = rp.scale;
full_screen = rp.full_screen;
text_align = rp.text_align;
drop_x = rp.drop_x;
drop_y = rp.drop_y;
drop_mod = rp.drop_mod;
drop_alpha = rp.drop_alpha;
color[0] = rp.color[0];
color[1] = rp.color[1];
color[2] = rp.color[2];
color[3] = rp.color[3];
/* If alpha is 0.0f, turn it into default 1.0f */
if (color[3] <= 0.0f)
color[3] = 1.0f;
if (font->block)
font->block->fullscreen = full_screen;
else
rsx_font_setup_viewport(rsx, font, dims, full_screen);
if ( (msg && *msg)
&& font->font_data
&& font->font_driver)
{
if (drop_x || drop_y)
{
float color_dark[4];
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;
rsx_font_render_message(rsx, font, msg, msg_len, scale, color_dark,
x + scale * drop_x / VIDEO_SCALE_W(rsx->vp.dims), y +
scale * drop_y / VIDEO_SCALE_H(rsx->vp.dims), text_align);
}
rsx_font_render_message(rsx, font, msg, msg_len, scale, color,
x, y, text_align);
}
if (!font->block)
{
/* Restore viewport */
rsxTextureControl(rsx->context, font->tex_unit->index,
GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
rsxSetBlendEnable(rsx->context, GCM_FALSE);
rsx_set_viewport(rsx, dims, false, true);
}
}
static const struct font_glyph *rsx_font_get_glyph(
void *data, uint32_t code)
{
rsx_font_t *font = (rsx_font_t*)data;
if (font && font->font_driver)
return font->font_driver->get_glyph((void*)font->font_data, code);
return NULL;
}
static void rsx_font_flush_block(unsigned dims, void *data)
{
rsx_font_t *font = (rsx_font_t*)data;
video_font_raster_block_t *block = font ? font->block : NULL;
rsx_t *rsx = font ? font->rsx : NULL;
if (!font || !block || !block->carr.coords.vertices || !rsx)
return;
rsx_font_setup_viewport(rsx, font, dims, block->fullscreen);
rsx_font_draw_vertices (rsx, font, (video_coords_t*)&block->carr.coords);
/* Restore viewport */
rsxTextureControl(rsx->context, font->tex_unit->index,
GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
rsxSetBlendEnable(rsx->context, GCM_FALSE);
rsx_set_viewport(rsx, dims, block->fullscreen, true);
}
static void rsx_font_bind_block(void *data, void *userdata)
{
rsx_font_t *font = (rsx_font_t*)data;
video_font_raster_block_t *block = (video_font_raster_block_t*)userdata;
if (font)
font->block = block;
}
static bool rsx_font_get_line_metrics(void* data, struct font_line_metrics **metrics)
{
rsx_font_t *font = (rsx_font_t*)data;
if (font && font->font_driver && font->font_data)
{
font->font_driver->get_line_metrics(font->font_data, metrics);
return true;
}
return false;
}
/*
* VIDEO DRIVER
*/
static bool rsx_load_texture_data(rsx_t* rsx, rsx_texture_t *texture,
const void *frame, unsigned width, unsigned height, unsigned pitch,
bool rgb32, bool menu, enum texture_filter_type filter_type)
{
u8 *texbuffer;
u32 mag_filter, min_filter;
const u8 *data = (u8*)frame;
u32 bytes = height * pitch;
/* The buffer is kept from load to load; a frame bigger than it
* (a taller frame, a wider pitch, 16 to 32 bits) gets a new one */
if (texture->data && bytes > texture->size)
{
/* The RSX may still be reading it, as in
* rsx_unload_texture_internal */
if (rsx && rsx->context)
rsxFinish(rsx->context, 0);
rsxFree(texture->data);
texture->data = NULL;
texture->size = 0;
}
if (!texture->data)
{
u32 size = texture->height * pitch;
if (size < bytes)
size = bytes;
if (!(texture->data = (u32*)rsxMemalign(128, size)))
return false;
texture->size = size;
rsxAddressToOffset(texture->data, &texture->offset);
}
texbuffer = (u8*)texture->data;
memcpy(texbuffer, data, height * pitch);
texture->tex.format = (rgb32
? GCM_TEXTURE_FORMAT_A8R8G8B8 :
(menu)
? GCM_TEXTURE_FORMAT_A4R4G4B4
: GCM_TEXTURE_FORMAT_R5G6B5)
| GCM_TEXTURE_FORMAT_LIN;
texture->tex.mipmap = 1;
texture->tex.dimension = GCM_TEXTURE_DIMS_2D;
texture->tex.cubemap = GCM_FALSE;
texture->tex.remap = ((GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_G << GCM_TEXTURE_REMAP_COLOR_G_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_R << GCM_TEXTURE_REMAP_COLOR_R_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_A << GCM_TEXTURE_REMAP_COLOR_A_SHIFT));
texture->tex.width = width;
texture->tex.height = height;
texture->tex.depth = 1;
texture->tex.location = GCM_LOCATION_RSX;
texture->tex.pitch = pitch;
texture->tex.offset = texture->offset;
switch (filter_type)
{
case TEXTURE_FILTER_MIPMAP_NEAREST:
case TEXTURE_FILTER_NEAREST:
min_filter = GCM_TEXTURE_NEAREST;
mag_filter = GCM_TEXTURE_NEAREST;
break;
case TEXTURE_FILTER_MIPMAP_LINEAR:
case TEXTURE_FILTER_LINEAR:
default:
min_filter = GCM_TEXTURE_LINEAR;
mag_filter = GCM_TEXTURE_LINEAR;
break;
}
texture->min_filter = min_filter;
texture->mag_filter = mag_filter;
texture->wrap_s = GCM_TEXTURE_CLAMP_TO_EDGE;
texture->wrap_t = GCM_TEXTURE_CLAMP_TO_EDGE;
return true;
}
static void rsx_set_projection(rsx_t *rsx,
struct video_ortho *ortho, bool allow_rotate)
{
static math_matrix_4x4 rot = {
{ 0.0f, 0.0f, 0.0f, 0.0f ,
0.0f, 0.0f, 0.0f, 0.0f ,
0.0f, 0.0f, 0.0f, 0.0f ,
0.0f, 0.0f, 0.0f, 1.0f }
};
float radians, cosine, sine;
/* Calculate projection. */
matrix_4x4_ortho(rsx->mvp_no_rot, ortho->left, ortho->right,
ortho->bottom, ortho->top, ortho->znear, ortho->zfar);
if (!allow_rotate)
{
rsx->mvp = rsx->mvp_no_rot;
return;
}
radians = M_PI * rsx->rotation / 180.0f;
cosine = cosf(radians);
sine = sinf(radians);
MAT_ELEM_4X4(rot, 0, 0) = cosine;
MAT_ELEM_4X4(rot, 0, 1) = -sine;
MAT_ELEM_4X4(rot, 1, 0) = sine;
MAT_ELEM_4X4(rot, 1, 1) = cosine;
matrix_4x4_multiply(rsx->mvp, rot, rsx->mvp_no_rot);
}
static void rsx_set_viewport(void *data, unsigned dims,
bool force_full, bool allow_rotate)
{
int i;
rsx_viewport_t vp;
struct video_ortho ortho = {0, 1, 0, 1, -1, 1};
rsx_t *rsx = (rsx_t*)data;
rsx->vp.full_dims = dims;
video_driver_update_viewport(&rsx->vp, force_full, rsx->keep_aspect, true);
vp.min = 0.0f;
vp.max = 1.0f;
vp.pos = VIDEO_POS_PACK(VIDEO_POS_X(rsx->vp.pos),
rsx->height - VIDEO_POS_Y(rsx->vp.pos)
- VIDEO_SCALE_H(rsx->vp.dims));
vp.dims = rsx->vp.dims;
vp.scale[0] = VIDEO_SCALE_W(vp.dims) * 0.5f;
vp.scale[1] = VIDEO_SCALE_H(vp.dims) * -0.5f;
vp.scale[2] = (vp.max - vp.min) * 0.5f;
vp.scale[3] = 0.0f;
vp.offset[0] = VIDEO_POS_X(vp.pos)
+ VIDEO_SCALE_W(vp.dims) * 0.5f;
vp.offset[1] = VIDEO_POS_Y(vp.pos)
+ VIDEO_SCALE_H(vp.dims) * 0.5f;
vp.offset[2] = (vp.max + vp.min) * 0.5f;
vp.offset[3] = 0.0f;
rsxSetViewport(rsx->context, VIDEO_POS_X(vp.pos),
VIDEO_POS_Y(vp.pos), VIDEO_SCALE_W(vp.dims),
VIDEO_SCALE_H(vp.dims), vp.min, vp.max,
vp.scale, vp.offset);
for (i = 0; i < 8; i++)
rsxSetViewportClip(rsx->context, i, rsx->width, rsx->height);
rsxSetScissor(rsx->context, VIDEO_POS_X(vp.pos),
VIDEO_POS_Y(vp.pos), VIDEO_SCALE_W(vp.dims),
VIDEO_SCALE_H(vp.dims));
rsx_set_projection(rsx, &ortho, allow_rotate);
}
static const gfx_ctx_driver_t* rsx_get_context(rsx_t* rsx)
{
const gfx_ctx_driver_t* gfx_ctx = NULL;
void* ctx_data = NULL;
settings_t* settings = config_get_ptr();
struct retro_hw_render_callback* hwr = video_driver_get_hw_context();
bool video_shared_context = settings->bools.video_shared_context;
enum gfx_ctx_api api = GFX_CTX_RSX_API;
rsx->shared_context_use = (video_shared_context && (hwr->context_type != RETRO_HW_CONTEXT_NONE));
if ((runloop_get_flags() & RUNLOOP_FLAG_CORE_SET_SHARED_CONTEXT)
&& (hwr->context_type != RETRO_HW_CONTEXT_NONE))
rsx->shared_context_use = true;
/* Native RSX uses one command FIFO, not separate GL contexts. */
if (hwr->context_type == RETRO_HW_CONTEXT_RSX)
rsx->shared_context_use = false;
gfx_ctx = video_context_driver_init_first(rsx,
settings->arrays.video_context_driver,
api, 1, 0, rsx->shared_context_use, &ctx_data);
if (ctx_data)
rsx->ctx_data = ctx_data;
return gfx_ctx;
}
#ifndef HAVE_THREADS
static bool rsx_tasks_finder(retro_task_t *task,void *userdata) { return task; }
task_finder_data_t rsx_tasks_finder_data = {rsx_tasks_finder, NULL};
#endif
static int rsx_make_buffer(rsx_buffer *buffer, u16 width, u16 height, int id)
{
int depth = sizeof(u32);
int pitch = depth * width;
int size = depth * width * height;
if (!(buffer->ptr = (uint32_t*)rsxMemalign (64, size)))
goto error;
if (rsxAddressToOffset(buffer->ptr, &buffer->offset) != 0)
goto error;
/* Register the display buffer with the RSX */
if (gcmSetDisplayBuffer(id, buffer->offset, pitch, width, height) != 0)
goto error;
buffer->width = width;
buffer->height = height;
buffer->id = id;
return 1;
error:
if (buffer->ptr)
rsxFree (buffer->ptr);
buffer->ptr = NULL;
return 0;
}
static int rsx_flip(gcmContextData *context, s32 buffer)
{
if (gcmSetFlip(context, buffer) == 0)
{
rsxFlushBuffer (context);
/* Prevent the RSX from continuing until the flip has finished. */
gcmSetWaitFlip (context);
return 1;
}
return 0;
}
#define GCM_LABEL_INDEX 255
static void rsx_wait_finish(gcmContextData *context, u32 sLabelVal)
{
rsxSetWriteBackendLabel(context, GCM_LABEL_INDEX, sLabelVal);
rsxFlushBuffer(context);
while(*(vu32 *)gcmGetLabelAddress(GCM_LABEL_INDEX) != sLabelVal)
usleep(30);
sLabelVal++;
}
static void rsx_wait_rsx_idle(gcmContextData *context)
{
u32 sLabelVal = 1;
rsxSetWriteBackendLabel(context, GCM_LABEL_INDEX, sLabelVal);
rsxSetWaitLabel(context, GCM_LABEL_INDEX, sLabelVal);
sLabelVal++;
rsx_wait_finish(context, sLabelVal);
}
static void rsx_wait_flip(void)
{
while(gcmGetFlipStatus() != 0)
usleep(200); /* Sleep, to not stress the cpu. */
gcmResetFlipStatus();
}
static gcmContextData *rsx_init_screen(rsx_t* gcm)
{
videoState state;
videoConfiguration vconfig;
videoResolution res; /* Screen Resolution */
unsigned resolution;
/* Context to keep track of the RSX buffer. */
gcmContextData *context = NULL;
static gcmContextData *saved_context = NULL;
if (!saved_context)
{
/* Allocate a 1MB buffer, aligned to a 1MB boundary
* to be our shared I/O memory with the RSX. */
void *host_addr = memalign(1024*1024, HOST_SIZE);
if (!host_addr)
goto error;
/* Initialise Reality, which sets up the
* command buffer and shared I/O memory */
#ifdef NV40TCL_RENDER_ENABLE
/* There was an API breakage on 2020-07-10, let's
* workaround this by using one of the new defines */
rsxInit(&context, CB_SIZE, HOST_SIZE, host_addr);
#else
context = rsxInit(CB_SIZE, HOST_SIZE, host_addr);
#endif
if (!context)
goto error;
saved_context = context;
rsx_host_addr = host_addr;
}
else
context = saved_context;
/* Get the state of the display */
if (videoGetState(0, 0, &state) != 0)
goto error;
/* Make sure display is enabled */
if (state.state != 0)
goto error;
/* The mode chosen through the display server when the display
* takes it, otherwise the one the system menu has it in */
resolution = ps3_display_server_resolution(state.displayMode.resolution);
if (videoGetResolution(resolution, &res) != 0)
goto error;
/* Configure the buffer format to xRGB */
memset(&vconfig, 0, sizeof(videoConfiguration));
vconfig.resolution = resolution;
vconfig.format = VIDEO_BUFFER_FORMAT_XRGB;
vconfig.pitch = res.width * sizeof(u32);
vconfig.aspect = state.displayMode.aspect;
gcm->width = res.width;
gcm->height = res.height;
rsx_wait_rsx_idle(context);
if (videoConfigure(0, &vconfig, NULL, 0) != 0)
goto error;
if (videoGetState(0, 0, &state) != 0)
goto error;
gcmSetFlipMode(GCM_FLIP_VSYNC); /* Wait for VSYNC to flip */
gcm->depth_pitch = res.width * sizeof(u32);
if (!(gcm->depth_buffer = (u32 *)rsxMemalign(64, (res.height * gcm->depth_pitch)))) /* Beware, if was (res.height * gcm->depth_pitch) * 2 */
goto error;
rsxAddressToOffset(gcm->depth_buffer, &gcm->depth_offset);
gcmResetFlipStatus();
return context;
error:
#if 0
if (context)
rsxFinish(context, 0);
if (gcm->host_addr)
free(gcm->host_addr);
#endif
return NULL;
}
static void rsx_init_render_target(rsx_t *rsx, rsx_buffer *buffer, int id)
{
u32 i;
memset(&rsx->surface[id], 0, sizeof(gcmSurface));
rsx->surface[id].colorFormat = GCM_SURFACE_X8R8G8B8;
rsx->surface[id].colorTarget = GCM_SURFACE_TARGET_0;
rsx->surface[id].colorLocation[0] = GCM_LOCATION_RSX;
rsx->surface[id].colorOffset[0] = buffer->offset;
rsx->surface[id].colorPitch[0] = rsx->width * 4;
for (i = 1; i < GCM_MAX_MRT_COUNT; i++)
{
rsx->surface[id].colorLocation[i] = GCM_LOCATION_RSX;
rsx->surface[id].colorOffset[i] = buffer->offset;
rsx->surface[id].colorPitch[i] = 64;
}
rsx->surface[id].depthFormat = GCM_SURFACE_ZETA_Z24S8;
rsx->surface[id].depthLocation = GCM_LOCATION_RSX;
rsx->surface[id].depthOffset = rsx->depth_offset;
rsx->surface[id].depthPitch = rsx->width * 4;
rsx->surface[id].type = GCM_SURFACE_TYPE_LINEAR;
rsx->surface[id].antiAlias = GCM_SURFACE_CENTER_1;
rsx->surface[id].width = rsx->width;
rsx->surface[id].height = rsx->height;
rsx->surface[id].x = 0;
rsx->surface[id].y = 0;
}
static bool rsx_init_vertices(rsx_t *rsx)
{
/* vertices for menu and core */
if (!(rsx->vertices = (rsx_vertex_t *)rsxMemalign(128,
sizeof(rsx_vertex_t) * RSX_MAX_VERTICES)))
return false;
rsx->vert_idx = 0;
rsx->vertices[0].x = 0.0f;
rsx->vertices[0].y = 0.0f;
rsx->vertices[0].u = 0.0f;
rsx->vertices[0].v = 1.0f;
rsx->vertices[0].r = 1.0f;
rsx->vertices[0].g = 1.0f;
rsx->vertices[0].b = 1.0f;
rsx->vertices[0].a = 1.0f;
rsx->vertices[1].x = 1.0f;
rsx->vertices[1].y = 0.0f;
rsx->vertices[1].u = 1.0f;
rsx->vertices[1].v = 1.0f;
rsx->vertices[1].r = 1.0f;
rsx->vertices[1].g = 1.0f;
rsx->vertices[1].b = 1.0f;
rsx->vertices[1].a = 1.0f;
rsx->vertices[2].x = 0.0f;
rsx->vertices[2].y = 1.0f;
rsx->vertices[2].u = 0.0f;
rsx->vertices[2].v = 0.0f;
rsx->vertices[2].r = 1.0f;
rsx->vertices[2].g = 1.0f;
rsx->vertices[2].b = 1.0f;
rsx->vertices[2].a = 1.0f;
rsx->vertices[3].x = 1.0f;
rsx->vertices[3].y = 1.0f;
rsx->vertices[3].u = 1.0f;
rsx->vertices[3].v = 0.0f;
rsx->vertices[3].r = 1.0f;
rsx->vertices[3].g = 1.0f;
rsx->vertices[3].b = 1.0f;
rsx->vertices[3].a = 1.0f;
#if RSX_MAX_TEXTURE_VERTICES > 0
/* Using preallocated texture vertices */
if (!(rsx->texture_vertices = (rsx_vertex_t *)rsxMemalign(128,
sizeof(rsx_vertex_t) * RSX_MAX_TEXTURE_VERTICES)))
return false;
rsx->texture_vert_idx = 0;
#endif
return true;
}
static bool rsx_init_shader(rsx_t *rsx)
{
u32 fpsize = 0;
u32 vpsize = 0;
rsx->vp_ucode[RSX_SHADER_MENU] = NULL;
rsx->fp_ucode[RSX_SHADER_MENU] = NULL;
rsx->vpo[RSX_SHADER_MENU] = (rsxVertexProgram*)modern_opaque_vpo;
rsx->fpo[RSX_SHADER_MENU] = (rsxFragmentProgram*)modern_opaque_fpo;
rsxVertexProgramGetUCode( rsx->vpo[RSX_SHADER_MENU], &rsx->vp_ucode[RSX_SHADER_MENU], &vpsize);
rsxFragmentProgramGetUCode(rsx->fpo[RSX_SHADER_MENU], &rsx->fp_ucode[RSX_SHADER_MENU], &fpsize);
rsx->fp_buffer[RSX_SHADER_MENU] = (u32*)rsxMemalign(64, fpsize);
if (!rsx->fp_buffer[RSX_SHADER_MENU])
{
RARCH_ERR("[RSX] Failed to allocate fp_buffer.\n");
return false;
}
memcpy(rsx->fp_buffer[RSX_SHADER_MENU], rsx->fp_ucode[RSX_SHADER_MENU], fpsize);
rsxAddressToOffset(rsx->fp_buffer[RSX_SHADER_MENU], &rsx->fp_offset[RSX_SHADER_MENU]);
rsx->proj_matrix[RSX_SHADER_MENU] = rsxVertexProgramGetConst(rsx->vpo[RSX_SHADER_MENU], "modelViewProj");
rsx->pos_index[RSX_SHADER_MENU] = rsxVertexProgramGetAttrib(rsx->vpo[RSX_SHADER_MENU], "position");
rsx->col_index[RSX_SHADER_MENU] = rsxVertexProgramGetAttrib(rsx->vpo[RSX_SHADER_MENU], "color");
rsx->uv_index[RSX_SHADER_MENU] = rsxVertexProgramGetAttrib(rsx->vpo[RSX_SHADER_MENU], "texcoord");
rsx->tex_unit[RSX_SHADER_MENU] = rsxFragmentProgramGetAttrib(rsx->fpo[RSX_SHADER_MENU], "texture");
rsx->vp_ucode[RSX_SHADER_STOCK_BLEND] = NULL;
rsx->fp_ucode[RSX_SHADER_STOCK_BLEND] = NULL;
rsx->vpo[RSX_SHADER_STOCK_BLEND] = (rsxVertexProgram *)modern_alpha_blend_vpo;
rsx->fpo[RSX_SHADER_STOCK_BLEND] = (rsxFragmentProgram *)modern_alpha_blend_fpo;
rsxVertexProgramGetUCode(rsx->vpo[RSX_SHADER_STOCK_BLEND], &rsx->vp_ucode[RSX_SHADER_STOCK_BLEND], &vpsize);
rsxFragmentProgramGetUCode(rsx->fpo[RSX_SHADER_STOCK_BLEND], &rsx->fp_ucode[RSX_SHADER_STOCK_BLEND], &fpsize);
rsx->fp_buffer[RSX_SHADER_STOCK_BLEND] = (u32 *)rsxMemalign(64, fpsize);
if (!rsx->fp_buffer[RSX_SHADER_STOCK_BLEND])
{
RARCH_ERR("[RSX] Failed to allocate fp_buffer.\n");
return false;
}
memcpy(rsx->fp_buffer[RSX_SHADER_STOCK_BLEND], rsx->fp_ucode[RSX_SHADER_STOCK_BLEND], fpsize);
rsxAddressToOffset(rsx->fp_buffer[RSX_SHADER_STOCK_BLEND], &rsx->fp_offset[RSX_SHADER_STOCK_BLEND]);
rsx->proj_matrix[RSX_SHADER_STOCK_BLEND] = rsxVertexProgramGetConst(rsx->vpo[RSX_SHADER_STOCK_BLEND], "modelViewProj");
rsx->pos_index[RSX_SHADER_STOCK_BLEND] = rsxVertexProgramGetAttrib(rsx->vpo[RSX_SHADER_STOCK_BLEND], "position");
rsx->col_index[RSX_SHADER_STOCK_BLEND] = rsxVertexProgramGetAttrib(rsx->vpo[RSX_SHADER_STOCK_BLEND], "color");
rsx->uv_index[RSX_SHADER_STOCK_BLEND] = rsxVertexProgramGetAttrib(rsx->vpo[RSX_SHADER_STOCK_BLEND], "texcoord");
rsx->tex_unit[RSX_SHADER_STOCK_BLEND] = rsxFragmentProgramGetAttrib(rsx->fpo[RSX_SHADER_STOCK_BLEND], "texture");
rsx->bgcolor[RSX_SHADER_STOCK_BLEND] = rsxFragmentProgramGetConst(rsx->fpo[RSX_SHADER_STOCK_BLEND], "bgcolor");
return true;
}
static uintptr_t rsx_load_texture_internal(void *video_data, void *data,
enum texture_filter_type filter_type);
static void* rsx_init(const video_info_t* video)
{
int i;
const gfx_ctx_driver_t* ctx_driver = NULL;
rsx_t* rsx = (rsx_t*)malloc(sizeof(rsx_t));
if (!rsx)
return NULL;
memset(rsx, 0, sizeof(rsx_t));
if (!(rsx->context = rsx_init_screen(rsx)))
goto error;
if (!(ctx_driver = rsx_get_context(rsx)))
goto error;
video_context_driver_set((const gfx_ctx_driver_t*)ctx_driver);
rsx->ctx_driver = ctx_driver;
rsx->video_info = *video;
for (i = 0; i < RSX_MAX_BUFFERS; i++)
{
if (!rsx_make_buffer(&rsx->buffers[i], rsx->width, rsx->height, i))
goto error;
rsx_init_render_target(rsx, &rsx->buffers[i], i);
}
#if defined(HAVE_MENU_BUFFER)
for (i = 0; i < RSX_MAX_MENU_BUFFERS; i++)
{
if (!rsx_make_buffer(&rsx->menuBuffers[i], rsx->width, rsx->height, i+RSX_MAX_BUFFERS))
goto error;
rsx_init_render_target(rsx, &rsx->menuBuffers[i], i+RSX_MAX_BUFFERS);
}
#endif
for (i = 0; i < RSX_MAX_TEXTURES; i++)
{
rsx->texture[i].data = NULL;
rsx->texture[i].height = rsx->height;
rsx->texture[i].width = rsx->width;
}
rsx->menu_texture.data = NULL;
rsx->menu_texture.height = rsx->height;
rsx->menu_texture.width = rsx->width;
if (!rsx_init_shader(rsx) || !rsx_init_vertices(rsx))
goto error;
{
static const uint32_t white = 0xffffffffu;
struct texture_image image;
memset(&image, 0, sizeof(image));
image.pixels = (uint32_t*)&white;
image.width = 1;
image.height = 1;
if (!(rsx->white_texture = (rsx_texture_t*)rsx_load_texture_internal(
rsx, &image, TEXTURE_FILTER_NEAREST)))
goto error;
}
rsx_flip(rsx->context, RSX_MAX_BUFFERS - 1);
rsx->vp.pos = VIDEO_POS_PACK(0, 0);
rsx->vp.dims = VIDEO_SCALE_PACK(rsx->width, rsx->height);
rsx->vp.full_dims = VIDEO_SCALE_PACK(rsx->width, rsx->height);
rsx->rgb32 = video->rgb32;
video_driver_set_output_dims(rsx->vp.dims);
rsx_set_viewport(rsx, rsx->vp.dims, false, true);
/* no input driver of this driver's own: the frontend starts the
* platform's */
input_driver_video_window(INPUT_WINDOW_PLATFORM, NULL);
rsx_context_bind_hw_render(rsx, true);
if (video->font_enable)
rsx->msg_rendering_enabled = true;
if (!rsx_labels_initialized)
{
*(volatile u32*)gcmGetLabelAddress(RSX_FONT_VERTEX_LABEL) = 0;
*(volatile u32*)gcmGetLabelAddress(RSX_RETIRED_MEMORY_LABEL) = 0;
rsx_labels_initialized = true;
}
rsx->hw_render = video_driver_get_hw_context()->context_type
== RETRO_HW_CONTEXT_RSX;
rsx->hw_interface.interface_type = RETRO_HW_RENDER_INTERFACE_RSX;
rsx->hw_interface.interface_version = RETRO_HW_RENDER_INTERFACE_RSX_VERSION;
rsx->hw_interface.handle = rsx;
rsx->hw_interface.context = rsx->context;
rsx->hw_interface.io_address = rsx_host_addr;
rsx->hw_interface.io_size = HOST_SIZE;
rsx->hw_interface.io_used = CB_SIZE + 64 * 1024;
rsx->hw_interface.set_image = rsx_set_hw_image;
rsx->hw_interface.retire_memory = rsx_retire_hw_memory;
return rsx;
error:
/* What the RSX has not drawn from goes. The display and depth
* buffers handed to it stay, as rsx_free leaves them. */
for (i = 0; i < RSX_MAX_SHADERS; i++)
if (rsx->fp_buffer[i])
rsxFree(rsx->fp_buffer[i]);
if (rsx->vertices)
rsxFree(rsx->vertices);
if (rsx->texture_vertices)
rsxFree(rsx->texture_vertices);
free(rsx);
return NULL;
}
static void rsx_update_viewport(rsx_t* rsx)
{
rsx->vp.full_dims = VIDEO_SCALE_PACK(rsx->width, rsx->height);
video_driver_update_viewport(&rsx->vp, false, rsx->keep_aspect, true);
rsx->should_resize = false;
}
static unsigned rsx_wrap_type_to_enum(enum gfx_wrap_type type)
{
switch (type)
{
case RARCH_WRAP_BORDER:
case RARCH_WRAP_EDGE:
return GCM_TEXTURE_CLAMP_TO_EDGE;
case RARCH_WRAP_REPEAT:
return GCM_TEXTURE_REPEAT;
case RARCH_WRAP_MIRRORED_REPEAT:
return GCM_TEXTURE_MIRRORED_REPEAT;
default:
break;
}
return 0;
}
static uintptr_t rsx_load_texture_internal(void *video_data, void *data,
enum texture_filter_type filter_type)
{
rsx_t *rsx = (rsx_t *)video_data;
struct texture_image *image = (struct texture_image*)data;
rsx_texture_t *texture = (rsx_texture_t *)calloc(1, sizeof(rsx_texture_t));
if (!texture)
return 0;
texture->width = image->width;
texture->height = image->height;
if (!rsx_load_texture_data(rsx, texture, image->pixels, image->width,
image->height, image->width*4, true, false, filter_type))
{
free(texture);
return 0;
}
return (uintptr_t)texture;
}
static void rsx_unload_texture_internal(void *data, uintptr_t handle)
{
rsx_texture_t *texture = (rsx_texture_t *)handle;
if (texture)
{
if (texture->data)
{
rsx_t *rsx = (rsx_t *)data;
/* Drain the RSX command queue before freeing texture
* memory. The GPU may still be reading this memory for
* the current frame's draw. rsxFinish blocks until all
* submitted commands complete — equivalent to
* vkQueueWaitIdle / D3D12 Signal+Wait.
*
* This was previously #if 0'd out, leaking every menu
* texture's RSX memory permanently. On PS3 with 256MB
* RAM, menu navigation would eventually exhaust memory. */
if (rsx && rsx->context)
rsxFinish(rsx->context, 0);
rsxFree(texture->data);
}
free(texture);
}
}
#ifdef HAVE_THREADS
typedef struct
{
void *video_data;
struct texture_image *image;
enum texture_filter_type filter_type;
uintptr_t handle;
} rsx_texture_cmd_t;
static uintptr_t rsx_texture_load_wrap(void *data)
{
rsx_texture_cmd_t *cmd = (rsx_texture_cmd_t*)data;
cmd->handle = rsx_load_texture_internal(
cmd->video_data, cmd->image, cmd->filter_type);
return 0;
}
static uintptr_t rsx_texture_unload_wrap(void *data)
{
rsx_texture_cmd_t *cmd = (rsx_texture_cmd_t*)data;
rsx_unload_texture_internal(cmd->video_data, cmd->handle);
return 0;
}
#endif
/* Written in place like the menu texture */
static void rsx_update_texture_internal(rsx_texture_t *texture,
const struct texture_image *ti)
{
memcpy(texture->data, ti->pixels, ti->height * ti->width * 4);
}
#ifdef HAVE_THREADS
typedef struct
{
rsx_texture_t *texture;
const struct texture_image *ti;
} rsx_update_cmd_t;
static uintptr_t rsx_texture_update_wrap(void *data)
{
rsx_update_cmd_t *cmd = (rsx_update_cmd_t*)data;
rsx_update_texture_internal(cmd->texture, cmd->ti);
return 1;
}
#endif
static enum video_texture_update rsx_update_texture(void *video_data,
uintptr_t id, const struct texture_image *ti, bool threaded)
{
rsx_texture_t *texture = (rsx_texture_t*)id;
if ( !texture || !texture->data
|| !ti || !ti->pixels || ti->pix10 || ti->fp16
|| texture->tex.format
!= (GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN)
|| texture->tex.width != ti->width
|| texture->tex.height != ti->height
|| texture->tex.pitch != ti->width * 4)
return VIDEO_TEXTURE_UPDATE_REFUSED;
#ifdef HAVE_THREADS
if (threaded)
{
rsx_update_cmd_t cmd;
cmd.texture = texture;
cmd.ti = ti;
video_thread_texture_handle(&cmd, rsx_texture_update_wrap);
return VIDEO_TEXTURE_UPDATE_DONE;
}
#endif
rsx_update_texture_internal(texture, ti);
return VIDEO_TEXTURE_UPDATE_DONE;
}
static uintptr_t rsx_load_texture(void *video_data, void *data,
bool threaded, enum texture_filter_type filter_type)
{
#ifdef HAVE_THREADS
if (threaded)
{
rsx_texture_cmd_t cmd;
cmd.video_data = video_data;
cmd.image = (struct texture_image *)data;
cmd.filter_type = filter_type;
cmd.handle = 0;
video_thread_texture_handle(&cmd, rsx_texture_load_wrap);
return cmd.handle;
}
#endif
return rsx_load_texture_internal(video_data, data, filter_type);
}
static void rsx_unload_texture(void *data,
bool threaded, uintptr_t handle)
{
if (!handle)
return;
#ifdef HAVE_THREADS
if (threaded)
{
rsx_texture_cmd_t cmd;
cmd.video_data = data;
cmd.image = NULL;
cmd.filter_type = TEXTURE_FILTER_LINEAR;
cmd.handle = handle;
video_thread_texture_handle(&cmd, rsx_texture_unload_wrap);
return;
}
#endif
rsx_unload_texture_internal(data, handle);
}
#if 0
/* TODO/FIXME - commenting this code out for now until it gets used */
static void rsx_fill_black(uint32_t *dst, uint32_t *dst_end, size_t sz)
{
if (sz > dst_end - dst)
sz = dst_end - dst;
memset(dst, 0, sz * 4);
}
static void rsx_blit_buffer(
rsx_buffer *buffer, const void *frame, unsigned width,
unsigned height, unsigned pitch, int rgb32, bool do_scaling)
{
int i;
uint32_t *dst;
uint32_t *dst_end;
int pre_clean;
int scale = 1, xofs = 0, yofs = 0;
if (width > buffer->width)
width = buffer->width;
if (height > buffer->height)
height = buffer->height;
if (do_scaling)
{
scale = buffer->width / width;
if (scale > buffer->height / height)
scale = buffer->height / height;
if (scale >= 10)
scale = 10;
if (scale >= 1)
{
xofs = (buffer->width - width * scale) / 2;
yofs = (buffer->height - height * scale) / 2;
}
else
scale = 1;
}
/* TODO/FIXME: let RSX do the copy */
pre_clean = xofs + buffer->width * yofs;
dst = buffer->ptr;
dst_end = buffer->ptr + buffer->width * buffer->height;
memset(dst, 0, pre_clean * 4);
dst += pre_clean;
if (scale == 1)
{
if (rgb32)
{
const uint8_t *src = frame;
for (i = 0; i < height; i++)
{
memcpy(dst, src, width * 4);
rsx_fill_black(dst + width, dst_end, buffer->width - width);
dst += buffer->width;
src += pitch;
}
}
else
{
const uint16_t *src = frame;
for (i = 0; i < height; i++)
{
int j;
for (j = 0; j < width; j++, src++, dst++)
{
u16 rgb565 = *src;
u8 r = ((rgb565 >> 8) & 0xf8);
u8 g = ((rgb565 >> 3) & 0xfc);
u8 b = ((rgb565 << 3) & 0xfc);
*dst = (r<<16) | (g<<8) | b;
}
rsx_fill_black(dst, dst_end, buffer->width - width);
dst += buffer->width - width;
src += pitch / 2 - width;
}
}
}
else
{
if (rgb32)
{
const uint32_t *src = frame;
for (i = 0; i < height; i++)
{
int j, l;
for (j = 0; j < width; j++, src++)
{
int k;
u32 c = *src;
for (k = 0; k < scale; k++, dst++)
for (l = 0; l < scale; l++)
dst[l * buffer->width] = c;
}
for (l = 0; l < scale; l++)
rsx_fill_black(dst + l * buffer->width, dst_end, buffer->width - width * scale);
dst += buffer->width * scale - width * scale;
src += pitch / 4 - width;
}
}
else
{
const uint16_t *src = frame;
for (i = 0; i < height; i++)
{
int j, l;
for (j = 0; j < width; j++, src++)
{
int k;
u16 rgb565 = *src;
u8 r = ((rgb565 >> 8) & 0xf8);
u8 g = ((rgb565 >> 3) & 0xfc);
u8 b = ((rgb565 << 3) & 0xfc);
u32 c = (r<<16) | (g<<8) | b;
for (k = 0; k < scale; k++, dst++)
for (l = 0; l < scale; l++)
dst[l * buffer->width] = c;
}
for (l = 0; l < scale; l++)
rsx_fill_black(dst + l * buffer->width, dst_end, buffer->width - width * scale);
dst += buffer->width * scale - width * scale;
src += pitch / 2 - width;
}
}
}
if (dst < dst_end)
memset(dst, 0, 4 * (dst_end - dst));
}
#endif
static void rsx_set_texture(rsx_t* rsx, rsx_texture_t *texture)
{
rsxInvalidateTextureCache(rsx->context, GCM_INVALIDATE_TEXTURE);
rsxLoadTexture(rsx->context, rsx->tex_unit[RSX_SHADER_MENU]->index, &texture->tex);
rsxTextureControl(rsx->context, rsx->tex_unit[RSX_SHADER_MENU]->index, GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
rsxTextureFilter(rsx->context, rsx->tex_unit[RSX_SHADER_MENU]->index, 0, texture->min_filter, texture->mag_filter, GCM_TEXTURE_CONVOLUTION_QUINCUNX);
rsxTextureWrapMode(rsx->context, rsx->tex_unit[RSX_SHADER_MENU]->index, texture->wrap_s, texture->wrap_t, GCM_TEXTURE_CLAMP_TO_EDGE,
0, GCM_TEXTURE_ZFUNC_LESS, 0);
}
static void rsx_set_menu_texture(rsx_t* rsx, rsx_texture_t *texture)
{
rsxInvalidateTextureCache(rsx->context, GCM_INVALIDATE_TEXTURE);
rsxLoadTexture(rsx->context, rsx->tex_unit[RSX_SHADER_STOCK_BLEND]->index, &texture->tex);
rsxTextureControl(rsx->context, rsx->tex_unit[RSX_SHADER_STOCK_BLEND]->index, GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
rsxTextureFilter(rsx->context, rsx->tex_unit[RSX_SHADER_STOCK_BLEND]->index, 0, texture->min_filter,
texture->mag_filter, GCM_TEXTURE_CONVOLUTION_QUINCUNX);
rsxTextureWrapMode(rsx->context, rsx->tex_unit[RSX_SHADER_STOCK_BLEND]->index, texture->wrap_s,
texture->wrap_t, GCM_TEXTURE_CLAMP_TO_EDGE, 0, GCM_TEXTURE_ZFUNC_LESS, 0);
}
static void rsx_clear_surface(rsx_t* rsx)
{
rsxSetColorMask(rsx->context,
GCM_COLOR_MASK_R
| GCM_COLOR_MASK_G
| GCM_COLOR_MASK_B
| GCM_COLOR_MASK_A);
rsxSetColorMaskMrt(rsx->context, 0);
rsxSetUserClipPlaneControl(rsx->context,
GCM_USER_CLIP_PLANE_DISABLE,
GCM_USER_CLIP_PLANE_DISABLE,
GCM_USER_CLIP_PLANE_DISABLE,
GCM_USER_CLIP_PLANE_DISABLE,
GCM_USER_CLIP_PLANE_DISABLE,
GCM_USER_CLIP_PLANE_DISABLE);
rsxSetClearColor(rsx->context, 0);
rsxSetClearDepthStencil(rsx->context, 0xffffff00);
rsxClearSurface(rsx->context,
GCM_CLEAR_R
| GCM_CLEAR_G
| GCM_CLEAR_B
| GCM_CLEAR_A
| GCM_CLEAR_S
| GCM_CLEAR_Z);
rsxSetZMinMaxControl(rsx->context, 0, 1, 1);
}
static void rsx_draw_vertices(rsx_t* rsx)
{
rsx_vertex_t *vertices = NULL;
int end_vert_idx = rsx->vert_idx + 4;
if (end_vert_idx > RSX_MAX_VERTICES)
{
rsx->vert_idx = 0;
end_vert_idx = rsx->vert_idx + 4;
}
vertices = &rsx->vertices[rsx->vert_idx];
vertices[rsx->vert_idx+0].x = 0.0f;
vertices[rsx->vert_idx+0].y = 0.0f;
vertices[rsx->vert_idx+0].u = 0.0f;
vertices[rsx->vert_idx+0].v = 1.0f;
vertices[rsx->vert_idx+0].r = 1.0f;
vertices[rsx->vert_idx+0].g = 1.0f;
vertices[rsx->vert_idx+0].b = 1.0f;
vertices[rsx->vert_idx+0].a = 1.0f;
vertices[rsx->vert_idx+1].x = 1.0f;
vertices[rsx->vert_idx+1].y = 0.0f;
vertices[rsx->vert_idx+1].u = 1.0f;
vertices[rsx->vert_idx+1].v = 1.0f;
vertices[rsx->vert_idx+1].r = 1.0f;
vertices[rsx->vert_idx+1].g = 1.0f;
vertices[rsx->vert_idx+1].b = 1.0f;
vertices[rsx->vert_idx+1].a = 1.0f;
vertices[rsx->vert_idx+2].x = 0.0f;
vertices[rsx->vert_idx+2].y = 1.0f;
vertices[rsx->vert_idx+2].u = 0.0f;
vertices[rsx->vert_idx+2].v = 0.0f;
vertices[rsx->vert_idx+2].r = 1.0f;
vertices[rsx->vert_idx+2].g = 1.0f;
vertices[rsx->vert_idx+2].b = 1.0f;
vertices[rsx->vert_idx+2].a = 1.0f;
vertices[rsx->vert_idx+3].x = 1.0f;
vertices[rsx->vert_idx+3].y = 1.0f;
vertices[rsx->vert_idx+3].u = 1.0f;
vertices[rsx->vert_idx+3].v = 0.0f;
vertices[rsx->vert_idx+3].r = 1.0f;
vertices[rsx->vert_idx+3].g = 1.0f;
vertices[rsx->vert_idx+3].b = 1.0f;
vertices[rsx->vert_idx+3].a = 1.0f;
rsxAddressToOffset(&vertices[rsx->vert_idx].x, &rsx->pos_offset[RSX_SHADER_MENU]);
rsxAddressToOffset(&vertices[rsx->vert_idx].u, &rsx->uv_offset[RSX_SHADER_MENU]);
rsxAddressToOffset(&vertices[rsx->vert_idx].r, &rsx->col_offset[RSX_SHADER_MENU]);
rsx->vert_idx = end_vert_idx;
rsxBindVertexArrayAttrib(rsx->context, rsx->pos_index[RSX_SHADER_MENU]->index, 0,
rsx->pos_offset[RSX_SHADER_MENU], sizeof(rsx_vertex_t), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, rsx->uv_index[RSX_SHADER_MENU]->index, 0,
rsx->uv_offset[RSX_SHADER_MENU], sizeof(rsx_vertex_t), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, rsx->col_index[RSX_SHADER_MENU]->index, 0,
rsx->col_offset[RSX_SHADER_MENU], sizeof(rsx_vertex_t), 4, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxLoadVertexProgram(rsx->context, rsx->vpo[RSX_SHADER_MENU], rsx->vp_ucode[RSX_SHADER_MENU]);
rsxSetVertexProgramParameter(rsx->context, rsx->vpo[RSX_SHADER_MENU], rsx->proj_matrix[RSX_SHADER_MENU], (float *)&rsx->mvp);
rsxLoadFragmentProgramLocation(rsx->context, rsx->fpo[RSX_SHADER_MENU], rsx->fp_offset[RSX_SHADER_MENU], GCM_LOCATION_RSX);
rsxClearSurface(rsx->context, GCM_CLEAR_Z);
rsxDrawVertexArray(rsx->context, GCM_TYPE_TRIANGLE_STRIP, 0, 4);
}
#if defined(HAVE_MENU)
static void rsx_draw_menu_vertices(rsx_t* rsx)
{
rsx_vertex_t *vertices = NULL;
int end_vert_idx = rsx->vert_idx + 4;
if (end_vert_idx > RSX_MAX_VERTICES)
{
rsx->vert_idx = 0;
end_vert_idx = rsx->vert_idx + 4;
}
vertices = &rsx->vertices[rsx->vert_idx];
vertices[rsx->vert_idx+0].x = 0.0f;
vertices[rsx->vert_idx+0].y = 0.0f;
vertices[rsx->vert_idx+0].u = 0.0f;
vertices[rsx->vert_idx+0].v = 1.0f;
vertices[rsx->vert_idx+0].r = 1.0f;
vertices[rsx->vert_idx+0].g = 1.0f;
vertices[rsx->vert_idx+0].b = 1.0f;
vertices[rsx->vert_idx+0].a = rsx->menu_texture_alpha;
vertices[rsx->vert_idx+1].x = 1.0f;
vertices[rsx->vert_idx+1].y = 0.0f;
vertices[rsx->vert_idx+1].u = 1.0f;
vertices[rsx->vert_idx+1].v = 1.0f;
vertices[rsx->vert_idx+1].r = 1.0f;
vertices[rsx->vert_idx+1].g = 1.0f;
vertices[rsx->vert_idx+1].b = 1.0f;
vertices[rsx->vert_idx+1].a = rsx->menu_texture_alpha;
vertices[rsx->vert_idx+2].x = 0.0f;
vertices[rsx->vert_idx+2].y = 1.0f;
vertices[rsx->vert_idx+2].u = 0.0f;
vertices[rsx->vert_idx+2].v = 0.0f;
vertices[rsx->vert_idx+2].r = 1.0f;
vertices[rsx->vert_idx+2].g = 1.0f;
vertices[rsx->vert_idx+2].b = 1.0f;
vertices[rsx->vert_idx+2].a = rsx->menu_texture_alpha;
vertices[rsx->vert_idx+3].x = 1.0f;
vertices[rsx->vert_idx+3].y = 1.0f;
vertices[rsx->vert_idx+3].u = 1.0f;
vertices[rsx->vert_idx+3].v = 0.0f;
vertices[rsx->vert_idx+3].r = 1.0f;
vertices[rsx->vert_idx+3].g = 1.0f;
vertices[rsx->vert_idx+3].b = 1.0f;
vertices[rsx->vert_idx+3].a = rsx->menu_texture_alpha;
rsxAddressToOffset(&vertices[rsx->vert_idx].x, &rsx->pos_offset[RSX_SHADER_STOCK_BLEND]);
rsxAddressToOffset(&vertices[rsx->vert_idx].u, &rsx->uv_offset[RSX_SHADER_STOCK_BLEND]);
rsxAddressToOffset(&vertices[rsx->vert_idx].r, &rsx->col_offset[RSX_SHADER_STOCK_BLEND]);
rsx->vert_idx = end_vert_idx;
rsxBindVertexArrayAttrib(rsx->context, rsx->pos_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->pos_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, rsx->uv_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->uv_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, rsx->col_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->col_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 4, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxLoadVertexProgram(rsx->context, rsx->vpo[RSX_SHADER_STOCK_BLEND], rsx->vp_ucode[RSX_SHADER_STOCK_BLEND]);
rsxSetVertexProgramParameter(rsx->context, rsx->vpo[RSX_SHADER_STOCK_BLEND], rsx->proj_matrix[RSX_SHADER_STOCK_BLEND], (float *)&rsx->mvp_no_rot);
rsxLoadFragmentProgramLocation(rsx->context, rsx->fpo[RSX_SHADER_STOCK_BLEND], rsx->fp_offset[RSX_SHADER_STOCK_BLEND], GCM_LOCATION_RSX);
rsxSetBlendEnable(rsx->context, GCM_TRUE);
rsxSetBlendFunc(rsx->context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA);
rsxSetBlendEquation(rsx->context, GCM_FUNC_ADD, GCM_FUNC_ADD);
rsxClearSurface(rsx->context, GCM_CLEAR_Z);
rsxDrawVertexArray(rsx->context, GCM_TYPE_TRIANGLE_STRIP, 0, 4);
rsxSetBlendEnable(rsx->context, GCM_FALSE);
}
#endif
#ifdef HAVE_OVERLAY
static void rsx_draw_overlay_vertices(rsx_t* rsx, unsigned image)
{
rsx_vertex_t *vertices = &rsx->overlay[image].vertices[0];
rsxAddressToOffset(&vertices[0].x, &rsx->pos_offset[RSX_SHADER_STOCK_BLEND]);
rsxAddressToOffset(&vertices[0].u, &rsx->uv_offset[RSX_SHADER_STOCK_BLEND]);
rsxAddressToOffset(&vertices[0].r, &rsx->col_offset[RSX_SHADER_STOCK_BLEND]);
rsxBindVertexArrayAttrib(rsx->context, rsx->pos_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->pos_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, rsx->uv_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->uv_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxBindVertexArrayAttrib(rsx->context, rsx->col_index[RSX_SHADER_STOCK_BLEND]->index, 0,
rsx->col_offset[RSX_SHADER_STOCK_BLEND], sizeof(rsx_vertex_t), 4, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
rsxLoadVertexProgram(rsx->context, rsx->vpo[RSX_SHADER_STOCK_BLEND], rsx->vp_ucode[RSX_SHADER_STOCK_BLEND]);
rsxSetVertexProgramParameter(rsx->context, rsx->vpo[RSX_SHADER_STOCK_BLEND], rsx->proj_matrix[RSX_SHADER_STOCK_BLEND], (float *)&rsx->mvp_no_rot);
rsxLoadFragmentProgramLocation(rsx->context, rsx->fpo[RSX_SHADER_STOCK_BLEND], rsx->fp_offset[RSX_SHADER_STOCK_BLEND], GCM_LOCATION_RSX);
rsxSetBlendEnable(rsx->context, GCM_TRUE);
rsxSetBlendFunc(rsx->context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA);
rsxSetBlendEquation(rsx->context, GCM_FUNC_ADD, GCM_FUNC_ADD);
rsxClearSurface(rsx->context, GCM_CLEAR_Z);
rsxDrawVertexArray(rsx->context, GCM_TYPE_TRIANGLE_STRIP, 0, 4);
rsxSetBlendEnable(rsx->context, GCM_FALSE);
}
static void rsx_overlay_vertex_geom(void *data,
unsigned image,
float x, float y,
float w, float h)
{
rsx_t *rsx = (rsx_t *)data;
rsx_overlay_t *o = NULL;
if (rsx && rsx->overlay && image < rsx->overlays)
o = (rsx_overlay_t *)&rsx->overlay[image];
if (!o)
return;
/* Flipped, so we preserve top-down semantics. */
y = 1.0f - y;
h = -h;
o->vertices[0].x = x;
o->vertices[0].y = y;
o->vertices[1].x = x + w;
o->vertices[1].y = y;
o->vertices[2].x = x;
o->vertices[2].y = y + h;
o->vertices[3].x = x + w;
o->vertices[3].y = y + h;
}
static void rsx_overlay_tex_geom(void *data,
unsigned image,
float x, float y,
float w, float h)
{
rsx_t *rsx = (rsx_t *)data;
rsx_overlay_t *o = NULL;
if (rsx && rsx->overlay && image < rsx->overlays)
o = (rsx_overlay_t *)&rsx->overlay[image];
if (!o)
return;
o->vertices[0].u = x;
o->vertices[0].v = y;
o->vertices[1].u = x + w;
o->vertices[1].v = y;
o->vertices[2].u = x;
o->vertices[2].v = y + h;
o->vertices[3].u = x + w;
o->vertices[3].v = y + h;
}
static void rsx_free_overlay(rsx_t *rsx)
{
unsigned i;
for (i = 0; i < rsx->overlays; i++)
{
if (rsx->overlay[i].texture.data && !rsx->overlay_borrowed)
rsxFree(rsx->overlay[i].texture.data);
if (rsx->overlay[i].vertices)
rsxFree(rsx->overlay[i].vertices);
}
free(rsx->overlay);
rsx->overlay = NULL;
rsx->overlays = 0;
rsx->overlay_borrowed = false;
}
static bool rsx_overlay_load(void *data,
const void *image_data, unsigned num_images)
{
unsigned i, j;
rsx_t *rsx = (rsx_t *)data;
const struct texture_image *images = (const struct texture_image *)image_data;
rsx_free_overlay(rsx);
rsx->overlay = (rsx_overlay_t *)calloc(num_images, sizeof(rsx_overlay_t));
if (!rsx->overlay)
return false;
rsx->overlays = num_images;
for (i = 0; i < num_images; i++)
{
rsx_overlay_t *o = (rsx_overlay_t *)&rsx->overlay[i];
if (!(o->vertices = (rsx_vertex_t *)rsxMemalign(128,
sizeof(rsx_vertex_t) * RSX_MAX_VERTICES)))
{
rsx_free_overlay(rsx);
return false;
}
/* Default. Stretch to whole screen. */
rsx_overlay_tex_geom(rsx, i, 0, 0, 1, 1);
rsx_overlay_vertex_geom(rsx, i, 0, 0, 1, 1);
for (j = 0; j < RSX_MAX_VERTICES; j++)
{
o->vertices[j].r = 1.0f;
o->vertices[j].g = 1.0f;
o->vertices[j].b = 1.0f;
o->vertices[j].a = 1.0f;
}
o->texture.data = NULL;
o->texture.height = images[i].height;
o->texture.width = images[i].width;
if (!rsx_load_texture_data(rsx, &o->texture, images[i].pixels,
images[i].width, images[i].height, images[i].width*4,
true, false, TEXTURE_FILTER_LINEAR))
{
rsx_free_overlay(rsx);
return false;
}
}
return true;
}
/* A page of the pack's textures: rsx_load_texture's, drawn from a
* copy of each rsx_texture_t, uploaded once for every page */
static bool rsx_overlay_load_textures(void *data,
const uintptr_t *textures, unsigned num_textures)
{
unsigned i, j;
rsx_t *rsx = (rsx_t *)data;
if (!rsx)
return false;
rsx_free_overlay(rsx);
if (!num_textures)
return true;
if (!(rsx->overlay = (rsx_overlay_t *)calloc(num_textures,
sizeof(rsx_overlay_t))))
return false;
rsx->overlays = num_textures;
rsx->overlay_borrowed = true;
for (i = 0; i < num_textures; i++)
{
rsx_overlay_t *o = &rsx->overlay[i];
if (!(o->vertices = (rsx_vertex_t *)rsxMemalign(128,
sizeof(rsx_vertex_t) * RSX_MAX_VERTICES)))
{
rsx_free_overlay(rsx);
return false;
}
if (textures[i])
o->texture = *(const rsx_texture_t*)textures[i];
/* Default. Stretch to whole screen. */
rsx_overlay_tex_geom(rsx, i, 0, 0, 1, 1);
rsx_overlay_vertex_geom(rsx, i, 0, 0, 1, 1);
for (j = 0; j < RSX_MAX_VERTICES; j++)
{
o->vertices[j].r = 1.0f;
o->vertices[j].g = 1.0f;
o->vertices[j].b = 1.0f;
o->vertices[j].a = 1.0f;
}
}
return true;
}
static void rsx_overlay_enable(void *data, bool state)
{
rsx_t *rsx = (rsx_t *)data;
rsx->overlay_enable = state;
}
static void rsx_overlay_full_screen(void *data, bool enable)
{
rsx_t *rsx = (rsx_t *)data;
rsx->overlay_full_screen = enable;
}
static void rsx_overlay_set_alpha(void *data, unsigned image, float mod)
{
rsx_t *rsx = (rsx_t *)data;
/* Called whenever the frontend likes, not only after a load that
* worked: no page is a NULL array, and an index off the end of the
* page is off the end of the allocation. */
if (rsx && rsx->overlay && image < rsx->overlays)
{
rsx->overlay[image].vertices[0].a = mod;
rsx->overlay[image].vertices[1].a = mod;
rsx->overlay[image].vertices[2].a = mod;
rsx->overlay[image].vertices[3].a = mod;
}
}
static void rsx_render_overlay(void *data)
{
unsigned i;
rsx_t *rsx = (rsx_t *)data;
rsx_set_viewport(rsx, VIDEO_SCALE_PACK(rsx->width, rsx->height), true, true);
for (i = 0; i < rsx->overlays; i++)
{
if (!rsx->overlay[i].vertices || !rsx->overlay[i].texture.data)
continue;
rsx_set_texture(rsx, &rsx->overlay[i].texture);
rsx_draw_overlay_vertices(rsx, i);
}
}
static const video_overlay_interface_t rsx_overlay_interface =
{
rsx_overlay_enable,
rsx_overlay_load,
rsx_overlay_load_textures,
rsx_overlay_tex_geom,
rsx_overlay_vertex_geom,
rsx_overlay_full_screen,
rsx_overlay_set_alpha,
};
static void rsx_get_overlay_interface(void *data,
const video_overlay_interface_t **iface) {*iface = &rsx_overlay_interface;}
#endif
static void rsx_update_screen(rsx_t* gcm)
{
rsx_buffer *buffer = NULL;
#if defined(HAVE_MENU_BUFFER)
if (gcm->menu_frame_enable)
{
buffer = &gcm->menuBuffers[gcm->menuBuffer];
gcm->menuBuffer = (gcm->menuBuffer + 1) % RSX_MAX_MENU_BUFFERS;
gcm->nextBuffer = RSX_MAX_BUFFERS + gcm->menuBuffer;
}
else
#endif
{
buffer = &gcm->buffers[gcm->currentBuffer];
gcm->currentBuffer = (gcm->currentBuffer + 1) % RSX_MAX_BUFFERS;
gcm->nextBuffer = gcm->currentBuffer;
}
rsx_flip(gcm->context, buffer->id);
if (gcm->vsync)
rsx_wait_flip();
rsxSetSurface(gcm->context, &gcm->surface[gcm->nextBuffer]);
#ifdef HAVE_SYSUTILS
cellSysutilCheckCallback();
#endif
}
static void rsx_draw_hw_image(rsx_t *gcm)
{
rsx_texture_t texture;
memset(&texture, 0, sizeof(texture));
texture.tex.format = GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN;
texture.tex.mipmap = 1;
texture.tex.dimension = GCM_TEXTURE_DIMS_2D;
texture.tex.remap = GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT
| GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT
| GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT
| GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT
| GCM_TEXTURE_REMAP_COLOR_A << GCM_TEXTURE_REMAP_COLOR_A_SHIFT
| GCM_TEXTURE_REMAP_COLOR_R << GCM_TEXTURE_REMAP_COLOR_R_SHIFT
| GCM_TEXTURE_REMAP_COLOR_G << GCM_TEXTURE_REMAP_COLOR_G_SHIFT
| GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT;
texture.tex.width = gcm->hw_image.width;
texture.tex.height = gcm->hw_image.height;
texture.tex.depth = 1;
texture.tex.pitch = gcm->hw_image.pitch;
texture.tex.offset = gcm->hw_image.offset;
texture.tex.location = GCM_LOCATION_RSX;
texture.min_filter = gcm->smooth ? GCM_TEXTURE_LINEAR : GCM_TEXTURE_NEAREST;
texture.mag_filter = texture.min_filter;
texture.wrap_s = GCM_TEXTURE_CLAMP_TO_EDGE;
texture.wrap_t = GCM_TEXTURE_CLAMP_TO_EDGE;
rsx_set_texture(gcm, &texture);
rsx_draw_vertices(gcm);
}
static bool rsx_frame(void* data, const void* frame,
unsigned dims,
uint64_t frame_count,
unsigned pitch, const char* msg, video_frame_info_t *video_info)
{
unsigned width = VIDEO_SCALE_W(dims);
unsigned height = VIDEO_SCALE_H(dims);
rsx_viewport_t vp;
rsx_t *gcm = (rsx_t*)data;
#ifdef HAVE_MENU
bool statistics_show = video_info->statistics_show;
struct font_params *osd_params = (struct font_params*)
&video_info->osd_stat_params;
bool menu_is_alive = (video_info->menu_st_flags & MENU_ST_FLAG_ALIVE) ? true : false;
#endif
#ifdef HAVE_GFX_WIDGETS
bool widgets_active = video_info->widgets_active;
#endif
bool draw = false;
rsx_collect_retired_memory();
rsx_fence_retired_memory(gcm->context);
/* The core shares the FIFO and leaves its own state behind */
if (gcm->hw_render && frame == RETRO_HW_FRAME_BUFFER_VALID)
{
unsigned attrib;
rsxSetSurface(gcm->context, &gcm->surface[gcm->nextBuffer]);
rsxSetColorMask(gcm->context, GCM_COLOR_MASK_R | GCM_COLOR_MASK_G
| GCM_COLOR_MASK_B | GCM_COLOR_MASK_A);
rsxSetColorMaskMrt(gcm->context, 0);
rsxSetDepthTestEnable(gcm->context, GCM_FALSE);
rsxSetDepthWriteEnable(gcm->context, GCM_TRUE);
rsxSetStencilTestEnable(gcm->context, GCM_FALSE);
rsxSetAlphaTestEnable(gcm->context, GCM_FALSE);
rsxSetShadeModel(gcm->context, GCM_SHADE_MODEL_SMOOTH);
rsxSetBlendEnable(gcm->context, GCM_FALSE);
rsxSetLogicOpEnable(gcm->context, GCM_FALSE);
rsxSetCullFaceEnable(gcm->context, GCM_FALSE);
rsxSetZMinMaxControl(gcm->context, GCM_FALSE, GCM_TRUE, GCM_TRUE);
rsxSetScissor(gcm->context, 0, 0, gcm->width, gcm->height);
rsxSetPolygonOffsetFillEnable(gcm->context, GCM_FALSE);
for (attrib = 0; attrib < 8; attrib++)
rsxSetViewportClip(gcm->context, attrib, gcm->width, gcm->height);
for (attrib = 0; attrib < 16; attrib++)
rsxBindVertexArrayAttrib(gcm->context, attrib, 0, 0,
0, 0, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
}
if (gcm->should_resize)
rsx_update_viewport(gcm);
vp.min = 0.0f;
vp.max = 1.0f;
vp.pos = VIDEO_POS_PACK(VIDEO_POS_X(gcm->vp.pos),
gcm->height - VIDEO_POS_Y(gcm->vp.pos)
- VIDEO_SCALE_H(gcm->vp.dims));
vp.dims = gcm->vp.dims;
vp.scale[0] = VIDEO_SCALE_W(vp.dims) * 0.5f;
vp.scale[1] = VIDEO_SCALE_H(vp.dims) * -0.5f;
vp.scale[2] = (vp.max - vp.min) * 0.5f;
vp.scale[3] = 0.0f;
vp.offset[0] = VIDEO_POS_X(vp.pos)
+ VIDEO_SCALE_W(vp.dims) * 0.5f;
vp.offset[1] = VIDEO_POS_Y(vp.pos)
+ VIDEO_SCALE_H(vp.dims) * 0.5f;
vp.offset[2] = (vp.max + vp.min) * 0.5f;
vp.offset[3] = 0.0f;
rsxSetViewport(gcm->context, VIDEO_POS_X(vp.pos),
VIDEO_POS_Y(vp.pos), VIDEO_SCALE_W(vp.dims),
VIDEO_SCALE_H(vp.dims), vp.min, vp.max,
vp.scale, vp.offset);
if (frame == RETRO_HW_FRAME_BUFFER_VALID)
{
if (gcm->hw_render && gcm->hw_image_valid)
{
rsx_draw_hw_image(gcm);
draw = true;
}
}
else if (frame && width && height)
{
gcm->tex_index = ((gcm->tex_index + 1) % RSX_MAX_TEXTURES);
if (rsx_load_texture_data(gcm,
&gcm->texture[gcm->tex_index],
frame, width, height, pitch, gcm->rgb32, false,
gcm->smooth
? TEXTURE_FILTER_LINEAR
: TEXTURE_FILTER_NEAREST))
{
/* TODO/FIXME - pipeline ID being used here is RSX_SHADER_MENU,
* shouldn't this be RSX_SHADER_STOCK_BLEND instead? */
rsx_set_texture(gcm, &gcm->texture[gcm->tex_index]);
rsx_draw_vertices(gcm);
draw = true;
}
}
#ifdef HAVE_MENU
if (gcm->menu_frame_enable && menu_is_alive)
{
/* A duped core frame stays behind the menu: the surface was
* cleared at the last flip */
if (!draw)
{
if (gcm->hw_render)
{
if (gcm->hw_image_valid)
rsx_draw_hw_image(gcm);
}
else if (gcm->texture[gcm->tex_index].data)
{
rsx_set_texture(gcm, &gcm->texture[gcm->tex_index]);
rsx_draw_vertices(gcm);
}
}
menu_driver_frame(menu_is_alive, video_info);
draw = true;
if (gcm->menu_texture.data)
{
/* TODO/FIXME - pipeline ID being used here
* is RSX_SHADER_STOCK_BLEND, shouldn't
* this be RSX_SHADER_MENU instead? */
rsx_set_menu_texture(gcm, &gcm->menu_texture);
rsx_draw_menu_vertices(gcm);
}
};
if (statistics_show)
if (osd_params)
font_driver_render_msg(gcm,
video_info->stat_text, video_info->stat_text_len,
osd_params, NULL);
#endif
#ifdef HAVE_OVERLAY
if (gcm->overlay_enable)
rsx_render_overlay(gcm);
#endif
#ifdef HAVE_GFX_WIDGETS
if (widgets_active)
gfx_widgets_frame(video_info);
#endif
if (msg)
font_driver_render_msg(gcm, msg, strlen(msg), NULL, NULL);
#if 0
/* TODO: translucid menu */
#endif
if (draw)
{
/* Only update when we draw to prevent flickering when core frame duping is enabled */
rsx_update_screen(gcm);
rsx_clear_surface(gcm);
}
gcm->vert_idx = 0;
return true;
}
static void rsx_set_nonblock_state(void* data, bool toggle,
bool a, unsigned b)
{
rsx_t* gcm = (rsx_t*)data;
if (gcm)
gcm->vsync = !toggle;
}
static bool rsx_alive(void* data) { return true; }
static bool rsx_focus(void* data) { return true; }
static bool rsx_suppress_screensaver(void* data, bool enable) { return false; }
static void rsx_free(void* data)
{
rsx_t* gcm = (rsx_t*)data;
if (!gcm)
return;
rsxClearSurface(gcm->context, GCM_CLEAR_Z);
gcmSetWaitFlip(gcm->context);
#if 0
/* TODO fix crash on loading core */
if (gcm->vertices)
rsxFree(gcm->vertices);
if (gcm->texture_vertices)
rsxFree(gcm->texture_vertices);
for (i = 0; i < RSX_MAX_TEXTURES; i++)
{
if (gcm->texture[i].data)
rsxFree(gcm->texture[i].data);
}
if (gcm->menu_texture.data)
rsxFree(gcm->menu_texture.data);
if (gcm->white_texture)
rsx_unload_texture_internal(gcm, (uintptr_t)gcm->white_texture);
for (i = 0; i < RSX_MAX_BUFFERS; i++)
rsxFree(gcm->buffers[i].ptr);
#if defined(HAVE_MENU_BUFFER)
for (i = 0; i < RSX_MAX_MENU_BUFFERS; i++)
rsxFree(gcm->menuBuffers[i].ptr);
#endif
if (gcm->depth_buffer)
rsxFree(gcm->depth_buffer);
if (gcm->fp_buffer)
rsxFree(gcm->fp_buffer);
rsxFinish(gcm->context, 1);
free(gcm->host_addr);
#endif
free (gcm);
}
static void rsx_set_texture_frame(void* data, const void* frame, bool rgb32,
unsigned dims, float alpha)
{
rsx_t* gcm = (rsx_t*)data;
gcm->menu_texture_alpha = alpha;
gcm->menu_width = VIDEO_SCALE_W(dims);
gcm->menu_height = VIDEO_SCALE_H(dims);
rsx_load_texture_data(gcm, &gcm->menu_texture, frame, VIDEO_SCALE_W(dims), VIDEO_SCALE_H(dims), VIDEO_SCALE_W(dims) * (rgb32 ? 4 : 2),
rgb32, true, gcm->smooth ? TEXTURE_FILTER_LINEAR : TEXTURE_FILTER_NEAREST);
}
static void rsx_set_texture_enable(void* data, bool state, bool full_screen)
{
rsx_t* gcm = (rsx_t*)data;
if (gcm)
gcm->menu_frame_enable = state;
}
static void rsx_set_rotation(void* data, unsigned rotation)
{
rsx_t* gcm = (rsx_t*)data;
struct video_ortho ortho = {0, 1, 0, 1, -1, 1};
if (!gcm)
return;
gcm->rotation = 90 * rotation;
gcm->should_resize = true;
rsx_set_projection(gcm, &ortho, true);
}
static void rsx_set_filtering(void* data, unsigned index, bool smooth, bool ctx_scaling)
{
rsx_t* gcm = (rsx_t*)data;
if (gcm)
gcm->smooth = smooth;
}
static void rsx_set_aspect_ratio(void* data, unsigned aspect_ratio_idx)
{
rsx_t* gcm = (rsx_t*)data;
if (!gcm)
return;
gcm->keep_aspect = true;
gcm->should_resize = true;
}
static void rsx_apply_state_changes(void* data)
{
rsx_t* gcm = (rsx_t*)data;
if (gcm)
gcm->should_resize = true;
}
static void rsx_viewport_info(void* data, struct video_viewport* vp)
{
rsx_t* gcm = (rsx_t*)data;
if (gcm)
*vp = gcm->vp;
}
#if 0
/* TODO/FIXME - does this function have to be hooked up as a function callback
* or can it be removed? */
static void rsx_set_osd_msg(void *data,
video_frame_info_t *video_info,
const char *msg, size_t msg_len,
const struct font_params *params, void *font)
{
rsx_t* gcm = (rsx_t*)data;
if (gcm && gcm->msg_rendering_enabled)
font_driver_render_msg(data, msg, msg_len, params, font);
}
#endif
static uint32_t rsx_get_flags(void *data) { return 0; }
/* --- GPU-native BCn compressed-texture upload (PoC) --- */
/* RSX (NV47/G70) natively samples DXT1/DXT23/DXT45 == BC1/BC2/BC3.
* Nothing above BC3 exists on this GPU. */
static bool rsx_gcm_compressed_format(enum texture_gpu_format fmt,
u32 *gcm_out, u32 *block_bytes)
{
switch (fmt)
{
case TEXTURE_GPU_FORMAT_BC1:
*gcm_out = GCM_TEXTURE_FORMAT_DXT1; *block_bytes = 8; return true;
case TEXTURE_GPU_FORMAT_BC2:
*gcm_out = GCM_TEXTURE_FORMAT_DXT23; *block_bytes = 16; return true;
case TEXTURE_GPU_FORMAT_BC3:
*gcm_out = GCM_TEXTURE_FORMAT_DXT45; *block_bytes = 16; return true;
default:
break;
}
return false;
}
static bool rsx_supports_texture_format(void *data, enum texture_gpu_format fmt)
{
u32 gcm, bb;
(void)data;
return rsx_gcm_compressed_format(fmt, &gcm, &bb);
}
static uintptr_t rsx_load_texture_compressed(void *video_data,
const struct texture_compressed *tc, bool threaded,
enum texture_filter_type filter_type)
{
rsx_t *rsx = (rsx_t*)video_data;
rsx_texture_t *texture;
u8 *dst;
size_t total = 0;
size_t off = 0;
u32 gcm_fmt = 0;
u32 block_bytes = 16;
u32 min_filter, mag_filter;
unsigned blocks_w0;
unsigned i;
/* Regular texture loads on this driver marshal to the video thread;
* the compressed path does not yet, so under threading decline here
* and let the CPU-decode fallback go through the marshalled path. */
if (threaded)
return 0;
if (!rsx || !tc || tc->num_mips == 0)
return 0;
if (!rsx_gcm_compressed_format(tc->format, &gcm_fmt, &block_bytes))
return 0;
if (!(texture = (rsx_texture_t*)malloc(sizeof(rsx_texture_t))))
return 0;
for (i = 0; i < tc->num_mips; i++)
total += tc->mips[i].size;
texture->width = tc->mips[0].width;
texture->height = tc->mips[0].height;
texture->data = (u32*)rsxMemalign(128, total);
if (!texture->data)
{
free(texture);
return 0;
}
texture->size = total;
rsxAddressToOffset(texture->data, &texture->offset);
/* Mip chain is stored contiguously; the RSX derives per-level
* offsets from the base offset and mip count. */
dst = (u8*)texture->data;
for (i = 0; i < tc->num_mips; i++)
{
memcpy(dst + off, tc->mips[i].data, tc->mips[i].size);
off += tc->mips[i].size;
}
blocks_w0 = (tc->mips[0].width + 3u) >> 2;
texture->tex.format = gcm_fmt | GCM_TEXTURE_FORMAT_LIN;
texture->tex.mipmap = tc->num_mips;
texture->tex.dimension = GCM_TEXTURE_DIMS_2D;
texture->tex.cubemap = GCM_FALSE;
/* DXT decodes to RGBA on-chip; reuse the driver's remap. NOTE: RPCS3
* uses an identity R,G,B,A swizzle for the DXT formats -- if colours
* come out with R/B swapped on hardware, this remap is why. */
texture->tex.remap = ((GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT)
| (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_G << GCM_TEXTURE_REMAP_COLOR_G_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_R << GCM_TEXTURE_REMAP_COLOR_R_SHIFT)
| (GCM_TEXTURE_REMAP_COLOR_A << GCM_TEXTURE_REMAP_COLOR_A_SHIFT));
texture->tex.width = tc->mips[0].width;
texture->tex.height = tc->mips[0].height;
texture->tex.depth = 1;
texture->tex.location = GCM_LOCATION_RSX;
/* Linear compressed surface: pitch is the byte width of one row of
* 4x4 blocks at mip 0. */
texture->tex.pitch = blocks_w0 * block_bytes;
texture->tex.offset = texture->offset;
if ( filter_type == TEXTURE_FILTER_NEAREST
|| filter_type == TEXTURE_FILTER_MIPMAP_NEAREST)
{
min_filter = GCM_TEXTURE_NEAREST;
mag_filter = GCM_TEXTURE_NEAREST;
}
else
{
min_filter = GCM_TEXTURE_LINEAR;
mag_filter = GCM_TEXTURE_LINEAR;
}
texture->min_filter = min_filter;
texture->mag_filter = mag_filter;
texture->wrap_s = GCM_TEXTURE_CLAMP_TO_EDGE;
texture->wrap_t = GCM_TEXTURE_CLAMP_TO_EDGE;
return (uintptr_t)texture;
}
static const video_poke_interface_t rsx_poke_interface = {
rsx_get_flags,
rsx_load_texture,
rsx_unload_texture,
NULL, /* set_video_mode */
NULL, /* get_refresh_rate */
rsx_set_filtering,
NULL, /* get_video_output_size */
NULL, /* get_video_output_prev */
NULL, /* get_video_output_next */
NULL, /* get_current_framebuffer */
NULL, /* get_proc_address */
rsx_set_aspect_ratio,
rsx_apply_state_changes,
rsx_set_texture_frame,
rsx_set_texture_enable,
font_driver_render_msg,
NULL, /* show_mouse */
NULL, /* grab_mouse_toggle */
NULL, /* get_current_shader */
NULL, /* get_current_software_framebuffer */
rsx_get_hw_render_interface,
NULL, /* set_hdr_menu_nits */
NULL, /* set_hdr_paper_white_nits */
NULL, /* set_hdr_expand_gamut */
NULL, /* set_hdr_scanlines */
NULL, /* set_hdr_subpixel_layout */
rsx_supports_texture_format,
rsx_load_texture_compressed,
NULL, /* present_last */
NULL, /* get_last_present_time */
NULL, /* hw_ring_install */
NULL, /* hw_ring_fence_new */
NULL, /* hw_ring_fence_free */
NULL, /* hw_ring_fence_signal */
NULL, /* hw_ring_fence_wait */
NULL, /* hw_ring_capture */
NULL, /* hw_ring_present_slot */
NULL, /* hw_ring_context_new */
NULL, /* hw_ring_context_free */
NULL, /* hw_ring_framebuffer */
rsx_update_texture
};
static void rsx_get_poke_interface(void* data,
const video_poke_interface_t** iface) { *iface = &rsx_poke_interface; }
static bool rsx_set_shader(void* data,
enum rarch_shader_type type, const char* path) { return false; }
#ifdef HAVE_GFX_WIDGETS
static bool rsx_widgets_enabled(void *data) { return true; }
#endif
static font_renderer_t rsx_font = {
rsx_font_init,
rsx_font_free,
rsx_font_render_msg,
"rsx",
rsx_font_get_glyph,
rsx_font_bind_block,
rsx_font_flush_block,
rsx_font_get_message_width,
rsx_font_get_line_metrics
};
video_driver_t video_gcm =
{
rsx_init,
rsx_frame,
rsx_set_nonblock_state,
rsx_alive,
rsx_focus,
rsx_suppress_screensaver,
NULL, /* has_windowed */
rsx_set_shader,
rsx_free,
"rsx",
rsx_set_viewport,
rsx_set_rotation,
rsx_viewport_info,
NULL, /* read_viewport */
#ifdef HAVE_OVERLAY
rsx_get_overlay_interface,
#endif
rsx_get_poke_interface,
rsx_wrap_type_to_enum,
NULL, /* shader_load_begin */
NULL, /* shader_load_step */
#ifdef HAVE_GFX_WIDGETS
rsx_widgets_enabled,
#endif
NULL, /* invalidate_hw_render_cache */
NULL, /* read_viewport_hdr */
&rsx_font
};
gfx_display_ctx_driver_t gfx_display_ctx_rsx = {
gfx_display_rsx_draw,
NULL, /* draw_pipeline */
gfx_display_rsx_blend_begin,
gfx_display_rsx_blend_end,
gfx_display_rsx_get_default_mvp,
gfx_display_rsx_get_default_vertices,
gfx_display_rsx_get_default_tex_coords,
&rsx_font,
GFX_VIDEO_DRIVER_RSX,
"rsx",
true,
true,
gfx_display_rsx_scissor_begin,
gfx_display_rsx_scissor_end
};