#include "../../SDL_internal.h"
#if SDL_VIDEO_RENDER_PS2
#include "../SDL_sysrender.h"
#include "SDL_hints.h"
#include <kernel.h>
#include <malloc.h>
#include <gsKit.h>
#include <dmaKit.h>
#include <gsToolkit.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
#include <gsInline.h>
#pragma GCC diagnostic pop
#define GS_BLACK GS_SETREG_RGBA(0x00, 0x00, 0x00, 0x80)
#define RENDER_QUEUE_PER_POOLSIZE 1024 * 256
#define RENDER_QUEUE_OS_POOLSIZE 1024 * 1024 * 2
typedef struct
{
GSGLOBAL *gsGlobal;
uint64_t drawColor;
int32_t vsync_callback_id;
uint8_t vsync;
} PS2_RenderData;
static int vsync_sema_id = 0;
static int vsync_handler()
{
iSignalSema(vsync_sema_id);
ExitHandler();
return 0;
}
static void gsKit_sync(GSGLOBAL *gsGlobal)
{
if (!gsGlobal->FirstFrame) WaitSema(vsync_sema_id);
while (PollSema(vsync_sema_id) >= 0)
;
}
static void gsKit_flip(GSGLOBAL *gsGlobal)
{
if (!gsGlobal->FirstFrame)
{
if (gsGlobal->DoubleBuffering == GS_SETTING_ON)
{
GS_SET_DISPFB2( gsGlobal->ScreenBuffer[
gsGlobal->ActiveBuffer & 1] / 8192,
gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 );
gsGlobal->ActiveBuffer ^= 1;
}
}
gsKit_setactive(gsGlobal);
}
static int PixelFormatToPS2PSM(Uint32 format)
{
switch (format) {
case SDL_PIXELFORMAT_ABGR1555:
return GS_PSM_CT16;
default:
return GS_PSM_CT32;
}
}
static void
PS2_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
{
}
static int
PS2_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
GSTEXTURE* ps2_tex = (GSTEXTURE*) SDL_calloc(1, sizeof(GSTEXTURE));
if (!ps2_tex)
return SDL_OutOfMemory();
ps2_tex->Width = texture->w;
ps2_tex->Height = texture->h;
ps2_tex->PSM = PixelFormatToPS2PSM(texture->format);
ps2_tex->Mem = memalign(128, gsKit_texture_size_ee(ps2_tex->Width, ps2_tex->Height, ps2_tex->PSM));
if (!ps2_tex->Mem)
{
SDL_free(ps2_tex);
return SDL_OutOfMemory();
}
texture->driverdata = ps2_tex;
return 0;
}
static int
PS2_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, void **pixels, int *pitch)
{
GSTEXTURE *ps2_texture = (GSTEXTURE *) texture->driverdata;
*pixels =
(void *) ((Uint8 *) ps2_texture->Mem + rect->y * ps2_texture->Width * SDL_BYTESPERPIXEL(texture->format) +
rect->x * SDL_BYTESPERPIXEL(texture->format));
*pitch = ps2_texture->Width * SDL_BYTESPERPIXEL(texture->format);
return 0;
}
static void
PS2_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
GSTEXTURE *ps2_texture = (GSTEXTURE *) texture->driverdata;
PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata;
gsKit_TexManager_invalidate(data->gsGlobal, ps2_texture);
}
static int
PS2_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, const void *pixels, int pitch)
{
const Uint8 *src;
Uint8 *dst;
int row, length,dpitch;
src = pixels;
PS2_LockTexture(renderer, texture, rect, (void **)&dst, &dpitch);
length = rect->w * SDL_BYTESPERPIXEL(texture->format);
if (length == pitch && length == dpitch) {
SDL_memcpy(dst, src, length*rect->h);
} else {
for (row = 0; row < rect->h; ++row) {
SDL_memcpy(dst, src, length);
src += pitch;
dst += dpitch;
}
}
PS2_UnlockTexture(renderer, texture);
return 0;
}
static void
PS2_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode)
{
GSTEXTURE *ps2_texture = (GSTEXTURE *) texture->driverdata;
uint32_t gsKitScaleMode = (scaleMode == SDL_ScaleModeNearest
? GS_FILTER_NEAREST
: GS_FILTER_LINEAR);
ps2_texture->Filter = gsKitScaleMode;
}
static int
PS2_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
{
return 0;
}
static int
PS2_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
{
return 0;
}
static int
PS2_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
{
PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata;
GSPRIMPOINT *vertices = (GSPRIMPOINT *) SDL_AllocateRenderVertices(renderer, count * sizeof (GSPRIMPOINT), 4, &cmd->data.draw.first);
uint8_t colorR, colorG, colorB, colorA;
gs_rgbaq rgbaq;
int i;
if (!vertices) {
return -1;
}
cmd->data.draw.count = count;
colorR = cmd->data.draw.r >> 1;
colorG = cmd->data.draw.g >> 1;
colorB = cmd->data.draw.b >> 1;
colorA = cmd->data.draw.a >> 1;
rgbaq = color_to_RGBAQ(colorR, colorG, colorB, colorA, 0.0f);
for (i = 0; i < count; i++, vertices++, points++) {
vertices->xyz2 = vertex_to_XYZ2(data->gsGlobal, points->x, points->y, 0);
vertices->rgbaq = rgbaq;
}
return 0;
}
static int
PS2_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
const float *xy, int xy_stride, const SDL_Color *color, int color_stride, const float *uv, int uv_stride,
int num_vertices, const void *indices, int num_indices, int size_indices,
float scale_x, float scale_y)
{
int i;
int count = indices ? num_indices : num_vertices;
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
cmd->data.draw.count = count;
size_indices = indices ? size_indices : 0;
if (texture) {
GSPRIMSTQPOINT *vertices = (GSPRIMSTQPOINT *) SDL_AllocateRenderVertices(renderer, count * sizeof (GSPRIMSTQPOINT), 4, &cmd->data.draw.first);
if (!vertices) {
return -1;
}
for (i = 0; i < count; i++) {
int j;
float *xy_;
float *uv_;
SDL_Color col_;
if (size_indices == 4) {
j = ((const Uint32 *)indices)[i];
} else if (size_indices == 2) {
j = ((const Uint16 *)indices)[i];
} else if (size_indices == 1) {
j = ((const Uint8 *)indices)[i];
} else {
j = i;
}
xy_ = (float *)((char*)xy + j * xy_stride);
col_ = *(SDL_Color *)((char*)color + j * color_stride);
uv_ = (float *)((char*)uv + j * uv_stride);
vertices->xyz2 = vertex_to_XYZ2(data->gsGlobal, xy_[0] * scale_x, xy_[1] * scale_y, 0);
vertices->stq = vertex_to_STQ(uv_[0], uv_[1]);
vertices->rgbaq = color_to_RGBAQ(col_.r >> 1, col_.g >> 1, col_.b >> 1, col_.a >> 1, 1.0f);
vertices++;
}
} else {
GSPRIMPOINT *vertices = (GSPRIMPOINT *) SDL_AllocateRenderVertices(renderer, count * sizeof (GSPRIMPOINT), 4, &cmd->data.draw.first);
if (!vertices) {
return -1;
}
for (i = 0; i < count; i++) {
int j;
float *xy_;
SDL_Color col_;
if (size_indices == 4) {
j = ((const Uint32 *)indices)[i];
} else if (size_indices == 2) {
j = ((const Uint16 *)indices)[i];
} else if (size_indices == 1) {
j = ((const Uint8 *)indices)[i];
} else {
j = i;
}
xy_ = (float *)((char*)xy + j * xy_stride);
col_ = *(SDL_Color *)((char*)color + j * color_stride);
vertices->xyz2 = vertex_to_XYZ2(data->gsGlobal, xy_[0] * scale_x, xy_[1] * scale_y, 0);
vertices->rgbaq = color_to_RGBAQ(col_.r >> 1, col_.g >> 1, col_.b >> 1, col_.a >> 1, 0.0f);
vertices++;
}
}
return 0;
}
static int
PS2_RenderSetViewPort(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
{
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
const SDL_Rect *viewport = &cmd->data.viewport.rect;
gsKit_set_display_offset(data->gsGlobal, viewport->x, viewport->y);
gsKit_set_scissor(data->gsGlobal, GS_SETREG_SCISSOR(viewport->x, viewport->y, viewport->w, viewport->h));
return 0;
}
static int
PS2_RenderSetClipRect(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
{
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
const SDL_Rect *rect = &cmd->data.cliprect.rect;
if(cmd->data.cliprect.enabled){
gsKit_set_scissor(data->gsGlobal, GS_SETREG_SCISSOR(rect->x, rect->y, rect->w, rect->h));
} else {
gsKit_set_scissor(data->gsGlobal, GS_SCISSOR_RESET);
}
return 0;
}
static int
PS2_RenderSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
{
int colorR, colorG, colorB, colorA;
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
colorR = (cmd->data.color.r) >> 1;
colorG = (cmd->data.color.g) >> 1;
colorB = (cmd->data.color.b) >> 1;
colorA = (cmd->data.color.a) >> 1;
data->drawColor = GS_SETREG_RGBAQ(colorR, colorG, colorB, colorA, 0x00);
return 0;
}
static int
PS2_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
{
int colorR, colorG, colorB, colorA;
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
colorR = (cmd->data.color.r) >> 1;
colorG = (cmd->data.color.g) >> 1;
colorB = (cmd->data.color.b) >> 1;
colorA = (cmd->data.color.a) >> 1;
gsKit_clear(data->gsGlobal, GS_SETREG_RGBAQ(colorR, colorG, colorB, colorA, 0x00));
return 0;
}
static void
PS2_SetBlendMode(PS2_RenderData *data, int blendMode)
{
#define A_COLOR_SOURCE 0
#define A_COLOR_DEST 1
#define A_COLOR_NULL 2
#define A_ALPHA_SOURCE 0
#define A_ALPHA_DEST 1
#define A_ALPHA_FIX 2
switch (blendMode)
{
case SDL_BLENDMODE_NONE: {
data->gsGlobal->PrimAlphaEnable = GS_SETTING_OFF;
break;
}
case SDL_BLENDMODE_BLEND:{
gsKit_set_primalpha(data->gsGlobal, GS_SETREG_ALPHA(A_COLOR_SOURCE, A_COLOR_DEST, A_ALPHA_SOURCE, A_COLOR_DEST, 0), 0);
data->gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
break;
}
case SDL_BLENDMODE_ADD: {
gsKit_set_primalpha(data->gsGlobal, GS_SETREG_ALPHA(A_COLOR_SOURCE, A_COLOR_NULL, A_ALPHA_FIX, A_COLOR_DEST, 0x80), 0);
data->gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
break;
}
case SDL_BLENDMODE_MUL:
case SDL_BLENDMODE_MOD: {
gsKit_set_primalpha(data->gsGlobal, GS_SETREG_ALPHA(A_COLOR_DEST, A_COLOR_NULL, A_ALPHA_SOURCE, A_COLOR_SOURCE, 0x80), 0);
data->gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
break;
}
}
}
static int
PS2_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd)
{
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
const size_t count = cmd->data.draw.count;
PS2_SetBlendMode(data, cmd->data.draw.blend);
if (cmd->data.draw.texture) {
const GSPRIMSTQPOINT *verts = (GSPRIMSTQPOINT *) (vertices + cmd->data.draw.first);
GSTEXTURE *ps2_tex = (GSTEXTURE *) cmd->data.draw.texture->driverdata;
gsKit_TexManager_bind(data->gsGlobal, ps2_tex);
gsKit_prim_list_triangle_goraud_texture_stq_3d(data->gsGlobal, ps2_tex, count, verts);
} else {
const GSPRIMPOINT *verts = (GSPRIMPOINT *) (vertices + cmd->data.draw.first);
gsKit_prim_list_triangle_gouraud_3d(data->gsGlobal, count, verts);
}
return 0;
}
int
PS2_RenderLines(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand * cmd)
{
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
const size_t count = cmd->data.draw.count;
const GSPRIMPOINT *verts = (GSPRIMPOINT *) (vertices + cmd->data.draw.first);
PS2_SetBlendMode(data, cmd->data.draw.blend);
gsKit_prim_list_line_goraud_3d(data->gsGlobal, count, verts);
return 0;
}
int
PS2_RenderPoints(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand * cmd)
{
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
const size_t count = cmd->data.draw.count;
const GSPRIMPOINT *verts = (GSPRIMPOINT *) (vertices + cmd->data.draw.first);
PS2_SetBlendMode(data, cmd->data.draw.blend);
gsKit_prim_list_points(data->gsGlobal, count, verts);
return 0;
}
static int
PS2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
{
while (cmd) {
switch (cmd->command) {
case SDL_RENDERCMD_SETVIEWPORT: {
PS2_RenderSetViewPort(renderer, cmd);
break;
}
case SDL_RENDERCMD_SETCLIPRECT: {
PS2_RenderSetClipRect(renderer, cmd);
break;
}
case SDL_RENDERCMD_SETDRAWCOLOR: {
PS2_RenderSetDrawColor(renderer, cmd);
break;
}
case SDL_RENDERCMD_CLEAR: {
PS2_RenderClear(renderer, cmd);
break;
}
case SDL_RENDERCMD_DRAW_POINTS: {
PS2_RenderPoints(renderer, vertices, cmd);
break;
}
case SDL_RENDERCMD_DRAW_LINES: {
PS2_RenderLines(renderer, vertices, cmd);
break;
}
case SDL_RENDERCMD_FILL_RECTS:
break;
case SDL_RENDERCMD_COPY:
break;
case SDL_RENDERCMD_COPY_EX:
break;
case SDL_RENDERCMD_GEOMETRY: {
PS2_RenderGeometry(renderer, vertices, cmd);
break;
}
case SDL_RENDERCMD_NO_OP:
break;
}
cmd = cmd->next;
}
return 0;
}
static int
PS2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
Uint32 format, void * pixels, int pitch)
{
return SDL_Unsupported();
}
static int
PS2_RenderPresent(SDL_Renderer * renderer)
{
PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata;
if (data->gsGlobal->DoubleBuffering == GS_SETTING_OFF) {
if (data->vsync == 2) gsKit_sync(data->gsGlobal);
else if (data->vsync == 1)
gsKit_vsync_wait();
gsKit_queue_exec(data->gsGlobal);
} else {
gsKit_queue_exec(data->gsGlobal);
gsKit_finish();
if (data->vsync == 2) gsKit_sync(data->gsGlobal);
else if (data->vsync == 1)
gsKit_vsync_wait();
gsKit_flip(data->gsGlobal);
}
gsKit_TexManager_nextFrame(data->gsGlobal);
gsKit_clear(data->gsGlobal, GS_BLACK);
return 0;
}
static void
PS2_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
GSTEXTURE *ps2_texture = (GSTEXTURE *) texture->driverdata;
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
if (data == NULL)
return;
if(ps2_texture == NULL)
return;
gsKit_TexManager_free(data->gsGlobal, ps2_texture);
SDL_free(ps2_texture->Mem);
SDL_free(ps2_texture);
texture->driverdata = NULL;
}
static void
PS2_DestroyRenderer(SDL_Renderer * renderer)
{
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
if (data) {
gsKit_clear(data->gsGlobal, GS_BLACK);
gsKit_vram_clear(data->gsGlobal);
gsKit_deinit_global(data->gsGlobal);
gsKit_remove_vsync_handler(data->vsync_callback_id);
SDL_free(data);
}
if (vsync_sema_id >= 0)
DeleteSema(vsync_sema_id);
SDL_free(renderer);
}
static int
PS2_SetVSync(SDL_Renderer * renderer, const int vsync)
{
PS2_RenderData *data = (PS2_RenderData *)renderer->driverdata;
SDL_bool dynamicVsync = SDL_GetHintBoolean(SDL_HINT_PS2_DYNAMIC_VSYNC, SDL_FALSE);
data->vsync = vsync ? (dynamicVsync ? 2 : 1) : 0;
return 0;
}
static SDL_Renderer *
PS2_CreateRenderer(SDL_Window * window, Uint32 flags)
{
SDL_Renderer *renderer;
PS2_RenderData *data;
GSGLOBAL *gsGlobal;
ee_sema_t sema;
SDL_bool dynamicVsync;
renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
if (!renderer) {
SDL_OutOfMemory();
return NULL;
}
data = (PS2_RenderData *) SDL_calloc(1, sizeof(*data));
if (!data) {
PS2_DestroyRenderer(renderer);
SDL_OutOfMemory();
return NULL;
}
sema.init_count = 0;
sema.max_count = 1;
sema.option = 0;
vsync_sema_id = CreateSema(&sema);
gsGlobal = gsKit_init_global_custom(RENDER_QUEUE_OS_POOLSIZE, RENDER_QUEUE_PER_POOLSIZE);
gsGlobal->Mode = GS_MODE_NTSC;
gsGlobal->Height = 448;
gsGlobal->PSM = GS_PSM_CT24;
gsGlobal->PSMZ = GS_PSMZ_16S;
gsGlobal->ZBuffering = GS_SETTING_OFF;
gsGlobal->DoubleBuffering = GS_SETTING_ON;
gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
gsGlobal->Dithering = GS_SETTING_OFF;
gsKit_set_primalpha(gsGlobal, GS_SETREG_ALPHA(0, 1, 0, 1, 0), 0);
dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
dmaKit_chan_init(DMA_CHANNEL_GIF);
gsKit_set_clamp(gsGlobal, GS_CMODE_REPEAT);
gsKit_vram_clear(gsGlobal);
gsKit_init_screen(gsGlobal);
gsKit_TexManager_init(gsGlobal);
data->vsync_callback_id = gsKit_add_vsync_handler(vsync_handler);
gsKit_mode_switch(gsGlobal, GS_ONESHOT);
gsKit_clear(gsGlobal, GS_BLACK);
data->gsGlobal = gsGlobal;
dynamicVsync = SDL_GetHintBoolean(SDL_HINT_PS2_DYNAMIC_VSYNC, SDL_FALSE);
data->vsync = flags & SDL_RENDERER_PRESENTVSYNC ? (dynamicVsync ? 2 : 1) : 0;
renderer->WindowEvent = PS2_WindowEvent;
renderer->CreateTexture = PS2_CreateTexture;
renderer->UpdateTexture = PS2_UpdateTexture;
renderer->LockTexture = PS2_LockTexture;
renderer->UnlockTexture = PS2_UnlockTexture;
renderer->SetTextureScaleMode = PS2_SetTextureScaleMode;
renderer->SetRenderTarget = PS2_SetRenderTarget;
renderer->QueueSetViewport = PS2_QueueSetViewport;
renderer->QueueSetDrawColor = PS2_QueueSetViewport;
renderer->QueueDrawPoints = PS2_QueueDrawPoints;
renderer->QueueDrawLines = PS2_QueueDrawPoints;
renderer->QueueGeometry = PS2_QueueGeometry;
renderer->RunCommandQueue = PS2_RunCommandQueue;
renderer->RenderReadPixels = PS2_RenderReadPixels;
renderer->RenderPresent = PS2_RenderPresent;
renderer->DestroyTexture = PS2_DestroyTexture;
renderer->DestroyRenderer = PS2_DestroyRenderer;
renderer->SetVSync = PS2_SetVSync;
renderer->info = PS2_RenderDriver.info;
renderer->driverdata = data;
renderer->window = window;
return renderer;
}
SDL_RenderDriver PS2_RenderDriver = {
.CreateRenderer = PS2_CreateRenderer,
.info = {
.name = "PS2 gsKit",
.flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE,
.num_texture_formats = 2,
.texture_formats = {
[0] = SDL_PIXELFORMAT_ABGR1555,
[1] = SDL_PIXELFORMAT_ABGR8888,
},
.max_texture_width = 1024,
.max_texture_height = 1024,
}
};
#endif