Renders on PSP
This commit is contained in:
@@ -52,8 +52,9 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
|||||||
set(_SAT_C_FLAGS "-m2 -mb -fno-builtin -fomit-frame-pointer")
|
set(_SAT_C_FLAGS "-m2 -mb -fno-builtin -fomit-frame-pointer")
|
||||||
set(CMAKE_C_FLAGS_INIT "${_SAT_C_FLAGS}")
|
set(CMAKE_C_FLAGS_INIT "${_SAT_C_FLAGS}")
|
||||||
|
|
||||||
# Yaul provides its own startup code and linker script.
|
# Yaul installs yaul.specs to ${YAUL_INSTALL_ROOT}/sh2eb-elf/lib/ and
|
||||||
# The kernel.ld script maps Saturn Work RAM (0x06000000+).
|
# ldscripts/yaul.x to ${YAUL_INSTALL_ROOT}/sh2eb-elf/lib/ldscripts/.
|
||||||
set(_YAUL_LD "${YAUL_INSTALL_ROOT}/share/yaul/ip/kernel.ld")
|
# GCC searches ${prefix}/${target}/lib/ for specs, so -specs=yaul.specs works
|
||||||
|
# without an absolute path once the SDK is installed.
|
||||||
set(CMAKE_EXE_LINKER_FLAGS_INIT
|
set(CMAKE_EXE_LINKER_FLAGS_INIT
|
||||||
"-T\"${_YAUL_LD}\" -Wl,--start-group -Wl,--end-group -nostartfiles")
|
"-specs=yaul.specs -Wl,--gc-sections -nostartfiles -Wl,--start-group -Wl,--end-group")
|
||||||
|
|||||||
+26
-54
@@ -18,7 +18,6 @@ ENV PATH="${YAUL_INSTALL_ROOT}/bin:${PATH}"
|
|||||||
# Toolchain source versions
|
# Toolchain source versions
|
||||||
ARG BINUTILS_VER=2.40
|
ARG BINUTILS_VER=2.40
|
||||||
ARG GCC_VER=12.3.0
|
ARG GCC_VER=12.3.0
|
||||||
ARG NEWLIB_VER=4.3.0.20230120
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# 1. Host build tools
|
# 1. Host build tools
|
||||||
@@ -51,11 +50,9 @@ RUN mkdir -p "${YAUL_INSTALL_ROOT}"
|
|||||||
RUN cd /tmp && \
|
RUN cd /tmp && \
|
||||||
wget -q "https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VER}.tar.xz" && \
|
wget -q "https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VER}.tar.xz" && \
|
||||||
wget -q "https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VER}/gcc-${GCC_VER}.tar.xz" && \
|
wget -q "https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VER}/gcc-${GCC_VER}.tar.xz" && \
|
||||||
wget -q "ftp://sourceware.org/pub/newlib/newlib-${NEWLIB_VER}.tar.gz" && \
|
|
||||||
tar xf "binutils-${BINUTILS_VER}.tar.xz" && \
|
tar xf "binutils-${BINUTILS_VER}.tar.xz" && \
|
||||||
tar xf "gcc-${GCC_VER}.tar.xz" && \
|
tar xf "gcc-${GCC_VER}.tar.xz" && \
|
||||||
tar xf "newlib-${NEWLIB_VER}.tar.gz" && \
|
rm "binutils-${BINUTILS_VER}.tar.xz" "gcc-${GCC_VER}.tar.xz"
|
||||||
rm "binutils-${BINUTILS_VER}.tar.xz" "gcc-${GCC_VER}.tar.xz" "newlib-${NEWLIB_VER}.tar.gz"
|
|
||||||
|
|
||||||
# Download GCC prerequisites (gmp, mpfr, mpc if not packaged)
|
# Download GCC prerequisites (gmp, mpfr, mpc if not packaged)
|
||||||
RUN cd /tmp/gcc-${GCC_VER} && contrib/download_prerequisites
|
RUN cd /tmp/gcc-${GCC_VER} && contrib/download_prerequisites
|
||||||
@@ -98,35 +95,10 @@ RUN mkdir -p /tmp/build-sh-gcc1 && cd /tmp/build-sh-gcc1 && \
|
|||||||
&& make install-gcc install-target-libgcc && \
|
&& make install-gcc install-target-libgcc && \
|
||||||
rm -rf /tmp/build-sh-gcc1
|
rm -rf /tmp/build-sh-gcc1
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# Newlib does not recognise the sh2eb CPU name, and Yaul ships its own C
|
||||||
# 5. newlib for sh2eb-elf (C runtime for embedded SH-2)
|
# runtime in libyaul/libc/ anyway. Stage 1 (compiler + libgcc) is all
|
||||||
# ---------------------------------------------------------------------------
|
# we need; Yaul's specs file overrides *startfile:/*endfile:/*lib: to empty
|
||||||
RUN mkdir -p /tmp/build-sh-newlib && cd /tmp/build-sh-newlib && \
|
# so nothing from a host C library is linked in.
|
||||||
/tmp/newlib-${NEWLIB_VER}/configure \
|
|
||||||
--target=sh2eb-elf \
|
|
||||||
--prefix="${YAUL_INSTALL_ROOT}" \
|
|
||||||
--disable-newlib-supplied-syscalls \
|
|
||||||
--enable-newlib-reent-small \
|
|
||||||
&& make -j"$(nproc)" && make install && \
|
|
||||||
rm -rf /tmp/build-sh-newlib
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# 6. sh2eb-elf GCC stage 2 (full build with newlib)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
RUN mkdir -p /tmp/build-sh-gcc2 && cd /tmp/build-sh-gcc2 && \
|
|
||||||
/tmp/gcc-${GCC_VER}/configure \
|
|
||||||
--target=sh2eb-elf \
|
|
||||||
--prefix="${YAUL_INSTALL_ROOT}" \
|
|
||||||
--enable-languages=c,c++ \
|
|
||||||
--with-newlib \
|
|
||||||
--disable-nls \
|
|
||||||
--disable-shared \
|
|
||||||
--disable-multilib \
|
|
||||||
--disable-libssp \
|
|
||||||
--disable-libgomp \
|
|
||||||
--disable-libquadmath \
|
|
||||||
&& make -j"$(nproc)" && make install && \
|
|
||||||
rm -rf /tmp/build-sh-gcc2
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# 7. m68k-elf binutils (Saturn 68EC000 sound CPU)
|
# 7. m68k-elf binutils (Saturn 68EC000 sound CPU)
|
||||||
@@ -159,7 +131,7 @@ RUN mkdir -p /tmp/build-m68k-gcc && cd /tmp/build-m68k-gcc && \
|
|||||||
rm -rf /tmp/build-m68k-gcc
|
rm -rf /tmp/build-m68k-gcc
|
||||||
|
|
||||||
# Clean up source tarballs/trees
|
# Clean up source tarballs/trees
|
||||||
RUN rm -rf /tmp/binutils-${BINUTILS_VER} /tmp/gcc-${GCC_VER} /tmp/newlib-${NEWLIB_VER}
|
RUN rm -rf /tmp/binutils-${BINUTILS_VER} /tmp/gcc-${GCC_VER}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# 9. Create m68keb-elf symlinks
|
# 9. Create m68keb-elf symlinks
|
||||||
@@ -189,25 +161,11 @@ RUN git clone --depth 1 --recurse-submodules \
|
|||||||
rm -rf /tmp/yaul /tmp/yaul-build
|
rm -rf /tmp/yaul /tmp/yaul-build
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# 11. Cross-compile zlib for sh2eb-elf
|
# 11. Cross-compile zlib for sh2eb-elf via CMake
|
||||||
# Install into ${YAUL_INSTALL_ROOT}/sh2eb-elf/ to match the Yaul sysroot
|
# Using CMake (not ./configure) so we can set
|
||||||
# layout: headers at .../sh2eb-elf/include, libs at .../sh2eb-elf/lib.
|
# CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY and skip link tests that
|
||||||
# ---------------------------------------------------------------------------
|
# the bare-metal cross-compiler can't satisfy.
|
||||||
RUN wget -q https://zlib.net/zlib-1.3.1.tar.gz -O /tmp/zlib.tar.gz && \
|
# Install into ${YAUL_INSTALL_ROOT}/sh2eb-elf/ (Yaul sysroot layout).
|
||||||
tar xf /tmp/zlib.tar.gz -C /tmp && \
|
|
||||||
cd /tmp/zlib-1.3.1 && \
|
|
||||||
CC="${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-gcc" \
|
|
||||||
AR="${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-ar" \
|
|
||||||
RANLIB="${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-ranlib" \
|
|
||||||
CFLAGS="-m2 -mb -fno-builtin -O2" \
|
|
||||||
./configure \
|
|
||||||
--prefix="${YAUL_INSTALL_ROOT}/sh2eb-elf" \
|
|
||||||
--static \
|
|
||||||
&& make -j"$(nproc)" && make install && \
|
|
||||||
rm -rf /tmp/zlib-1.3.1 /tmp/zlib.tar.gz
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# 12. Cross-compile libzip for sh2eb-elf
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
RUN printf '%s\n' \
|
RUN printf '%s\n' \
|
||||||
'set(CMAKE_SYSTEM_NAME Generic)' \
|
'set(CMAKE_SYSTEM_NAME Generic)' \
|
||||||
@@ -221,6 +179,20 @@ RUN printf '%s\n' \
|
|||||||
'set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)' \
|
'set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)' \
|
||||||
> /tmp/sat-xc.cmake
|
> /tmp/sat-xc.cmake
|
||||||
|
|
||||||
|
RUN wget -q https://zlib.net/zlib-1.3.1.tar.gz -O /tmp/zlib.tar.gz && \
|
||||||
|
tar xf /tmp/zlib.tar.gz -C /tmp && \
|
||||||
|
cmake -S /tmp/zlib-1.3.1 -B /tmp/zlib-build \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=/tmp/sat-xc.cmake \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${YAUL_INSTALL_ROOT}/sh2eb-elf" \
|
||||||
|
-DCMAKE_C_FLAGS="-m2 -mb -fno-builtin -O2" \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
&& cmake --build /tmp/zlib-build -- -j"$(nproc)" \
|
||||||
|
&& cmake --install /tmp/zlib-build \
|
||||||
|
&& rm -rf /tmp/zlib-1.3.1 /tmp/zlib.tar.gz /tmp/zlib-build
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 12. Cross-compile libzip for sh2eb-elf (reuses /tmp/sat-xc.cmake above)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
RUN wget -q https://libzip.org/download/libzip-1.10.1.tar.gz -O /tmp/libzip.tar.gz && \
|
RUN wget -q https://libzip.org/download/libzip-1.10.1.tar.gz -O /tmp/libzip.tar.gz && \
|
||||||
tar xf /tmp/libzip.tar.gz -C /tmp && \
|
tar xf /tmp/libzip.tar.gz -C /tmp && \
|
||||||
cmake -S /tmp/libzip-1.10.1 -B /tmp/libzip-build \
|
cmake -S /tmp/libzip-1.10.1 -B /tmp/libzip-build \
|
||||||
@@ -242,7 +214,7 @@ RUN wget -q https://libzip.org/download/libzip-1.10.1.tar.gz -O /tmp/libzip.tar.
|
|||||||
-DBUILD_TOOLS=OFF \
|
-DBUILD_TOOLS=OFF \
|
||||||
&& cmake --build /tmp/libzip-build -- -j"$(nproc)" \
|
&& cmake --build /tmp/libzip-build -- -j"$(nproc)" \
|
||||||
&& cmake --install /tmp/libzip-build \
|
&& cmake --install /tmp/libzip-build \
|
||||||
&& rm -rf /tmp/libzip-1.10.1 /tmp/libzip.tar.gz /tmp/libzip-build /tmp/sat-xc.cmake
|
&& rm -rf /tmp/libzip-1.10.1 /tmp/libzip.tar.gz /tmp/libzip-build /tmp/sat-xc.cmake /tmp/zlib.tar.gz 2>/dev/null || true
|
||||||
|
|
||||||
WORKDIR /workdir
|
WORKDIR /workdir
|
||||||
VOLUME ["/workdir"]
|
VOLUME ["/workdir"]
|
||||||
|
|||||||
@@ -71,33 +71,33 @@ rtexture_t renderTextureCreate(
|
|||||||
uint16_t w, uint16_t h,
|
uint16_t w, uint16_t h,
|
||||||
const uint8_t *indices, const color_t *palette
|
const uint8_t *indices, const color_t *palette
|
||||||
) {
|
) {
|
||||||
#ifdef renderPlatformTextureCreate
|
#ifdef renderPlatformTextureCreate
|
||||||
return renderPlatformTextureCreate(w, h, indices, palette);
|
return renderPlatformTextureCreate(w, h, indices, palette);
|
||||||
#else
|
#else
|
||||||
return RTEXTURE_NONE;
|
return RTEXTURE_NONE;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderTextureDispose(rtexture_t tex) {
|
void renderTextureDispose(rtexture_t tex) {
|
||||||
#ifdef renderPlatformTextureDispose
|
#ifdef renderPlatformTextureDispose
|
||||||
renderPlatformTextureDispose(tex);
|
renderPlatformTextureDispose(tex);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
color_t *renderTextureGetPalette(rtexture_t tex) {
|
color_t *renderTextureGetPalette(rtexture_t tex) {
|
||||||
#ifdef renderPlatformTextureGetPalette
|
#ifdef renderPlatformTextureGetPalette
|
||||||
return renderPlatformTextureGetPalette(tex);
|
return renderPlatformTextureGetPalette(tex);
|
||||||
#else
|
#else
|
||||||
return NULL;
|
return NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *renderTextureGetIndices(rtexture_t tex) {
|
uint8_t *renderTextureGetIndices(rtexture_t tex) {
|
||||||
#ifdef renderPlatformTextureGetIndices
|
#ifdef renderPlatformTextureGetIndices
|
||||||
return renderPlatformTextureGetIndices(tex);
|
return renderPlatformTextureGetIndices(tex);
|
||||||
#else
|
#else
|
||||||
return NULL;
|
return NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
rtilemapchunk_t renderTilemapChunkCreate(
|
rtilemapchunk_t renderTilemapChunkCreate(
|
||||||
@@ -106,19 +106,19 @@ rtilemapchunk_t renderTilemapChunkCreate(
|
|||||||
rtexture_t tileset,
|
rtexture_t tileset,
|
||||||
const uint8_t *tileIndices
|
const uint8_t *tileIndices
|
||||||
) {
|
) {
|
||||||
#ifdef renderPlatformTilemapChunkCreate
|
#ifdef renderPlatformTilemapChunkCreate
|
||||||
return renderPlatformTilemapChunkCreate(
|
return renderPlatformTilemapChunkCreate(
|
||||||
chunkW, chunkH, tileW, tileH, tileset, tileIndices
|
chunkW, chunkH, tileW, tileH, tileset, tileIndices
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
return RTILEMAPCHUNK_INVALID;
|
return RTILEMAPCHUNK_INVALID;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderTilemapChunkDispose(rtilemapchunk_t chunk) {
|
void renderTilemapChunkDispose(rtilemapchunk_t chunk) {
|
||||||
#ifdef renderPlatformTilemapChunkDispose
|
#ifdef renderPlatformTilemapChunkDispose
|
||||||
renderPlatformTilemapChunkDispose(chunk);
|
renderPlatformTilemapChunkDispose(chunk);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderTilemapChunk(
|
void renderTilemapChunk(
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH ((int32_t)DUSK_DISPLAY_WIDTH)
|
||||||
|
#define SCREEN_HEIGHT ((int32_t)DUSK_DISPLAY_HEIGHT)
|
||||||
@@ -35,7 +35,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
errorChain(sceneInit());
|
errorChain(sceneInit());
|
||||||
|
|
||||||
consolePrint("Engine initialized");
|
consolePrint("Engine initialized");
|
||||||
sceneSet(SCENE_TYPE_TEST);
|
sceneSet(SCENE_TYPE_WHITE32);
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(overworld)
|
add_subdirectory(overworld)
|
||||||
add_subdirectory(test)
|
add_subdirectory(rainbownothing)
|
||||||
|
add_subdirectory(white32)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
scenerainbownothing.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scene/rainbownothing/scenerainbownothing.h"
|
||||||
|
#include "display/render/render.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
|
||||||
|
#define RAINBOW_PERIOD 60
|
||||||
|
#define RAINBOW_COUNT 6
|
||||||
|
|
||||||
|
static const color_t RAINBOW_COLORS[RAINBOW_COUNT] = {
|
||||||
|
{ 255, 0, 0, 255 }, /* red */
|
||||||
|
{ 255, 128, 0, 255 }, /* orange */
|
||||||
|
{ 255, 255, 0, 255 }, /* yellow */
|
||||||
|
{ 0, 255, 0, 255 }, /* green */
|
||||||
|
{ 0, 0, 255, 255 }, /* blue */
|
||||||
|
{ 148, 0, 211, 255 }, /* violet */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int32_t sceneRainbowNothingFrame;
|
||||||
|
|
||||||
|
errorret_t sceneRainbowNothingInit(scenedata_t *data) {
|
||||||
|
sceneRainbowNothingFrame = 0;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneRainbowNothingUpdate(scenedata_t *data) {
|
||||||
|
sceneRainbowNothingFrame++;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneRainbowNothingRender(scenedata_t *data) {
|
||||||
|
int32_t step = (sceneRainbowNothingFrame / RAINBOW_PERIOD) % RAINBOW_COUNT;
|
||||||
|
renderClear(RAINBOW_COLORS[step]);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneRainbowNothingDispose(scenedata_t *data) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "scene/scenebase.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t _unused;
|
||||||
|
} scenerainbownothing_t;
|
||||||
|
|
||||||
|
errorret_t sceneRainbowNothingInit(scenedata_t *data);
|
||||||
|
errorret_t sceneRainbowNothingUpdate(scenedata_t *data);
|
||||||
|
errorret_t sceneRainbowNothingRender(scenedata_t *data);
|
||||||
|
errorret_t sceneRainbowNothingDispose(scenedata_t *data);
|
||||||
@@ -17,10 +17,17 @@ scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = {
|
|||||||
.dispose = sceneOverworldDispose
|
.dispose = sceneOverworldDispose
|
||||||
},
|
},
|
||||||
|
|
||||||
[SCENE_TYPE_TEST] = {
|
[SCENE_TYPE_RAINBOWNOTHING] = {
|
||||||
.init = sceneTestInit,
|
.init = sceneRainbowNothingInit,
|
||||||
.update = sceneTestUpdate,
|
.update = sceneRainbowNothingUpdate,
|
||||||
.render = sceneTestRender,
|
.render = sceneRainbowNothingRender,
|
||||||
.dispose = sceneTestDispose
|
.dispose = sceneRainbowNothingDispose
|
||||||
|
},
|
||||||
|
|
||||||
|
[SCENE_TYPE_WHITE32] = {
|
||||||
|
.init = sceneWhite32Init,
|
||||||
|
.update = sceneWhite32Update,
|
||||||
|
.render = sceneWhite32Render,
|
||||||
|
.dispose = sceneWhite32Dispose
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "scene/scenebase.h"
|
#include "scene/scenebase.h"
|
||||||
#include "scene/overworld/sceneoverworld.h"
|
#include "scene/overworld/sceneoverworld.h"
|
||||||
#include "scene/test/scenetest.h"
|
#include "scene/rainbownothing/scenerainbownothing.h"
|
||||||
|
#include "scene/white32/scenewhite32.h"
|
||||||
|
|
||||||
typedef union scenedata_u {
|
typedef union scenedata_u {
|
||||||
sceneoverworld_t overworld;
|
sceneoverworld_t overworld;
|
||||||
scenetest_t test;
|
scenerainbownothing_t rainbownothing;
|
||||||
|
scenewhite32_t white32;
|
||||||
} scenedata_t;
|
} scenedata_t;
|
||||||
|
|
||||||
typedef errorret_t (*scenecallback_t)(scenedata_t *);
|
typedef errorret_t (*scenecallback_t)(scenedata_t *);
|
||||||
@@ -27,7 +29,8 @@ typedef struct {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
SCENE_TYPE_NULL,
|
SCENE_TYPE_NULL,
|
||||||
SCENE_TYPE_OVERWORLD,
|
SCENE_TYPE_OVERWORLD,
|
||||||
SCENE_TYPE_TEST,
|
SCENE_TYPE_RAINBOWNOTHING,
|
||||||
|
SCENE_TYPE_WHITE32,
|
||||||
SCENE_TYPE_COUNT
|
SCENE_TYPE_COUNT
|
||||||
} scenetype_t;
|
} scenetype_t;
|
||||||
|
|
||||||
|
|||||||
@@ -6,211 +6,31 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "scene/test/scenetest.h"
|
#include "scene/test/scenetest.h"
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/render/render.h"
|
#include "display/render/render.h"
|
||||||
#include "display/color.h"
|
#include "display/color.h"
|
||||||
#include "time/time.h"
|
#include "engine/engine.h"
|
||||||
#include "util/fixed.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
/* Initial data for the 3×3 test texture. After create, we write directly
|
#define SCENE_TEST_FRAMES 200
|
||||||
* to the texture's CPU buffers via renderTextureGet* — these statics
|
|
||||||
* are only referenced in sceneTestInit. */
|
|
||||||
static const uint8_t initIndices[3 * 3] = {
|
|
||||||
0, 1, 2,
|
|
||||||
3, 4, 5,
|
|
||||||
6, 7, 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const color_t initPalette[256] = {
|
static int32_t sceneTestFrame;
|
||||||
[0] = { 255, 0, 0, 255 }, /* red */
|
|
||||||
[1] = { 0, 255, 0, 255 }, /* green */
|
|
||||||
[2] = { 0, 0, 255, 255 }, /* blue */
|
|
||||||
[3] = { 255, 255, 0, 255 }, /* yellow */
|
|
||||||
[4] = { 255, 0, 255, 255 }, /* magenta */
|
|
||||||
[5] = { 0, 255, 255, 255 }, /* cyan */
|
|
||||||
[6] = { 255, 165, 0, 255 }, /* orange */
|
|
||||||
[7] = { 128, 0, 255, 255 }, /* purple */
|
|
||||||
[8] = { 255, 255, 255, 255 }, /* white */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Rainbow hue cycle using 120°-offset cosines. */
|
|
||||||
static color_t hueColor(float t) {
|
|
||||||
uint8_t r = (uint8_t)((cosf(t) * 0.5f + 0.5f) * 255.0f);
|
|
||||||
uint8_t g = (uint8_t)((cosf(t - 2.094f) * 0.5f + 0.5f) * 255.0f);
|
|
||||||
uint8_t b = (uint8_t)((cosf(t - 4.189f) * 0.5f + 0.5f) * 255.0f);
|
|
||||||
return color(r, g, b, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
static rtexture_t testTex;
|
|
||||||
|
|
||||||
/* ---- aspect ratio for 3D tests (matches linux default 640×480) ---------- */
|
|
||||||
#define ASPECT FIXED(640.0f / 480.0f)
|
|
||||||
/* fovY ≈ 60° in radians */
|
|
||||||
#define FOV_Y FIXED(1.0472f)
|
|
||||||
|
|
||||||
/* ---- Tilemap test -------------------------------------------------------- */
|
|
||||||
/* Two 16×16 tiles packed side-by-side in a 32×16 tileset texture.
|
|
||||||
* The chunk is 64px wider than the 640px window; the scroll range is exactly
|
|
||||||
* 4 tile widths = 2 checkerboard periods, so the wrap is seamless. */
|
|
||||||
#define TILEMAP_TILE_W 16
|
|
||||||
#define TILEMAP_TILE_H 16
|
|
||||||
#define TILEMAP_CHUNK_W 44 /* tiles; 44*16=704px */
|
|
||||||
#define TILEMAP_CHUNK_H 13 /* tiles; 13*16=208px */
|
|
||||||
#define TILEMAP_Y 262 /* screen-space y: just below 2D tests */
|
|
||||||
#define TILEMAP_SCROLL_SPEED 40.0f /* px / second */
|
|
||||||
#define TILEMAP_SCROLL_RANGE 64.0f /* px; = 4 tile widths = 2 chk periods */
|
|
||||||
|
|
||||||
static rtexture_t tilemapTileset;
|
|
||||||
static rtilemapchunk_t tilemapChunk;
|
|
||||||
|
|
||||||
/* =========================================================================
|
|
||||||
* Test 1 — single 2D textured quad
|
|
||||||
* top-left quadrant (x: 50–240, y: 30–190)
|
|
||||||
* ======================================================================= */
|
|
||||||
static void renderTest2DQuad(void) {
|
|
||||||
renderSprite(50, 30, 192, 160, 0, testTex, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =========================================================================
|
|
||||||
* Test 2 — three 2D quads with Z-ordering
|
|
||||||
* top-right quadrant (x: 340–620, y: 30–200)
|
|
||||||
* Depth: 0 = frontmost, 32767 = backmost.
|
|
||||||
* Back quad (depth 24000) tinted blue-grey, drawn first visually behind.
|
|
||||||
* Mid quad (depth 12000) tinted orange.
|
|
||||||
* Front quad (depth 0) full texture, drawn on top.
|
|
||||||
* ======================================================================= */
|
|
||||||
static void renderTest2DZOrder(void) {
|
|
||||||
renderSprite(340, 30, 160, 160, 24000, testTex, color(100, 100, 220, 255));
|
|
||||||
renderSprite(380, 60, 160, 160, 12000, testTex, color(220, 140, 60, 255));
|
|
||||||
renderSprite(420, 90, 160, 160, 0, testTex, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =========================================================================
|
|
||||||
* Test 3 — single 3D textured quad spinning around Y axis
|
|
||||||
* World position: left side, x=-180 cm.
|
|
||||||
* ======================================================================= */
|
|
||||||
static void renderTest3DQuad(void) {
|
|
||||||
float angle = fixedToFloat(TIME.time);
|
|
||||||
renderQuad3D(
|
|
||||||
-180, 0, 0,
|
|
||||||
(int16_t)(cosf(angle) * 60.0f), 0, (int16_t)(sinf(angle) * 60.0f),
|
|
||||||
0, 60, 0,
|
|
||||||
0, testTex, COLOR_WHITE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =========================================================================
|
|
||||||
* Test 4 — two 3D quads overlapping (depth test)
|
|
||||||
* Right side of world space.
|
|
||||||
* Quad A (behind, z=-60 cm): blue tint.
|
|
||||||
* Quad B (in front, z=60 cm): white/texture.
|
|
||||||
* Both quads share screen-space centre so depth buffer decides ordering.
|
|
||||||
* ======================================================================= */
|
|
||||||
static void renderTest3DOverlap(void) {
|
|
||||||
/* behind */
|
|
||||||
renderQuad3D(
|
|
||||||
150, 0, -60,
|
|
||||||
60, 0, 0,
|
|
||||||
0, 60, 0,
|
|
||||||
0, testTex, color(100, 100, 220, 255)
|
|
||||||
);
|
|
||||||
/* in front */
|
|
||||||
renderQuad3D(
|
|
||||||
190, 0, 60,
|
|
||||||
60, 0, 0,
|
|
||||||
0, 60, 0,
|
|
||||||
0, testTex, COLOR_WHITE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =========================================================================
|
|
||||||
* Test 5 — scrolling tilemap (bottom of screen)
|
|
||||||
* Checkerboard of two hue-cycling tiles.
|
|
||||||
* x offset scrolls left by TILEMAP_SCROLL_SPEED px/s, wraps seamlessly.
|
|
||||||
* ======================================================================= */
|
|
||||||
static void renderTestTilemap(void) {
|
|
||||||
float t = fixedToFloat(TIME.time);
|
|
||||||
float scrollPx = fmodf(t * TILEMAP_SCROLL_SPEED, TILEMAP_SCROLL_RANGE);
|
|
||||||
int16_t x = -(int16_t)(int)scrollPx;
|
|
||||||
renderTilemapChunk(x, TILEMAP_Y, 8000, tilemapChunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Lifecycle ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
errorret_t sceneTestInit(scenedata_t *data) {
|
errorret_t sceneTestInit(scenedata_t *data) {
|
||||||
testTex = renderTextureCreate(3, 3, initIndices, initPalette);
|
sceneTestFrame = 0;
|
||||||
assertTrue(testTex != RTEXTURE_NONE, "Failed to create test texture");
|
|
||||||
|
|
||||||
/* Tileset: 32×16, two 16×16 tiles — left half = index 0, right = index 1 */
|
|
||||||
uint8_t tsIdx[32 * 16];
|
|
||||||
color_t tsPal[256];
|
|
||||||
memoryZero(tsPal, sizeof(tsPal));
|
|
||||||
for(int y = 0; y < 16; y++)
|
|
||||||
for(int x = 0; x < 32; x++)
|
|
||||||
tsIdx[y * 32 + x] = (x < 16) ? 0 : 1;
|
|
||||||
tsPal[0] = color(210, 180, 100, 255); /* warm sand — overridden each frame */
|
|
||||||
tsPal[1] = color( 60, 130, 60, 255); /* cool grass — overridden each frame */
|
|
||||||
tilemapTileset = renderTextureCreate(32, 16, tsIdx, tsPal);
|
|
||||||
assertTrue(tilemapTileset != RTEXTURE_NONE, "Failed to create tilemap tileset");
|
|
||||||
|
|
||||||
/* Chunk: checkerboard — (col+row)%2 selects tile 0 or tile 1 */
|
|
||||||
uint8_t chunkIdx[TILEMAP_CHUNK_W * TILEMAP_CHUNK_H];
|
|
||||||
for(int row = 0; row < TILEMAP_CHUNK_H; row++)
|
|
||||||
for(int col = 0; col < TILEMAP_CHUNK_W; col++)
|
|
||||||
chunkIdx[row * TILEMAP_CHUNK_W + col] = (uint8_t)((col + row) % 2);
|
|
||||||
tilemapChunk = renderTilemapChunkCreate(
|
|
||||||
TILEMAP_CHUNK_W, TILEMAP_CHUNK_H,
|
|
||||||
TILEMAP_TILE_W, TILEMAP_TILE_H,
|
|
||||||
tilemapTileset,
|
|
||||||
chunkIdx
|
|
||||||
);
|
|
||||||
/* RTILEMAPCHUNK_INVALID is valid on platforms that don't implement chunk
|
|
||||||
* creation yet; renderTilemapChunk() and renderTilemapChunkDispose() both
|
|
||||||
* handle it as a no-op. */
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneTestUpdate(scenedata_t *data) {
|
errorret_t sceneTestUpdate(scenedata_t *data) {
|
||||||
float t = fixedToFloat(TIME.time);
|
sceneTestFrame++;
|
||||||
|
if(sceneTestFrame >= SCENE_TEST_FRAMES) engineExit();
|
||||||
/* Palette demo: entry 8 (bottom-right pixel) cycles through hue */
|
|
||||||
renderTextureGetPalette(testTex)[8] = hueColor(t * 2.0f);
|
|
||||||
|
|
||||||
/* Index demo: center pixel (1,1) steps through entries 0-8 once per second */
|
|
||||||
renderTextureGetIndices(testTex)[1 * 3 + 1] = (uint8_t)((uint32_t)t % 9u);
|
|
||||||
|
|
||||||
/* Tilemap palette animation: complementary hues so tiles always contrast */
|
|
||||||
color_t *tilePal = renderTextureGetPalette(tilemapTileset);
|
|
||||||
tilePal[0] = hueColor(t * 0.7f);
|
|
||||||
tilePal[1] = hueColor(t * 0.7f + 3.14159f);
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneTestRender(scenedata_t *data) {
|
errorret_t sceneTestRender(scenedata_t *data) {
|
||||||
renderClear(color(24, 24, 40, 255));
|
renderClear(color(0, 128, 255, 255));
|
||||||
|
renderSprite(0, 0, 32, 32, 0, RTEXTURE_NONE, COLOR_WHITE);
|
||||||
/* 2D tests — no camera needed */
|
|
||||||
renderTest2DQuad();
|
|
||||||
renderTest2DZOrder();
|
|
||||||
|
|
||||||
/* 3D tests — shared camera looking slightly down from above */
|
|
||||||
renderSetProjection(FOV_Y, ASPECT, FIXED(10), FIXED(10000));
|
|
||||||
renderSetView(0, 150, 400, 0, 0, 0);
|
|
||||||
renderTest3DQuad();
|
|
||||||
renderTest3DOverlap();
|
|
||||||
|
|
||||||
/* Tilemap test — full-width scrolling strip below the 2D tests */
|
|
||||||
renderTestTilemap();
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneTestDispose(scenedata_t *data) {
|
errorret_t sceneTestDispose(scenedata_t *data) {
|
||||||
renderTilemapChunkDispose(tilemapChunk);
|
|
||||||
renderTextureDispose(tilemapTileset);
|
|
||||||
renderTextureDispose(testTex);
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "scene/scenebase.h"
|
#include "scene/scenebase.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t unused;
|
int32_t frame;
|
||||||
} scenetest_t;
|
} scenetest_t;
|
||||||
|
|
||||||
errorret_t sceneTestInit(scenedata_t *data);
|
errorret_t sceneTestInit(scenedata_t *data);
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
scenewhite32.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scene/white32/scenewhite32.h"
|
||||||
|
#include "display/render/render.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
#include "engine/engine.h"
|
||||||
|
|
||||||
|
errorret_t sceneWhite32Init(scenedata_t *data) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneWhite32Update(scenedata_t *data) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneWhite32Render(scenedata_t *data) {
|
||||||
|
renderClear(COLOR_CORNFLOWER_BLUE);
|
||||||
|
renderSprite(0, 0, 32, 32, 0, RTEXTURE_NONE, COLOR_WHITE);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneWhite32Dispose(scenedata_t *data) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "scene/scenebase.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *nothing;
|
||||||
|
} scenewhite32_t;
|
||||||
|
|
||||||
|
errorret_t sceneWhite32Init(scenedata_t *data);
|
||||||
|
errorret_t sceneWhite32Update(scenedata_t *data);
|
||||||
|
errorret_t sceneWhite32Render(scenedata_t *data);
|
||||||
|
errorret_t sceneWhite32Dispose(scenedata_t *data);
|
||||||
@@ -9,11 +9,11 @@
|
|||||||
#include "display/render/renderpsp.h"
|
#include "display/render/renderpsp.h"
|
||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "log/log.h"
|
|
||||||
#include <pspgu.h>
|
#include <pspgu.h>
|
||||||
#include <pspgum.h>
|
#include <pspgum.h>
|
||||||
#include <pspdisplay.h>
|
#include <pspdisplay.h>
|
||||||
#include <pspge.h>
|
#include <pspge.h>
|
||||||
|
#include <pspkernel.h>
|
||||||
|
|
||||||
/* GU framebuffer stride must be power-of-two; 512 is the standard for 480-wide. */
|
/* GU framebuffer stride must be power-of-two; 512 is the standard for 480-wide. */
|
||||||
#define PSP_BUF_W 512
|
#define PSP_BUF_W 512
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
static uint32_t __attribute__((aligned(64))) displayList[0x10000];
|
static uint32_t __attribute__((aligned(64))) displayList[0x10000];
|
||||||
|
|
||||||
errorret_t displayPSPInit(void) {
|
errorret_t displayPSPInit(void) {
|
||||||
logDebug("[PSP] displayPSPInit: start\n");
|
|
||||||
DISPLAY.whichBuffer = 0;
|
DISPLAY.whichBuffer = 0;
|
||||||
|
|
||||||
sceGuInit();
|
sceGuInit();
|
||||||
@@ -58,23 +57,19 @@ errorret_t displayPSPInit(void) {
|
|||||||
|
|
||||||
sceDisplaySetMode(0, PSP_SCREEN_W, PSP_SCREEN_H);
|
sceDisplaySetMode(0, PSP_SCREEN_W, PSP_SCREEN_H);
|
||||||
sceGuDisplay(GU_TRUE);
|
sceGuDisplay(GU_TRUE);
|
||||||
logDebug("[PSP] displayPSPInit: GU setup done, calling renderPSPInit\n");
|
|
||||||
|
|
||||||
errorChain(renderPSPInit());
|
errorChain(renderPSPInit());
|
||||||
logDebug("[PSP] displayPSPInit: done\n");
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t displayPSPFlush(ropbuffer_t *buf) {
|
errorret_t displayPSPFlush(ropbuffer_t *buf) {
|
||||||
logDebug("[PSP] displayPSPFlush: enter\n");
|
|
||||||
assertNotNull(buf, "PSP flush: null ropbuffer");
|
assertNotNull(buf, "PSP flush: null ropbuffer");
|
||||||
errorChain(renderPSPFlush(buf));
|
errorChain(renderPSPFlush(buf));
|
||||||
logDebug("[PSP] displayPSPFlush: done\n");
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t displayPSPSwap(void) {
|
errorret_t displayPSPSwap(void) {
|
||||||
logDebug("[PSP] displayPSPSwap\n");
|
sceDisplayWaitVblankStart();
|
||||||
sceGuSwapBuffers();
|
sceGuSwapBuffers();
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -83,4 +78,5 @@ void displayPSPDispose(void) {
|
|||||||
renderPSPDispose();
|
renderPSPDispose();
|
||||||
sceGuDisplay(GU_FALSE);
|
sceGuDisplay(GU_FALSE);
|
||||||
sceGuTerm();
|
sceGuTerm();
|
||||||
|
sceKernelExitGame();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "display/color.h"
|
#include "display/color.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "log/log.h"
|
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <psputils.h> /* sceKernelDcacheWritebackRange */
|
#include <psputils.h> /* sceKernelDcacheWritebackRange */
|
||||||
#include <pspgu.h>
|
#include <pspgu.h>
|
||||||
@@ -29,15 +28,16 @@ static uint32_t __attribute__((aligned(64))) displayList[DISPLAY_LIST_SIZE / 4];
|
|||||||
|
|
||||||
/* GU T8 (8-bit indexed) textures.
|
/* GU T8 (8-bit indexed) textures.
|
||||||
* cpuIndices : w*h row-major bytes, user-writable source of truth.
|
* cpuIndices : w*h row-major bytes, user-writable source of truth.
|
||||||
* gpuIndices : tbw*h padded for sceGuTexImage, re-derived at bind.
|
* gpuIndices : tbw*tph padded for sceGuTexImage, re-derived at bind.
|
||||||
* palette : 256 RGBA entries, user-writable source of truth.
|
* palette : 256 RGBA entries, user-writable source of truth.
|
||||||
* Converted to ABGR on-the-fly at bind into pspAbgrBuf.
|
* Converted to ABGR on-the-fly at bind into pspAbgrBuf.
|
||||||
* tbw : power-of-two stride ≥ 8 required by the GU. */
|
* tbw : power-of-two stride ≥ 16 required by T8 (16-byte cache line).
|
||||||
|
* tph : power-of-two height required by sceGuTexImage (≥ 1). */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *cpuIndices;
|
uint8_t *cpuIndices;
|
||||||
uint8_t *gpuIndices;
|
uint8_t *gpuIndices;
|
||||||
color_t palette[256];
|
color_t palette[256];
|
||||||
uint16_t w, h, tbw;
|
uint16_t w, h, tbw, tph;
|
||||||
} psptexentry_t;
|
} psptexentry_t;
|
||||||
|
|
||||||
/* Shared 16-byte-aligned ABGR buffer used during every CLUT load. */
|
/* Shared 16-byte-aligned ABGR buffer used during every CLUT load. */
|
||||||
@@ -48,11 +48,13 @@ static uint16_t pspTexNext = 1; /* 0 = white fallback */
|
|||||||
|
|
||||||
/* ---- Vertex types -------------------------------------------------------- */
|
/* ---- Vertex types -------------------------------------------------------- */
|
||||||
|
|
||||||
/* 2D sprite: two corner vertices define the rect (GU_SPRITES uses 2 verts). */
|
/* 2D sprite: two corner vertices define the rect (GU_SPRITES uses 2 verts).
|
||||||
|
* Must match GU_TEXTURE_32BITF|GU_COLOR_8888|GU_VERTEX_32BITF — the PSP GU
|
||||||
|
* reference samples always use 32-bit float coords for textured 2D sprites. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t u, v; /* texel coords (integer, not normalised) */
|
float u, v; /* texel coords (float, in texels) */
|
||||||
uint32_t color; /* ABGR */
|
uint32_t color; /* ABGR */
|
||||||
int16_t x, y, z;
|
float x, y, z;
|
||||||
} __attribute__((packed)) GuVert2D;
|
} __attribute__((packed)) GuVert2D;
|
||||||
|
|
||||||
/* 3D triangle vertex */
|
/* 3D triangle vertex */
|
||||||
@@ -71,8 +73,15 @@ static uint32_t toABGR(color_t c) {
|
|||||||
((uint32_t)c.r);
|
((uint32_t)c.r);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smallest power-of-two ≥ n and ≥ 8 (PSP GU minimum stride for T8). */
|
/* Smallest power-of-two ≥ n and ≥ 16 (T8 cache line is 16 bytes = 16 pixels). */
|
||||||
static uint16_t texturePow2(uint16_t n) {
|
static uint16_t texturePow2(uint16_t n) {
|
||||||
|
uint16_t p = 16;
|
||||||
|
while(p < n) p = (uint16_t)(p << 1);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smallest power-of-two ≥ n and ≥ 8 (PSP minimum texture dimension > 4). */
|
||||||
|
static uint16_t texturePow2Height(uint16_t n) {
|
||||||
uint16_t p = 8;
|
uint16_t p = 8;
|
||||||
while(p < n) p = (uint16_t)(p << 1);
|
while(p < n) p = (uint16_t)(p << 1);
|
||||||
return p;
|
return p;
|
||||||
@@ -81,21 +90,20 @@ static uint16_t texturePow2(uint16_t n) {
|
|||||||
/* ---- Init ---------------------------------------------------------------- */
|
/* ---- Init ---------------------------------------------------------------- */
|
||||||
|
|
||||||
errorret_t renderPSPInit(void) {
|
errorret_t renderPSPInit(void) {
|
||||||
logDebug("[PSP] renderPSPInit: start\n");
|
/* White 8×8 fallback: all pixels = index 0 → palette[0] = white.
|
||||||
/* White 1×1 fallback: index 0 → palette[0] = white */
|
* PSP requires texture width/height > 4; tbw >= 16 for T8. */
|
||||||
psptexentry_t *e = &pspTexTable[0];
|
psptexentry_t *e = &pspTexTable[0];
|
||||||
e->cpuIndices = (uint8_t *)memalign(16, 1);
|
e->cpuIndices = (uint8_t *)memalign(16, 8 * 8);
|
||||||
assertNotNull(e->cpuIndices, "PSP: failed to allocate fallback cpu index buffer");
|
assertNotNull(e->cpuIndices, "PSP: failed to allocate fallback cpu index buffer");
|
||||||
e->cpuIndices[0] = 0;
|
memoryZero(e->cpuIndices, 8 * 8);
|
||||||
|
|
||||||
e->gpuIndices = (uint8_t *)memalign(16, 8); /* tbw=8 minimum */
|
e->gpuIndices = (uint8_t *)memalign(16, 16 * 8); /* tbw=16, tph=8 */
|
||||||
assertNotNull(e->gpuIndices, "PSP: failed to allocate fallback gpu index buffer");
|
assertNotNull(e->gpuIndices, "PSP: failed to allocate fallback gpu index buffer");
|
||||||
memoryZero(e->gpuIndices, 8);
|
memoryZero(e->gpuIndices, 16 * 8);
|
||||||
|
|
||||||
memoryZero(e->palette, 256 * sizeof(color_t));
|
memoryZero(e->palette, 256 * sizeof(color_t));
|
||||||
e->palette[0] = COLOR_WHITE;
|
e->palette[0] = COLOR_WHITE;
|
||||||
e->w = 1; e->h = 1; e->tbw = 8;
|
e->w = 8; e->h = 8; e->tbw = 16; e->tph = 8;
|
||||||
logDebug("[PSP] renderPSPInit: done\n");
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +116,7 @@ rtexture_t renderPSPTextureCreate(
|
|||||||
assertTrue(pspTexNext < PSP_RTEXTURE_MAX, "PSP texture table full");
|
assertTrue(pspTexNext < PSP_RTEXTURE_MAX, "PSP texture table full");
|
||||||
|
|
||||||
uint16_t tbw = texturePow2(w);
|
uint16_t tbw = texturePow2(w);
|
||||||
|
uint16_t tph = texturePow2Height(h);
|
||||||
rtexture_t handle = (rtexture_t)pspTexNext++;
|
rtexture_t handle = (rtexture_t)pspTexNext++;
|
||||||
psptexentry_t *e = &pspTexTable[handle];
|
psptexentry_t *e = &pspTexTable[handle];
|
||||||
|
|
||||||
@@ -116,7 +125,8 @@ rtexture_t renderPSPTextureCreate(
|
|||||||
assertNotNull(e->cpuIndices, "PSP: failed to allocate cpu index buffer");
|
assertNotNull(e->cpuIndices, "PSP: failed to allocate cpu index buffer");
|
||||||
memoryCopy(e->cpuIndices, indices, cpuBytes);
|
memoryCopy(e->cpuIndices, indices, cpuBytes);
|
||||||
|
|
||||||
uint32_t gpuBytes = (uint32_t)tbw * h;
|
/* GPU buffer must cover tbw*tph rows — sceGuTexImage requires power-of-2 height. */
|
||||||
|
uint32_t gpuBytes = (uint32_t)tbw * tph;
|
||||||
e->gpuIndices = (uint8_t *)memalign(16, gpuBytes);
|
e->gpuIndices = (uint8_t *)memalign(16, gpuBytes);
|
||||||
assertNotNull(e->gpuIndices, "PSP: failed to allocate gpu index buffer");
|
assertNotNull(e->gpuIndices, "PSP: failed to allocate gpu index buffer");
|
||||||
memoryZero(e->gpuIndices, gpuBytes);
|
memoryZero(e->gpuIndices, gpuBytes);
|
||||||
@@ -124,7 +134,7 @@ rtexture_t renderPSPTextureCreate(
|
|||||||
memoryCopy(e->gpuIndices + row * tbw, indices + row * w, w);
|
memoryCopy(e->gpuIndices + row * tbw, indices + row * w, w);
|
||||||
|
|
||||||
memoryCopy(e->palette, palette, 256 * sizeof(color_t));
|
memoryCopy(e->palette, palette, 256 * sizeof(color_t));
|
||||||
e->w = w; e->h = h; e->tbw = tbw;
|
e->w = w; e->h = h; e->tbw = tbw; e->tph = tph;
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,22 +160,26 @@ static void bindTexture(rtexture_t tex) {
|
|||||||
? &pspTexTable[tex]
|
? &pspTexTable[tex]
|
||||||
: &pspTexTable[0];
|
: &pspTexTable[0];
|
||||||
|
|
||||||
/* Re-pad cpuIndices → gpuIndices (stride tbw, rows w wide). */
|
/* Write texture indices and palette via uncached alias so data lands in
|
||||||
memoryZero(e->gpuIndices, (uint32_t)e->tbw * e->h);
|
* physical RAM immediately — the GE DMA reads physical RAM, not CPU cache,
|
||||||
for(uint16_t row = 0; row < e->h; row++)
|
* so writing through the uncached alias (|0x40000000) avoids stale reads. */
|
||||||
memoryCopy(e->gpuIndices + row * e->tbw, e->cpuIndices + row * e->w, e->w);
|
uint8_t *gpuUC = (uint8_t *)((uint32_t)e->gpuIndices | 0x40000000u);
|
||||||
sceKernelDcacheWritebackRange(e->gpuIndices, (uint32_t)e->tbw * e->h);
|
uint32_t *clutUC = (uint32_t *)((uint32_t)pspAbgrBuf | 0x40000000u);
|
||||||
|
|
||||||
/* Convert palette color_t RGBA → ABGR into the shared aligned buffer. */
|
memoryZero(gpuUC, (uint32_t)e->tbw * e->tph);
|
||||||
for(int i = 0; i < 256; i++) pspAbgrBuf[i] = toABGR(e->palette[i]);
|
for(uint16_t row = 0; row < e->h; row++)
|
||||||
sceKernelDcacheWritebackRange(pspAbgrBuf, 256 * sizeof(uint32_t));
|
memoryCopy(gpuUC + row * e->tbw, e->cpuIndices + row * e->w, e->w);
|
||||||
|
|
||||||
|
for(int i = 0; i < 256; i++) clutUC[i] = toABGR(e->palette[i]);
|
||||||
|
|
||||||
sceGuTexMode(GU_PSM_T8, 0, 0, GU_FALSE);
|
sceGuTexMode(GU_PSM_T8, 0, 0, GU_FALSE);
|
||||||
|
sceGuTexImage(0, texturePow2Height(e->w), texturePow2Height(e->h), e->tbw, e->gpuIndices);
|
||||||
sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0);
|
sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0);
|
||||||
sceGuClutLoad(32, pspAbgrBuf); /* 32 × 8 entries = 256 */
|
sceGuClutLoad(32, pspAbgrBuf);
|
||||||
sceGuTexImage(0, e->tbw, e->h, e->tbw, e->gpuIndices);
|
sceGuTexFlush();
|
||||||
sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
|
sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
|
||||||
sceGuTexFilter(GU_NEAREST, GU_NEAREST);
|
sceGuTexFilter(GU_NEAREST, GU_NEAREST);
|
||||||
|
sceGuTexWrap(GU_CLAMP, GU_CLAMP);
|
||||||
sceGuTexScale(1.0f, 1.0f);
|
sceGuTexScale(1.0f, 1.0f);
|
||||||
sceGuTexOffset(0.0f, 0.0f);
|
sceGuTexOffset(0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
@@ -183,33 +197,38 @@ static void draw2DSprite(const ropsprite_t *s) {
|
|||||||
float v1 = ((s->uvY + s->uvH) / 255.0f) * (float)e->h;
|
float v1 = ((s->uvY + s->uvH) / 255.0f) * (float)e->h;
|
||||||
|
|
||||||
bindTexture(s->texture);
|
bindTexture(s->texture);
|
||||||
|
sceGuAmbientColor(abgr);
|
||||||
|
|
||||||
GuVert2D *verts = (GuVert2D *)sceGuGetMemory(2 * sizeof(GuVert2D));
|
GuVert2D *verts = (GuVert2D *)sceGuGetMemory(2 * sizeof(GuVert2D));
|
||||||
assertNotNull(verts, "PSP: failed to allocate sprite vertices");
|
assertNotNull(verts, "PSP: failed to allocate sprite vertices");
|
||||||
|
|
||||||
verts[0].u = (uint16_t)u0; verts[0].v = (uint16_t)v0;
|
verts[0].u = u0; verts[0].v = v0;
|
||||||
verts[0].color = abgr;
|
verts[0].color = abgr;
|
||||||
verts[0].x = s->x; verts[0].y = s->y; verts[0].z = 0;
|
verts[0].x = (float)s->x; verts[0].y = (float)s->y; verts[0].z = 0.0f;
|
||||||
|
|
||||||
verts[1].u = (uint16_t)u1; verts[1].v = (uint16_t)v1;
|
verts[1].u = u1; verts[1].v = v1;
|
||||||
verts[1].color = abgr;
|
verts[1].color = abgr;
|
||||||
verts[1].x = (int16_t)(s->x + s->w);
|
verts[1].x = (float)(s->x + s->w);
|
||||||
verts[1].y = (int16_t)(s->y + s->h);
|
verts[1].y = (float)(s->y + s->h);
|
||||||
verts[1].z = 0;
|
verts[1].z = 0.0f;
|
||||||
|
|
||||||
sceGuDrawArray(
|
sceGuDrawArray(
|
||||||
GU_SPRITES,
|
GU_SPRITES,
|
||||||
GU_TEXTURE_16BIT | GU_COLOR_8888 | GU_VERTEX_16BIT | GU_TRANSFORM_2D,
|
GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_2D,
|
||||||
2, 0, verts
|
2, 0, verts
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw3DQuad(const ropquad3d_t *q) {
|
static void draw3DQuad(const ropquad3d_t *q) {
|
||||||
logDebug("[PSP] draw3DQuad: enter tex=%u\n", (unsigned)q->texture);
|
psptexentry_t *e = (q->texture < PSP_RTEXTURE_MAX && pspTexTable[q->texture].cpuIndices)
|
||||||
|
? &pspTexTable[q->texture]
|
||||||
|
: &pspTexTable[0];
|
||||||
uint32_t abgr = toABGR(q->tint);
|
uint32_t abgr = toABGR(q->tint);
|
||||||
float u0 = q->uvX / 255.0f, v0 = q->uvY / 255.0f;
|
/* UV in 0-255 range → scale to texel coords the same way draw2DSprite does. */
|
||||||
float u1 = (q->uvX + q->uvW) / 255.0f;
|
float u0 = (q->uvX / 255.0f) * (float)e->w;
|
||||||
float v1 = (q->uvY + q->uvH) / 255.0f;
|
float v0 = (q->uvY / 255.0f) * (float)e->h;
|
||||||
|
float u1 = ((q->uvX + q->uvW) / 255.0f) * (float)e->w;
|
||||||
|
float v1 = ((q->uvY + q->uvH) / 255.0f) * (float)e->h;
|
||||||
|
|
||||||
float cx = (float)q->cx, cy = (float)q->cy, cz = (float)q->cz;
|
float cx = (float)q->cx, cy = (float)q->cy, cz = (float)q->cz;
|
||||||
float rx = (float)q->rx, ry = (float)q->ry, rz = (float)q->rz;
|
float rx = (float)q->rx, ry = (float)q->ry, rz = (float)q->rz;
|
||||||
@@ -220,12 +239,9 @@ static void draw3DQuad(const ropquad3d_t *q) {
|
|||||||
float blx = cx-rx-ux, bly = cy-ry-uy, blz = cz-rz-uz;
|
float blx = cx-rx-ux, bly = cy-ry-uy, blz = cz-rz-uz;
|
||||||
float brx = cx+rx-ux, bry = cy+ry-uy, brz = cz+rz-uz;
|
float brx = cx+rx-ux, bry = cy+ry-uy, brz = cz+rz-uz;
|
||||||
|
|
||||||
logDebug("[PSP] draw3DQuad: bindTexture\n");
|
|
||||||
bindTexture(q->texture);
|
bindTexture(q->texture);
|
||||||
|
|
||||||
logDebug("[PSP] draw3DQuad: getMemory\n");
|
|
||||||
GuVert3D *verts = (GuVert3D *)sceGuGetMemory(6 * sizeof(GuVert3D));
|
GuVert3D *verts = (GuVert3D *)sceGuGetMemory(6 * sizeof(GuVert3D));
|
||||||
logDebug("[PSP] draw3DQuad: verts=0x%08x\n", (unsigned)verts);
|
|
||||||
assertNotNull(verts, "PSP: failed to allocate 3D quad vertices");
|
assertNotNull(verts, "PSP: failed to allocate 3D quad vertices");
|
||||||
|
|
||||||
verts[0] = (GuVert3D){u0,v0, abgr, tlx,tly,tlz};
|
verts[0] = (GuVert3D){u0,v0, abgr, tlx,tly,tlz};
|
||||||
@@ -235,21 +251,16 @@ static void draw3DQuad(const ropquad3d_t *q) {
|
|||||||
verts[4] = (GuVert3D){u1,v1, abgr, brx,bry,brz};
|
verts[4] = (GuVert3D){u1,v1, abgr, brx,bry,brz};
|
||||||
verts[5] = (GuVert3D){u1,v0, abgr, trx,try_,trz};
|
verts[5] = (GuVert3D){u1,v0, abgr, trx,try_,trz};
|
||||||
|
|
||||||
logDebug("[PSP] draw3DQuad: sceGuDrawArray\n");
|
|
||||||
sceGuDrawArray(
|
sceGuDrawArray(
|
||||||
GU_TRIANGLES,
|
GU_TRIANGLES,
|
||||||
GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_3D,
|
GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_3D,
|
||||||
6, 0, verts
|
6, 0, verts
|
||||||
);
|
);
|
||||||
logDebug("[PSP] draw3DQuad: done\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Flush --------------------------------------------------------------- */
|
/* ---- Flush --------------------------------------------------------------- */
|
||||||
|
|
||||||
errorret_t renderPSPFlush(ropbuffer_t *buf) {
|
errorret_t renderPSPFlush(ropbuffer_t *buf) {
|
||||||
logDebug("[PSP] renderPSPFlush: byteCount=%u count=%u\n",
|
|
||||||
(unsigned)buf->byteCount, (unsigned)buf->count);
|
|
||||||
|
|
||||||
sceGuStart(GU_DIRECT, displayList);
|
sceGuStart(GU_DIRECT, displayList);
|
||||||
|
|
||||||
sceGuEnable(GU_TEXTURE_2D);
|
sceGuEnable(GU_TEXTURE_2D);
|
||||||
@@ -257,14 +268,12 @@ errorret_t renderPSPFlush(ropbuffer_t *buf) {
|
|||||||
sceGuDepthFunc(GU_GEQUAL); /* PSP uses reversed depth */
|
sceGuDepthFunc(GU_GEQUAL); /* PSP uses reversed depth */
|
||||||
|
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
uint32_t opIdx = 0;
|
|
||||||
while(offset < buf->byteCount) {
|
while(offset < buf->byteCount) {
|
||||||
const ropheader_t *hdr = (const ropheader_t *)(buf->data + offset);
|
const ropheader_t *hdr = (const ropheader_t *)(buf->data + offset);
|
||||||
ropop_t op = (ropop_t)hdr->op;
|
ropop_t op = (ropop_t)hdr->op;
|
||||||
|
|
||||||
switch(op) {
|
switch(op) {
|
||||||
case ROP_CLEAR: {
|
case ROP_CLEAR: {
|
||||||
logDebug("[PSP] op[%u] ROP_CLEAR\n", (unsigned)opIdx);
|
|
||||||
const ropclear_t *c = (const ropclear_t *)hdr;
|
const ropclear_t *c = (const ropclear_t *)hdr;
|
||||||
uint32_t abgr = toABGR(c->color);
|
uint32_t abgr = toABGR(c->color);
|
||||||
sceGuClearColor(abgr);
|
sceGuClearColor(abgr);
|
||||||
@@ -273,12 +282,10 @@ errorret_t renderPSPFlush(ropbuffer_t *buf) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ROP_DRAW_SPRITE:
|
case ROP_DRAW_SPRITE:
|
||||||
logDebug("[PSP] op[%u] ROP_DRAW_SPRITE\n", (unsigned)opIdx);
|
|
||||||
draw2DSprite((const ropsprite_t *)hdr);
|
draw2DSprite((const ropsprite_t *)hdr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ROP_SET_PROJECTION: {
|
case ROP_SET_PROJECTION: {
|
||||||
logDebug("[PSP] op[%u] ROP_SET_PROJECTION\n", (unsigned)opIdx);
|
|
||||||
const ropprojection_t *p = (const ropprojection_t *)hdr;
|
const ropprojection_t *p = (const ropprojection_t *)hdr;
|
||||||
sceGumMatrixMode(GU_PROJECTION);
|
sceGumMatrixMode(GU_PROJECTION);
|
||||||
sceGumLoadIdentity();
|
sceGumLoadIdentity();
|
||||||
@@ -296,7 +303,6 @@ errorret_t renderPSPFlush(ropbuffer_t *buf) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ROP_SET_VIEW: {
|
case ROP_SET_VIEW: {
|
||||||
logDebug("[PSP] op[%u] ROP_SET_VIEW\n", (unsigned)opIdx);
|
|
||||||
const ropview_t *v = (const ropview_t *)hdr;
|
const ropview_t *v = (const ropview_t *)hdr;
|
||||||
ScePspFVector3 eye = {(float)v->eyeX, (float)v->eyeY, (float)v->eyeZ};
|
ScePspFVector3 eye = {(float)v->eyeX, (float)v->eyeY, (float)v->eyeZ};
|
||||||
ScePspFVector3 target = {(float)v->tgtX, (float)v->tgtY, (float)v->tgtZ};
|
ScePspFVector3 target = {(float)v->tgtX, (float)v->tgtY, (float)v->tgtZ};
|
||||||
@@ -309,23 +315,17 @@ errorret_t renderPSPFlush(ropbuffer_t *buf) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ROP_DRAW_QUAD_3D:
|
case ROP_DRAW_QUAD_3D:
|
||||||
logDebug("[PSP] op[%u] ROP_DRAW_QUAD_3D\n", (unsigned)opIdx);
|
|
||||||
draw3DQuad((const ropquad3d_t *)hdr);
|
draw3DQuad((const ropquad3d_t *)hdr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
logDebug("[PSP] op[%u] unknown op=%u offset=%u\n",
|
|
||||||
(unsigned)opIdx, (unsigned)op, (unsigned)offset);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
offset += ropOpSize(op);
|
offset += ropOpSize(op);
|
||||||
opIdx++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logDebug("[PSP] renderPSPFlush: sceGuFinish\n");
|
|
||||||
sceGuFinish();
|
sceGuFinish();
|
||||||
sceGuSync(0, 0);
|
sceGuSync(0, 0);
|
||||||
logDebug("[PSP] renderPSPFlush: done\n");
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user