Submitted by Admin on

Additional info on how to build lib manually and interesting information about porting PS3 to SDL3.

First article is available here https://16rom.com/en/blog/ps3-psl1ght-port-sdl3

Build manually

If you already have compiler PS3 development tools you can build SDL3 and stuff manually without using custom ps3libraries repository.

Get the latest https://github.com/onesixromcom/SDL/tree/ps3 code.

Download cmake file which is neede for building PS3 libraries with cmake. https://github.com/onesixromcom/ps3libraries/blob/master/depends/ps3build.cmake

For build SDL3 library run the commands

cd SDL
mkdir build-ppc && cd build-ppc
cmake -Wno-dev -DCMAKE_TOOLCHAIN_FILE="/path/to/ps3build.cmake" -DCMAKE_INSTALL_PREFIX="$PS3DEV/portlibs/ppu" -DCMAKE_PREFIX_PATH="$PS3DEV/portlibs/ppu" -DSDL_TESTS=OFF  -DSDL_EXAMPLES=OFF -DSDL_TEST_LIBRARY=OFF -DSDL_STATIC=ON \  ..
cmake --build . 
cmake --install .

Build SDL_mixer https://github.com/libsdl-org/SDL_mixer the same way with commands

cd SDL_mixer
mkdir build-ppc && cd build-ppc
cmake -Wno-dev -DCMAKE_TOOLCHAIN_FILE="/path/to/ps3build.cmake"  -DCMAKE_INSTALL_PREFIX="$PS3DEV/portlibs/ppu" -DCMAKE_PREFIX_PATH="$PS3DEV/portlibs/ppu" -DBUILD_SHARED_LIBS=OFF -DSDLMIXER_TESTS=OFF -DSDLMIXER_EXAMPLES=OFF -DSDL_STATIC=ON  -DSDLMIXER_OPUS=OFF -DSDLMIXER_VORBIS_VORBISFILE=OFF -DSDLMIXER_WAVPACK=OFF -DSDLMIXER_OGG=OFF -DSDLMIXER_FLAC=OFF -DSDLMIXER_MP3_MPG123=OFF -DSDLMIXER_GME=OFF -DSDLMIXER_MOD_XMP=OFF -DSDLMIXER_MIDI_TIMIDITY=OFF ..
cmake --build . 
cmake --install .

For SDL_ttf you should patch/edit CMakeLists.txt file to build static lib.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7b27db8..35e1251 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,7 +71,7 @@ include(GNUInstallDirs)
 include(CheckSymbolExists)
 
 set(PLATFORM_SUPPORTS_SHARED ON)
-if(EMSCRIPTEN OR VITA OR PSP OR PS2 OR N3DS OR RISCOS)
+if(EMSCRIPTEN OR VITA OR PSP OR PS2 OR PS3 OR N3DS OR RISCOS)
   set(PLATFORM_SUPPORTS_SHARED OFF)
 endif() 

and use the same flow with ps3build.cmake file.

cd SDL_ttf
./external/download.sh
mkdir build-ppc && cd build-ppc
cmake -Wno-dev -DCMAKE_TOOLCHAIN_FILE="/path/to/ps3build.cmake"  -DCMAKE_INSTALL_PREFIX="$PS3DEV/portlibs/ppu" -DCMAKE_PREFIX_PATH="$PS3DEV/portlibs/ppu" -DBUILD_SHARED_LIBS=OFF -DSDLTTF_VENDORED=ON -DSDLTTF_HARFBUZZ=OFF -DSDLTTF_SAMPLES=OFF ..
cmake --build . 
cmake --install .

What issues were found in porting PS3 for SDL3?

First of all I didn't wrote all the code from scratch. I've used ported version of PSL1GHT for SDL2.

https://github.com/ps3aqua/SDL2_PSL1GHT.git

https://github.com/NiQ1/SDL2_PS3.git

https://github.com/sergiou87/SDL2_PSL1GHT.git

They look almost the same with some changes between each other.

I've choose PS3 prefix insteam of psl1ght.

All of sdl2 psl1ght implementation do not draw simple graphics correct. There is an issue with used color in drawing. My implementaion draw points,lines and rectangles correct.

Also I've added a rumble support, which was missed in previous one.

Exit app was not implemented in correct way, since videoOutRegisterCallback should be used only to handle changing of the screen resolution. So I've splitted sysUtilRegisterCallback in two ways. One the main will handle app exit, the other one in PS3_events.c will handle XMB open/close events. Works nice.

Changing resolution in game is not implemented.

Mouse and Keyboard handling was not tested and it's not ready for now.

The most hard part.

I didn't read much about PS3 hardware when start to work on this project. I've replaced deprecated SDL functions with new one, fix issue with threads and implement SDL_RunApp() callbacks. But I stuck at implementing SDL_RENDERCMD_COPY command for renderer.

PSL1GHT documentaion lacks information about rsxSetTransferScaleSurface function. The only thing we have is from "rsx/commands_inc.h" file

/*! Initiate an asynchronous RSX blit.
\param context Pointer to the context object
\param scale Specify the transfer geometry & parameters.
\param surface Specify the surface to blit to.
*/
void RSX_FUNC(SetTransferScaleSurface)(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSurface *surface);

The issue comes with the gcmTransferScale object which have lot's of parameters.

/*! Specify scaled image blit geometry and format for rsxSetTransferImage() */
typedef struct _gcmTransferScale
{
	/*! Not sure what this dones. Possible values:
      - \ref GCM_TRANSFER_CONVERSION_DITHER
      - \ref GCM_TRANSFER_CONVERSION_TRUNCATE
      - \ref GCM_TRANSFER_CONVERSION_SUBTRACT_TRUNCATE
	 */	
	u32 conversion;

	/*! Format of image data. Possible values:
	  - \ref GCM_TRANSFER_SCALE_FORMAT_A1R5G5B5
	  - \ref GCM_TRANSFER_SCALE_FORMAT_X1R5G5B5
	  - \ref GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_X8R8G8B8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_CR8YB8CB8YA8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_YB8CR8YA8CB8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_R5G6B5
	  - \ref GCM_TRANSFER_SCALE_FORMAT_Y8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_AY8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_EYB8ECR8EYA8ECB8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_ECR8EYB8ECB8EYA8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_A8B8G8R8
	  - \ref GCM_TRANSFER_SCALE_FORMAT_X8B8G8R8
	 */
	u32 format;

	/*! Blit operation. Possible values:
	  - \ref GCM_TRANSFER_OPERATION_SRCCOPY_AND
	  - \ref GCM_TRANSFER_OPERATION_ROP_AND
	  - \ref GCM_TRANSFER_OPERATION_BLEND_AND
	  - \ref GCM_TRANSFER_OPERATION_SRCCOPY
	  - \ref GCM_TRANSFER_OPERATION_SRCCOPY_PREMULT
	  - \ref GCM_TRANSFER_OPERATION_BLEND_PREMULT
	 */
	u32 operation;

	/*! X origin of clipping rectangle, within the destination surface. */
	s16 clipX;

	/*! Y origin of clipping rectangle, within the destination surface. */
	s16 clipY;

	/*! Width of clipping rectangle, within the destination surface. */
	u16 clipW;

	/*! Height of clipping rectangle, within the destination surface. */
	u16 clipH;

	/*! X origin of destination rectangle. */
	s16 outX;

	/*! Y origin of destination rectangle. */
	s16 outY;

	/*! Width of the destination rectangle. */
	u16 outW;

	/*! Height of the destination rectangle. */
	u16 outH;

	/*! Ratio in X direction of the source rectangle size to the destination rectangle size, encoded as a 32-bit signed fixed-point number. Such a value can be obtained from a floating point number by rsxGetFixedSint32(). */
	s32 ratioX;

	/*! Ratio in Y direction of the source rectangle size to the destination rectangle size, encoded as a 32-bit signed fixed-point number. Such a value can be obtained from a floating point number by rsxGetFixedSint32(). */
	s32 ratioY;

	/*! Width of the source rectangle. */
	u16 inW;

	/*! Height of the source rectangle. */
	u16 inH;

	/*! Pitch size, in bytes, of the source image data (width multiplied by the number of bytes in each pixel). */
	u16 pitch;

	/*! How the origin of each pixel is determined. Possible values:
	 - \ref GCM_TRANSFER_ORIGIN_CENTER
	 - \ref GCM_TRANSFER_ORIGIN_CORNER
	*/
	u8 origin;

	/*! Sampling for scaled blits. Possible values:
	 - \ref GCM_TRANSFER_INTERPOLATOR_NEAREST: no interpolation
	 - \ref GCM_TRANSFER_INTERPOLATOR_LINEAR: bilinear interpolation
	*/
	u8 interp;

	/*! Image data offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). */
	u32 offset;

	/*! X origin of destination rectangle. */
	u16 inX;

	/*! Y origin of destination rectangle. */
	u16 inY;
} gcmTransferScale;

It's ok to copy surface in original size to the current buffer, no issues there. But the error (or freeze on PS3 hardware) appeares when destination is out of the screen. There will be memory Access violation. PS3 rsx can't draw surfaces outside the screen.

To be honest I've tried to solve this issue with AI first, but more questions I've asked the more gallucinations were produced by AI! Finally I've found leaked official docs of PS3SDK and after that everything become clear for me. Simple conditions fixed the issue and textures are drawn on the screen correct now!

gcmTransferScale scale;
scale.conversion = GCM_TRANSFER_CONVERSION_TRUNCATE;
scale.format = GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8;
scale.operation = GCM_TRANSFER_OPERATION_SRCCOPY;
scale.conversion = GCM_TRANSFER_CONVERSION_TRUNCATE;
scale.origin = GCM_TRANSFER_ORIGIN_CORNER;
scale.clipX = (s16)dstx;
scale.clipY = (s16)dsty;
scale.clipW = (u16)dstw;
scale.clipH = (u16)dsth;
scale.outX = (s16)dstx;
scale.outY = (s16)dsty;
scale.outW = (u16)dstw;
scale.outH = (u16)dsth;
scale.ratioX = rsxGetFixedSint32(dstw/srcw);
scale.ratioY = rsxGetFixedSint32(dsth/srch);
scale.inX = rsxGetFixedUint16(srcx);
scale.inY = rsxGetFixedUint16(srcy);
scale.inW = (u16)(srcw & ~1);
scale.inH = (u16)srch;
scale.offset = src_offset;
scale.pitch = surface_src->pitch;

// Fix offscreen drawing
if (dstx < 0 || dstx + dstw > (s32)surface->w) {
	scale.clipX = SDL_max(dstx, 0);
	scale.outX = scale.clipX;
	scale.clipW = SDL_min(dstw + SDL_min(dstx, 0), surface->w - scale.clipX);
	scale.outW = scale.clipW;
	if (dstx < 0) {
		scale.inX = rsxGetFixedUint16(srcx + (SDL_min(dstx, 0) * -1));
	}
}
if (dsty < 0 || dsty + dsth > (s32)surface->h) {
	scale.clipY = SDL_max(dsty, 0);
	scale.outY = scale.clipY;
	scale.clipH = SDL_min(dsth + SDL_min(dsty, 0), surface->h - scale.clipY);
	scale.outH = scale.clipH;
	if (dsty < 0) {
		scale.inY = rsxGetFixedUint16(srcy + (SDL_min(dsty, 0) * -1));
	}
}

Other issue was with setup with new flow with SDL_MAIN_USE_CALLBACKS. The issue was with OPD. On PS3's PPC64 ABI, function pointers are actually descriptors (a struct with the real address + TOC pointer), not raw addresses. If SDL is calling them as plain pointers it will jump to garbage.PSL1GHT uses PPU ELF ABI where function pointers go through an OPD (Object Procedure Descriptor) section. If SDL's callback table stores/calls function pointers without respecting OPD, it jumps to the descriptor address instead of the actual code.

extern SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]);
extern SDL_AppResult SDL_AppIterate(void *appstate);
extern SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event);
extern void SDL_AppQuit(void *appstate, SDL_AppResult result);

SDL_AppResult result = SDL_InitMainCallbacks(argc, argv,
	&SDL_AppInit,
	&SDL_AppIterate,
	&SDL_AppEvent,
	&SDL_AppQuit
);

What about joystick?

PS3 gamepad was a bit tricky to handle. To add a gamepad to the app use the SDL_AppEvent and standard add gamepad flow.

SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
    if (event->type == SDL_EVENT_GAMEPAD_ADDED)
    {
        AddGamepad();
    }
}

I've implemented rumble function and joystick as a gamepad.

Rumble is working on real PS3 with simple code.

SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
    if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
        if (event->gbutton.button == SDL_GAMEPAD_BUTTON_SOUTH) {
            SDL_RumbleGamepad(gamepad, 144, 1 , 1000);
        }
    }
}

Sound is also working!

In SDL_AppInit function use the code from example:

SDL_AudioSpec spec;

if (!SDL_Init(SDL_INIT_AUDIO)) {
	return SDL_APP_FAILURE;
}

spec.channels = 1;
spec.format = SDL_AUDIO_F32;
spec.freq = 8000;
stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL);
if (!stream) {
	SDL_Log("Couldn't create audio stream: %s", SDL_GetError());
	return SDL_APP_FAILURE;
}
SDL_ResumeAudioStreamDevice(stream);

Debugging

For debug in emulator it's ok to use printf();flush(stdout);

On real device you can try to use libdebugger or custom solution via sockets. You can find it here https://github.com/ps3dev/PSL1GHT/tree/master/samples/network/debugtest

To load ELF files via network use ps3load program from /usr/local/ps3dev/bin/ps3load and installeg PKG on PS3 ps3loadx from this repository https://github.com/bucanero/ps3loadx.git

Conclusion

It was fun.

PCSX3 emulator is very nice for fast testing on local machine. And it handle lot's of error of the incorrect PS3 flows implementations. The app that working fine in emulator could not work on real hardware at all. This was the biggest issue when I was testing render copy command.

I've read SDL's code a lot, debug lot of steps on how it's working. It was very interesing.

My nights were sleepless.

It was really fun to see that it's working on real hardware without issues.

Repo is still here https://github.com/onesixromcom/SDL/tree/ps3. I'm not sure that it will be added to main SDL branch but we'll see.

Next challange is to port my engine to PS3.

Tags