mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
The Vita driver copies the core's frame into its texture a row at a time, the frame's width of each row, or in one copy when the core's rows are the texture's; textures are allocated at their row stride, the width rounded to 8. Both drivers copy with the C library's memcpy, NEON-tuned on their targets, and memory/neon/memcpy-neon.S goes. samples/gfx/gxm_frame_upload runs the driver's frame upload against the psp2 stubs with guarded GPU memory: core rows longer, as long and shorter than the texture's, widths not of 8, rows copied once each, nothing written past the texture or read past the frame.
3059 lines
94 KiB
C
3059 lines
94 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2015-2017 - Sergi Granell (xerpi)
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
*
|
|
* 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 <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <psp2/gxm.h>
|
|
#include <psp2/types.h>
|
|
#include <psp2/kernel/sysmem.h>
|
|
#include <psp2/display.h>
|
|
#include <psp2/common_dialog.h>
|
|
|
|
#include <retro_inline.h>
|
|
#include <encodings/utf.h>
|
|
#include <string/stdstring.h>
|
|
#include <formats/image.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
|
|
|
|
#include "../font_driver.h"
|
|
#include "../video_driver.h"
|
|
|
|
#include <defines/psp_defines.h>
|
|
|
|
#include "../../driver.h"
|
|
#include "../../retroarch.h"
|
|
#include "../../verbosity.h"
|
|
#include "../../configuration.h"
|
|
#include "../../input/input_osk.h"
|
|
|
|
/* Defines */
|
|
|
|
#define RGBA8(r,g,b,a) ((((a)&0xFF)<<24) | (((b)&0xFF)<<16) | (((g)&0xFF)<<8) | (((r)&0xFF)<<0))
|
|
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
|
|
#define DISPLAY_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8
|
|
#define DISPLAY_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8
|
|
#define DISPLAY_BUFFER_COUNT 3
|
|
#define DISPLAY_MAX_PENDING_SWAPS 2
|
|
#define DEFAULT_TEMP_POOL_SIZE (1 * 1024 * 1024)
|
|
#define GXM_TEX_MAX_SIZE 4096
|
|
|
|
/* Types */
|
|
|
|
typedef enum
|
|
{
|
|
GXM_VIDEO_MODE_960x544 = 0,
|
|
GXM_VIDEO_MODE_1280x720
|
|
} gxm_video_mode_t;
|
|
|
|
typedef struct gxm_video_mode_data
|
|
{
|
|
/* The mode's pixel size, packed; stride is the row the display
|
|
* scans out, which is not always the width. */
|
|
unsigned dims;
|
|
int stride;
|
|
} gxm_video_mode_data_t;
|
|
|
|
typedef struct gxm_color_vertex
|
|
{
|
|
float x;
|
|
float y;
|
|
float z;
|
|
unsigned int color;
|
|
} gxm_color_vertex_t;
|
|
|
|
typedef struct gxm_texture_vertex
|
|
{
|
|
float x;
|
|
float y;
|
|
float z;
|
|
float u;
|
|
float v;
|
|
} gxm_texture_vertex_t;
|
|
|
|
typedef struct gxm_texture_tint_vertex
|
|
{
|
|
float x;
|
|
float y;
|
|
float z;
|
|
float u;
|
|
float v;
|
|
float r;
|
|
float g;
|
|
float b;
|
|
float a;
|
|
} gxm_texture_tint_vertex_t;
|
|
|
|
typedef struct gxm_texture
|
|
{
|
|
SceGxmTexture gxm_tex;
|
|
SceUID data_UID;
|
|
SceUID palette_UID;
|
|
SceGxmRenderTarget *gxm_rtgt;
|
|
SceGxmColorSurface gxm_sfc;
|
|
SceGxmDepthStencilSurface gxm_sfd;
|
|
SceUID depth_UID;
|
|
} gxm_texture_t;
|
|
|
|
typedef struct gxm_display_data
|
|
{
|
|
void *address;
|
|
} gxm_display_data_t;
|
|
|
|
typedef struct gxm_fragment_programs
|
|
{
|
|
SceGxmFragmentProgram *color;
|
|
SceGxmFragmentProgram *texture;
|
|
SceGxmFragmentProgram *textureTint;
|
|
} gxm_fragment_programs_t;
|
|
|
|
typedef struct vita_menu_frame
|
|
{
|
|
gxm_texture_t *texture;
|
|
/* The menu frame's pixel size, packed. */
|
|
unsigned dims;
|
|
bool active;
|
|
} vita_menu_t;
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
struct vita_overlay_data
|
|
{
|
|
gxm_texture_t *tex;
|
|
float x;
|
|
float y;
|
|
float w;
|
|
float h;
|
|
float tex_x;
|
|
float tex_y;
|
|
float tex_w;
|
|
float tex_h;
|
|
float alpha_mod;
|
|
/* The overlay image's pixel size, packed; tex_w and tex_h above
|
|
* are a fraction of it and stay the floats they are. */
|
|
unsigned dims;
|
|
};
|
|
#endif
|
|
|
|
typedef struct vita_video
|
|
{
|
|
gxm_texture_t *texture;
|
|
/* Sampled by a quad drawn with no texture: solid colour */
|
|
gxm_texture_t *white_texture;
|
|
SceGxmTextureFormat format;
|
|
/* The size of the frame the core last handed over, packed. */
|
|
unsigned dims;
|
|
SceGxmTextureFilter tex_filter;
|
|
|
|
video_viewport_t vp;
|
|
|
|
math_matrix_4x4 mvp, mvp_no_rot;
|
|
|
|
vita_menu_t menu;
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
struct vita_overlay_data *overlay;
|
|
unsigned overlays;
|
|
#endif
|
|
/* The size the driver is presenting at, packed. */
|
|
unsigned video_dims;
|
|
unsigned rotation;
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
bool overlay_enable;
|
|
bool overlay_full_screen;
|
|
/* overlay[].tex are the overlay pack's (load_textures) */
|
|
bool overlay_borrowed;
|
|
#endif
|
|
bool fullscreen;
|
|
bool vsync;
|
|
bool rgb32;
|
|
bool vblank_not_reached;
|
|
bool keep_aspect;
|
|
bool should_resize;
|
|
} vita_video_t;
|
|
|
|
typedef struct
|
|
{
|
|
vita_video_t *vita;
|
|
gxm_texture_t *texture;
|
|
/* Textures the atlas outgrew: a frame in flight may still draw from
|
|
* them, and freeing one means waiting for the GPU, so they go when
|
|
* the font does - the atlas grows at most twice */
|
|
gxm_texture_t *retired[2];
|
|
unsigned retired_count;
|
|
const font_renderer_driver_t *font_driver;
|
|
void *font_data;
|
|
struct font_atlas *atlas;
|
|
} vita_font_t;
|
|
|
|
/* Shader program blobs */
|
|
|
|
extern const SceGxmProgram color_v_gxp;
|
|
extern const SceGxmProgram color_f_gxp;
|
|
extern const SceGxmProgram texture_v_gxp;
|
|
extern const SceGxmProgram texture_f_gxp;
|
|
extern const SceGxmProgram texture_tint_v_gxp;
|
|
extern const SceGxmProgram texture_tint_f_gxp;
|
|
|
|
/* File-scope state */
|
|
|
|
static gxm_video_mode_data_t video_mode_data;
|
|
static gxm_video_mode_t video_mode_initial;
|
|
static SceGxmMultisampleMode current_msaa = SCE_GXM_MULTISAMPLE_4X;
|
|
|
|
static const SceGxmProgram *const colorVertexProgramGxp = &color_v_gxp;
|
|
static const SceGxmProgram *const colorFragmentProgramGxp = &color_f_gxp;
|
|
static const SceGxmProgram *const textureVertexProgramGxp = &texture_v_gxp;
|
|
static const SceGxmProgram *const textureFragmentProgramGxp = &texture_f_gxp;
|
|
static const SceGxmProgram *const textureTintVertexProgramGxp = &texture_tint_v_gxp;
|
|
static const SceGxmProgram *const textureTintFragmentProgramGxp = &texture_tint_f_gxp;
|
|
|
|
static int gxm_initialized = 0;
|
|
static unsigned int clear_color_u = 0xFF000000;
|
|
static int clip_rect_x_min = 0;
|
|
static int clip_rect_y_min = 0;
|
|
static int clip_rect_x_max = 0;
|
|
static int clip_rect_y_max = 0;
|
|
static int vblank_wait = 1;
|
|
static int drawing = 0;
|
|
static int clipping_enabled = 0;
|
|
|
|
static SceUID vdmRingBufferUid;
|
|
static SceUID vertexRingBufferUid;
|
|
static SceUID fragmentRingBufferUid;
|
|
static SceUID fragmentUsseRingBufferUid;
|
|
|
|
static SceGxmContextParams contextParams;
|
|
static SceGxmRenderTarget *renderTarget = NULL;
|
|
static SceUID displayBufferUid[DISPLAY_BUFFER_COUNT];
|
|
static void *displayBufferData[DISPLAY_BUFFER_COUNT];
|
|
static SceGxmColorSurface displaySurface[DISPLAY_BUFFER_COUNT];
|
|
static SceGxmSyncObject *displayBufferSync[DISPLAY_BUFFER_COUNT];
|
|
static SceUID depthBufferUid;
|
|
static SceUID stencilBufferUid;
|
|
static SceGxmDepthStencilSurface depthSurface;
|
|
static void *depthBufferData = NULL;
|
|
static void *stencilBufferData = NULL;
|
|
|
|
static unsigned int backBufferIndex = 0;
|
|
static unsigned int frontBufferIndex = 0;
|
|
|
|
static SceGxmShaderPatcher *shaderPatcher = NULL;
|
|
|
|
static SceGxmShaderPatcherId colorVertexProgramId;
|
|
static SceGxmShaderPatcherId colorFragmentProgramId;
|
|
static SceGxmShaderPatcherId textureVertexProgramId;
|
|
static SceGxmShaderPatcherId textureFragmentProgramId;
|
|
static SceGxmShaderPatcherId textureTintVertexProgramId;
|
|
static SceGxmShaderPatcherId textureTintFragmentProgramId;
|
|
|
|
static SceUID patcherBufferUid;
|
|
static SceUID patcherVertexUsseUid;
|
|
static SceUID patcherFragmentUsseUid;
|
|
|
|
static SceUID linearIndicesUid;
|
|
static uint16_t *linearIndices = NULL;
|
|
|
|
static float gxm_ortho_matrix[4*4];
|
|
static SceGxmContext *gxm_context = NULL;
|
|
static SceGxmVertexProgram *gxm_colorVertexProgram = NULL;
|
|
static SceGxmFragmentProgram *gxm_colorFragmentProgram = NULL;
|
|
static SceGxmVertexProgram *gxm_textureVertexProgram = NULL;
|
|
static SceGxmFragmentProgram *gxm_textureFragmentProgram = NULL;
|
|
static SceGxmVertexProgram *gxm_textureTintVertexProgram = NULL;
|
|
static SceGxmFragmentProgram *gxm_textureTintFragmentProgram = NULL;
|
|
static const SceGxmProgramParameter *gxm_colorWvpParam = NULL;
|
|
static const SceGxmProgramParameter *gxm_textureWvpParam = NULL;
|
|
static const SceGxmProgramParameter *gxm_textureTintWvpParam = NULL;
|
|
|
|
|
|
static struct
|
|
{
|
|
gxm_fragment_programs_t blend_mode_normal;
|
|
gxm_fragment_programs_t blend_mode_add;
|
|
} gxm_fragmentPrograms;
|
|
|
|
/* Temporary memory pool */
|
|
static void *pool_addr = NULL;
|
|
static SceUID poolUid;
|
|
static unsigned int pool_index = 0;
|
|
static unsigned int pool_size = 0;
|
|
|
|
static SceKernelMemBlockType MemBlockType = SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW;
|
|
|
|
static const float gxm_vertexes[8] = {
|
|
0, 0,
|
|
1, 0,
|
|
0, 1,
|
|
1, 1
|
|
};
|
|
|
|
static const float gxm_tex_coords[8] = {
|
|
0, 1,
|
|
1, 1,
|
|
0, 0,
|
|
1, 0
|
|
};
|
|
|
|
static const float gxm_colors[16] = {
|
|
1.0f, 1.0f, 1.0f, 1.0f,
|
|
1.0f, 1.0f, 1.0f, 1.0f,
|
|
1.0f, 1.0f, 1.0f, 1.0f,
|
|
1.0f, 1.0f, 1.0f, 1.0f,
|
|
};
|
|
|
|
/* Forward declarations */
|
|
|
|
static void *gpu_alloc(SceKernelMemBlockType type, unsigned int size,
|
|
unsigned int alignment, unsigned int attribs, SceUID *uid);
|
|
static void gpu_free(SceUID uid);
|
|
static void *vertex_usse_alloc(unsigned int size, SceUID *uid,
|
|
unsigned int *usse_offset);
|
|
static void vertex_usse_free(SceUID uid);
|
|
static void gxm_render_overlay(void *data);
|
|
static uintptr_t gxm_load_texture(void *video_data, void *data,
|
|
bool threaded, enum texture_filter_type filter_type);
|
|
static void gxm_set_projection(vita_video_t *vita,
|
|
struct video_ortho *ortho, bool allow_rotate);
|
|
|
|
|
|
static void *gpu_alloc(SceKernelMemBlockType type, unsigned int size,
|
|
unsigned int alignment, unsigned int attribs, SceUID *uid)
|
|
{
|
|
void *mem;
|
|
|
|
if (type == SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW)
|
|
size = ALIGN(size, 256*1024);
|
|
else
|
|
size = ALIGN(size, 4*1024);
|
|
|
|
*uid = sceKernelAllocMemBlock("gpu_mem", type, size, NULL);
|
|
|
|
if (*uid < 0)
|
|
return NULL;
|
|
|
|
if (sceKernelGetMemBlockBase(*uid, &mem) < 0)
|
|
return NULL;
|
|
|
|
if (sceGxmMapMemory(mem, size, attribs) < 0)
|
|
return NULL;
|
|
|
|
return mem;
|
|
}
|
|
|
|
static void gpu_free(SceUID uid)
|
|
{
|
|
void *mem = NULL;
|
|
if (sceKernelGetMemBlockBase(uid, &mem) < 0)
|
|
return;
|
|
sceGxmUnmapMemory(mem);
|
|
sceKernelFreeMemBlock(uid);
|
|
}
|
|
|
|
static void *vertex_usse_alloc(unsigned int size, SceUID *uid,
|
|
unsigned int *usse_offset)
|
|
{
|
|
void *mem = NULL;
|
|
|
|
size = ALIGN(size, 4096);
|
|
*uid = sceKernelAllocMemBlock("vertex_usse", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, size, NULL);
|
|
|
|
if (sceKernelGetMemBlockBase(*uid, &mem) < 0)
|
|
return NULL;
|
|
if (sceGxmMapVertexUsseMemory(mem, size, usse_offset) < 0)
|
|
return NULL;
|
|
|
|
return mem;
|
|
}
|
|
|
|
static void vertex_usse_free(SceUID uid)
|
|
{
|
|
void *mem = NULL;
|
|
if (sceKernelGetMemBlockBase(uid, &mem) < 0)
|
|
return;
|
|
sceGxmUnmapVertexUsseMemory(mem);
|
|
sceKernelFreeMemBlock(uid);
|
|
}
|
|
|
|
static void *fragment_usse_alloc(unsigned int size, SceUID *uid,
|
|
unsigned int *usse_offset)
|
|
{
|
|
void *mem = NULL;
|
|
|
|
size = ALIGN(size, 4096);
|
|
*uid = sceKernelAllocMemBlock("fragment_usse", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, size, NULL);
|
|
|
|
if (sceKernelGetMemBlockBase(*uid, &mem) < 0)
|
|
return NULL;
|
|
if (sceGxmMapFragmentUsseMemory(mem, size, usse_offset) < 0)
|
|
return NULL;
|
|
|
|
return mem;
|
|
}
|
|
|
|
static void fragment_usse_free(SceUID uid)
|
|
{
|
|
void *mem = NULL;
|
|
if (sceKernelGetMemBlockBase(uid, &mem) < 0)
|
|
return;
|
|
sceGxmUnmapFragmentUsseMemory(mem);
|
|
sceKernelFreeMemBlock(uid);
|
|
}
|
|
|
|
static void matrix_init_orthographic(float *m, float left, float right,
|
|
float bottom, float top, float near, float far)
|
|
{
|
|
m[0x0] = 2.0f/(right-left);
|
|
m[0x4] = 0.0f;
|
|
m[0x8] = 0.0f;
|
|
m[0xC] = -(right+left)/(right-left);
|
|
|
|
m[0x1] = 0.0f;
|
|
m[0x5] = 2.0f/(top-bottom);
|
|
m[0x9] = 0.0f;
|
|
m[0xD] = -(top+bottom)/(top-bottom);
|
|
|
|
m[0x2] = 0.0f;
|
|
m[0x6] = 0.0f;
|
|
m[0xA] = -2.0f/(far-near);
|
|
m[0xE] = (far+near)/(far-near);
|
|
|
|
m[0x3] = 0.0f;
|
|
m[0x7] = 0.0f;
|
|
m[0xB] = 0.0f;
|
|
m[0xF] = 1.0f;
|
|
}
|
|
|
|
|
|
/* Static functions */
|
|
|
|
static void *patcher_host_alloc(void *user_data, unsigned int size)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
static void patcher_host_free(void *user_data, void *mem)
|
|
{
|
|
free(mem);
|
|
}
|
|
|
|
static int gxm_switch_video_mode(gxm_video_mode_t video_mode)
|
|
{
|
|
SceGxmRenderTargetParams renderTargetParams;
|
|
int i, x, y;
|
|
|
|
if(video_mode > video_mode_initial)
|
|
return -1;
|
|
switch (video_mode)
|
|
{
|
|
case GXM_VIDEO_MODE_960x544:
|
|
video_mode_data.dims = VIDEO_SCALE_PACK(960, 544);
|
|
video_mode_data.stride = 960;
|
|
break;
|
|
|
|
case GXM_VIDEO_MODE_1280x720:
|
|
video_mode_data.dims = VIDEO_SCALE_PACK(1280, 720);
|
|
video_mode_data.stride = 1280;
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
clip_rect_x_max = VIDEO_SCALE_W(video_mode_data.dims);
|
|
clip_rect_y_max = VIDEO_SCALE_H(video_mode_data.dims);
|
|
|
|
if (renderTarget != NULL)
|
|
{
|
|
sceGxmDestroyRenderTarget(renderTarget);
|
|
|
|
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++)
|
|
{
|
|
/* clear the buffer then deallocate */
|
|
memset(displayBufferData[i], 0,
|
|
VIDEO_SCALE_H(video_mode_data.dims)
|
|
* video_mode_data.stride * 4);
|
|
gpu_free(displayBufferUid[i]);
|
|
}
|
|
}
|
|
|
|
/* allocate memory and sync objects for display buffers */
|
|
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++)
|
|
{
|
|
/* allocate memory for display */
|
|
displayBufferData[i] = gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
|
4 * video_mode_data.stride
|
|
* VIDEO_SCALE_H(video_mode_data.dims),
|
|
SCE_GXM_COLOR_SURFACE_ALIGNMENT,
|
|
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
|
&displayBufferUid[i]);
|
|
|
|
/* memset the buffer to black */
|
|
for (y = 0; y < (int)VIDEO_SCALE_H(video_mode_data.dims); y++)
|
|
{
|
|
unsigned int *row = (unsigned int *)displayBufferData[i] + y*video_mode_data.stride;
|
|
for (x = 0; x < (int)VIDEO_SCALE_W(video_mode_data.dims); x++)
|
|
row[x] = 0xff000000;
|
|
}
|
|
|
|
/* initialize a color surface for this display buffer */
|
|
sceGxmColorSurfaceInit(
|
|
&displaySurface[i],
|
|
DISPLAY_COLOR_FORMAT,
|
|
SCE_GXM_COLOR_SURFACE_LINEAR,
|
|
(current_msaa == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE : SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
|
|
SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
|
|
VIDEO_SCALE_W(video_mode_data.dims),
|
|
VIDEO_SCALE_H(video_mode_data.dims),
|
|
video_mode_data.stride,
|
|
displayBufferData[i]);
|
|
|
|
}
|
|
|
|
/* set up parameters */
|
|
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
|
|
renderTargetParams.flags = 0;
|
|
renderTargetParams.width = VIDEO_SCALE_W(video_mode_data.dims);
|
|
renderTargetParams.height = VIDEO_SCALE_H(video_mode_data.dims);
|
|
renderTargetParams.scenesPerFrame = 1;
|
|
renderTargetParams.multisampleMode = current_msaa;
|
|
renderTargetParams.multisampleLocations = 0;
|
|
renderTargetParams.driverMemBlock = -1; /* Invalid UID */
|
|
|
|
/* create the render target */
|
|
sceGxmCreateRenderTarget(&renderTargetParams, &renderTarget);
|
|
|
|
matrix_init_orthographic(gxm_ortho_matrix, 0.0f,
|
|
VIDEO_SCALE_W(video_mode_data.dims),
|
|
VIDEO_SCALE_H(video_mode_data.dims), 0.0f, 0.0f, 1.0f);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gxm_display_callback(const void *callback_data)
|
|
{
|
|
SceDisplayFrameBuf framebuf;
|
|
const gxm_display_data_t *display_data = (const gxm_display_data_t *)callback_data;
|
|
|
|
memset(&framebuf, 0x00, sizeof(SceDisplayFrameBuf));
|
|
framebuf.size = sizeof(SceDisplayFrameBuf);
|
|
framebuf.base = display_data->address;
|
|
framebuf.pitch = video_mode_data.stride;
|
|
framebuf.pixelformat = DISPLAY_PIXEL_FORMAT;
|
|
framebuf.width = VIDEO_SCALE_W(video_mode_data.dims);
|
|
framebuf.height = VIDEO_SCALE_H(video_mode_data.dims);
|
|
if (sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME)
|
|
== SCE_DISPLAY_ERROR_INVALID_RESOLUTION)
|
|
{
|
|
if (video_mode_initial)
|
|
gxm_switch_video_mode(GXM_VIDEO_MODE_960x544);
|
|
}
|
|
|
|
if (vblank_wait)
|
|
sceDisplayWaitVblankStart();
|
|
}
|
|
|
|
static void gxm_free_fragment_programs(gxm_fragment_programs_t *out)
|
|
{
|
|
sceGxmShaderPatcherReleaseFragmentProgram(shaderPatcher, out->color);
|
|
sceGxmShaderPatcherReleaseFragmentProgram(shaderPatcher, out->texture);
|
|
sceGxmShaderPatcherReleaseFragmentProgram(shaderPatcher, out->textureTint);
|
|
}
|
|
|
|
static void gxm_make_fragment_programs(gxm_fragment_programs_t *out,
|
|
const SceGxmBlendInfo *blend_info, SceGxmMultisampleMode msaa)
|
|
{
|
|
int err = sceGxmShaderPatcherCreateFragmentProgram(
|
|
shaderPatcher,
|
|
colorFragmentProgramId,
|
|
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
|
msaa,
|
|
blend_info,
|
|
colorVertexProgramGxp,
|
|
&out->color);
|
|
|
|
err = sceGxmShaderPatcherCreateFragmentProgram(
|
|
shaderPatcher,
|
|
textureFragmentProgramId,
|
|
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
|
msaa,
|
|
blend_info,
|
|
textureVertexProgramGxp,
|
|
&out->texture);
|
|
|
|
err = sceGxmShaderPatcherCreateFragmentProgram(
|
|
shaderPatcher,
|
|
textureTintFragmentProgramId,
|
|
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
|
msaa,
|
|
blend_info,
|
|
textureTintVertexProgramGxp,
|
|
&out->textureTint);
|
|
/* The results have never been acted on here */
|
|
(void)err;
|
|
}
|
|
|
|
static void gxm_set_blend_mode_add(int enable)
|
|
{
|
|
gxm_fragment_programs_t *in = enable ? &gxm_fragmentPrograms.blend_mode_add
|
|
: &gxm_fragmentPrograms.blend_mode_normal;
|
|
|
|
gxm_colorFragmentProgram = in->color;
|
|
gxm_textureFragmentProgram = in->texture;
|
|
gxm_textureTintFragmentProgram = in->textureTint;
|
|
}
|
|
|
|
static int gxm_init_internal(unsigned int temp_pool_size,
|
|
SceGxmMultisampleMode msaa, gxm_video_mode_t video_mode)
|
|
{
|
|
uint32_t idx;
|
|
SceGxmInitializeParams initializeParams;
|
|
void *fragmentUsseRingBuffer;
|
|
unsigned int fragmentUsseRingBufferOffset;
|
|
void *fragmentRingBuffer;
|
|
void *vertexRingBuffer;
|
|
void *vdmRingBuffer;
|
|
gxm_display_data_t displayData;
|
|
unsigned int depthStrideInSamples;
|
|
unsigned int sampleCount;
|
|
unsigned int alignedHeight;
|
|
unsigned int alignedWidth;
|
|
SceGxmShaderPatcherParams patcherParams;
|
|
void *patcherFragmentUsse;
|
|
unsigned int patcherFragmentUsseOffset;
|
|
void *patcherVertexUsse;
|
|
unsigned int patcherVertexUsseOffset;
|
|
void *patcherBuffer;
|
|
unsigned int patcherFragmentUsseSize;
|
|
unsigned int patcherVertexUsseSize;
|
|
unsigned int patcherBufferSize;
|
|
SceGxmVertexStream colorVertexStreams[1];
|
|
SceGxmVertexAttribute colorVertexAttributes[2];
|
|
const SceGxmProgramParameter *paramColorColorAttribute;
|
|
const SceGxmProgramParameter *paramColorPositionAttribute;
|
|
SceGxmVertexStream textureVertexStreams[1];
|
|
SceGxmVertexAttribute textureVertexAttributes[2];
|
|
const SceGxmProgramParameter *paramTextureTexcoordAttribute;
|
|
const SceGxmProgramParameter *paramTexturePositionAttribute;
|
|
SceGxmVertexStream textureTintVertexStreams[1];
|
|
SceGxmVertexAttribute textureTintVertexAttributes[3];
|
|
const SceGxmProgramParameter *paramTextureTintColorAttribute;
|
|
const SceGxmProgramParameter *paramTextureTintTexcoordAttribute;
|
|
const SceGxmProgramParameter *paramTextureTintPositionAttribute;
|
|
/* Blend modes. Fields are in SceGxmBlendInfo declaration order:
|
|
* colorMask, colorFunc, alphaFunc, colorSrc, colorDst, alphaSrc,
|
|
* alphaDst.
|
|
*/
|
|
static const SceGxmBlendInfo blend_info = {
|
|
SCE_GXM_COLOR_MASK_ALL,
|
|
SCE_GXM_BLEND_FUNC_ADD,
|
|
SCE_GXM_BLEND_FUNC_ADD,
|
|
SCE_GXM_BLEND_FACTOR_SRC_ALPHA,
|
|
SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
|
SCE_GXM_BLEND_FACTOR_SRC_ALPHA,
|
|
SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
|
|
};
|
|
static const SceGxmBlendInfo blend_info_add = {
|
|
SCE_GXM_COLOR_MASK_ALL,
|
|
SCE_GXM_BLEND_FUNC_ADD,
|
|
SCE_GXM_BLEND_FUNC_ADD,
|
|
SCE_GXM_BLEND_FACTOR_ONE,
|
|
SCE_GXM_BLEND_FACTOR_ONE,
|
|
SCE_GXM_BLEND_FACTOR_ONE,
|
|
SCE_GXM_BLEND_FACTOR_ONE
|
|
};
|
|
int err;
|
|
unsigned int i;
|
|
|
|
if (gxm_initialized)
|
|
return 1;
|
|
|
|
video_mode_initial = video_mode;
|
|
current_msaa = msaa;
|
|
|
|
memset(&initializeParams, 0, sizeof(SceGxmInitializeParams));
|
|
initializeParams.flags = 0;
|
|
initializeParams.displayQueueMaxPendingCount = DISPLAY_MAX_PENDING_SWAPS;
|
|
initializeParams.displayQueueCallback = gxm_display_callback;
|
|
initializeParams.displayQueueCallbackDataSize = sizeof(gxm_display_data_t);
|
|
initializeParams.parameterBufferSize = SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE;
|
|
|
|
err = sceGxmInitialize(&initializeParams);
|
|
|
|
/* allocate ring buffer memory using default sizes */
|
|
vdmRingBuffer = gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
|
SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE,
|
|
4,
|
|
SCE_GXM_MEMORY_ATTRIB_READ,
|
|
&vdmRingBufferUid);
|
|
|
|
vertexRingBuffer = gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
|
SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE,
|
|
4,
|
|
SCE_GXM_MEMORY_ATTRIB_READ,
|
|
&vertexRingBufferUid);
|
|
|
|
fragmentRingBuffer = gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
|
SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE,
|
|
4,
|
|
SCE_GXM_MEMORY_ATTRIB_READ,
|
|
&fragmentRingBufferUid);
|
|
|
|
fragmentUsseRingBuffer = fragment_usse_alloc(
|
|
SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE,
|
|
&fragmentUsseRingBufferUid,
|
|
&fragmentUsseRingBufferOffset);
|
|
|
|
memset(&contextParams, 0, sizeof(SceGxmContextParams));
|
|
contextParams.hostMem = malloc(SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE);
|
|
contextParams.hostMemSize = SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE;
|
|
contextParams.vdmRingBufferMem = vdmRingBuffer;
|
|
contextParams.vdmRingBufferMemSize = SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE;
|
|
contextParams.vertexRingBufferMem = vertexRingBuffer;
|
|
contextParams.vertexRingBufferMemSize = SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE;
|
|
contextParams.fragmentRingBufferMem = fragmentRingBuffer;
|
|
contextParams.fragmentRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE;
|
|
contextParams.fragmentUsseRingBufferMem = fragmentUsseRingBuffer;
|
|
contextParams.fragmentUsseRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE;
|
|
contextParams.fragmentUsseRingBufferOffset = fragmentUsseRingBufferOffset;
|
|
|
|
err = sceGxmCreateContext(&contextParams, &gxm_context);
|
|
err = gxm_switch_video_mode(video_mode);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
displayData.address = displayBufferData[0];
|
|
gxm_display_callback(&displayData);
|
|
|
|
/* allocate memory and sync objects for display buffers */
|
|
/* create a sync object that we will associate with this buffer */
|
|
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++)
|
|
err = sceGxmSyncObjectCreate(&displayBufferSync[i]);
|
|
|
|
/* compute the memory footprint of the depth buffer */
|
|
alignedWidth = ALIGN(VIDEO_SCALE_W(video_mode_data.dims),
|
|
SCE_GXM_TILE_SIZEX);
|
|
alignedHeight = ALIGN(VIDEO_SCALE_H(video_mode_data.dims),
|
|
SCE_GXM_TILE_SIZEY);
|
|
sampleCount = alignedWidth*alignedHeight;
|
|
depthStrideInSamples = alignedWidth;
|
|
if (current_msaa == SCE_GXM_MULTISAMPLE_4X)
|
|
{
|
|
/* samples increase in X and Y */
|
|
sampleCount *= 4;
|
|
depthStrideInSamples *= 2;
|
|
}
|
|
else if (current_msaa == SCE_GXM_MULTISAMPLE_2X)
|
|
/* samples increase in Y only */
|
|
sampleCount *= 2;
|
|
|
|
/* allocate the depth buffer */
|
|
depthBufferData = gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
|
4*sampleCount,
|
|
SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT,
|
|
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
|
&depthBufferUid);
|
|
|
|
/* allocate the stencil buffer */
|
|
stencilBufferData = gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
|
4*sampleCount,
|
|
SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT,
|
|
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
|
&stencilBufferUid);
|
|
|
|
/* create the SceGxmDepthStencilSurface structure */
|
|
err = sceGxmDepthStencilSurfaceInit(
|
|
&depthSurface,
|
|
SCE_GXM_DEPTH_STENCIL_FORMAT_S8D24,
|
|
SCE_GXM_DEPTH_STENCIL_SURFACE_TILED,
|
|
depthStrideInSamples,
|
|
depthBufferData,
|
|
stencilBufferData);
|
|
|
|
/* Set the stencil test reference. This is assumed to stay at 1 from
|
|
* here on, for region clipping.
|
|
*/
|
|
sceGxmSetFrontStencilRef(gxm_context, 1);
|
|
/* Set the stencil function. Not strictly needed: the set-clip-rectangle
|
|
* path has to call this at the start of every scene anyway.
|
|
*/
|
|
sceGxmSetFrontStencilFunc(
|
|
gxm_context,
|
|
SCE_GXM_STENCIL_FUNC_ALWAYS,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
0xFF,
|
|
0xFF);
|
|
|
|
/* set buffer sizes for this sample */
|
|
patcherBufferSize = 64*1024;
|
|
patcherVertexUsseSize = 64*1024;
|
|
patcherFragmentUsseSize = 64*1024;
|
|
|
|
/* allocate memory for buffers and USSE code */
|
|
patcherBuffer = gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
|
patcherBufferSize,
|
|
4,
|
|
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
|
&patcherBufferUid);
|
|
|
|
patcherVertexUsse = vertex_usse_alloc(
|
|
patcherVertexUsseSize,
|
|
&patcherVertexUsseUid,
|
|
&patcherVertexUsseOffset);
|
|
|
|
patcherFragmentUsse = fragment_usse_alloc(
|
|
patcherFragmentUsseSize,
|
|
&patcherFragmentUsseUid,
|
|
&patcherFragmentUsseOffset);
|
|
|
|
/* create a shader patcher */
|
|
memset(&patcherParams, 0, sizeof(SceGxmShaderPatcherParams));
|
|
patcherParams.userData = NULL;
|
|
patcherParams.hostAllocCallback = &patcher_host_alloc;
|
|
patcherParams.hostFreeCallback = &patcher_host_free;
|
|
patcherParams.bufferAllocCallback = NULL;
|
|
patcherParams.bufferFreeCallback = NULL;
|
|
patcherParams.bufferMem = patcherBuffer;
|
|
patcherParams.bufferMemSize = patcherBufferSize;
|
|
patcherParams.vertexUsseAllocCallback = NULL;
|
|
patcherParams.vertexUsseFreeCallback = NULL;
|
|
patcherParams.vertexUsseMem = patcherVertexUsse;
|
|
patcherParams.vertexUsseMemSize = patcherVertexUsseSize;
|
|
patcherParams.vertexUsseOffset = patcherVertexUsseOffset;
|
|
patcherParams.fragmentUsseAllocCallback = NULL;
|
|
patcherParams.fragmentUsseFreeCallback = NULL;
|
|
patcherParams.fragmentUsseMem = patcherFragmentUsse;
|
|
patcherParams.fragmentUsseMemSize = patcherFragmentUsseSize;
|
|
patcherParams.fragmentUsseOffset = patcherFragmentUsseOffset;
|
|
|
|
err = sceGxmShaderPatcherCreate(&patcherParams, &shaderPatcher);
|
|
|
|
/* check the shaders */
|
|
err = sceGxmProgramCheck(colorVertexProgramGxp);
|
|
err = sceGxmProgramCheck(colorFragmentProgramGxp);
|
|
err = sceGxmProgramCheck(textureVertexProgramGxp);
|
|
err = sceGxmProgramCheck(textureFragmentProgramGxp);
|
|
err = sceGxmProgramCheck(textureTintVertexProgramGxp);
|
|
err = sceGxmProgramCheck(textureTintFragmentProgramGxp);
|
|
|
|
/* register programs with the patcher */
|
|
err = sceGxmShaderPatcherRegisterProgram(shaderPatcher,
|
|
colorVertexProgramGxp, &colorVertexProgramId);
|
|
err = sceGxmShaderPatcherRegisterProgram(shaderPatcher,
|
|
colorFragmentProgramGxp, &colorFragmentProgramId);
|
|
err = sceGxmShaderPatcherRegisterProgram(shaderPatcher,
|
|
textureVertexProgramGxp, &textureVertexProgramId);
|
|
err = sceGxmShaderPatcherRegisterProgram(shaderPatcher,
|
|
textureFragmentProgramGxp, &textureFragmentProgramId);
|
|
err = sceGxmShaderPatcherRegisterProgram(shaderPatcher,
|
|
textureTintVertexProgramGxp, &textureTintVertexProgramId);
|
|
err = sceGxmShaderPatcherRegisterProgram(shaderPatcher,
|
|
textureTintFragmentProgramGxp, &textureTintFragmentProgramId);
|
|
|
|
/* Allocate a 64k * 2 bytes = 128 KiB buffer and store all possible */
|
|
/* 16-bit indices in linear ascending order, so we can use this for */
|
|
/* all drawing operations where we don't want to use indexing. */
|
|
linearIndices = (uint16_t *)gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
|
UINT16_MAX*sizeof(uint16_t),
|
|
sizeof(uint16_t),
|
|
SCE_GXM_MEMORY_ATTRIB_READ,
|
|
&linearIndicesUid);
|
|
|
|
/* Range of i must be greater than uint16_t, this doesn't endless-loop */
|
|
for (idx = 0; idx <= UINT16_MAX; ++idx)
|
|
linearIndices[idx] = idx;
|
|
|
|
paramColorPositionAttribute = sceGxmProgramFindParameterByName(colorVertexProgramGxp, "aPosition");
|
|
paramColorColorAttribute = sceGxmProgramFindParameterByName(colorVertexProgramGxp, "aColor");
|
|
|
|
/* create color vertex format */
|
|
/* x,y,z: 3 float 32 bits */
|
|
colorVertexAttributes[0].streamIndex = 0;
|
|
colorVertexAttributes[0].offset = 0;
|
|
colorVertexAttributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
|
colorVertexAttributes[0].componentCount = 3; /* (x, y, z) */
|
|
colorVertexAttributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(paramColorPositionAttribute);
|
|
/* color: 4 unsigned char = 32 bits */
|
|
colorVertexAttributes[1].streamIndex = 0;
|
|
colorVertexAttributes[1].offset = 12; /* (x, y, z) * 4 = 12 bytes */
|
|
colorVertexAttributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_U8N;
|
|
colorVertexAttributes[1].componentCount = 4; /* (color) */
|
|
colorVertexAttributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(paramColorColorAttribute);
|
|
/* 16 bit (short) indices */
|
|
colorVertexStreams[0].stride = sizeof(gxm_color_vertex_t);
|
|
colorVertexStreams[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT;
|
|
|
|
/* create color shaders */
|
|
err = sceGxmShaderPatcherCreateVertexProgram(
|
|
shaderPatcher,
|
|
colorVertexProgramId,
|
|
colorVertexAttributes,
|
|
2,
|
|
colorVertexStreams,
|
|
1,
|
|
&gxm_colorVertexProgram);
|
|
|
|
paramTexturePositionAttribute = sceGxmProgramFindParameterByName(textureVertexProgramGxp, "aPosition");
|
|
|
|
paramTextureTexcoordAttribute = sceGxmProgramFindParameterByName(textureVertexProgramGxp, "aTexcoord");
|
|
|
|
/* create texture vertex format */
|
|
/* x,y,z: 3 float 32 bits */
|
|
textureVertexAttributes[0].streamIndex = 0;
|
|
textureVertexAttributes[0].offset = 0;
|
|
textureVertexAttributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
|
textureVertexAttributes[0].componentCount = 3; /* (x, y, z) */
|
|
textureVertexAttributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(paramTexturePositionAttribute);
|
|
/* u,v: 2 floats 32 bits */
|
|
textureVertexAttributes[1].streamIndex = 0;
|
|
textureVertexAttributes[1].offset = 12; /* (x, y, z) * 4 = 12 bytes */
|
|
textureVertexAttributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
|
textureVertexAttributes[1].componentCount = 2; /* (u, v) */
|
|
textureVertexAttributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(paramTextureTexcoordAttribute);
|
|
/* 16 bit (short) indices */
|
|
textureVertexStreams[0].stride = sizeof(gxm_texture_vertex_t);
|
|
textureVertexStreams[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT;
|
|
|
|
/* create texture shaders */
|
|
err = sceGxmShaderPatcherCreateVertexProgram(
|
|
shaderPatcher,
|
|
textureVertexProgramId,
|
|
textureVertexAttributes,
|
|
2,
|
|
textureVertexStreams,
|
|
1,
|
|
&gxm_textureVertexProgram);
|
|
|
|
paramTextureTintPositionAttribute = sceGxmProgramFindParameterByName(textureTintVertexProgramGxp, "aPosition");
|
|
paramTextureTintTexcoordAttribute = sceGxmProgramFindParameterByName(textureTintVertexProgramGxp, "aTexcoord");
|
|
paramTextureTintColorAttribute = sceGxmProgramFindParameterByName(textureTintVertexProgramGxp, "aColor");
|
|
|
|
/* create texture vertex format */
|
|
/* x,y,z: 3 float 32 bits */
|
|
textureTintVertexAttributes[0].streamIndex = 0;
|
|
textureTintVertexAttributes[0].offset = 0;
|
|
textureTintVertexAttributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
|
textureTintVertexAttributes[0].componentCount = 3; /* (x, y, z) */
|
|
textureTintVertexAttributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(paramTextureTintPositionAttribute);
|
|
/* u,v: 2 floats 32 bits */
|
|
textureTintVertexAttributes[1].streamIndex = 0;
|
|
textureTintVertexAttributes[1].offset = 12; /* (x, y, z) * 4 = 12 bytes */
|
|
textureTintVertexAttributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
|
textureTintVertexAttributes[1].componentCount = 2; /* (u, v) */
|
|
textureTintVertexAttributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(paramTextureTintTexcoordAttribute);
|
|
/* r,g,b,a: 4 floats 32 bits */
|
|
textureTintVertexAttributes[2].streamIndex = 0;
|
|
textureTintVertexAttributes[2].offset = 20; /* (u, v) * 4 = 8 bytes */
|
|
textureTintVertexAttributes[2].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
|
|
textureTintVertexAttributes[2].componentCount = 4; /* (r, g, b, a) */
|
|
textureTintVertexAttributes[2].regIndex = sceGxmProgramParameterGetResourceIndex(paramTextureTintColorAttribute);
|
|
/* 16 bit (short) indices */
|
|
textureTintVertexStreams[0].stride = sizeof(gxm_texture_tint_vertex_t);
|
|
textureTintVertexStreams[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT;
|
|
|
|
/* create texture shaders */
|
|
err = sceGxmShaderPatcherCreateVertexProgram(
|
|
shaderPatcher,
|
|
textureTintVertexProgramId,
|
|
textureTintVertexAttributes,
|
|
3,
|
|
textureTintVertexStreams,
|
|
1,
|
|
&gxm_textureTintVertexProgram);
|
|
|
|
/* Create variations of the fragment program based on blending mode */
|
|
gxm_make_fragment_programs(&gxm_fragmentPrograms.blend_mode_normal,
|
|
&blend_info, current_msaa);
|
|
gxm_make_fragment_programs(&gxm_fragmentPrograms.blend_mode_add,
|
|
&blend_info_add, current_msaa);
|
|
|
|
/* Default to "normal" blending mode (non-additive) */
|
|
gxm_set_blend_mode_add(0);
|
|
|
|
/* find vertex uniforms by name and cache parameter information */
|
|
|
|
gxm_colorWvpParam = sceGxmProgramFindParameterByName(colorVertexProgramGxp, "wvp");
|
|
|
|
gxm_textureWvpParam = sceGxmProgramFindParameterByName(textureVertexProgramGxp, "wvp");
|
|
|
|
gxm_textureTintWvpParam = sceGxmProgramFindParameterByName(textureTintVertexProgramGxp, "wvp");
|
|
|
|
/* Allocate memory for the memory pool */
|
|
pool_size = temp_pool_size;
|
|
pool_addr = gpu_alloc(
|
|
SCE_KERNEL_MEMBLOCK_TYPE_USER_RW,
|
|
pool_size,
|
|
sizeof(void *),
|
|
SCE_GXM_MEMORY_ATTRIB_READ,
|
|
&poolUid);
|
|
|
|
|
|
matrix_init_orthographic(gxm_ortho_matrix, 0.0f,
|
|
VIDEO_SCALE_W(video_mode_data.dims),
|
|
VIDEO_SCALE_H(video_mode_data.dims), 0.0f, 0.0f, 1.0f);
|
|
|
|
backBufferIndex = 0;
|
|
frontBufferIndex = 0;
|
|
|
|
gxm_initialized = 1;
|
|
return 1;
|
|
}
|
|
|
|
static void gxm_set_viewport(int x, int y, int width, int height)
|
|
{
|
|
float vh = VIDEO_SCALE_H(video_mode_data.dims);
|
|
float sw = width / 2.;
|
|
float sh = height / 2.;
|
|
float x_scale = sw;
|
|
float x_port = x + sw;
|
|
float y_scale = -(sh);
|
|
float y_port = vh - y - sh;
|
|
sceGxmSetViewport(gxm_context, x_port, x_scale, y_port, y_scale,
|
|
-0.5f, 0.5f);
|
|
}
|
|
|
|
static void *gxm_pool_memalign(unsigned int size, unsigned int alignment)
|
|
{
|
|
unsigned int new_index = (pool_index + alignment - 1) & ~(alignment - 1);
|
|
if ((new_index + size) < pool_size)
|
|
{
|
|
void *addr = (void *)((unsigned int)pool_addr + new_index);
|
|
pool_index = new_index + size;
|
|
return addr;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void gxm_draw_rectangle(float x, float y, float w, float h,
|
|
unsigned int color)
|
|
{
|
|
void *vertexDefaultBuffer;
|
|
gxm_color_vertex_t *vertices = (gxm_color_vertex_t *)
|
|
gxm_pool_memalign(
|
|
4 * sizeof(gxm_color_vertex_t), /* 4 vertices */
|
|
sizeof(gxm_color_vertex_t));
|
|
|
|
uint16_t *indices = (uint16_t *)gxm_pool_memalign(
|
|
4 * sizeof(uint16_t), /* 4 indices */
|
|
sizeof(uint16_t));
|
|
|
|
vertices[0].x = x;
|
|
vertices[0].y = y;
|
|
vertices[0].z = +0.5f;
|
|
vertices[0].color = color;
|
|
|
|
vertices[1].x = x + w;
|
|
vertices[1].y = y;
|
|
vertices[1].z = +0.5f;
|
|
vertices[1].color = color;
|
|
|
|
vertices[2].x = x;
|
|
vertices[2].y = y + h;
|
|
vertices[2].z = +0.5f;
|
|
vertices[2].color = color;
|
|
|
|
vertices[3].x = x + w;
|
|
vertices[3].y = y + h;
|
|
vertices[3].z = +0.5f;
|
|
vertices[3].color = color;
|
|
|
|
indices[0] = 0;
|
|
indices[1] = 1;
|
|
indices[2] = 2;
|
|
indices[3] = 3;
|
|
|
|
sceGxmSetVertexProgram(gxm_context, gxm_colorVertexProgram);
|
|
sceGxmSetFragmentProgram(gxm_context, gxm_colorFragmentProgram);
|
|
|
|
sceGxmReserveVertexDefaultUniformBuffer(gxm_context,
|
|
&vertexDefaultBuffer);
|
|
sceGxmSetUniformDataF(vertexDefaultBuffer, gxm_colorWvpParam, 0, 16,
|
|
gxm_ortho_matrix);
|
|
|
|
sceGxmSetVertexStream(gxm_context, 0, vertices);
|
|
sceGxmDraw(gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP,
|
|
SCE_GXM_INDEX_FORMAT_U16, indices, 4);
|
|
}
|
|
|
|
static void gxm_set_clip_rectangle(int x_min, int y_min, int x_max, int y_max)
|
|
{
|
|
gxm_set_viewport(0, 0, VIDEO_SCALE_W(video_mode_data.dims),
|
|
VIDEO_SCALE_H(video_mode_data.dims));
|
|
clipping_enabled = 1;
|
|
clip_rect_x_min = x_min;
|
|
clip_rect_y_min = y_min;
|
|
clip_rect_x_max = x_max;
|
|
clip_rect_y_max = y_max;
|
|
/* We can only draw during a scene, but we can cache the values
|
|
* since they're not going to have any visible effect till the
|
|
* scene starts anyways */
|
|
if (drawing)
|
|
{
|
|
sceGxmSetFrontDepthWriteEnable(gxm_context,
|
|
SCE_GXM_DEPTH_WRITE_DISABLED);
|
|
/* clear the stencil buffer to 0 */
|
|
sceGxmSetFrontStencilFunc(
|
|
gxm_context,
|
|
SCE_GXM_STENCIL_FUNC_NEVER,
|
|
SCE_GXM_STENCIL_OP_ZERO,
|
|
SCE_GXM_STENCIL_OP_ZERO,
|
|
SCE_GXM_STENCIL_OP_ZERO,
|
|
0xFF,
|
|
0xFF);
|
|
gxm_draw_rectangle(0, 0, VIDEO_SCALE_W(video_mode_data.dims),
|
|
VIDEO_SCALE_H(video_mode_data.dims), 0);
|
|
/* set the stencil to 1 in the desired region */
|
|
sceGxmSetFrontStencilFunc(
|
|
gxm_context,
|
|
SCE_GXM_STENCIL_FUNC_NEVER,
|
|
SCE_GXM_STENCIL_OP_REPLACE,
|
|
SCE_GXM_STENCIL_OP_REPLACE,
|
|
SCE_GXM_STENCIL_OP_REPLACE,
|
|
0xFF,
|
|
0xFF);
|
|
gxm_draw_rectangle(x_min, y_min, x_max - x_min, y_max - y_min, 0);
|
|
sceGxmSetFrontDepthWriteEnable(gxm_context,
|
|
SCE_GXM_DEPTH_WRITE_ENABLED);
|
|
if(clipping_enabled)
|
|
/* set the stencil function to only accept pixels
|
|
* where the stencil is 1 */
|
|
sceGxmSetFrontStencilFunc(
|
|
gxm_context,
|
|
SCE_GXM_STENCIL_FUNC_EQUAL,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
0xFF,
|
|
0xFF);
|
|
else
|
|
sceGxmSetFrontStencilFunc(
|
|
gxm_context,
|
|
SCE_GXM_STENCIL_FUNC_ALWAYS,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
0xFF,
|
|
0xFF);
|
|
}
|
|
}
|
|
|
|
|
|
static int tex_format_to_bytespp(SceGxmTextureFormat format)
|
|
{
|
|
switch (format & 0x9f000000U)
|
|
{
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U8:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_S8:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_P8:
|
|
return 1;
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U4U4U4U4:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U8U3U3U2:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U1U5U5U5:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U5U6U5:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_S5S5U6:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U8U8:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_S8S8:
|
|
return 2;
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U8U8U8:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_S8S8S8:
|
|
return 3;
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U8U8U8U8:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_S8S8S8S8:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_F32:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U32:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_S32:
|
|
default:
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
static void gxm_free_texture(gxm_texture_t *texture)
|
|
{
|
|
if (texture)
|
|
{
|
|
if (texture->gxm_rtgt)
|
|
sceGxmDestroyRenderTarget(texture->gxm_rtgt);
|
|
if (texture->depth_UID)
|
|
gpu_free(texture->depth_UID);
|
|
if (texture->palette_UID)
|
|
gpu_free(texture->palette_UID);
|
|
gpu_free(texture->data_UID);
|
|
free(texture);
|
|
}
|
|
}
|
|
|
|
static gxm_texture_t *gxm_create_empty_texture_format(unsigned int w,
|
|
unsigned int h, SceGxmTextureFormat format)
|
|
{
|
|
gxm_texture_t *texture;
|
|
void *texture_data;
|
|
int tex_size;
|
|
if (w > GXM_TEX_MAX_SIZE || h > GXM_TEX_MAX_SIZE)
|
|
return NULL;
|
|
|
|
texture = malloc(sizeof(*texture));
|
|
if (!texture)
|
|
return NULL;
|
|
|
|
memset(texture, 0, sizeof(gxm_texture_t));
|
|
|
|
/* Rows are gxm_texture_get_stride() apart: the width to 8 */
|
|
tex_size = ((w + 7) & ~7) * h * tex_format_to_bytespp(format);
|
|
|
|
/* Allocate a GPU buffer for the texture */
|
|
texture_data = gpu_alloc(
|
|
MemBlockType,
|
|
tex_size,
|
|
SCE_GXM_TEXTURE_ALIGNMENT,
|
|
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
|
&texture->data_UID);
|
|
|
|
if (!texture_data)
|
|
{
|
|
free(texture);
|
|
return NULL;
|
|
}
|
|
|
|
/* Clear the texture */
|
|
memset(texture_data, 0, tex_size);
|
|
|
|
/* Create the gxm texture */
|
|
sceGxmTextureInitLinear(
|
|
&texture->gxm_tex,
|
|
texture_data,
|
|
format,
|
|
w,
|
|
h,
|
|
0);
|
|
|
|
if ((format & 0x9f000000U) == SCE_GXM_TEXTURE_BASE_FORMAT_P8)
|
|
{
|
|
const int pal_size = 256 * sizeof(uint32_t);
|
|
|
|
void *texture_palette = gpu_alloc(
|
|
MemBlockType,
|
|
pal_size,
|
|
SCE_GXM_PALETTE_ALIGNMENT,
|
|
SCE_GXM_MEMORY_ATTRIB_READ,
|
|
&texture->palette_UID);
|
|
|
|
if (!texture_palette)
|
|
{
|
|
texture->palette_UID = 0;
|
|
gxm_free_texture(texture);
|
|
return NULL;
|
|
}
|
|
|
|
memset(texture_palette, 0, pal_size);
|
|
|
|
sceGxmTextureSetPalette(&texture->gxm_tex, texture_palette);
|
|
}
|
|
else
|
|
texture->palette_UID = 0;
|
|
|
|
return texture;
|
|
}
|
|
|
|
static unsigned int gxm_texture_get_stride(const gxm_texture_t *texture)
|
|
{
|
|
return ((sceGxmTextureGetWidth(&texture->gxm_tex) + 7) & ~7)
|
|
* tex_format_to_bytespp(sceGxmTextureGetFormat(&texture->gxm_tex));
|
|
}
|
|
|
|
/* @height rows of @row_len bytes from @src, @pitch bytes apart, into a
|
|
* texture's rows @stride bytes apart; one copy when the two line up.
|
|
* @row_len is no more than either. */
|
|
static void gxm_copy_rows(void *dst, unsigned stride, const void *src,
|
|
unsigned pitch, unsigned row_len, unsigned height)
|
|
{
|
|
uint8_t *out = (uint8_t*)dst;
|
|
const uint8_t *in = (const uint8_t*)src;
|
|
|
|
if (!height)
|
|
return;
|
|
if (stride == pitch)
|
|
{
|
|
memcpy(out, in, (size_t)pitch * (height - 1) + row_len);
|
|
return;
|
|
}
|
|
for (; height; height--, out += stride, in += pitch)
|
|
memcpy(out, in, row_len);
|
|
}
|
|
|
|
static void gxm_texture_set_filters(gxm_texture_t *texture,
|
|
SceGxmTextureFilter min_filter, SceGxmTextureFilter mag_filter)
|
|
{
|
|
sceGxmTextureSetMinFilter(&texture->gxm_tex, min_filter);
|
|
sceGxmTextureSetMagFilter(&texture->gxm_tex, mag_filter);
|
|
}
|
|
|
|
static inline void draw_texture_scale_generic(const gxm_texture_t *texture,
|
|
float x, float y, float x_scale, float y_scale)
|
|
{
|
|
gxm_texture_vertex_t *vertices = (gxm_texture_vertex_t *)
|
|
gxm_pool_memalign(
|
|
4 * sizeof(gxm_texture_vertex_t), /* 4 vertices */
|
|
sizeof(gxm_texture_vertex_t));
|
|
|
|
const float w = x_scale * sceGxmTextureGetWidth(&texture->gxm_tex);
|
|
const float h = y_scale * sceGxmTextureGetHeight(&texture->gxm_tex);
|
|
|
|
vertices[0].x = x;
|
|
vertices[0].y = y;
|
|
vertices[0].z = +0.5f;
|
|
vertices[0].u = 0.0f;
|
|
vertices[0].v = 0.0f;
|
|
|
|
vertices[1].x = x + w;
|
|
vertices[1].y = y;
|
|
vertices[1].z = +0.5f;
|
|
vertices[1].u = 1.0f;
|
|
vertices[1].v = 0.0f;
|
|
|
|
vertices[2].x = x;
|
|
vertices[2].y = y + h;
|
|
vertices[2].z = +0.5f;
|
|
vertices[2].u = 0.0f;
|
|
vertices[2].v = 1.0f;
|
|
|
|
vertices[3].x = x + w;
|
|
vertices[3].y = y + h;
|
|
vertices[3].z = +0.5f;
|
|
vertices[3].u = 1.0f;
|
|
vertices[3].v = 1.0f;
|
|
|
|
/* Set the texture to the TEXUNIT0 */
|
|
sceGxmSetFragmentTexture(gxm_context, 0, &texture->gxm_tex);
|
|
|
|
sceGxmSetVertexStream(gxm_context, 0, vertices);
|
|
sceGxmDraw(gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP,
|
|
SCE_GXM_INDEX_FORMAT_U16, linearIndices, 4);
|
|
}
|
|
|
|
static void gxm_draw_texture_scale(const gxm_texture_t *texture,
|
|
float x, float y, float x_scale, float y_scale)
|
|
{
|
|
void *vertex_wvp_buffer;
|
|
sceGxmSetVertexProgram(gxm_context, gxm_textureVertexProgram);
|
|
sceGxmSetFragmentProgram(gxm_context, gxm_textureFragmentProgram);
|
|
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vertex_wvp_buffer);
|
|
sceGxmSetUniformDataF(vertex_wvp_buffer, gxm_textureWvpParam, 0, 16,
|
|
gxm_ortho_matrix);
|
|
draw_texture_scale_generic(texture, x, y, x_scale, y_scale);
|
|
}
|
|
|
|
static inline void draw_texture_tint_part_scale_generic(
|
|
const gxm_texture_t *texture, float x, float y,
|
|
float tex_x, float tex_y, float tex_w, float tex_h,
|
|
float x_scale, float y_scale, unsigned int color)
|
|
{
|
|
int n;
|
|
gxm_texture_tint_vertex_t *vertices = (gxm_texture_tint_vertex_t *)
|
|
gxm_pool_memalign(
|
|
4 * sizeof(gxm_texture_tint_vertex_t), /* 4 vertices */
|
|
sizeof(gxm_texture_tint_vertex_t));
|
|
|
|
const float w = sceGxmTextureGetWidth(&texture->gxm_tex);
|
|
const float h = sceGxmTextureGetHeight(&texture->gxm_tex);
|
|
|
|
const float u0 = tex_x/w;
|
|
const float v0 = tex_y/h;
|
|
const float u1 = (tex_x+tex_w)/w;
|
|
const float v1 = (tex_y+tex_h)/h;
|
|
|
|
tex_w *= x_scale;
|
|
tex_h *= y_scale;
|
|
|
|
vertices[0].x = x;
|
|
vertices[0].y = y;
|
|
vertices[0].z = +0.5f;
|
|
vertices[0].u = u0;
|
|
vertices[0].v = v0;
|
|
|
|
vertices[1].x = x + tex_w;
|
|
vertices[1].y = y;
|
|
vertices[1].z = +0.5f;
|
|
vertices[1].u = u1;
|
|
vertices[1].v = v0;
|
|
|
|
vertices[2].x = x;
|
|
vertices[2].y = y + tex_h;
|
|
vertices[2].z = +0.5f;
|
|
vertices[2].u = u0;
|
|
vertices[2].v = v1;
|
|
|
|
vertices[3].x = x + tex_w;
|
|
vertices[3].y = y + tex_h;
|
|
vertices[3].z = +0.5f;
|
|
vertices[3].u = u1;
|
|
vertices[3].v = v1;
|
|
|
|
for (n = 0; n < 4; n++)
|
|
{
|
|
vertices[n].r = ((color >> 8*0) & 0xFF)/255.0f;
|
|
vertices[n].g = ((color >> 8*1) & 0xFF)/255.0f;
|
|
vertices[n].b = ((color >> 8*2) & 0xFF)/255.0f;
|
|
vertices[n].a = ((color >> 8*3) & 0xFF)/255.0f;
|
|
}
|
|
|
|
/* Set the texture to the TEXUNIT0 */
|
|
sceGxmSetFragmentTexture(gxm_context, 0, &texture->gxm_tex);
|
|
|
|
sceGxmSetVertexStream(gxm_context, 0, vertices);
|
|
sceGxmDraw(gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP,
|
|
SCE_GXM_INDEX_FORMAT_U16, linearIndices, 4);
|
|
}
|
|
|
|
static void gxm_draw_texture_tint_part_scale(
|
|
const gxm_texture_t *texture, float x, float y,
|
|
float tex_x, float tex_y, float tex_w, float tex_h,
|
|
float x_scale, float y_scale, unsigned int color)
|
|
{
|
|
void *vertex_wvp_buffer;
|
|
sceGxmSetVertexProgram(gxm_context, gxm_textureTintVertexProgram);
|
|
sceGxmSetFragmentProgram(gxm_context,
|
|
gxm_textureTintFragmentProgram);
|
|
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vertex_wvp_buffer);
|
|
sceGxmSetUniformDataF(vertex_wvp_buffer, gxm_textureWvpParam, 0, 16,
|
|
gxm_ortho_matrix);
|
|
draw_texture_tint_part_scale_generic(texture, x, y, tex_x, tex_y, tex_w,
|
|
tex_h, x_scale, y_scale, color);
|
|
}
|
|
|
|
static void gxm_draw_texture_scale_rotate(const gxm_texture_t *texture,
|
|
float x, float y, float x_scale, float y_scale, float rad)
|
|
{
|
|
float center_y_scaled;
|
|
float center_x_scaled;
|
|
float h;
|
|
float w;
|
|
int i;
|
|
float s;
|
|
float c;
|
|
const float center_x = sceGxmTextureGetWidth(&texture->gxm_tex) / 2.0f;
|
|
const float center_y = sceGxmTextureGetHeight(&texture->gxm_tex) / 2.0f;
|
|
void *vertex_wvp_buffer;
|
|
gxm_texture_vertex_t *vertices;
|
|
|
|
sceGxmSetVertexProgram(gxm_context, gxm_textureVertexProgram);
|
|
sceGxmSetFragmentProgram(gxm_context, gxm_textureFragmentProgram);
|
|
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vertex_wvp_buffer);
|
|
sceGxmSetUniformDataF(vertex_wvp_buffer, gxm_textureWvpParam, 0, 16,
|
|
gxm_ortho_matrix);
|
|
|
|
vertices = (gxm_texture_vertex_t *)
|
|
gxm_pool_memalign(
|
|
4 * sizeof(gxm_texture_vertex_t), /* 4 vertices */
|
|
sizeof(gxm_texture_vertex_t));
|
|
|
|
w = x_scale * sceGxmTextureGetWidth(&texture->gxm_tex);
|
|
h = y_scale * sceGxmTextureGetHeight(&texture->gxm_tex);
|
|
center_x_scaled = x_scale * center_x;
|
|
center_y_scaled = y_scale * center_y;
|
|
|
|
vertices[0].x = -center_x_scaled;
|
|
vertices[0].y = -center_y_scaled;
|
|
vertices[0].z = +0.5f;
|
|
vertices[0].u = 0.0f;
|
|
vertices[0].v = 0.0f;
|
|
|
|
vertices[1].x = -center_x_scaled + w;
|
|
vertices[1].y = -center_y_scaled;
|
|
vertices[1].z = +0.5f;
|
|
vertices[1].u = 1.0f;
|
|
vertices[1].v = 0.0f;
|
|
|
|
vertices[2].x = -center_x_scaled;
|
|
vertices[2].y = -center_y_scaled + h;
|
|
vertices[2].z = +0.5f;
|
|
vertices[2].u = 0.0f;
|
|
vertices[2].v = 1.0f;
|
|
|
|
vertices[3].x = -center_x_scaled + w;
|
|
vertices[3].y = -center_y_scaled + h;
|
|
vertices[3].z = +0.5f;
|
|
vertices[3].u = 1.0f;
|
|
vertices[3].v = 1.0f;
|
|
|
|
c = cosf(rad);
|
|
s = sinf(rad);
|
|
for (i = 0; i < 4; ++i)
|
|
{
|
|
/* Rotate and translate */
|
|
float _x = vertices[i].x;
|
|
float _y = vertices[i].y;
|
|
vertices[i].x = _x*c - _y*s + x + center_x_scaled;
|
|
vertices[i].y = _x*s + _y*c + y + center_y_scaled;
|
|
}
|
|
|
|
/* Set the texture to the TEXUNIT0 */
|
|
sceGxmSetFragmentTexture(gxm_context, 0, &texture->gxm_tex);
|
|
|
|
sceGxmSetVertexStream(gxm_context, 0, vertices);
|
|
sceGxmDraw(gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP,
|
|
SCE_GXM_INDEX_FORMAT_U16, linearIndices, 4);
|
|
}
|
|
|
|
static void gxm_draw_array_textured_mat(const gxm_texture_t *texture,
|
|
const gxm_texture_tint_vertex_t *vertices, size_t count, float *mat)
|
|
{
|
|
void *vertex_wvp_buffer;
|
|
sceGxmSetVertexProgram(gxm_context, gxm_textureTintVertexProgram);
|
|
sceGxmSetFragmentProgram(gxm_context,
|
|
gxm_textureTintFragmentProgram);
|
|
|
|
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vertex_wvp_buffer);
|
|
sceGxmSetUniformDataF(vertex_wvp_buffer, gxm_textureWvpParam, 0, 16,
|
|
mat);
|
|
|
|
/* Set the texture to the TEXUNIT0 */
|
|
sceGxmSetFragmentTexture(gxm_context, 0, &texture->gxm_tex);
|
|
|
|
sceGxmSetVertexStream(gxm_context, 0, vertices);
|
|
sceGxmDraw(gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP,
|
|
SCE_GXM_INDEX_FORMAT_U16, linearIndices, count);
|
|
}
|
|
|
|
/*
|
|
* DISPLAY DRIVER
|
|
*/
|
|
|
|
static const float *gfx_display_gxm_get_default_vertices(void)
|
|
{
|
|
return &gxm_vertexes[0];
|
|
}
|
|
|
|
static const float *gfx_display_gxm_get_default_tex_coords(void)
|
|
{
|
|
return &gxm_tex_coords[0];
|
|
}
|
|
|
|
static void *gfx_display_gxm_get_default_mvp(void *data)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
if (!vita)
|
|
return NULL;
|
|
return &vita->mvp_no_rot;
|
|
}
|
|
|
|
static void gfx_display_gxm_draw(gfx_display_ctx_draw_t *draw,
|
|
void *data, unsigned video_dims)
|
|
{
|
|
gxm_texture_tint_vertex_t *vertices;
|
|
unsigned i;
|
|
gxm_texture_t *texture = NULL;
|
|
const float *vertex = NULL;
|
|
const float *tex_coord = NULL;
|
|
const float *color = NULL;
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
if (!vita || !draw)
|
|
return;
|
|
|
|
texture = draw->texture
|
|
? (gxm_texture_t*)draw->texture : vita->white_texture;
|
|
vertex = draw->coords->vertex;
|
|
tex_coord = draw->coords->tex_coord;
|
|
color = draw->coords->color;
|
|
|
|
if (!vertex)
|
|
vertex = &gxm_vertexes[0];
|
|
if (!tex_coord)
|
|
tex_coord = &gxm_tex_coords[0];
|
|
if (!texture)
|
|
return;
|
|
if (!color)
|
|
color = &gxm_colors[0];
|
|
|
|
gxm_set_viewport(VIDEO_POS_X(draw->pos), VIDEO_POS_Y(draw->pos),
|
|
VIDEO_SCALE_W(draw->dims), VIDEO_SCALE_H(draw->dims));
|
|
vertices = (gxm_texture_tint_vertex_t *)
|
|
gxm_pool_memalign(
|
|
draw->coords->vertices * sizeof(gxm_texture_tint_vertex_t),
|
|
sizeof(gxm_texture_tint_vertex_t));
|
|
|
|
for (i = 0; i < draw->coords->vertices; i++)
|
|
{
|
|
vertices[i].x = *vertex++;
|
|
vertices[i].y = *vertex++;
|
|
vertices[i].z = 1.0f;
|
|
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++;
|
|
}
|
|
|
|
gxm_draw_array_textured_mat(texture, vertices, draw->coords->vertices,
|
|
vita->mvp_no_rot.data);
|
|
}
|
|
|
|
static void gfx_display_gxm_scissor_begin(void *data, unsigned video_dims,
|
|
int x, int y, unsigned dims)
|
|
{
|
|
unsigned width = VIDEO_SCALE_W(dims);
|
|
unsigned height = VIDEO_SCALE_H(dims);
|
|
gxm_set_clip_rectangle(x, y, x + width, y + height);
|
|
sceGxmSetRegionClip(gxm_context, SCE_GXM_REGION_CLIP_OUTSIDE, x, y,
|
|
x + width, y + height);
|
|
}
|
|
|
|
static void gfx_display_gxm_scissor_end(void *data, unsigned video_dims)
|
|
{
|
|
unsigned video_width = VIDEO_SCALE_W(video_dims);
|
|
unsigned video_height = VIDEO_SCALE_H(video_dims);
|
|
sceGxmSetRegionClip(gxm_context, SCE_GXM_REGION_CLIP_NONE, 0, 0,
|
|
video_width, video_height);
|
|
clipping_enabled = 0;
|
|
sceGxmSetFrontStencilFunc(
|
|
gxm_context,
|
|
SCE_GXM_STENCIL_FUNC_ALWAYS,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
SCE_GXM_STENCIL_OP_KEEP,
|
|
0xFF,
|
|
0xFF);
|
|
}
|
|
|
|
/*
|
|
* FONT DRIVER
|
|
*/
|
|
|
|
/* A texture holding all of @atlas, at its size */
|
|
static gxm_texture_t *gxm_font_make_texture(struct font_atlas *atlas)
|
|
{
|
|
unsigned j, k;
|
|
unsigned stride;
|
|
uint8_t *tex32;
|
|
const uint8_t *frame32;
|
|
unsigned pitch;
|
|
gxm_texture_t *texture = gxm_create_empty_texture_format(
|
|
atlas->width,
|
|
atlas->height,
|
|
SCE_GXM_TEXTURE_FORMAT_U8_R111);
|
|
|
|
if (!texture)
|
|
return NULL;
|
|
|
|
gxm_texture_set_filters(texture,
|
|
SCE_GXM_TEXTURE_FILTER_POINT,
|
|
SCE_GXM_TEXTURE_FILTER_LINEAR);
|
|
|
|
stride = gxm_texture_get_stride(texture);
|
|
tex32 = sceGxmTextureGetData(&texture->gxm_tex);
|
|
frame32 = atlas->buffer;
|
|
pitch = atlas->width;
|
|
|
|
for (j = 0; j < atlas->height; j++)
|
|
for (k = 0; k < atlas->width; k++)
|
|
tex32[k + j * stride] = frame32[k + j * pitch];
|
|
|
|
atlas->dirty = false;
|
|
return texture;
|
|
}
|
|
|
|
static void *gxm_font_init(void *data,
|
|
const char *font_path, float font_size,
|
|
bool is_threaded)
|
|
{
|
|
struct font_atlas *atlas = NULL;
|
|
vita_font_t *font = (vita_font_t*)calloc(1, sizeof(*font));
|
|
|
|
if (!font)
|
|
return NULL;
|
|
|
|
font->vita = (vita_video_t*)data;
|
|
|
|
if (!font_renderer_create_default(
|
|
&font->font_driver,
|
|
&font->font_data, font_path, font_size, FONT_ATLAS_FORMAT_A8))
|
|
goto error;
|
|
|
|
font->atlas = font->font_driver->get_atlas(font->font_data);
|
|
atlas = font->atlas;
|
|
|
|
if (!atlas)
|
|
goto error;
|
|
|
|
/* The atlas may grow; the texture follows it */
|
|
atlas->max_dims = VIDEO_SCALE_PACK(2048, 2048);
|
|
|
|
if (!(font->texture = gxm_font_make_texture(atlas)))
|
|
goto error;
|
|
|
|
font->atlas->dirty = false;
|
|
|
|
return font;
|
|
|
|
error:
|
|
free(font);
|
|
return NULL;
|
|
}
|
|
|
|
static void gxm_font_free(void *data, bool is_threaded)
|
|
{
|
|
vita_font_t *font = (vita_font_t*)data;
|
|
if (!font)
|
|
return;
|
|
|
|
if (font->font_driver && font->font_data)
|
|
font->font_driver->free(font->font_data);
|
|
|
|
if (gxm_initialized)
|
|
sceGxmFinish(gxm_context);
|
|
gxm_free_texture(font->texture);
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < font->retired_count; i++)
|
|
gxm_free_texture(font->retired[i]);
|
|
}
|
|
|
|
free(font);
|
|
}
|
|
|
|
static int gxm_font_get_message_width(void *data, const char *msg,
|
|
size_t msg_len, float scale)
|
|
{
|
|
vita_font_t *font = (vita_font_t*)data;
|
|
if (!font)
|
|
return 0;
|
|
return font_renderer_get_message_width(font->font_driver,
|
|
font->font_data, msg, msg_len, scale);
|
|
}
|
|
|
|
static void gxm_font_render_message(
|
|
vita_video_t *vita,
|
|
vita_font_t *font, const char *msg, size_t msg_len, float scale,
|
|
const unsigned int color, float pos_x, float pos_y,
|
|
unsigned width, unsigned height, unsigned text_align)
|
|
{
|
|
float line_height;
|
|
struct font_line_metrics *line_metrics = NULL;
|
|
int pre_x = roundf(pos_x * width);
|
|
int x = 0;
|
|
int y = 0;
|
|
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(vita->vp.dims);
|
|
|
|
#define FONT_LAYOUT_ALIGNED aligned
|
|
#define FONT_LAYOUT_LINE(line, line_width, count, bytes) \
|
|
do \
|
|
{ \
|
|
(void)(count); \
|
|
x = pre_x; \
|
|
y = roundf((1.0f - (pos_y - (float)(line) * line_height)) * height); \
|
|
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 \
|
|
{ \
|
|
if (font->atlas->dirty) \
|
|
{ \
|
|
unsigned j; \
|
|
unsigned int stride = gxm_texture_get_stride(font->texture); \
|
|
uint8_t *tex32 = sceGxmTextureGetData(&font->texture->gxm_tex); \
|
|
const uint8_t *frame32 = font->atlas->buffer; \
|
|
unsigned int pitch = font->atlas->width; \
|
|
/* Copy only the dirty rectangle tracked by the font \
|
|
* renderers, one row at a time */ \
|
|
unsigned int x0 = VIDEO_SCALE_W(font->atlas->dirty_xy0); \
|
|
unsigned int y0 = VIDEO_SCALE_H(font->atlas->dirty_xy0); \
|
|
unsigned int x1 = VIDEO_SCALE_W(font->atlas->dirty_xy1); \
|
|
unsigned int y1 = VIDEO_SCALE_H(font->atlas->dirty_xy1); \
|
|
if (x1 <= x0 || y1 <= y0 || x1 > pitch || y1 > font->atlas->height) \
|
|
{ \
|
|
x0 = 0; \
|
|
y0 = 0; \
|
|
x1 = pitch; \
|
|
y1 = font->atlas->height; \
|
|
} \
|
|
for (j = y0; j < y1; j++) \
|
|
memcpy(tex32 + x0 + j * stride, \
|
|
frame32 + x0 + j * pitch, x1 - x0); \
|
|
font->atlas->dirty = false; \
|
|
} \
|
|
gxm_draw_texture_tint_part_scale(font->texture, \
|
|
x + ((glyph)->draw_offset_x + (pen_x)) * scale, \
|
|
y + ((glyph)->draw_offset_y + (pen_y)) * scale, \
|
|
VIDEO_SCALE_W((glyph)->atlas_pos), VIDEO_SCALE_H((glyph)->atlas_pos), \
|
|
VIDEO_SCALE_W((glyph)->dims), VIDEO_SCALE_H((glyph)->dims), \
|
|
scale, \
|
|
scale, \
|
|
color); \
|
|
} while (0)
|
|
#include "../font_layout.h"
|
|
}
|
|
|
|
static void gxm_set_viewport_wrapper(void *data, unsigned dims,
|
|
bool force_full, bool allow_rotate)
|
|
{
|
|
struct video_ortho ortho = {0, 1, 0, 1, -1, 1};
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
vita->vp.full_dims = dims;
|
|
video_driver_update_viewport(&vita->vp, force_full,
|
|
vita->keep_aspect, true);
|
|
|
|
gxm_set_viewport(VIDEO_POS_X(vita->vp.pos), VIDEO_POS_Y(vita->vp.pos), VIDEO_SCALE_W(vita->vp.dims),
|
|
VIDEO_SCALE_H(vita->vp.dims));
|
|
gxm_set_projection(vita, &ortho, allow_rotate);
|
|
}
|
|
|
|
static void gxm_font_render_msg(
|
|
void *userdata,
|
|
void *data,
|
|
const char *msg, size_t msg_len,
|
|
const struct font_params *params)
|
|
{
|
|
font_params_resolved_t rp;
|
|
int drop_x, drop_y;
|
|
unsigned color, r, g, b, alpha;
|
|
enum text_alignment text_align;
|
|
float x, y, scale, drop_mod, drop_alpha;
|
|
bool full_screen = false;
|
|
vita_video_t *vita = (vita_video_t *)userdata;
|
|
vita_font_t *font = (vita_font_t *)data;
|
|
unsigned width = VIDEO_SCALE_W(vita->video_dims);
|
|
unsigned height = VIDEO_SCALE_H(vita->video_dims);
|
|
|
|
if (!font || !msg || !*msg)
|
|
return;
|
|
|
|
/* Asked for before anything is laid out: when it has grown, a
|
|
* texture at its size takes the old one's place */
|
|
if (font->font_driver && font->font_data)
|
|
{
|
|
font->atlas = font->font_driver->get_atlas(font->font_data);
|
|
if ( font->retired_count < 2
|
|
&& ( sceGxmTextureGetWidth(&font->texture->gxm_tex) != font->atlas->width
|
|
|| sceGxmTextureGetHeight(&font->texture->gxm_tex) != font->atlas->height))
|
|
{
|
|
gxm_texture_t *texture = gxm_font_make_texture(font->atlas);
|
|
if (texture)
|
|
{
|
|
font->retired[font->retired_count++] = font->texture;
|
|
font->texture = texture;
|
|
}
|
|
}
|
|
}
|
|
|
|
font_driver_resolve_params(params, &rp);
|
|
x = rp.x;
|
|
y = rp.y;
|
|
scale = rp.scale;
|
|
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;
|
|
r = rp.rgba[0];
|
|
g = rp.rgba[1];
|
|
b = rp.rgba[2];
|
|
alpha = rp.rgba[3];
|
|
full_screen = rp.full_screen;
|
|
color = RGBA8(r,g,b,alpha);
|
|
|
|
|
|
gxm_set_viewport_wrapper(vita, VIDEO_SCALE_PACK(width, height), full_screen, false);
|
|
|
|
if (drop_x || drop_y)
|
|
{
|
|
unsigned r_dark = r * drop_mod;
|
|
unsigned g_dark = g * drop_mod;
|
|
unsigned b_dark = b * drop_mod;
|
|
unsigned alpha_dark = alpha * drop_alpha;
|
|
unsigned color_dark = RGBA8(r_dark,g_dark,b_dark,alpha_dark);
|
|
|
|
gxm_font_render_message(vita, font, msg, msg_len, scale, color_dark,
|
|
x + scale * drop_x / width, y +
|
|
scale * drop_y / height, width, height, text_align);
|
|
}
|
|
|
|
gxm_font_render_message(vita, font, msg, msg_len, scale,
|
|
color, x, y, width, height, text_align);
|
|
}
|
|
|
|
static const struct font_glyph *gxm_font_get_glyph(
|
|
void *data, uint32_t code)
|
|
{
|
|
vita_font_t *font = (vita_font_t*)data;
|
|
if (font && font->font_driver)
|
|
return font->font_driver->get_glyph((void*)font->font_data, code);
|
|
return NULL;
|
|
}
|
|
|
|
static bool gxm_font_get_line_metrics(void* data,
|
|
struct font_line_metrics **metrics)
|
|
{
|
|
vita_font_t *font = (vita_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 void *gxm_gfx_init(const video_info_t *video)
|
|
{
|
|
unsigned int color;
|
|
unsigned temp_width = PSP_FB_WIDTH;
|
|
unsigned temp_height = PSP_FB_HEIGHT;
|
|
vita_video_t *vita = (vita_video_t *)
|
|
calloc(1, sizeof(vita_video_t));
|
|
|
|
if (!vita)
|
|
return NULL;
|
|
|
|
|
|
gxm_init_internal((1 * 1024 * 1024), SCE_GXM_MULTISAMPLE_4X,
|
|
(sceKernelGetModelForCDialog() == SCE_KERNEL_MODEL_VITATV)
|
|
? GXM_VIDEO_MODE_1280x720 : GXM_VIDEO_MODE_960x544 );
|
|
/* TODO/FIXME - is this color just black? */
|
|
color = RGBA8(0x00, 0x00, 0x00, 0xFF);
|
|
clear_color_u = color;
|
|
vblank_wait = video->vsync;
|
|
|
|
if (video->rgb32)
|
|
vita->format = SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1RGB;
|
|
else
|
|
vita->format = SCE_GXM_TEXTURE_FORMAT_R5G6B5;
|
|
|
|
temp_width = VIDEO_SCALE_W(video_mode_data.dims);
|
|
temp_height = VIDEO_SCALE_H(video_mode_data.dims);
|
|
|
|
vita->fullscreen = video->fullscreen;
|
|
|
|
vita->texture = NULL;
|
|
vita->menu.texture = NULL;
|
|
vita->menu.active = 0;
|
|
vita->menu.dims = 0;
|
|
{
|
|
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;
|
|
vita->white_texture = (gxm_texture_t*)gxm_load_texture(vita, &image,
|
|
false, TEXTURE_FILTER_NEAREST);
|
|
}
|
|
|
|
vita->vsync = video->vsync;
|
|
vita->rgb32 = video->rgb32;
|
|
|
|
vita->tex_filter = video->smooth
|
|
? SCE_GXM_TEXTURE_FILTER_LINEAR : SCE_GXM_TEXTURE_FILTER_POINT;
|
|
|
|
vita->video_dims = VIDEO_SCALE_PACK(temp_width, temp_height);
|
|
|
|
video_driver_set_output_dims(VIDEO_SCALE_PACK(temp_width, temp_height));
|
|
gxm_set_viewport_wrapper(vita, VIDEO_SCALE_PACK(temp_width, temp_height), false, true);
|
|
|
|
/* no input driver of this driver's own: the frontend starts the
|
|
* platform's */
|
|
input_driver_video_window(INPUT_WINDOW_PLATFORM, NULL);
|
|
|
|
vita->keep_aspect = true;
|
|
vita->should_resize = true;
|
|
#ifdef HAVE_OVERLAY
|
|
vita->overlay_enable = false;
|
|
#endif
|
|
|
|
return vita;
|
|
}
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
static void gxm_free_overlay(vita_video_t *vita)
|
|
{
|
|
unsigned i;
|
|
|
|
if (!vita->overlay_borrowed)
|
|
{
|
|
if (gxm_initialized)
|
|
sceGxmFinish(gxm_context);
|
|
|
|
for (i = 0; i < vita->overlays; i++)
|
|
gxm_free_texture(vita->overlay[i].tex);
|
|
}
|
|
free(vita->overlay);
|
|
vita->overlay = NULL;
|
|
vita->overlays = 0;
|
|
vita->overlay_borrowed = false;
|
|
}
|
|
#endif
|
|
|
|
static void gxm_update_viewport(vita_video_t* vita)
|
|
{
|
|
unsigned temp_width = VIDEO_SCALE_W(video_mode_data.dims);
|
|
unsigned temp_height = VIDEO_SCALE_H(video_mode_data.dims);
|
|
bool is_rotated = (vita->rotation == ORIENTATION_VERTICAL)
|
|
|| (vita->rotation == ORIENTATION_FLIPPED_ROTATED);
|
|
|
|
/* For rotated displays, swap dimensions before viewport calculation */
|
|
if (is_rotated && vita->keep_aspect)
|
|
{
|
|
vita->vp.full_dims = VIDEO_SCALE_PACK(temp_height, temp_width);
|
|
}
|
|
else
|
|
{
|
|
vita->vp.full_dims = VIDEO_SCALE_PACK(temp_width, temp_height);
|
|
}
|
|
|
|
video_driver_update_viewport(&vita->vp, false, vita->keep_aspect, true);
|
|
|
|
/* For rotated displays, swap x and y */
|
|
if (is_rotated && vita->keep_aspect)
|
|
{
|
|
unsigned tmp = VIDEO_POS_X(vita->vp.pos);
|
|
vita->vp.pos = VIDEO_POS_PACK(VIDEO_POS_Y(vita->vp.pos), tmp);
|
|
}
|
|
|
|
/* Ensure even dimensions */
|
|
{
|
|
unsigned vp_w = VIDEO_SCALE_W(vita->vp.dims);
|
|
unsigned vp_h = VIDEO_SCALE_H(vita->vp.dims);
|
|
vita->vp.dims = VIDEO_SCALE_PACK(vp_w + (vp_w & 0x1),
|
|
vp_h + (vp_h & 0x1));
|
|
}
|
|
|
|
vita->should_resize = false;
|
|
}
|
|
|
|
/* A system common dialog (the keyboard psp_input.c opens) is drawn by
|
|
* the OS into the back buffer this frame is about to present, but only
|
|
* when asked, once per frame, after our scene has ended. The sync
|
|
* object orders its rendering after ours. */
|
|
static void gxm_common_dialog_update(void)
|
|
{
|
|
SceCommonDialogUpdateParam param;
|
|
|
|
memset(¶m, 0, sizeof(param));
|
|
param.renderTarget.colorFormat = DISPLAY_COLOR_FORMAT;
|
|
param.renderTarget.surfaceType = SCE_GXM_COLOR_SURFACE_LINEAR;
|
|
param.renderTarget.width = VIDEO_SCALE_W(video_mode_data.dims);
|
|
param.renderTarget.height = VIDEO_SCALE_H(video_mode_data.dims);
|
|
param.renderTarget.strideInPixels = video_mode_data.stride;
|
|
param.renderTarget.colorSurfaceData = displayBufferData[backBufferIndex];
|
|
param.renderTarget.depthSurfaceData = depthBufferData;
|
|
param.displaySyncObject = displayBufferSync[backBufferIndex];
|
|
|
|
sceCommonDialogUpdate(¶m);
|
|
}
|
|
|
|
/* The core's frame into the texture it is drawn from, made anew when
|
|
* its size changes. Only the frame's @dims of each row are copied: a
|
|
* row of the frame can be longer than the texture's. */
|
|
static void gxm_upload_frame(vita_video_t *vita, const void *frame,
|
|
unsigned dims, unsigned pitch)
|
|
{
|
|
if (vita->dims != dims && vita->texture)
|
|
{
|
|
if (gxm_initialized)
|
|
sceGxmFinish(gxm_context);
|
|
gxm_free_texture(vita->texture);
|
|
vita->texture = NULL;
|
|
}
|
|
|
|
if (!vita->texture)
|
|
{
|
|
vita->dims = dims;
|
|
vita->texture = gxm_create_empty_texture_format(VIDEO_SCALE_W(dims),
|
|
VIDEO_SCALE_H(dims), vita->format);
|
|
if (!vita->texture)
|
|
return;
|
|
gxm_texture_set_filters(vita->texture, vita->tex_filter,
|
|
vita->tex_filter);
|
|
}
|
|
|
|
gxm_copy_rows(sceGxmTextureGetData(&vita->texture->gxm_tex),
|
|
gxm_texture_get_stride(vita->texture), frame, pitch,
|
|
VIDEO_SCALE_W(dims)
|
|
* ((vita->format == SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1RGB) ? 4 : 2),
|
|
VIDEO_SCALE_H(dims));
|
|
}
|
|
|
|
static bool gxm_frame(void *data, const void *frame,
|
|
unsigned dims, uint64_t frame_count,
|
|
unsigned pitch, const char *msg, video_frame_info_t *video_info)
|
|
{
|
|
gxm_display_data_t displayData;
|
|
vita_video_t *vita = (vita_video_t *)data;
|
|
unsigned temp_width = PSP_FB_WIDTH;
|
|
unsigned temp_height = PSP_FB_HEIGHT;
|
|
#ifdef HAVE_MENU
|
|
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 statistics_show = video_info->statistics_show;
|
|
struct font_params *osd_params = (struct font_params*)&video_info->osd_stat_params;
|
|
|
|
if (frame && !(vita->texture
|
|
&& sceGxmTextureGetData(&vita->texture->gxm_tex) == frame))
|
|
gxm_upload_frame(vita, frame, dims, pitch);
|
|
|
|
if (vita->should_resize)
|
|
gxm_update_viewport(vita);
|
|
|
|
temp_width = VIDEO_SCALE_W(video_mode_data.dims);
|
|
temp_height = VIDEO_SCALE_H(video_mode_data.dims);
|
|
|
|
pool_index = 0;
|
|
sceGxmBeginScene(
|
|
gxm_context,
|
|
0,
|
|
renderTarget,
|
|
NULL,
|
|
NULL,
|
|
displayBufferSync[backBufferIndex],
|
|
&displaySurface[backBufferIndex],
|
|
&depthSurface);
|
|
|
|
drawing = 1;
|
|
/* in the current way, the library keeps the region clip across scenes */
|
|
if (clipping_enabled)
|
|
gxm_set_clip_rectangle(clip_rect_x_min, clip_rect_y_min,
|
|
clip_rect_x_max, clip_rect_y_max);
|
|
|
|
gxm_draw_rectangle(0,0, temp_width, temp_height,
|
|
clear_color_u);
|
|
|
|
if (vita->texture)
|
|
{
|
|
if (vita->fullscreen)
|
|
gxm_draw_texture_scale(vita->texture,
|
|
0, 0,
|
|
temp_width / (float)VIDEO_SCALE_W(vita->dims),
|
|
temp_height / (float)VIDEO_SCALE_H(vita->dims));
|
|
else
|
|
{
|
|
const float radian = 270 * 0.0174532925f;
|
|
const float rad = vita->rotation * radian;
|
|
float scalex = VIDEO_SCALE_W(vita->vp.dims)
|
|
/ (float)VIDEO_SCALE_W(vita->dims);
|
|
float scaley = VIDEO_SCALE_H(vita->vp.dims)
|
|
/ (float)VIDEO_SCALE_H(vita->dims);
|
|
gxm_draw_texture_scale_rotate(vita->texture,VIDEO_POS_X(vita->vp.pos),
|
|
VIDEO_POS_Y(vita->vp.pos), scalex, scaley, rad);
|
|
}
|
|
}
|
|
|
|
if (vita->menu.active)
|
|
{
|
|
#ifdef HAVE_MENU
|
|
menu_driver_frame(menu_is_alive, video_info);
|
|
#endif
|
|
|
|
if (vita->menu.texture)
|
|
{
|
|
if (vita->fullscreen)
|
|
gxm_draw_texture_scale(vita->menu.texture,
|
|
0, 0,
|
|
temp_width / (float)VIDEO_SCALE_W(vita->menu.dims),
|
|
temp_height / (float)VIDEO_SCALE_H(vita->menu.dims));
|
|
else
|
|
{
|
|
if (VIDEO_SCALE_W(vita->menu.dims)
|
|
> VIDEO_SCALE_H(vita->menu.dims))
|
|
{
|
|
float scale = temp_height
|
|
/ (float)VIDEO_SCALE_H(vita->menu.dims);
|
|
float w = VIDEO_SCALE_W(vita->menu.dims) * scale;
|
|
gxm_draw_texture_scale(vita->menu.texture,
|
|
temp_width / 2.0f - w/2.0f, 0.0f,
|
|
scale, scale);
|
|
}
|
|
else
|
|
{
|
|
float scale = temp_width
|
|
/ (float)VIDEO_SCALE_W(vita->menu.dims);
|
|
float h = VIDEO_SCALE_H(vita->menu.dims) * scale;
|
|
gxm_draw_texture_scale(vita->menu.texture,
|
|
0.0f, temp_height / 2.0f - h/2.0f,
|
|
scale, scale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (statistics_show)
|
|
{
|
|
if (osd_params)
|
|
font_driver_render_msg(vita, video_info->stat_text, video_info->stat_text_len,
|
|
osd_params, NULL);
|
|
}
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
if (vita->overlay_enable)
|
|
gxm_render_overlay(vita);
|
|
#endif
|
|
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
if (widgets_active)
|
|
gfx_widgets_frame(video_info);
|
|
#endif
|
|
if (msg && *msg)
|
|
font_driver_render_msg(vita, msg, strlen(msg), NULL, NULL);
|
|
|
|
sceGxmEndScene(gxm_context, NULL, NULL);
|
|
drawing = 0;
|
|
|
|
if (input_osk_native_active())
|
|
gxm_common_dialog_update();
|
|
|
|
sceGxmPadHeartbeat(&displaySurface[backBufferIndex],
|
|
displayBufferSync[backBufferIndex]);
|
|
/* queue the display swap for this frame */
|
|
displayData.address = displayBufferData[backBufferIndex];
|
|
sceGxmDisplayQueueAddEntry(
|
|
displayBufferSync[frontBufferIndex], /* OLD fb */
|
|
displayBufferSync[backBufferIndex], /* NEW fb */
|
|
&displayData);
|
|
|
|
/* update buffer indices */
|
|
frontBufferIndex = backBufferIndex;
|
|
backBufferIndex = (backBufferIndex + 1) % DISPLAY_BUFFER_COUNT;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void gxm_set_nonblock_state(void *data, bool toggle,
|
|
bool c, unsigned d)
|
|
{
|
|
vita_video_t *vita = (vita_video_t *)data;
|
|
|
|
if (vita)
|
|
{
|
|
vita->vsync = !toggle;
|
|
vblank_wait = vita->vsync;
|
|
}
|
|
}
|
|
|
|
/* Stubs */
|
|
static bool gxm_alive(void *data) { return true; }
|
|
static bool gxm_focus(void *data) { return true; }
|
|
static bool gxm_suppress_screensaver(void *a, bool b) { return false; }
|
|
static bool gxm_set_shader(void *data,
|
|
enum rarch_shader_type type, const char *path) { return false; }
|
|
|
|
static void gxm_free(void *data)
|
|
{
|
|
unsigned int i;
|
|
vita_video_t *vita = (vita_video_t *)data;
|
|
|
|
if (gxm_initialized)
|
|
{
|
|
/* wait until rendering is done */
|
|
sceGxmFinish(gxm_context);
|
|
/* clean up allocations */
|
|
sceGxmShaderPatcherReleaseVertexProgram(shaderPatcher,
|
|
gxm_colorVertexProgram);
|
|
sceGxmShaderPatcherReleaseVertexProgram(shaderPatcher,
|
|
gxm_textureVertexProgram);
|
|
|
|
gxm_free_fragment_programs(&gxm_fragmentPrograms.blend_mode_normal);
|
|
gxm_free_fragment_programs(&gxm_fragmentPrograms.blend_mode_add);
|
|
|
|
gpu_free(linearIndicesUid);
|
|
|
|
/* wait until display queue is finished before deallocating display buffers */
|
|
sceGxmDisplayQueueFinish();
|
|
|
|
/* clean up display queue */
|
|
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++)
|
|
{
|
|
/* clear the buffer then deallocate */
|
|
memset(displayBufferData[i], 0,
|
|
VIDEO_SCALE_H(video_mode_data.dims)
|
|
* video_mode_data.stride * 4);
|
|
gpu_free(displayBufferUid[i]);
|
|
|
|
/* destroy the sync object */
|
|
sceGxmSyncObjectDestroy(displayBufferSync[i]);
|
|
}
|
|
|
|
/* free the depth and stencil buffer */
|
|
gpu_free(depthBufferUid);
|
|
gpu_free(stencilBufferUid);
|
|
|
|
/* unregister programs and destroy shader patcher */
|
|
sceGxmShaderPatcherUnregisterProgram(shaderPatcher, colorFragmentProgramId);
|
|
sceGxmShaderPatcherUnregisterProgram(shaderPatcher, colorVertexProgramId);
|
|
sceGxmShaderPatcherUnregisterProgram(shaderPatcher,
|
|
textureFragmentProgramId);
|
|
sceGxmShaderPatcherUnregisterProgram(shaderPatcher,
|
|
textureTintFragmentProgramId);
|
|
sceGxmShaderPatcherUnregisterProgram(shaderPatcher,
|
|
textureTintVertexProgramId);
|
|
sceGxmShaderPatcherUnregisterProgram(shaderPatcher, textureVertexProgramId);
|
|
|
|
sceGxmShaderPatcherDestroy(shaderPatcher);
|
|
fragment_usse_free(patcherFragmentUsseUid);
|
|
vertex_usse_free(patcherVertexUsseUid);
|
|
gpu_free(patcherBufferUid);
|
|
|
|
/* destroy the render target */
|
|
sceGxmDestroyRenderTarget(renderTarget);
|
|
renderTarget = NULL;
|
|
|
|
/* destroy the gxm_context */
|
|
sceGxmDestroyContext(gxm_context);
|
|
fragment_usse_free(fragmentUsseRingBufferUid);
|
|
gpu_free(fragmentRingBufferUid);
|
|
gpu_free(vertexRingBufferUid);
|
|
gpu_free(vdmRingBufferUid);
|
|
free(contextParams.hostMem);
|
|
|
|
gpu_free(poolUid);
|
|
|
|
/* terminate libgxm */
|
|
sceGxmTerminate();
|
|
|
|
gxm_initialized = 0;
|
|
}
|
|
|
|
if (vita->menu.texture)
|
|
{
|
|
gxm_free_texture(vita->menu.texture);
|
|
vita->menu.texture = NULL;
|
|
}
|
|
|
|
if (vita->texture)
|
|
{
|
|
gxm_free_texture(vita->texture);
|
|
vita->texture = NULL;
|
|
}
|
|
|
|
if (vita->white_texture)
|
|
{
|
|
gxm_free_texture(vita->white_texture);
|
|
vita->white_texture = NULL;
|
|
}
|
|
}
|
|
|
|
static void gxm_set_projection(vita_video_t *vita,
|
|
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(vita->mvp_no_rot, ortho->left, ortho->right,
|
|
ortho->bottom, ortho->top, ortho->znear, ortho->zfar);
|
|
|
|
if (!allow_rotate)
|
|
{
|
|
vita->mvp = vita->mvp_no_rot;
|
|
return;
|
|
}
|
|
|
|
radians = M_PI * vita->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(vita->mvp, rot, vita->mvp_no_rot);
|
|
}
|
|
|
|
static void gxm_set_rotation(void *data,
|
|
unsigned rotation)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
struct video_ortho ortho = {0, 1, 0, 1, -1, 1};
|
|
|
|
if (!vita)
|
|
return;
|
|
|
|
vita->rotation = rotation;
|
|
vita->should_resize = true;
|
|
gxm_set_projection(vita, &ortho, true);
|
|
}
|
|
|
|
static void gxm_viewport_info(void *data,
|
|
struct video_viewport *vp)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
if (vita)
|
|
*vp = vita->vp;
|
|
}
|
|
|
|
static void gxm_set_filtering(void *data, unsigned index,
|
|
bool smooth, bool ctx_scaling)
|
|
{
|
|
vita_video_t *vita = (vita_video_t *)data;
|
|
|
|
if (vita)
|
|
{
|
|
vita->tex_filter = smooth
|
|
? SCE_GXM_TEXTURE_FILTER_LINEAR
|
|
: SCE_GXM_TEXTURE_FILTER_POINT;
|
|
gxm_texture_set_filters(vita->texture,vita->tex_filter,
|
|
vita->tex_filter);
|
|
}
|
|
}
|
|
|
|
static void gxm_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
if (!vita)
|
|
return;
|
|
vita->keep_aspect = true;
|
|
vita->should_resize = true;
|
|
}
|
|
|
|
static void gxm_apply_state_changes(void *data)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
if (vita)
|
|
vita->should_resize = true;
|
|
}
|
|
|
|
static void gxm_set_texture_frame(void *data,
|
|
const void *frame, bool rgb32,
|
|
unsigned dims, float alpha)
|
|
{
|
|
void *tex_p;
|
|
unsigned int stride;
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
/* Recreate when either dimension changes, not both. The original
|
|
* condition used && between the width/height comparisons, so a
|
|
* resize that only changed one dimension (e.g. 480x272 -> 480x544
|
|
* when the menu toggles portrait mode, or a width-only change on
|
|
* menu-size setting updates) would skip the destroy-recreate step;
|
|
* the subsequent per-pixel write below would then index past the
|
|
* old texture's allocation (new size > old) or leave stale border
|
|
* pixels (new size < old). */
|
|
if ( vita->menu.texture
|
|
&& dims != vita->menu.dims)
|
|
{
|
|
if (gxm_initialized)
|
|
sceGxmFinish(gxm_context);
|
|
gxm_free_texture(vita->menu.texture);
|
|
vita->menu.texture = NULL;
|
|
}
|
|
|
|
if (!vita->menu.texture)
|
|
{
|
|
if (rgb32)
|
|
vita->menu.texture = gxm_create_empty_texture_format(VIDEO_SCALE_W(dims),
|
|
VIDEO_SCALE_H(dims), SCE_GXM_TEXTURE_FORMAT_A8B8G8R8);
|
|
else
|
|
vita->menu.texture = gxm_create_empty_texture_format(
|
|
VIDEO_SCALE_W(dims), VIDEO_SCALE_H(dims), SCE_GXM_TEXTURE_FORMAT_U4U4U4U4_RGBA);
|
|
vita->menu.dims = dims;
|
|
}
|
|
|
|
gxm_texture_set_filters(vita->menu.texture,
|
|
SCE_GXM_TEXTURE_FILTER_LINEAR,
|
|
SCE_GXM_TEXTURE_FILTER_LINEAR);
|
|
|
|
tex_p = sceGxmTextureGetData(&vita->menu.texture->gxm_tex);
|
|
stride = gxm_texture_get_stride(vita->menu.texture);
|
|
|
|
{
|
|
unsigned row_len = VIDEO_SCALE_W(dims) * (rgb32 ? 4 : 2);
|
|
gxm_copy_rows(tex_p, stride, frame, row_len, row_len,
|
|
VIDEO_SCALE_H(dims));
|
|
}
|
|
}
|
|
|
|
static void gxm_set_texture_enable(void *data, bool state, bool full_screen)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
vita->menu.active = state;
|
|
}
|
|
|
|
static uintptr_t gxm_load_texture(void *video_data, void *data,
|
|
bool threaded, enum texture_filter_type filter_type)
|
|
{
|
|
struct texture_image *image = (struct texture_image*)data;
|
|
gxm_texture_t *texture = gxm_create_empty_texture_format(
|
|
image->width, image->height,
|
|
SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ARGB);
|
|
|
|
if (!texture)
|
|
return 0;
|
|
|
|
if ( (filter_type == TEXTURE_FILTER_MIPMAP_LINEAR)
|
|
|| (filter_type == TEXTURE_FILTER_LINEAR))
|
|
gxm_texture_set_filters(texture,
|
|
SCE_GXM_TEXTURE_FILTER_LINEAR,
|
|
SCE_GXM_TEXTURE_FILTER_LINEAR);
|
|
|
|
gxm_copy_rows(sceGxmTextureGetData(&texture->gxm_tex),
|
|
gxm_texture_get_stride(texture), image->pixels, image->width * 4,
|
|
image->width * 4, image->height);
|
|
|
|
return (uintptr_t)texture;
|
|
}
|
|
|
|
/* Written in place like the menu texture: no replacement to load,
|
|
* and no sceGxmFinish to unload the old one */
|
|
static enum video_texture_update gxm_update_texture(void *video_data,
|
|
uintptr_t id, const struct texture_image *ti, bool threaded)
|
|
{
|
|
gxm_texture_t *texture = (gxm_texture_t*)id;
|
|
|
|
if ( !texture || !ti || !ti->pixels || ti->pix10 || ti->fp16
|
|
|| sceGxmTextureGetFormat(&texture->gxm_tex)
|
|
!= SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ARGB
|
|
|| sceGxmTextureGetWidth(&texture->gxm_tex) != ti->width
|
|
|| sceGxmTextureGetHeight(&texture->gxm_tex) != ti->height)
|
|
return VIDEO_TEXTURE_UPDATE_REFUSED;
|
|
|
|
gxm_copy_rows(sceGxmTextureGetData(&texture->gxm_tex),
|
|
gxm_texture_get_stride(texture), ti->pixels, ti->width * 4,
|
|
ti->width * 4, ti->height);
|
|
|
|
return VIDEO_TEXTURE_UPDATE_DONE;
|
|
}
|
|
|
|
static void gxm_unload_texture(void *data,
|
|
bool threaded, uintptr_t handle)
|
|
{
|
|
gxm_texture_t *texture = (gxm_texture_t*)handle;
|
|
if (!texture)
|
|
return;
|
|
|
|
/* TODO: We really want to defer this deletion instead,
|
|
* but this will do for now. */
|
|
if (gxm_initialized)
|
|
sceGxmFinish(gxm_context);
|
|
gxm_free_texture(texture);
|
|
|
|
#if 0
|
|
free(texture);
|
|
#endif
|
|
}
|
|
|
|
static bool gxm_get_current_sw_framebuffer(void *data,
|
|
struct retro_framebuffer *framebuffer)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
if ( !vita->texture
|
|
|| (vita->dims != VIDEO_SCALE_PACK(framebuffer->width,
|
|
framebuffer->height)))
|
|
{
|
|
if (vita->texture)
|
|
{
|
|
if (gxm_initialized)
|
|
sceGxmFinish(gxm_context);
|
|
gxm_free_texture(vita->texture);
|
|
vita->texture = NULL;
|
|
}
|
|
|
|
vita->dims = VIDEO_SCALE_PACK(framebuffer->width,
|
|
framebuffer->height);
|
|
vita->texture = gxm_create_empty_texture_format(
|
|
VIDEO_SCALE_W(vita->dims), VIDEO_SCALE_H(vita->dims),
|
|
vita->format);
|
|
gxm_texture_set_filters(vita->texture,
|
|
vita->tex_filter,vita->tex_filter);
|
|
}
|
|
|
|
framebuffer->data = sceGxmTextureGetData(&vita->texture->gxm_tex);
|
|
framebuffer->pitch = gxm_texture_get_stride(vita->texture);
|
|
framebuffer->format = vita->rgb32
|
|
? RETRO_PIXEL_FORMAT_XRGB8888
|
|
: RETRO_PIXEL_FORMAT_RGB565;
|
|
framebuffer->memory_flags = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
static uint32_t gxm_get_flags(void *data)
|
|
{
|
|
uint32_t flags = 0;
|
|
|
|
BIT32_SET(flags, GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED);
|
|
|
|
return flags;
|
|
}
|
|
|
|
/* --- GPU-native BCn compressed-texture upload --- */
|
|
/* The Vita GPU (SGX543) samples UBC1/UBC2/UBC3, which are exactly
|
|
* BC1/BC2/BC3 (DXT1/DXT3/DXT5). Nothing above BC3 exists here (no UBC for
|
|
* BC6H/BC7, and UBC4/UBC5 are single/two-channel only). */
|
|
static bool gxm_bc_to_ubc(enum texture_gpu_format fmt,
|
|
SceGxmTextureFormat *out, unsigned *block_bytes)
|
|
{
|
|
switch (fmt)
|
|
{
|
|
case TEXTURE_GPU_FORMAT_BC1:
|
|
*out = SCE_GXM_TEXTURE_FORMAT_UBC1_ABGR;
|
|
*block_bytes = 8;
|
|
return true;
|
|
case TEXTURE_GPU_FORMAT_BC2:
|
|
*out = SCE_GXM_TEXTURE_FORMAT_UBC2_ABGR;
|
|
*block_bytes = 16;
|
|
return true;
|
|
case TEXTURE_GPU_FORMAT_BC3:
|
|
*out = SCE_GXM_TEXTURE_FORMAT_UBC3_ABGR;
|
|
*block_bytes = 16;
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool gxm_supports_texture_format(void *data,
|
|
enum texture_gpu_format fmt)
|
|
{
|
|
unsigned bb;
|
|
SceGxmTextureFormat gxm;
|
|
return gxm_bc_to_ubc(fmt, &gxm, &bb);
|
|
}
|
|
|
|
static uintptr_t gxm_load_texture_compressed(void *video_data,
|
|
const struct texture_compressed *tc, bool threaded,
|
|
enum texture_filter_type filter_type)
|
|
{
|
|
unsigned i;
|
|
gxm_texture_t *texture;
|
|
void *tex_data;
|
|
SceGxmTextureFormat gxm_fmt = 0;
|
|
unsigned block_bytes = 16;
|
|
size_t total = 0;
|
|
size_t off = 0;
|
|
int err;
|
|
|
|
(void)video_data;
|
|
(void)threaded;
|
|
|
|
if (!tc || tc->num_mips == 0)
|
|
return 0;
|
|
if (!gxm_bc_to_ubc(tc->format, &gxm_fmt, &block_bytes))
|
|
return 0;
|
|
if (tc->mips[0].width > GXM_TEX_MAX_SIZE || tc->mips[0].height > GXM_TEX_MAX_SIZE)
|
|
return 0;
|
|
|
|
if (!(texture = malloc(sizeof(*texture))))
|
|
return 0;
|
|
memset(texture, 0, sizeof(*texture));
|
|
|
|
for (i = 0; i < tc->num_mips; i++)
|
|
total += tc->mips[i].size;
|
|
|
|
tex_data = gpu_alloc(
|
|
MemBlockType,
|
|
total,
|
|
SCE_GXM_TEXTURE_ALIGNMENT,
|
|
SCE_GXM_MEMORY_ATTRIB_READ,
|
|
&texture->data_UID);
|
|
|
|
if (!tex_data)
|
|
{
|
|
free(texture);
|
|
return 0;
|
|
}
|
|
|
|
/* Mip chain is copied contiguously; block-compressed layout is derived
|
|
* by GXM from the UBC format + dimensions. */
|
|
for (i = 0; i < tc->num_mips; i++)
|
|
{
|
|
memcpy((uint8_t*)tex_data + off, tc->mips[i].data, tc->mips[i].size);
|
|
off += tc->mips[i].size;
|
|
}
|
|
|
|
/* Block-compressed data uses the linear texture type. GXM stores the
|
|
* memory layout (linear/swizzled/tiled) as a property of the texture,
|
|
* and a linear-typed texture is sampled as plain row-major blocks --
|
|
* exactly the DDS block order copied above, so no Morton reorder is
|
|
* needed. Verified against Vita3K (the reference emulator), which reads
|
|
* linear/swizzled/tiled BCn per that type field; Sony's GXT tooling
|
|
* emits the swizzled type, but that is a tooling choice, not a hardware
|
|
* requirement. Should a GXM revision refuse InitLinear for a UBC format,
|
|
* the err check below returns 0 and video_driver_texture_load falls back
|
|
* to the CPU decode path. */
|
|
err = sceGxmTextureInitLinear(
|
|
&texture->gxm_tex,
|
|
tex_data,
|
|
gxm_fmt,
|
|
tc->mips[0].width,
|
|
tc->mips[0].height,
|
|
(tc->num_mips > 0) ? (tc->num_mips - 1) : 0);
|
|
|
|
if (err < 0)
|
|
{
|
|
gxm_free_texture(texture);
|
|
return 0;
|
|
}
|
|
|
|
if ( filter_type == TEXTURE_FILTER_MIPMAP_LINEAR
|
|
|| filter_type == TEXTURE_FILTER_LINEAR)
|
|
gxm_texture_set_filters(texture,
|
|
SCE_GXM_TEXTURE_FILTER_LINEAR,
|
|
SCE_GXM_TEXTURE_FILTER_LINEAR);
|
|
|
|
return (uintptr_t)texture;
|
|
}
|
|
|
|
static const video_poke_interface_t vita_poke_interface = {
|
|
gxm_get_flags,
|
|
gxm_load_texture,
|
|
gxm_unload_texture,
|
|
NULL, /* set_video_mode */
|
|
NULL, /* get_refresh_rate */
|
|
gxm_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 */
|
|
gxm_set_aspect_ratio,
|
|
gxm_apply_state_changes,
|
|
gxm_set_texture_frame,
|
|
gxm_set_texture_enable,
|
|
font_driver_render_msg,
|
|
NULL, /* show_mouse */
|
|
NULL, /* grab_mouse_toggle */
|
|
NULL, /* get_current_shader */
|
|
gxm_get_current_sw_framebuffer,
|
|
NULL, /* 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 */
|
|
gxm_supports_texture_format,
|
|
gxm_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 */
|
|
gxm_update_texture
|
|
};
|
|
|
|
static void gxm_get_poke_interface(void *data,
|
|
const video_poke_interface_t **iface)
|
|
{
|
|
*iface = &vita_poke_interface;
|
|
}
|
|
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
static bool gxm_widgets_enabled(void *data) { return true; }
|
|
#endif
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
static void gxm_overlay_tex_geom(void *data, unsigned image, float x,
|
|
float y, float w, float h);
|
|
static void gxm_overlay_vertex_geom(void *data, unsigned image, float x,
|
|
float y, float w, float h);
|
|
|
|
static bool gxm_overlay_load(void *data, const void *image_data, unsigned num_images)
|
|
{
|
|
unsigned i,j,k;
|
|
unsigned int stride, pitch;
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
const struct texture_image *images = (const struct texture_image*)image_data;
|
|
|
|
gxm_free_overlay(vita);
|
|
vita->overlay = (struct vita_overlay_data*)calloc(num_images,
|
|
sizeof(*vita->overlay));
|
|
if (!vita->overlay)
|
|
return false;
|
|
|
|
vita->overlays = num_images;
|
|
|
|
for (i = 0; i < num_images; i++)
|
|
{
|
|
uint32_t *tex32;
|
|
const uint32_t *frame32;
|
|
struct vita_overlay_data *o = (struct vita_overlay_data*)&vita->overlay[i];
|
|
o->dims = VIDEO_SCALE_PACK(images[i].width, images[i].height);
|
|
o->tex = gxm_create_empty_texture_format(
|
|
VIDEO_SCALE_W(o->dims), VIDEO_SCALE_H(o->dims),
|
|
SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ARGB);
|
|
gxm_texture_set_filters(o->tex, SCE_GXM_TEXTURE_FILTER_LINEAR,
|
|
SCE_GXM_TEXTURE_FILTER_LINEAR);
|
|
stride = gxm_texture_get_stride(o->tex);
|
|
stride /= 4;
|
|
tex32 = sceGxmTextureGetData(&o->tex->gxm_tex);
|
|
frame32 = images[i].pixels;
|
|
pitch = VIDEO_SCALE_W(o->dims);
|
|
for (j = 0; j < VIDEO_SCALE_H(o->dims); j++)
|
|
for (k = 0; k < VIDEO_SCALE_W(o->dims); k++)
|
|
tex32[k + j*stride] = frame32[k + j*pitch];
|
|
|
|
gxm_overlay_tex_geom(vita, i, 0, 0, 1, 1); /* Default. Stretch to whole screen. */
|
|
gxm_overlay_vertex_geom(vita, i, 0, 0, 1, 1);
|
|
vita->overlay[i].alpha_mod = 1.0f;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void gxm_overlay_tex_geom(void *data, unsigned image,
|
|
float x, float y, float w, float h)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
struct vita_overlay_data *o = NULL;
|
|
|
|
if (vita && vita->overlay && image < vita->overlays)
|
|
o = (struct vita_overlay_data*)&vita->overlay[image];
|
|
|
|
if (o)
|
|
{
|
|
o->tex_x = x;
|
|
o->tex_y = y;
|
|
o->tex_w = w * VIDEO_SCALE_W(o->dims);
|
|
o->tex_h = h * VIDEO_SCALE_H(o->dims);
|
|
}
|
|
}
|
|
|
|
static void gxm_overlay_vertex_geom(void *data, unsigned image,
|
|
float x, float y, float w, float h)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
struct vita_overlay_data *o = NULL;
|
|
|
|
/* Flipped, so we preserve top-down semantics. */
|
|
/* y = 1.0f - y;
|
|
h = -h;
|
|
*/
|
|
|
|
if (vita && vita->overlay && image < vita->overlays)
|
|
o = (struct vita_overlay_data*)&vita->overlay[image];
|
|
|
|
if (o)
|
|
{
|
|
o->w = w * VIDEO_SCALE_W(video_mode_data.dims)
|
|
/ VIDEO_SCALE_W(o->dims);
|
|
o->h = h * VIDEO_SCALE_H(video_mode_data.dims)
|
|
/ VIDEO_SCALE_H(o->dims);
|
|
o->x = VIDEO_SCALE_W(video_mode_data.dims) * (1 - w) / 2 + x;
|
|
o->y = VIDEO_SCALE_H(video_mode_data.dims) * (1 - h) / 2 + y;
|
|
}
|
|
}
|
|
|
|
/* A page of the pack's textures: gxm_load_texture's, uploaded once
|
|
* for every page; no texture of the page's own, so nothing to wait
|
|
* for on a switch */
|
|
static bool gxm_overlay_load_textures(void *data,
|
|
const uintptr_t *textures, unsigned num_textures)
|
|
{
|
|
unsigned i;
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
if (!vita)
|
|
return false;
|
|
|
|
gxm_free_overlay(vita);
|
|
if (!num_textures)
|
|
return true;
|
|
if (!(vita->overlay = (struct vita_overlay_data*)calloc(num_textures,
|
|
sizeof(*vita->overlay))))
|
|
return false;
|
|
vita->overlays = num_textures;
|
|
vita->overlay_borrowed = true;
|
|
|
|
for (i = 0; i < num_textures; i++)
|
|
{
|
|
struct vita_overlay_data *o = &vita->overlay[i];
|
|
gxm_texture_t *tex = (gxm_texture_t*)textures[i];
|
|
|
|
if (!tex)
|
|
continue;
|
|
o->tex = tex;
|
|
o->dims = VIDEO_SCALE_PACK(
|
|
sceGxmTextureGetWidth(&tex->gxm_tex),
|
|
sceGxmTextureGetHeight(&tex->gxm_tex));
|
|
gxm_overlay_tex_geom(vita, i, 0, 0, 1, 1); /* Default. Stretch to whole screen. */
|
|
gxm_overlay_vertex_geom(vita, i, 0, 0, 1, 1);
|
|
o->alpha_mod = 1.0f;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void gxm_overlay_enable(void *data, bool state)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
vita->overlay_enable = state;
|
|
}
|
|
|
|
static void gxm_overlay_full_screen(void *data, bool enable)
|
|
{
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
vita->overlay_full_screen = enable;
|
|
}
|
|
|
|
static void gxm_overlay_set_alpha(void *data, unsigned image, float mod)
|
|
{
|
|
vita_video_t *vita = (vita_video_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 (vita && vita->overlay && image < vita->overlays)
|
|
vita->overlay[image].alpha_mod = mod;
|
|
}
|
|
|
|
static void gxm_render_overlay(void *data)
|
|
{
|
|
unsigned i;
|
|
vita_video_t *vita = (vita_video_t*)data;
|
|
|
|
for (i = 0; i < vita->overlays; i++)
|
|
{
|
|
if (!vita->overlay[i].tex)
|
|
continue;
|
|
gxm_draw_texture_tint_part_scale(vita->overlay[i].tex,
|
|
vita->overlay[i].x,
|
|
vita->overlay[i].y,
|
|
vita->overlay[i].tex_x,
|
|
vita->overlay[i].tex_y,
|
|
vita->overlay[i].tex_w,
|
|
vita->overlay[i].tex_h,
|
|
vita->overlay[i].w,
|
|
vita->overlay[i].h,
|
|
RGBA8(0xFF,0xFF,0xFF,(uint8_t)VIDEO_ALPHA_BYTE(vita->overlay[i].alpha_mod)));
|
|
}
|
|
}
|
|
|
|
static const video_overlay_interface_t gxm_overlay_interface = {
|
|
gxm_overlay_enable,
|
|
gxm_overlay_load,
|
|
gxm_overlay_load_textures,
|
|
gxm_overlay_tex_geom,
|
|
gxm_overlay_vertex_geom,
|
|
gxm_overlay_full_screen,
|
|
gxm_overlay_set_alpha,
|
|
};
|
|
|
|
static void gxm_get_overlay_interface(void *data, const video_overlay_interface_t **iface)
|
|
{
|
|
*iface = &gxm_overlay_interface;
|
|
}
|
|
#endif
|
|
|
|
static font_renderer_t gxm_font = {
|
|
gxm_font_init,
|
|
gxm_font_free,
|
|
gxm_font_render_msg,
|
|
"vita2d",
|
|
gxm_font_get_glyph,
|
|
NULL, /* bind_block */
|
|
NULL, /* flush */
|
|
gxm_font_get_message_width,
|
|
gxm_font_get_line_metrics
|
|
};
|
|
|
|
video_driver_t video_gxm = {
|
|
gxm_gfx_init,
|
|
gxm_frame,
|
|
gxm_set_nonblock_state,
|
|
gxm_alive,
|
|
gxm_focus,
|
|
gxm_suppress_screensaver,
|
|
NULL, /* has_windowed */
|
|
gxm_set_shader,
|
|
gxm_free,
|
|
"vita2d",
|
|
gxm_set_viewport_wrapper,
|
|
gxm_set_rotation,
|
|
gxm_viewport_info,
|
|
NULL, /* read_viewport */
|
|
#ifdef HAVE_OVERLAY
|
|
gxm_get_overlay_interface,
|
|
#endif
|
|
gxm_get_poke_interface,
|
|
NULL, /* wrap_type_to_enum */
|
|
NULL, /* shader_load_begin */
|
|
NULL, /* shader_load_step */
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
gxm_widgets_enabled,
|
|
#endif
|
|
NULL, /* invalidate_hw_render_cache */
|
|
NULL, /* read_viewport_hdr */
|
|
&gxm_font
|
|
};
|
|
|
|
gfx_display_ctx_driver_t gfx_display_ctx_gxm = {
|
|
gfx_display_gxm_draw,
|
|
NULL, /* draw_pipeline */
|
|
NULL, /* blend_begin */
|
|
NULL, /* blend_end */
|
|
gfx_display_gxm_get_default_mvp,
|
|
gfx_display_gxm_get_default_vertices,
|
|
gfx_display_gxm_get_default_tex_coords,
|
|
&gxm_font,
|
|
GFX_VIDEO_DRIVER_GXM,
|
|
"vita2d",
|
|
true,
|
|
true,
|
|
gfx_display_gxm_scissor_begin,
|
|
gfx_display_gxm_scissor_end
|
|
};
|