Submitted by Admin on

One more adventure with porting SDL3 to PS3.

Well last time I spent lots of time to find out why textures are not rendered correct and lots of fatal errors occured. Well that was done and fixed.

Then when I run my game I saw the biggest (for me) issue - transparency flag in images was not recognized by RSX. It just render an image in the black rectange. That was awful.

And I've decided it's time figure out how to use RSX instead of simple SDL_Surface to handle drawing.

Oh my.. if I would know at the beginning how much pain I will receive from it I didnt start at all. Reading all those docs and examples for ps3 dev were really useful. But wihout the help of claude it was a mess and I would spent much more time on it. I was using a free chat. no cli. That gives me a time to understand waht's going on. All code was placed and edited manually.

First we need a verted and fragment programs.

texture.vsh

void main(
    float3 position : POSITION,
    float2 texcoord : TEXCOORD0,

    uniform float4x4 modelViewProj,

    out float4 oPosition : POSITION,
    out float2 oTexcoord : TEXCOORD0
)
{
    oPosition = mul(float4(position, 1.0), modelViewProj);
    oTexcoord = texcoord;
}

texture.fsh

void main(
    float2 texcoord : TEXCOORD0,

    uniform sampler2D tex : TEXUNIT0,

    out float4 color : COLOR
)
{
    color = tex2D(tex, texcoord);

}

then compile them and create a header file to make it available in ram (and prevent file loading for sure).

${PS3DEV}/bin/cgcomp -f SDL_FOLDER/src/render/ps3/texture.fsh SDL_FOLDER/src/render/ps3/texture.fpo
${PS3DEV}/bin/cgcomp -v SDL_FOLDER/src/render/ps3/texture.vsh SDL_FOLDER/src/render/ps3/texture.vpo

xxd -n ps3_texture_vpo -i SDL_FOLDERL/src/render/ps3/texture.vpo  > SDL_FOLDER/src/render/ps3/ps3_texture_vpo.h
xxd -n ps3_texture_fpo -i SDL_FOLDER/src/render/ps3/texture.fpo  > SDL_FOLDER/src/render/ps3/ps3_texture_fpo.h

now we can include them and try load load into RSX

#include "./ps3_texture_vpo.h"
#include "./ps3_texture_fpo.h"

typedef struct {
    float x, y, z;
    float u, v;
} TexVertex;

...

    data->vpo = (rsxVertexProgram *)ps3_texture_vpo;
    data->fpo = (rsxFragmentProgram *)ps3_texture_fpo;

    rsxVertexProgramGetUCode(data->vpo, &data->vp_ucode, &data->vp_ucode_size);
    rsxLoadVertexProgram(data->context, data->vpo, data->vp_ucode);

    rsxFragmentProgramGetUCode(data->fpo, &data->fp_ucode_cpu, &data->fp_ucode_size);

    void *fp_ucode_rsx = rsxMemalign(64, data->fp_ucode_size);
    memcpy(fp_ucode_rsx, data->fp_ucode_cpu, data->fp_ucode_size);

    rsxAddressToOffset(fp_ucode_rsx, &data->fp_offset);

    rsxLoadFragmentProgramLocation(data->context, data->fpo, data->fp_offset, GCM_LOCATION_RSX);

To draw a texture this function was created (unpolished version)

void PS3_DrawTexturedQuad(PS3_RenderData *data, PS3_TextureData *tdata,
                           float dstx, float dsty, float dstw, float dsth,
                           float srcx, float srcy, float srcw, float srch)
{
    TexVertex *quad = (TexVertex *)rsxMemalign(16, 4 * sizeof(TexVertex));

    float x0 = dstx,        y0 = dsty;
    float x1 = dstx + dstw, y1 = dsty + dsth;

    float u0 = srcx / (float)tdata->rsx_texture.width;
    float v0 = srcy / (float)tdata->rsx_texture.height;
    float u1 = (srcx + srcw) / (float)tdata->rsx_texture.width;
    float v1 = (srcy + srch) / (float)tdata->rsx_texture.height;

    quad[0] = (TexVertex){ x0, y0, 0.0f, u0, v0 };
    quad[1] = (TexVertex){ x1, y0, 0.0f, u1, v0 };
    quad[2] = (TexVertex){ x0, y1, 0.0f, u0, v1 };
    quad[3] = (TexVertex){ x1, y1, 0.0f, u1, v1 };

    u32 quad_offset;
    rsxAddressToOffset(quad, &quad_offset);

    rsxLoadVertexProgram(data->context, data->vpo, data->vp_ucode);
    rsxLoadFragmentProgramLocation(data->context, data->fpo, data->fp_offset, GCM_LOCATION_RSX);

    rsxBindVertexArrayAttrib(data->context, 0, 0,
        quad_offset + offsetof(TexVertex, x),
        sizeof(TexVertex), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);

    rsxBindVertexArrayAttrib(data->context, 8, 0,
        quad_offset + offsetof(TexVertex, u),
        sizeof(TexVertex), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);

    const rsxProgramConst *mvp_const = rsxVertexProgramGetConst(data->vpo, "modelViewProj");
    if (!mvp_const) {
        SDL_Log("modelViewProj constant not found in vertex program!");
    }

    rsxSetVertexProgramParameter(data->context, data->vpo, mvp_const, data->ortho_matrix);

    // Texture + sampler state
    rsxLoadTexture(data->context, 0, &tdata->rsx_texture);
    rsxTextureControl(data->context, 0, GCM_TRUE, 0, 12 << 8, GCM_TEXTURE_MAX_ANISO_1);
    rsxTextureFilter(data->context, 0, 0, GCM_TEXTURE_LINEAR, GCM_TEXTURE_LINEAR, GCM_TEXTURE_CONVOLUTION_QUINCUNX);
    rsxTextureWrapMode(data->context, 0, GCM_TEXTURE_CLAMP_TO_EDGE, GCM_TEXTURE_CLAMP_TO_EDGE,
                        GCM_TEXTURE_CLAMP_TO_EDGE, 0, GCM_TEXTURE_ZFUNC_LESS, 0);

    // Blend state (only needs setting once per state change, not necessarily every draw,
    //    but harmless to set redundantly while getting this working)
    rsxSetBlendFunc(data->context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA,
                                    GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA);
    rsxSetBlendEquation(data->context, GCM_FUNC_ADD, GCM_FUNC_ADD);
    rsxSetBlendEnable(data->context, GCM_TRUE);

    // Draw
    rsxDrawVertexArray(data->context, GCM_TYPE_TRIANGLE_STRIP, 0, 4);
}

And in render queue it appears like this

typedef struct
{
    SDL_FRect srcRect;
    SDL_FRect dstRect;
} PS3_CopyData;
        case SDL_RENDERCMD_COPY:
        case SDL_RENDERCMD_COPY_EX:
        {
            PS3_TextureData *tdata = (PS3_TextureData*)cmd->data.draw.texture->internal;

            PS3_CopyData copyData;
            SDL_memcpy(&copyData, ((Uint8 *)vertices) + cmd->data.draw.first, sizeof(PS3_CopyData));

            SDL_FRect *srcrect = &copyData.srcRect;
            SDL_FRect *dstrect = &copyData.dstRect;

            PS3_DrawTexturedQuad(data, tdata,
                dstrect->x, dstrect->y, dstrect->w, dstrect->h,
                srcrect->x, srcrect->y, srcrect->w, srcrect->h
            );
            break;
        }

Same thing should be done for simple primitives drawing:

typedef struct {
    float x, y, z;
    float r, g, b, a;
} ColorVertex;

To draw lines with RSX

    SDL_SetHintWithPriority(SDL_HINT_RENDER_LINE_METHOD, "2", SDL_HINT_OVERRIDE);

Points, Lines and Rectangles drawn by this function

void PS3_DrawColoredPrimitive(PS3_RenderData *data, u8 primitive_type,
                               ColorVertex *verts, u32 count,
                               Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
    ColorVertex *gpu_verts = (ColorVertex *)rsxMemalign(16, count * sizeof(ColorVertex));
    memcpy(gpu_verts, verts, count * sizeof(ColorVertex));

    u32 vert_offset;
    rsxAddressToOffset(gpu_verts, &vert_offset);

    rsxLoadVertexProgram(data->context, data->vpo_color, data->vp_ucode_color);
    rsxLoadFragmentProgramLocation(data->context, data->fpo_color, data->fp_offset_color, GCM_LOCATION_RSX);

    const rsxProgramConst *mvp_const = rsxVertexProgramGetConst(data->vpo_color, "modelViewProj");
    rsxSetVertexProgramParameter(data->context, data->vpo_color, mvp_const, data->ortho_matrix);

    const rsxProgramAttrib *pos_attr = rsxVertexProgramGetAttrib(data->vpo_color, "position");
    const rsxProgramAttrib *col_attr = rsxVertexProgramGetAttrib(data->vpo_color, "color");

    rsxBindVertexArrayAttrib(data->context, pos_attr->index, 0,
        vert_offset + offsetof(ColorVertex, x),
        sizeof(ColorVertex), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);
    rsxBindVertexArrayAttrib(data->context, col_attr->index, 0,
        vert_offset + offsetof(ColorVertex, r),
        sizeof(ColorVertex), 4, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX);

    rsxSetBlendFunc(data->context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA,
                                    GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA);
    rsxSetBlendEquation(data->context, GCM_FUNC_ADD, GCM_FUNC_ADD);
    // TODO: use blen only when need
    // rsxSetBlendEnable(data->context, renderer->blendMode == SDL_BLENDMODE_NONE ? GCM_FALSE : GCM_TRUE);
    rsxSetBlendEnable(data->context, GCM_FALSE);
    // TODO: control pixel size?
    // rsxSetPointSize(data->context, 4.0f);
    rsxDrawVertexArray(data->context, primitive_type, 0, count);
}

Source code is not very optimized. Possibly I forget to release some memory. But for this time I'm happy that it's working (at least in emulator).

Also music is working too.

https://github.com/onesixromcom/SDL/tree/ps3-rsx

TODO

  • Textures are not updated in some cases.
  • Handle drawing of viewport correct for clear screen.

Video file