diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e4306017..8fd81c57 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -48,6 +48,7 @@ jobs:
- { name: 'Nintendo 3DS', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, n3ds: true, werror: true, clang-tidy: false, container: 'devkitpro/devkitarm:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake' }
- { name: 'Xbox One', os: 'windows-latest', generator: 'Visual Studio 17 2022', dx5: false, config: false, msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64', cmake-args: '-DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.26100.0', xbox-one: true}
- { name: 'Android', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, android: true, werror: true, clang-tidy: false,}
+ - { name: 'Vita', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, vita: true, werror: true, clang-tidy: false, cmake-args: '--toolchain /usr/local/vitasdk/share/vita.toolchain.cmake'}
steps:
- name: Setup vcvars
if: ${{ !!matrix.msvc }}
@@ -117,6 +118,18 @@ jobs:
- name: Setup ninja
if: ${{ matrix.msvc }}
uses: ashutoshvarma/setup-ninja@master
+
+ - name: Setup vitasdk
+ if: ${{ matrix.vita }}
+ run: |
+ git clone https://github.com/vitasdk/vdpm
+ cd vdpm
+ ./bootstrap-vitasdk.sh
+ export VITASDK=/usr/local/vitasdk
+ export PATH=$VITASDK/bin:$PATH
+ echo "VITASDK=/usr/local/vitasdk" >> $GITHUB_ENV
+ echo "$VITASDK/bin" >> $GITHUB_PATH
+ ./install-all.sh
- uses: actions/checkout@v4
@@ -177,7 +190,7 @@ jobs:
run: cmake --build build --verbose --config Release
- name: Package (CPack)
- if: ${{ !matrix.n3ds && !matrix.android }}
+ if: ${{ !matrix.n3ds && !matrix.android && !matrix.vita }}
run: |
cd build
success=0
@@ -225,6 +238,13 @@ jobs:
mkdir dist
mv *.3dsx dist/
mv *.cia dist/
+
+ - name: Package (Vita)
+ if: ${{ matrix.vita }}
+ run: |
+ cd build
+ mkdir dist
+ mv *.vpk dist/
- name: Package (Android)
if: ${{ matrix.android }}
@@ -246,6 +266,7 @@ jobs:
build/dist/*.3dsx
build/dist/*.cia
build/dist/*.apk
+ build/dist/*.vpk
flatpak:
name: "Flatpak (${{ matrix.arch }})"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a4ec707..60e742d0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -46,18 +46,18 @@ option(ISLE_BUILD_ASSETS "Build assets from the /assets directory" OFF)
option(ISLE_ASAN "Enable Address Sanitizer" OFF)
option(ISLE_UBSAN "Enable Undefined Behavior Sanitizer" OFF)
option(ISLE_WERROR "Treat warnings as errors" OFF)
-option(ISLE_DEBUG "Enable imgui debug" ON)
+cmake_dependent_option(ISLE_DEBUG "Enable imgui debug" ON "NOT VITA" OFF)
cmake_dependent_option(ISLE_USE_DX5 "Build with internal DirectX 5 SDK" "${NOT_MINGW}" "WIN32;CMAKE_SIZEOF_VOID_P EQUAL 4" OFF)
cmake_dependent_option(ISLE_MINIWIN "Use miniwin" ON "NOT ISLE_USE_DX5" OFF)
cmake_dependent_option(ISLE_EXTENSIONS "Use extensions" ON "NOT ISLE_USE_DX5;NOT WINDOWS_STORE" OFF)
-cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN;NOT NINTENDO_3DS;NOT WINDOWS_STORE" OFF)
+cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN;NOT NINTENDO_3DS;NOT WINDOWS_STORE;NOT VITA" OFF)
cmake_dependent_option(ISLE_COMPILE_SHADERS "Compile shaders" ON "SDL_SHADERCROSS_BIN;TARGET Python3::Interpreter" OFF)
-option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON)
+cmake_dependent_option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON "NOT VITA" OFF)
option(ENABLE_CLANG_TIDY "Enable clang-tidy")
option(DOWNLOAD_DEPENDENCIES "Download dependencies" ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" CACHE PATH "Directory where to put executables and dll")
set(ISLE_EMSCRIPTEN_HOST "" CACHE STRING "Host URL for Emscripten streaming (e.g., https://test.com)")
-cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN" OFF)
+cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN;NOT VITA" OFF)
message(STATUS "Isle app: ${ISLE_BUILD_APP}")
message(STATUS "Config app: ${ISLE_BUILD_CONFIG}")
@@ -90,6 +90,7 @@ if (DOWNLOAD_DEPENDENCIES)
SDL3
GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git"
GIT_TAG "main"
+ UPDATE_DISCONNECTED TRUE
EXCLUDE_FROM_ALL
)
endif()
@@ -100,6 +101,7 @@ if (DOWNLOAD_DEPENDENCIES)
iniparser
GIT_REPOSITORY "https://gitlab.com/iniparser/iniparser.git"
GIT_TAG "main"
+ UPDATE_DISCONNECTED TRUE
EXCLUDE_FROM_ALL
)
block()
@@ -598,6 +600,12 @@ if (ISLE_BUILD_APP)
ISLE/android/config.cpp
)
endif()
+ if(VITA)
+ target_sources(isle PRIVATE
+ ISLE/vita/config.cpp
+ ISLE/vita/messagebox.cpp
+ )
+ endif()
if(Python3_FOUND)
if(NOT DEFINED PYTHON_PIL_AVAILABLE)
execute_process(
@@ -639,18 +647,18 @@ if (ISLE_BUILD_CONFIG)
qt_add_executable(isle-config WIN32
LEGO1/mxdirectx/mxdirectxinfo.cpp
LEGO1/mxdirectx/legodxinfo.cpp
- CONFIG/config.cpp
- CONFIG/AboutDlg.cpp
- CONFIG/MainDlg.cpp
- CONFIG/detectdx5.cpp
- CONFIG/res/config.rc
- CONFIG/res/config.qrc
+ CONFIG/qt/config.cpp
+ CONFIG/qt/AboutDlg.cpp
+ CONFIG/qt/MainDlg.cpp
+ CONFIG/qt/detectdx5.cpp
+ CONFIG/qt/res/config.rc
+ CONFIG/qt/res/config.qrc
)
target_link_libraries(isle-config PRIVATE Qt6::Core Qt6::Widgets)
set_property(TARGET isle-config PROPERTY AUTOMOC ON)
set_property(TARGET isle-config PROPERTY AUTORCC ON)
set_property(TARGET isle-config PROPERTY AUTOUIC ON)
- set_property(TARGET isle-config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res")
+ set_property(TARGET isle-config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/qt/res")
list(APPEND isle_targets isle-config)
target_compile_definitions(isle-config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG)
target_include_directories(isle-config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1")
@@ -782,7 +790,7 @@ endif()
if(BUILD_SHARED_LIBS)
list(APPEND install_extra_targets lego1)
endif()
-if (NOT (NINTENDO_3DS OR WINDOWS_STORE))
+if (NOT (NINTENDO_3DS OR WINDOWS_STORE OR VITA))
install(TARGETS isle ${install_extra_targets}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
@@ -881,6 +889,35 @@ if(WINDOWS_STORE)
PATTERN "*/*.msix"
PATTERN "*/*.msixbundle")
endif()
+if(VITA)
+ include("${VITASDK}/share/vita.cmake" REQUIRED)
+
+ add_subdirectory(CONFIG/vita)
+
+ set(ISLE_PACKAGE_NAME "vita-isle")
+ set(VITA_APP_NAME "Lego Island")
+ set(VITA_TITLEID "LEGO00001")
+ set(VITA_VERSION "01.00")
+
+ vita_create_self(isle.self isle UNSAFE)
+
+ set(VPK_FILE_ARGS "")
+ file(GLOB_RECURSE SCE_SYS_FILES packaging/vita/sce_sys/*)
+ foreach(FILE ${SCE_SYS_FILES})
+ file(RELATIVE_PATH REL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/packaging/vita/ ${FILE})
+ list(APPEND VPK_FILE_ARGS FILE ${FILE} ${REL_FILE})
+ endforeach()
+
+ vita_create_vpk(isle.vpk ${VITA_TITLEID} isle.self
+ FILE "${CMAKE_CURRENT_BINARY_DIR}/CONFIG/vita/isle-config.self" isle-config.self
+ FILE "${CMAKE_CURRENT_BINARY_DIR}/CONFIG/vita/config_plugin.rco" config_plugin.rco
+ VERSION ${VITA_VERSION}
+ NAME ${VITA_APP_NAME}
+ ${VPK_FILE_ARGS}
+ )
+ add_dependencies(isle.vpk-vpk config_plugin.rco_target)
+endif()
+
if(MSVC OR IOS)
set(CPACK_GENERATOR ZIP)
if(IOS)
diff --git a/CONFIG/AboutDlg.cpp b/CONFIG/qt/AboutDlg.cpp
similarity index 100%
rename from CONFIG/AboutDlg.cpp
rename to CONFIG/qt/AboutDlg.cpp
diff --git a/CONFIG/AboutDlg.h b/CONFIG/qt/AboutDlg.h
similarity index 100%
rename from CONFIG/AboutDlg.h
rename to CONFIG/qt/AboutDlg.h
diff --git a/CONFIG/MainDlg.cpp b/CONFIG/qt/MainDlg.cpp
similarity index 100%
rename from CONFIG/MainDlg.cpp
rename to CONFIG/qt/MainDlg.cpp
diff --git a/CONFIG/MainDlg.h b/CONFIG/qt/MainDlg.h
similarity index 100%
rename from CONFIG/MainDlg.h
rename to CONFIG/qt/MainDlg.h
diff --git a/CONFIG/config.cpp b/CONFIG/qt/config.cpp
similarity index 100%
rename from CONFIG/config.cpp
rename to CONFIG/qt/config.cpp
diff --git a/CONFIG/config.h b/CONFIG/qt/config.h
similarity index 100%
rename from CONFIG/config.h
rename to CONFIG/qt/config.h
diff --git a/CONFIG/detectdx5.cpp b/CONFIG/qt/detectdx5.cpp
similarity index 100%
rename from CONFIG/detectdx5.cpp
rename to CONFIG/qt/detectdx5.cpp
diff --git a/CONFIG/detectdx5.h b/CONFIG/qt/detectdx5.h
similarity index 100%
rename from CONFIG/detectdx5.h
rename to CONFIG/qt/detectdx5.h
diff --git a/CONFIG/res/about.ui b/CONFIG/qt/res/about.ui
similarity index 100%
rename from CONFIG/res/about.ui
rename to CONFIG/qt/res/about.ui
diff --git a/CONFIG/res/add.svg b/CONFIG/qt/res/add.svg
similarity index 100%
rename from CONFIG/res/add.svg
rename to CONFIG/qt/res/add.svg
diff --git a/CONFIG/res/config.qrc b/CONFIG/qt/res/config.qrc
similarity index 100%
rename from CONFIG/res/config.qrc
rename to CONFIG/qt/res/config.qrc
diff --git a/CONFIG/res/config.rc b/CONFIG/qt/res/config.rc
similarity index 100%
rename from CONFIG/res/config.rc
rename to CONFIG/qt/res/config.rc
diff --git a/CONFIG/res/lego.ico b/CONFIG/qt/res/lego.ico
similarity index 100%
rename from CONFIG/res/lego.ico
rename to CONFIG/qt/res/lego.ico
diff --git a/CONFIG/res/lego1.png b/CONFIG/qt/res/lego1.png
similarity index 100%
rename from CONFIG/res/lego1.png
rename to CONFIG/qt/res/lego1.png
diff --git a/CONFIG/res/lego2.png b/CONFIG/qt/res/lego2.png
similarity index 100%
rename from CONFIG/res/lego2.png
rename to CONFIG/qt/res/lego2.png
diff --git a/CONFIG/res/maindialog.ui b/CONFIG/qt/res/maindialog.ui
similarity index 100%
rename from CONFIG/res/maindialog.ui
rename to CONFIG/qt/res/maindialog.ui
diff --git a/CONFIG/res/remove.svg b/CONFIG/qt/res/remove.svg
similarity index 100%
rename from CONFIG/res/remove.svg
rename to CONFIG/qt/res/remove.svg
diff --git a/CONFIG/res/shark.png b/CONFIG/qt/res/shark.png
similarity index 100%
rename from CONFIG/res/shark.png
rename to CONFIG/qt/res/shark.png
diff --git a/CONFIG/vita/CMakeLists.txt b/CONFIG/vita/CMakeLists.txt
new file mode 100644
index 00000000..6d44f518
--- /dev/null
+++ b/CONFIG/vita/CMakeLists.txt
@@ -0,0 +1,73 @@
+cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR)
+
+project(isle-config LANGUAGES CXX C VERSION 0.1)
+
+include("${VITASDK}/share/vita.cmake" REQUIRED)
+
+include(FetchContent)
+FetchContent_Declare(
+ ScePaf_External
+ URL https://github.com/olebeck/ScePaf/releases/download/v21/ScePaf-1.0.0.zip
+ URL_HASH SHA256=357b914a5c99ea17afe0edc8787a05cbf2ecce2f1d73bb9be69f371a294b8943
+ UPDATE_DISCONNECTED TRUE
+)
+FetchContent_MakeAvailable(ScePaf_External)
+
+
+FetchContent_Declare(
+ iniparser_paf
+ GIT_REPOSITORY "https://gitlab.com/iniparser/iniparser.git"
+ GIT_TAG "v4.2.6"
+ UPDATE_DISCONNECTED TRUE
+ EXCLUDE_FROM_ALL
+ PATCH_COMMAND patch -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/iniparser_paf.patch
+)
+block()
+ set(BUILD_DOCS off)
+ set(BUILD_SHARED_LIBS off)
+ FetchContent_MakeAvailable(iniparser_paf)
+endblock()
+
+add_executable(isle-config
+ src/paf_runtime.cpp
+ src/app.cpp
+)
+
+set_target_properties(isle-config PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+target_compile_options(isle-config PRIVATE
+ -fno-rtti -fno-exceptions -Wl,-q -Wall -fno-builtin -fshort-wchar -Wno-unused-function -Wno-sign-compare -fno-use-cxa-atexit
+)
+
+target_link_options(isle-config PRIVATE
+ -nostartfiles -nostdlib
+)
+
+target_link_libraries(isle-config PRIVATE
+ SceAppMgr_stub
+ SceLibKernel_stub
+ SceSysmodule_stub
+
+ ScePafToplevel_stub
+ ScePafResource_stub
+ ScePafWidget_stub
+ ScePafCommon_stub
+ ScePafStdc_stub
+ SceAppSettings_stub
+ SceFios2_stub
+ SceLibc_stub
+
+ iniparser_paf-static
+)
+
+vita_create_self(isle-config.self isle-config
+ CONFIG exports.yml
+ UNSAFE
+ STRIPPED
+ REL_OPTIMIZE
+)
+
+include(${scepaf_external_SOURCE_DIR}/rco.cmake)
+make_rco(cxml/config_plugin.xml config_plugin.rco)
diff --git a/CONFIG/vita/cxml/config_plugin.xml b/CONFIG/vita/cxml/config_plugin.xml
new file mode 100644
index 00000000..2f1df305
--- /dev/null
+++ b/CONFIG/vita/cxml/config_plugin.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CONFIG/vita/cxml/locale/en.xml b/CONFIG/vita/cxml/locale/en.xml
new file mode 100644
index 00000000..22435a39
--- /dev/null
+++ b/CONFIG/vita/cxml/locale/en.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CONFIG/vita/cxml/settings.xml b/CONFIG/vita/cxml/settings.xml
new file mode 100644
index 00000000..e9dad3fb
--- /dev/null
+++ b/CONFIG/vita/cxml/settings.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CONFIG/vita/exports.yml b/CONFIG/vita/exports.yml
new file mode 100644
index 00000000..d20ef9e2
--- /dev/null
+++ b/CONFIG/vita/exports.yml
@@ -0,0 +1,8 @@
+isle-config:
+ attributes: 0
+ process_image: true
+ version:
+ major: 1
+ minor: 1
+ main:
+ start: module_start
\ No newline at end of file
diff --git a/CONFIG/vita/iniparser_paf.patch b/CONFIG/vita/iniparser_paf.patch
new file mode 100644
index 00000000..e2cdacc8
--- /dev/null
+++ b/CONFIG/vita/iniparser_paf.patch
@@ -0,0 +1,53 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 4f7fdba..d388fc8 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -1,7 +1,7 @@
+ cmake_minimum_required(VERSION 3.18)
+
+ project(
+- iniparser
++ iniparser_paf
+ DESCRIPTION "C library for parsing INI-style files"
+ HOMEPAGE_URL https://gitlab.com/iniparser/iniparser/
+ LANGUAGES C
+@@ -108,6 +108,19 @@ foreach(TARGET_TYPE ${TARGET_TYPES})
+ -pedantic)
+ endif(UNIX)
+
++ if(VITA)
++ target_compile_options(
++ ${TARGET_NAME}
++ PRIVATE -Wl,-q -Wall -fshort-wchar)
++ target_link_libraries(
++ ${TARGET_NAME}
++ PRIVATE SceLibc_stub
++ )
++ target_link_options(
++ ${TARGET_NAME}
++ PUBLIC -nostartfiles -nostdlib)
++ endif()
++
+ # install targets
+ install(
+ TARGETS ${TARGET_NAME}
+diff --git a/src/iniparser.c b/src/iniparser.c
+index 1086b46..8e0e9c2 100644
+--- a/src/iniparser.c
++++ b/src/iniparser.c
+@@ -14,6 +14,15 @@
+ #include
+ #include "iniparser.h"
+
++#ifdef __vita__
++extern FILE* _Stderr;
++#undef stderr
++#define stderr _Stderr
++extern const char _Ctype[];
++#undef __locale_ctype_ptr
++#define __locale_ctype_ptr() _Ctype
++#endif
++
+ /*---------------------------- Defines -------------------------------------*/
+ #define ASCIILINESZ (1024)
+ #define INI_INVALID_KEY ((char*)-1)
diff --git a/CONFIG/vita/src/app.cpp b/CONFIG/vita/src/app.cpp
new file mode 100644
index 00000000..a97841db
--- /dev/null
+++ b/CONFIG/vita/src/app.cpp
@@ -0,0 +1,430 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+int sceLibcHeapSize = 10 * 1024 * 1024;
+
+const char* g_iniPath = "ux0:data/isledecomp/isle/isle.ini";
+
+paf::Framework* g_fw;
+paf::Plugin* g_configPlugin;
+sce::AppSettings* g_appSettings;
+sce::AppSettings::Interface* g_appSetIf;
+
+void merge_dicts(dictionary* dst, dictionary* src)
+{
+ for (int i = 0; i < src->n; i++) {
+ dictionary_set(dst, src->key[i], src->val[i]);
+ }
+}
+
+struct setting_map {
+ const char* key_ini;
+ const char* key_app;
+ const char type;
+};
+
+// mapping from ini key to settings.xml key
+const setting_map key_map[] = {
+ // Game
+ {"isle:diskpath", "disk_path", 's'},
+ {"isle:cdpath", "cd_path", 's'},
+ {"isle:savepath", "save_path", 's'},
+ {"isle:Transition Type", "transition_type", 'i'},
+ {"isle:Music", "music", 'b'},
+ {"isle:3DSound", "3d_sound", 'b'},
+
+ // Graphics
+ {"isle:Island Texture", "island_texture_quality", 'i'},
+ {"isle:Island Quality", "island_model_quality", 'i'},
+ //{"isle:Max LOD", "max_lod", 'f'},
+ //{"isle:Max Allowed Extras", "max_extras", 'i' },
+ {"isle:MSAA", "msaa", 'i'},
+
+ // Controls
+ {"isle:Touch Scheme", "touch_control_scheme", 'i'},
+ {"isle:Haptic", "rumble", 'b'},
+
+ // Extensions
+ {"extensions:texture loader", "texture_loader_extension", 'b'},
+ {"texture loader:texture path", "texture_loader_path", 's'}
+};
+
+struct Config {
+ sce::AppSettings* settings;
+ dictionary* dict;
+ char buffer[128];
+
+#define GetDictInt(x, name) x = iniparser_getint(this->dict, name, x)
+#define GetDictFloat(x, name) x = iniparser_getdouble(this->dict, name, x)
+#define GetDictString(x, name) \
+ { \
+ const char* val = iniparser_getstring(this->dict, name, nullptr); \
+ if (val != nullptr) { \
+ x = val; \
+ } \
+ }
+#define GetDictBool(x, name) x = iniparser_getboolean(this->dict, name, x)
+
+#define SetDictBool(NAME, VALUE) \
+ { \
+ const char* v = VALUE ? "true" : "false"; \
+ sceClibPrintf("SetIniBool(%s, %s)\n", NAME, v); \
+ iniparser_set(this->dict, NAME, v); \
+ }
+#define SetDictInt(NAME, VALUE) \
+ { \
+ sceClibSnprintf(buffer, sizeof(buffer), "%d", VALUE); \
+ sceClibPrintf("SetIniInt(%s, %d)\n", NAME, VALUE); \
+ iniparser_set(this->dict, NAME, buffer); \
+ }
+#define SetDictFloat(NAME, VALUE) \
+ { \
+ sceClibSnprintf(buffer, sizeof(buffer), "%f", VALUE); \
+ sceClibPrintf("SetIniFloat(%s, %f)\n", NAME, VALUE); \
+ iniparser_set(this->dict, NAME, buffer); \
+ }
+#define SetDictString(NAME, VALUE) \
+ { \
+ sceClibPrintf("SetString(%s, %s)\n", NAME, VALUE); \
+ iniparser_set(this->dict, NAME, VALUE); \
+ }
+
+ void Init(sce::AppSettings* settings)
+ {
+ this->settings = settings;
+ dict = dictionary_new(0);
+
+ // set defaults
+ iniparser_set(this->dict, "isle", nullptr);
+ iniparser_set(dict, "extensions", NULL);
+ iniparser_set(this->dict, "texture loader", nullptr);
+
+ SetDictString("isle:diskpath", "ux0:data/isledecomp/isle/disk");
+ SetDictString("isle:cdpath", "ux0:data/isledecomp/isle/cd");
+ SetDictString("isle:savepath", "ux0:data/isledecomp/isle");
+ SetDictInt("isle:MSAA", 4);
+
+ SetDictInt("isle:Display Bit Depth", 32);
+ SetDictBool("isle:Flip Surfaces", false);
+ SetDictBool("isle:Full Screen", true);
+ SetDictBool("isle:Exclusive Full Screen", true);
+ SetDictBool("isle:Wide View Angle", true);
+
+ SetDictInt("isle:Transition Type", 3); // 3: Mosaic
+ SetDictInt("isle:Touch Scheme", 2);
+
+ SetDictBool("isle:3DSound", true);
+ SetDictBool("isle:Music", true);
+ SetDictBool("isle:Haptic", true);
+
+ SetDictBool("isle:UseJoystick", true);
+ SetDictInt("isle:JoystickIndex", 0);
+ SetDictBool("isle:Draw Cursor", true);
+
+ SetDictBool("extensions:texture loader", false);
+ SetDictString("texture loader:texture path", "textures/");
+
+ SetDictBool("isle:Back Buffers in Video RAM", true);
+
+ SetDictInt("isle:Island Quality", 2);
+ SetDictInt("isle:Island Texture", 1);
+ SetDictInt("isle:MSAA", 4);
+
+ SetDictFloat("isle:Max LOD", 3.5);
+ SetDictInt("isle:Max Allowed Extras", 20);
+
+ SetDictInt("isle:Aspect Ratio", 0);
+ SetDictInt("isle:Horizontal Resolution", 640);
+ SetDictInt("isle:Vertical Resolution", 480);
+ SetDictFloat("isle:Frame Delta", 10.0f);
+ }
+
+ void LoadIni()
+ {
+ dictionary* ini = iniparser_load(g_iniPath);
+ if (ini) {
+ merge_dicts(this->dict, ini);
+ iniparser_freedict(ini);
+ }
+ }
+
+ bool SaveIni()
+ {
+ FILE* fd = fopen(g_iniPath, "w");
+ if (fd) {
+ iniparser_dump_ini(this->dict, fd);
+ }
+ else {
+ sceClibPrintf("failed to write isle.ini\n");
+ }
+ return true;
+ }
+
+ void ToSettings()
+ {
+ const int len = sizeof(key_map) / sizeof(key_map[0]);
+ for (int i = 0; i < len; i++) {
+ const setting_map m = key_map[i];
+ switch (m.type) {
+ case 'f': // float, AppSettings doesnt have float so just use string
+ case 's': {
+ const char* value = iniparser_getstring(this->dict, m.key_ini, "");
+ this->settings->SetString(m.key_app, value);
+ sceClibPrintf("ini->settings %s = %s\n", m.key_app, value);
+ break;
+ }
+ case 'i': {
+ int32_t value = iniparser_getint(this->dict, m.key_ini, 0);
+ this->settings->SetInt(m.key_app, value);
+ sceClibPrintf("ini->settings %s = %d\n", m.key_app, value);
+ break;
+ }
+ case 'b': {
+ bool value = iniparser_getboolean(this->dict, m.key_ini, 0) == 1;
+ this->settings->SetBool(m.key_app, value);
+ sceClibPrintf("ini->settings %s = %s\n", m.key_app, value ? "true" : "false");
+ break;
+ }
+ default: {
+ sceClibPrintf("invalid setting map entry %s %s %c\n", m.key_app, m.key_ini, m.type);
+ }
+ }
+ }
+ }
+
+ void FromSettings()
+ {
+ const int len = sizeof(key_map) / sizeof(key_map[0]);
+ for (int i = 0; i < len; i++) {
+ const setting_map m = key_map[i];
+ switch (m.type) {
+ case 'f': // float, AppSettings doesnt have float so just use string
+ case 's': {
+ const char* def = iniparser_getstring(this->dict, m.key_ini, "");
+ this->settings->GetString(m.key_app, this->buffer, sizeof(this->buffer), def);
+ iniparser_set(this->dict, m.key_ini, buffer);
+ sceClibPrintf("settings->ini %s = %s\n", m.key_ini, buffer);
+ break;
+ }
+ case 'i': {
+ int32_t value = iniparser_getint(this->dict, m.key_ini, 0);
+ this->settings->GetInt(m.key_app, &value, value);
+ SetDictInt(m.key_ini, value);
+ sceClibPrintf("settings->ini %s = %d\n", m.key_ini, value);
+ break;
+ }
+ case 'b': {
+ bool value = iniparser_getboolean(this->dict, m.key_ini, 0) == 1;
+ this->settings->GetBool(m.key_app, &value, value);
+ SetDictBool(m.key_ini, value);
+ sceClibPrintf("settings->ini %s = %s\n", m.key_ini, value ? "true" : "false");
+ break;
+ }
+ default: {
+ sceClibPrintf("invalid setting map entry %s %s %c\n", m.key_app, m.key_ini, m.type);
+ }
+ }
+ }
+ }
+};
+
+Config g_config;
+
+paf::Plugin* load_config_plugin(paf::Framework* paf_fw)
+{
+ paf::Plugin::InitParam pluginParam;
+ pluginParam.name = "config_plugin";
+ pluginParam.caller_name = "__main__";
+ pluginParam.resource_file = "app0:/config_plugin.rco";
+ pluginParam.init_func = NULL;
+ pluginParam.start_func = NULL;
+ pluginParam.stop_func = NULL;
+ pluginParam.exit_func = NULL;
+ paf::Plugin::LoadSync(pluginParam);
+ return paf_fw->FindPlugin("config_plugin");
+}
+
+int load_app_settings_plugin()
+{
+ paf::Plugin::InitParam pluginParam;
+ sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_BXCE);
+ sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_INI_FILE_PROCESSOR);
+ sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_COMMON_GUI_DIALOG);
+
+ pluginParam.name = "app_settings_plugin";
+ pluginParam.resource_file = "vs0:vsh/common/app_settings_plugin.rco";
+ pluginParam.caller_name = "__main__";
+ pluginParam.set_param_func = sce::AppSettings::PluginSetParamCB;
+ pluginParam.init_func = sce::AppSettings::PluginInitCB;
+ pluginParam.start_func = sce::AppSettings::PluginStartCB;
+ pluginParam.stop_func = sce::AppSettings::PluginStopCB;
+ pluginParam.exit_func = sce::AppSettings::PluginExitCB;
+ pluginParam.module_file = "vs0:vsh/common/app_settings.suprx";
+ pluginParam.draw_priority = 0x96;
+ paf::Plugin::LoadSync(pluginParam);
+ return 0;
+}
+
+int exit_type = 0;
+
+void save_and_exit()
+{
+ g_config.FromSettings();
+ g_config.SaveIni();
+ g_fw->RequestShutdown();
+ exit_type = 1;
+}
+
+void save_and_launch()
+{
+ g_config.FromSettings();
+ g_config.SaveIni();
+ g_fw->RequestShutdown();
+ exit_type = 2;
+}
+
+void CBOnStartPageTransition(const char* elementId, int32_t type)
+{
+}
+
+void CBOnPageActivate(const char* elementId, int32_t type)
+{
+}
+
+void CBOnPageDeactivate(const char* elementId, int32_t type)
+{
+}
+
+int32_t CBOnCheckVisible(const char* elementId, bool* pIsVisible)
+{
+ *pIsVisible = true;
+ return SCE_OK;
+}
+
+int32_t CBOnPreCreate(const char* elementId, sce::AppSettings::Element* element)
+{
+ return SCE_OK;
+}
+
+int32_t CBOnPostCreate(const char* elementId, paf::ui::Widget* widget)
+{
+ return SCE_OK;
+}
+
+int32_t CBOnPress(const char* elementId, const char* newValue)
+{
+ if (sce_paf_strcmp(elementId, "save_exit_button") == 0) {
+ save_and_exit();
+ return SCE_OK;
+ }
+
+ if (sce_paf_strcmp(elementId, "save_launch_button") == 0) {
+ save_and_launch();
+ return SCE_OK;
+ }
+
+ sceClibPrintf("OnPress %s %s\n", elementId, newValue);
+ return SCE_OK;
+}
+
+int32_t CBOnPress2(const char* elementId, const char* newValue)
+{
+ return SCE_OK;
+}
+
+void CBOnTerm(int32_t result)
+{
+ if (exit_type == 0) {
+ sceKernelExitProcess(0);
+ }
+}
+
+const wchar_t* CBOnGetString(const char* elementId)
+{
+ wchar_t* res = g_configPlugin->GetString(elementId);
+ if (res[0] != 0) {
+ return res;
+ }
+ return L"unknown string";
+}
+
+int32_t CBOnGetSurface(paf::graph::Surface** surf, const char* elementId)
+{
+ return SCE_OK;
+}
+
+void open_settings()
+{
+ g_config.Init(g_appSettings);
+ g_config.LoadIni();
+ g_config.ToSettings();
+
+ sce::AppSettings::InterfaceCallbacks ifCb;
+ ifCb.onStartPageTransitionCb = CBOnStartPageTransition;
+ ifCb.onPageActivateCb = CBOnPageActivate;
+ ifCb.onPageDeactivateCb = CBOnPageDeactivate;
+ ifCb.onCheckVisible = CBOnCheckVisible;
+ ifCb.onPreCreateCb = CBOnPreCreate;
+ ifCb.onPostCreateCb = CBOnPostCreate;
+ ifCb.onPressCb = CBOnPress;
+ ifCb.onPressCb2 = CBOnPress2;
+ ifCb.onTermCb = CBOnTerm;
+ ifCb.onGetStringCb = (sce::AppSettings::InterfaceCallbacks::GetStringCallback) CBOnGetString;
+ ifCb.onGetSurfaceCb = CBOnGetSurface;
+
+ paf::wstring msg_save_exit(g_configPlugin->GetString("msg_save_exit"));
+ paf::wstring msg_save_launch(g_configPlugin->GetString("msg_save_launch"));
+ paf::wstring msg_exit(g_configPlugin->GetString("msg_exit"));
+
+ paf::Plugin* appSetPlug = paf::Plugin::Find("app_settings_plugin");
+ g_appSetIf = (sce::AppSettings::Interface*) appSetPlug->GetInterface(1);
+ g_appSetIf->Show(&ifCb);
+ g_appSetIf->AddFooterButton("save_exit_button", &msg_save_exit, 1);
+ g_appSetIf->AddFooterButton("save_launch_button", &msg_save_launch, 2);
+ g_appSetIf->ShowFooter();
+}
+
+int main()
+{
+ paf::Framework::InitParam fwParam;
+ fwParam.mode = paf::Framework::Mode_Normal;
+
+ paf::Framework* paf_fw = new paf::Framework(fwParam);
+ g_fw = paf_fw;
+
+ paf_fw->LoadCommonResourceSync();
+ load_app_settings_plugin();
+ paf::Plugin* configPlugin = load_config_plugin(paf_fw);
+ g_configPlugin = configPlugin;
+ configPlugin->SetLocale(Locale_EN);
+
+ size_t fileSize = 0;
+ const char* mimeType = nullptr;
+ auto settingsXmlFile = configPlugin->GetResource()->GetFile("settings.xml", &fileSize, &mimeType);
+
+ sce::AppSettings::InitParam settingsParam;
+ settingsParam.xml_file = settingsXmlFile;
+ settingsParam.alloc_cb = sce_paf_malloc;
+ settingsParam.free_cb = sce_paf_free;
+ settingsParam.realloc_cb = sce_paf_realloc;
+ settingsParam.safemem_offset = 0;
+ settingsParam.safemem_size = 0x400;
+
+ sce::AppSettings::GetInstance(settingsParam, &g_appSettings);
+ g_appSettings->Initialize();
+
+ open_settings();
+ paf_fw->Run();
+
+ if (exit_type == 2) {
+ int ret = sceAppMgrLoadExec("app0:/eboot.bin", NULL, NULL);
+ printf("sceAppMgrLoadExec: %08x\n", ret);
+ }
+ return 0;
+}
diff --git a/CONFIG/vita/src/paf_runtime.cpp b/CONFIG/vita/src/paf_runtime.cpp
new file mode 100644
index 00000000..31266a1d
--- /dev/null
+++ b/CONFIG/vita/src/paf_runtime.cpp
@@ -0,0 +1,169 @@
+#include
+#include
+#include
+#include
+#include
+
+char sceUserMainThreadName[] = "isle_config";
+int sceUserMainThreadPriority = 0x10000100;
+int sceUserMainThreadCpuAffinityMask = 0x70000;
+SceSize sceUserMainThreadStackSize = 0x4000;
+
+void operator delete(void* ptr, unsigned int n)
+{
+ return sce_paf_free(ptr);
+}
+
+extern "C"
+{
+ void user_malloc_init()
+ {
+ }
+
+ void user_malloc_finalize(void)
+ {
+ }
+
+ void* user_malloc(size_t size)
+ {
+ return sce_paf_malloc(size);
+ }
+
+ void user_free(void* ptr)
+ {
+ sce_paf_free(ptr);
+ }
+
+ void* user_calloc(size_t nelem, size_t size)
+ {
+ return sce_paf_calloc(nelem, size);
+ }
+
+ void* user_realloc(void* ptr, size_t new_size)
+ {
+ return sce_paf_realloc(ptr, new_size);
+ }
+
+ void* user_memalign(size_t boundary, size_t size)
+ {
+ return sce_paf_memalign(boundary, size);
+ }
+
+ void* user_reallocalign(void* ptr, size_t size, size_t boundary)
+ {
+ sceClibPrintf("[PAF2LIBC] reallocalign is not supported\n");
+ abort();
+ return NULL;
+ }
+
+ int user_malloc_stats(struct malloc_managed_size* mmsize)
+ {
+ sceClibPrintf("malloc_stats\n");
+ abort();
+ return 0;
+ }
+
+ int user_malloc_stats_fast(struct malloc_managed_size* mmsize)
+ {
+ sceClibPrintf("user_malloc_stats_fast\n");
+ abort();
+ return 0;
+ }
+
+ size_t user_malloc_usable_size(void* ptr)
+ {
+ return sce_paf_musable_size(ptr);
+ }
+}
+
+void* user_new(std::size_t size)
+{
+ return sce_paf_malloc(size);
+}
+
+void* user_new(std::size_t size, const std::nothrow_t& x)
+{
+ return sce_paf_malloc(size);
+}
+
+void* user_new_array(std::size_t size)
+{
+ return sce_paf_malloc(size);
+}
+
+void* user_new_array(std::size_t size, const std::nothrow_t& x)
+{
+ return sce_paf_malloc(size);
+}
+
+void user_delete(void* ptr)
+{
+ sce_paf_free(ptr);
+}
+
+void user_delete(void* ptr, const std::nothrow_t& x)
+{
+ sce_paf_free(ptr);
+}
+
+void user_delete_array(void* ptr)
+{
+ sce_paf_free(ptr);
+}
+
+void user_delete_array(void* ptr, const std::nothrow_t& x)
+{
+ sce_paf_free(ptr);
+}
+
+int paf_init()
+{
+ int load_res;
+ ScePafInit init_param;
+ SceSysmoduleOpt sysmodule_opt;
+
+ init_param.global_heap_size = 0x1000000;
+ init_param.cdlg_mode = 0;
+ init_param.global_heap_alignment = 0;
+ init_param.global_heap_disable_assert_on_alloc_failure = 0;
+
+ load_res = 0xDEADBEEF;
+ sysmodule_opt.flags = 0;
+ sysmodule_opt.result = &load_res;
+
+ int res = sceSysmoduleLoadModuleInternalWithArg(
+ SCE_SYSMODULE_INTERNAL_PAF,
+ sizeof(init_param),
+ &init_param,
+ &sysmodule_opt
+ );
+ if ((res | load_res) != 0) {
+ sceClibPrintf(
+ "[PAF PRX Loader] Failed to load the PAF prx. (return value 0x%x, result code 0x%x )\n",
+ res,
+ load_res
+ );
+ return -1;
+ }
+ return 0;
+}
+
+int main();
+
+extern "C" int module_start(SceSize args, void* argp)
+{
+ int res = paf_init();
+ if (res < 0) {
+ sceKernelExitProcess(res);
+ return SCE_KERNEL_START_FAILED;
+ }
+
+ res = main();
+ sceKernelExitProcess(res);
+ return SCE_KERNEL_START_SUCCESS;
+}
+
+extern "C" void _start()
+{
+ module_start(0, nullptr);
+}
diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp
index c7444362..7b0fa23d 100644
--- a/ISLE/isleapp.cpp
+++ b/ISLE/isleapp.cpp
@@ -77,6 +77,14 @@
#include "android/config.h"
#endif
+#ifdef __vita__
+#include "vita/config.h"
+#include "vita/messagebox.h"
+
+#include
+#include
+#endif
+
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
// GLOBAL: ISLE 0x410030
@@ -316,6 +324,23 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
// Create global app instance
g_isle = new IsleApp();
+#ifdef __vita__
+ SceAppUtilInitParam appUtilInitParam = {0};
+ SceAppUtilBootParam appUtilBootParam = {0};
+ sceAppUtilInit(&appUtilInitParam, &appUtilBootParam);
+ SceAppUtilAppEventParam eventParam = {0};
+ sceAppUtilReceiveAppEvent(&eventParam);
+ if (eventParam.type == 0x05) {
+ g_isle->LoadConfig();
+ char buffer[2048];
+ sceAppUtilAppEventParseLiveArea(&eventParam, buffer);
+ if (strstr(buffer, "-config")) {
+ sceClibPrintf("Loading Config App.\n");
+ sceAppMgrLoadExec("app0:/isle-config.self", NULL, NULL);
+ }
+ }
+#endif
+
switch (g_isle->ParseArguments(argc, argv)) {
case SDL_APP_FAILURE:
Any_ShowSimpleMessageBox(
@@ -452,6 +477,19 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
break;
}
+#ifdef __vita__
+ // reject back touch panel
+ switch (event->type) {
+ case SDL_EVENT_FINGER_MOTION:
+ case SDL_EVENT_FINGER_DOWN:
+ case SDL_EVENT_FINGER_UP:
+ case SDL_EVENT_FINGER_CANCELED:
+ if (event->tfinger.touchID == 2) {
+ return SDL_APP_CONTINUE;
+ }
+ }
+#endif
+
switch (event->type) {
case SDL_EVENT_WINDOW_FOCUS_GAINED:
if (!IsleDebug_Enabled()) {
@@ -556,7 +594,11 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
}
break;
+#ifdef __vita__ // conflicts with screenshot button combination
+ case SDL_GAMEPAD_BUTTON_BACK:
+#else
case SDL_GAMEPAD_BUTTON_START:
+#endif
if (InputManager()) {
InputManager()->QueueEvent(c_notificationKeyPress, SDLK_ESCAPE, 0, 0, SDLK_ESCAPE);
}
@@ -884,7 +926,7 @@ MxResult IsleApp::SetupWindow()
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, g_targetHeight);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, m_fullScreen);
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE);
-#if defined(MINIWIN) && !defined(__3DS__) && !defined(WINDOWS_STORE)
+#if defined(MINIWIN) && !defined(__3DS__) && !defined(WINDOWS_STORE) && !defined(__vita__)
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
@@ -1110,6 +1152,9 @@ bool IsleApp::LoadConfig()
Android_SetupDefaultConfigOverrides(dict);
#endif
+#ifdef __vita__
+ VITA_SetupDefaultConfigOverrides(dict);
+#endif
iniparser_dump_ini(dict, iniFP);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig.GetData());
fclose(iniFP);
diff --git a/ISLE/vita/config.cpp b/ISLE/vita/config.cpp
new file mode 100644
index 00000000..40979ba2
--- /dev/null
+++ b/ISLE/vita/config.cpp
@@ -0,0 +1,13 @@
+#include "config.h"
+
+#include
+#include
+
+void VITA_SetupDefaultConfigOverrides(dictionary* p_dictionary)
+{
+ SDL_Log("Overriding default config for VITA");
+
+ iniparser_set(p_dictionary, "isle:diskpath", "ux0:data/isledecomp/isle/disk");
+ iniparser_set(p_dictionary, "isle:cdpath", "ux0:data/isledecomp/isle/cd");
+ iniparser_set(p_dictionary, "isle:MSAA", "4");
+}
diff --git a/ISLE/vita/config.h b/ISLE/vita/config.h
new file mode 100644
index 00000000..9c725c6f
--- /dev/null
+++ b/ISLE/vita/config.h
@@ -0,0 +1,8 @@
+#ifndef VITA_CONFIG_H
+#define VITA_CONFIG_H
+
+#include "dictionary.h"
+
+void VITA_SetupDefaultConfigOverrides(dictionary* p_dictionary);
+
+#endif // VITA_CONFIG_H
diff --git a/ISLE/vita/messagebox.cpp b/ISLE/vita/messagebox.cpp
new file mode 100644
index 00000000..6b3a485e
--- /dev/null
+++ b/ISLE/vita/messagebox.cpp
@@ -0,0 +1,52 @@
+#include "messagebox.h"
+
+#include "../../miniwin/src/d3drm/backends/gxm/gxm_context.h"
+
+#include
+#include
+
+bool Vita_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, const char* title, const char* message, SDL_Window* window)
+{
+ int ret;
+ SceMsgDialogParam param;
+ SceMsgDialogUserMessageParam msgParam;
+ SceMsgDialogButtonsParam buttonParam;
+ SceMsgDialogResult dialog_result;
+ SceCommonDialogErrorCode init_result;
+ bool setup_minimal_gxm = false;
+
+ SDL_zero(param);
+ sceMsgDialogParamInit(¶m);
+ param.mode = SCE_MSG_DIALOG_MODE_USER_MSG;
+
+ SDL_zero(msgParam);
+ char message_data[0x1000];
+ SDL_snprintf(message_data, sizeof(message_data), "%s\r\n\r\n%s", title, message);
+
+ msgParam.msg = (const SceChar8*) message_data;
+ msgParam.buttonType = SCE_MSG_DIALOG_BUTTON_TYPE_OK;
+ param.userMsgParam = &msgParam;
+
+ if (!gxm) {
+ gxm = (GXMContext*) SDL_malloc(sizeof(GXMContext));
+ }
+ if (ret = gxm->init(SCE_GXM_MULTISAMPLE_NONE); ret < 0) {
+ return false;
+ }
+
+ init_result = (SceCommonDialogErrorCode) sceMsgDialogInit(¶m);
+ if (init_result >= 0) {
+ while (sceMsgDialogGetStatus() == SCE_COMMON_DIALOG_STATUS_RUNNING) {
+ gxm->clear(0, 0, 0, true);
+ gxm->swap_display();
+ }
+ SDL_zero(dialog_result);
+ sceMsgDialogGetResult(&dialog_result);
+ sceMsgDialogTerm();
+ return dialog_result.buttonId == SCE_MSG_DIALOG_BUTTON_ID_OK;
+ }
+ else {
+ return false;
+ }
+ return true;
+}
diff --git a/ISLE/vita/messagebox.h b/ISLE/vita/messagebox.h
new file mode 100644
index 00000000..7ad877ee
--- /dev/null
+++ b/ISLE/vita/messagebox.h
@@ -0,0 +1,8 @@
+#ifndef VITA_MESSAGE_BOX_H
+#define VITA_MESSAGE_BOX_H
+
+#include
+
+bool Vita_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, const char* title, const char* message, SDL_Window* window);
+
+#endif // VITA_MESSAGE_BOX_H
diff --git a/README.md b/README.md
index a6bd8eff..435e7628 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,7 @@ Please note: this project is primarily dedicated to achieving platform independe
| Xbox One | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
| iOS | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
| Android | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
+| Playstation Vita | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
We are actively working to support more platforms. If you have experience with a particular platform, we encourage you to contribute to `isle-portable`. You can find a [list of ongoing efforts](https://github.com/isledecomp/isle-portable/wiki/Work%E2%80%90in%E2%80%90progress-ports) in our Wiki.
diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt
index 07f40b04..4293780e 100644
--- a/miniwin/CMakeLists.txt
+++ b/miniwin/CMakeLists.txt
@@ -30,7 +30,7 @@ target_compile_definitions(miniwin PRIVATE
list(APPEND GRAPHICS_BACKENDS USE_SOFTWARE_RENDER)
list(APPEND GRAPHICS_BACKENDS USE_SDL_GPU)
-if(NOT WINDOWS_STORE)
+if(NOT (VITA OR WINDOWS_STORE))
find_package(OpenGL)
if(OpenGL_FOUND)
message(STATUS "Found OpenGL: enabling OpenGL 1.x renderer")
@@ -69,6 +69,25 @@ if(NOT WINDOWS_STORE)
endif()
endif()
+if(VITA)
+ add_subdirectory(src/d3drm/backends/gxm/shaders)
+
+ target_sources(miniwin PRIVATE
+ src/d3drm/backends/gxm/gxm_context.cpp
+ src/d3drm/backends/gxm/gxm_renderer.cpp
+ src/d3drm/backends/gxm/gxm_memory.cpp
+ src/d3drm/backends/gxm/tlsf.c
+ )
+ target_link_libraries(miniwin PRIVATE
+ SceGxm_stub
+ SceRazorCapture_stub
+ SceRazorHud_stub
+ taihen_stub
+ gxm_shaders
+ )
+ list(APPEND GRAPHICS_BACKENDS USE_GXM)
+ list(REMOVE_ITEM GRAPHICS_BACKENDS USE_SOFTWARE_RENDER USE_SDL_GPU)
+endif()
if(NINTENDO_3DS)
if(ISLE_DEBUG)
diff --git a/miniwin/src/d3drm/backends/gxm/gxm_context.cpp b/miniwin/src/d3drm/backends/gxm/gxm_context.cpp
new file mode 100644
index 00000000..1e13d1d0
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/gxm_context.cpp
@@ -0,0 +1,680 @@
+#include "gxm_context.h"
+
+#include "gxm_memory.h"
+#include "shaders/gxm_shaders.h"
+#include "tlsf.h"
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+#include
+
+static bool gxm_initialized = false;
+
+bool with_razor_capture;
+bool with_razor_hud;
+
+#define CDRAM_POOL_SIZE 64 * 1024 * 1024
+#define VITA_GXM_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8
+
+typedef struct GXMDisplayData {
+ void* address;
+ int index;
+} GXMDisplayData;
+
+static void display_callback(const void* callback_data)
+{
+ const GXMDisplayData* display_data = (const GXMDisplayData*) callback_data;
+
+ SceDisplayFrameBuf framebuf;
+ SDL_memset(&framebuf, 0x00, sizeof(SceDisplayFrameBuf));
+ framebuf.size = sizeof(SceDisplayFrameBuf);
+ framebuf.base = display_data->address;
+ framebuf.pitch = VITA_GXM_SCREEN_STRIDE;
+ framebuf.pixelformat = VITA_GXM_PIXEL_FORMAT;
+ framebuf.width = VITA_GXM_SCREEN_WIDTH;
+ framebuf.height = VITA_GXM_SCREEN_HEIGHT;
+ sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME);
+ sceDisplayWaitSetFrameBuf();
+}
+
+#ifdef GXM_WITH_RAZOR
+static int load_suprx(const char* name)
+{
+ sceClibPrintf("loading %s\n", name);
+ int modid = _sceKernelLoadModule(name, 0, nullptr);
+ if (modid < 0) {
+ sceClibPrintf("%s load: 0x%08x\n", name, modid);
+ return modid;
+ }
+ int status;
+ int ret = sceKernelStartModule(modid, 0, nullptr, 0, nullptr, &status);
+ if (ret < 0) {
+ sceClibPrintf("%s start: 0x%08x\n", name, ret);
+ }
+ return ret;
+}
+
+static void load_razor()
+{
+ with_razor_capture = false;
+ with_razor_hud = false;
+ if (load_suprx("app0:librazorcapture_es4.suprx") >= 0) {
+ with_razor_capture = true;
+ }
+
+ if (with_razor_capture) {
+ // sceRazorGpuCaptureEnableSalvage("ux0:data/gpu_crash.sgx");
+ }
+
+ if (with_razor_hud) {
+ sceRazorGpuTraceSetFilename("ux0:data/gpu_trace", 3);
+ }
+}
+#endif
+
+int gxm_library_init()
+{
+ if (gxm_initialized) {
+ return 0;
+ }
+
+#ifdef GXM_WITH_RAZOR
+ load_razor();
+#endif
+
+ SceGxmInitializeParams initializeParams;
+ SDL_memset(&initializeParams, 0, sizeof(SceGxmInitializeParams));
+ initializeParams.flags = 0;
+ initializeParams.displayQueueMaxPendingCount = VITA_GXM_PENDING_SWAPS;
+ initializeParams.displayQueueCallback = display_callback;
+ initializeParams.displayQueueCallbackDataSize = sizeof(GXMDisplayData);
+ initializeParams.parameterBufferSize = SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE;
+
+ int err = sceGxmInitialize(&initializeParams);
+ if (err != 0) {
+ SDL_LogError(SDL_LOG_CATEGORY_RENDER, "gxm init failed: %d", err);
+ return err;
+ }
+ gxm_initialized = true;
+ return 0;
+}
+
+GXMContext* gxm;
+
+void GXMContext::init_cdram_allocator()
+{
+ // allocator
+ this->cdramMem = vita_mem_alloc(
+ SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
+ CDRAM_POOL_SIZE,
+ 16,
+ SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
+ &this->cdramUID,
+ "cdram_pool"
+ );
+ this->cdramPool = SDL_malloc(tlsf_size());
+ tlsf_create(this->cdramPool);
+ tlsf_add_pool(this->cdramPool, this->cdramMem, CDRAM_POOL_SIZE);
+}
+
+int GXMContext::init_context()
+{
+ int ret;
+
+ const unsigned int patcherBufferSize = 64 * 1024;
+ const unsigned int patcherVertexUsseSize = 64 * 1024;
+ const unsigned int patcherFragmentUsseSize = 64 * 1024;
+
+ // allocate buffers
+ this->vdmRingBuffer = vita_mem_alloc(
+ SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
+ SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE,
+ 4,
+ SCE_GXM_MEMORY_ATTRIB_READ,
+ &this->vdmRingBufferUid,
+ "vdmRingBuffer"
+ );
+
+ this->vertexRingBuffer = vita_mem_alloc(
+ SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
+ SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE,
+ 4,
+ SCE_GXM_MEMORY_ATTRIB_READ,
+ &this->vertexRingBufferUid,
+ "vertexRingBuffer"
+ );
+
+ this->fragmentRingBuffer = vita_mem_alloc(
+ SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
+ SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE,
+ 4,
+ SCE_GXM_MEMORY_ATTRIB_READ,
+ &this->fragmentRingBufferUid,
+ "fragmentRingBuffer"
+ );
+
+ this->fragmentUsseRingBuffer = vita_mem_fragment_usse_alloc(
+ SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE,
+ &this->fragmentUsseRingBufferUid,
+ &this->fragmentUsseRingBufferOffset
+ );
+
+ // create context
+ SceGxmContextParams contextParams;
+ memset(&contextParams, 0, sizeof(SceGxmContextParams));
+ contextParams.hostMem = SDL_malloc(SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE);
+ contextParams.hostMemSize = SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE;
+ contextParams.vdmRingBufferMem = this->vdmRingBuffer;
+ contextParams.vdmRingBufferMemSize = SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE;
+ contextParams.vertexRingBufferMem = this->vertexRingBuffer;
+ contextParams.vertexRingBufferMemSize = SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE;
+ contextParams.fragmentRingBufferMem = this->fragmentRingBuffer;
+ contextParams.fragmentRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE;
+ contextParams.fragmentUsseRingBufferMem = this->fragmentUsseRingBuffer;
+ contextParams.fragmentUsseRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE;
+ contextParams.fragmentUsseRingBufferOffset = this->fragmentUsseRingBufferOffset;
+
+ ret = SCE_ERR(sceGxmCreateContext, &contextParams, &this->context);
+ if (ret < 0) {
+ return ret;
+ }
+ this->contextHostMem = contextParams.hostMem;
+
+ // shader patcher
+ this->patcherBuffer = vita_mem_alloc(
+ SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
+ patcherBufferSize,
+ 4,
+ SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
+ &this->patcherBufferUid,
+ "patcherBuffer"
+ );
+
+ this->patcherVertexUsse =
+ vita_mem_vertex_usse_alloc(patcherVertexUsseSize, &this->patcherVertexUsseUid, &this->patcherVertexUsseOffset);
+
+ this->patcherFragmentUsse = vita_mem_fragment_usse_alloc(
+ patcherFragmentUsseSize,
+ &this->patcherFragmentUsseUid,
+ &this->patcherFragmentUsseOffset
+ );
+
+ SceGxmShaderPatcherParams patcherParams;
+ 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 = this->patcherBuffer;
+ patcherParams.bufferMemSize = patcherBufferSize;
+ patcherParams.vertexUsseAllocCallback = NULL;
+ patcherParams.vertexUsseFreeCallback = NULL;
+ patcherParams.vertexUsseMem = this->patcherVertexUsse;
+ patcherParams.vertexUsseMemSize = patcherVertexUsseSize;
+ patcherParams.vertexUsseOffset = this->patcherVertexUsseOffset;
+ patcherParams.fragmentUsseAllocCallback = NULL;
+ patcherParams.fragmentUsseFreeCallback = NULL;
+ patcherParams.fragmentUsseMem = this->patcherFragmentUsse;
+ patcherParams.fragmentUsseMemSize = patcherFragmentUsseSize;
+ patcherParams.fragmentUsseOffset = this->patcherFragmentUsseOffset;
+
+ ret = SCE_ERR(sceGxmShaderPatcherCreate, &patcherParams, &this->shaderPatcher);
+ if (ret < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+int GXMContext::create_display_buffers(SceGxmMultisampleMode msaaMode)
+{
+ int ret;
+
+ const uint32_t alignedWidth = ALIGN(VITA_GXM_SCREEN_WIDTH, SCE_GXM_TILE_SIZEX);
+ const uint32_t alignedHeight = ALIGN(VITA_GXM_SCREEN_HEIGHT, SCE_GXM_TILE_SIZEY);
+ uint32_t sampleCount = alignedWidth * alignedHeight;
+ uint32_t depthStrideInSamples = alignedWidth;
+
+ if (msaaMode == SCE_GXM_MULTISAMPLE_4X) {
+ sampleCount *= 4;
+ depthStrideInSamples *= 2;
+ }
+ else if (msaaMode == SCE_GXM_MULTISAMPLE_2X) {
+ sampleCount *= 2;
+ }
+
+ // render target
+ SceGxmRenderTargetParams renderTargetParams;
+ memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
+ renderTargetParams.flags = 0;
+ renderTargetParams.width = VITA_GXM_SCREEN_WIDTH;
+ renderTargetParams.height = VITA_GXM_SCREEN_HEIGHT;
+ renderTargetParams.scenesPerFrame = 1;
+ renderTargetParams.multisampleMode = msaaMode;
+ renderTargetParams.multisampleLocations = 0;
+ renderTargetParams.driverMemBlock = -1; // Invalid UID
+ ret = SCE_ERR(sceGxmCreateRenderTarget, &renderTargetParams, &this->renderTarget);
+ if (ret < 0) {
+ return ret;
+ }
+ this->renderTargetInit = true;
+
+ for (int i = 0; i < GXM_DISPLAY_BUFFER_COUNT; i++) {
+ this->displayBuffers[i] = vita_mem_alloc(
+ SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
+ 4 * VITA_GXM_SCREEN_STRIDE * VITA_GXM_SCREEN_HEIGHT,
+ SCE_GXM_COLOR_SURFACE_ALIGNMENT,
+ SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
+ &this->displayBuffersUid[i],
+ "displayBuffers"
+ );
+
+ ret = SCE_ERR(
+ sceGxmColorSurfaceInit,
+ &this->displayBuffersSurface[i],
+ SCE_GXM_COLOR_FORMAT_A8B8G8R8,
+ SCE_GXM_COLOR_SURFACE_LINEAR,
+ (msaaMode == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE
+ : SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
+ SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
+ VITA_GXM_SCREEN_WIDTH,
+ VITA_GXM_SCREEN_HEIGHT,
+ VITA_GXM_SCREEN_STRIDE,
+ this->displayBuffers[i]
+ );
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = SCE_ERR(sceGxmSyncObjectCreate, &this->displayBuffersSync[i]);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ // depth & stencil
+ this->depthBufferData = vita_mem_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,
+ &this->depthBufferUid,
+ "depthBufferData"
+ );
+
+ this->stencilBufferData = vita_mem_alloc(
+ SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
+ 1 * sampleCount,
+ SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT,
+ SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
+ &this->stencilBufferUid,
+ "stencilBufferData"
+ );
+
+ ret = SCE_ERR(
+ sceGxmDepthStencilSurfaceInit,
+ &this->depthSurface,
+ SCE_GXM_DEPTH_STENCIL_FORMAT_DF32_S8,
+ SCE_GXM_DEPTH_STENCIL_SURFACE_TILED,
+ depthStrideInSamples,
+ this->depthBufferData,
+ this->stencilBufferData
+ );
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+void GXMContext::destroy_display_buffers()
+{
+ if (this->renderTargetInit) {
+ sceGxmFinish(this->context);
+ sceGxmDestroyRenderTarget(this->renderTarget);
+ this->renderTargetInit = false;
+ }
+ for (int i = 0; i < GXM_DISPLAY_BUFFER_COUNT; i++) {
+ if (this->displayBuffers[i]) {
+ vita_mem_free(this->displayBuffersUid[i]);
+ this->displayBuffers[i] = nullptr;
+ this->displayBuffersUid[i] = -1;
+ sceGxmSyncObjectDestroy(this->displayBuffersSync[i]);
+ }
+ }
+
+ if (this->depthBufferData) {
+ vita_mem_free(this->depthBufferUid);
+ this->depthBufferData = nullptr;
+ this->depthBufferUid = -1;
+ }
+
+ if (this->stencilBufferData) {
+ vita_mem_free(this->stencilBufferUid);
+ this->stencilBufferData = nullptr;
+ this->stencilBufferUid = -1;
+ }
+}
+
+void GXMContext::init_clear_mesh()
+{
+ this->clearVertices = static_cast(this->alloc(sizeof(GXMVertex2D) * 4, 4));
+ this->clearVertices[0] = {.position = {-1.0, 1.0}, .texCoord = {0, 0}};
+ this->clearVertices[1] = {.position = {1.0, 1.0}, .texCoord = {0, 0}};
+ this->clearVertices[2] = {.position = {-1.0, -1.0}, .texCoord = {0, 0}};
+ this->clearVertices[3] = {.position = {1.0, -1.0}, .texCoord = {0, 0}};
+
+ this->clearIndices = static_cast(this->alloc(sizeof(uint16_t) * 4, 4));
+ this->clearIndices[0] = 0;
+ this->clearIndices[1] = 1;
+ this->clearIndices[2] = 2;
+ this->clearIndices[3] = 3;
+}
+
+int GXMContext::register_base_shaders()
+{
+ int ret;
+ // register plane, color, image shaders
+ ret = SCE_ERR(
+ sceGxmShaderPatcherRegisterProgram,
+ this->shaderPatcher,
+ planeVertexProgramGxp,
+ &this->planeVertexProgramId
+ );
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = SCE_ERR(
+ sceGxmShaderPatcherRegisterProgram,
+ this->shaderPatcher,
+ colorFragmentProgramGxp,
+ &this->colorFragmentProgramId
+ );
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = SCE_ERR(
+ sceGxmShaderPatcherRegisterProgram,
+ this->shaderPatcher,
+ imageFragmentProgramGxp,
+ &this->imageFragmentProgramId
+ );
+ if (ret < 0) {
+ return ret;
+ }
+ this->color_uColor = sceGxmProgramFindParameterByName(colorFragmentProgramGxp, "uColor"); // vec4
+ return 0;
+}
+
+int GXMContext::patch_base_shaders(SceGxmMultisampleMode msaaMode)
+{
+ int ret;
+ {
+ GET_SHADER_PARAM(positionAttribute, planeVertexProgramGxp, "aPosition", -1);
+ GET_SHADER_PARAM(texCoordAttribute, planeVertexProgramGxp, "aTexCoord", -1);
+
+ SceGxmVertexAttribute vertexAttributes[2];
+ SceGxmVertexStream vertexStreams[1];
+
+ // position
+ vertexAttributes[0].streamIndex = 0;
+ vertexAttributes[0].offset = 0;
+ vertexAttributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
+ vertexAttributes[0].componentCount = 2;
+ vertexAttributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(positionAttribute);
+
+ // uv
+ vertexAttributes[1].streamIndex = 0;
+ vertexAttributes[1].offset = 8;
+ vertexAttributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
+ vertexAttributes[1].componentCount = 2;
+ vertexAttributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(texCoordAttribute);
+
+ vertexStreams[0].stride = sizeof(GXMVertex2D);
+ vertexStreams[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT;
+
+ ret = SCE_ERR(
+ sceGxmShaderPatcherCreateVertexProgram,
+ this->shaderPatcher,
+ this->planeVertexProgramId,
+ vertexAttributes,
+ 2,
+ vertexStreams,
+ 1,
+ &this->planeVertexProgram
+ );
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = SCE_ERR(
+ sceGxmShaderPatcherCreateFragmentProgram,
+ this->shaderPatcher,
+ this->colorFragmentProgramId,
+ SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
+ msaaMode,
+ NULL,
+ planeVertexProgramGxp,
+ &this->colorFragmentProgram
+ );
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = SCE_ERR(
+ sceGxmShaderPatcherCreateFragmentProgram,
+ this->shaderPatcher,
+ this->imageFragmentProgramId,
+ SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
+ msaaMode,
+ &blendInfoTransparent,
+ planeVertexProgramGxp,
+ &this->imageFragmentProgram
+ );
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void GXMContext::destroy_base_shaders()
+{
+ sceGxmShaderPatcherReleaseVertexProgram(this->shaderPatcher, this->planeVertexProgram);
+ sceGxmShaderPatcherReleaseFragmentProgram(this->shaderPatcher, this->colorFragmentProgram);
+ sceGxmShaderPatcherReleaseFragmentProgram(this->shaderPatcher, this->imageFragmentProgram);
+}
+
+int GXMContext::init(SceGxmMultisampleMode msaaMode)
+{
+ int ret = 0;
+ ret = gxm_library_init();
+ if (ret < 0) {
+ return ret;
+ }
+ if (this->cdramPool == nullptr) {
+ this->init_cdram_allocator();
+ }
+
+ if (this->context == nullptr) {
+ ret = this->init_context();
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (this->planeVertexProgramId == 0) {
+ ret = this->register_base_shaders();
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (this->clearVertices == nullptr) {
+ this->init_clear_mesh();
+ }
+
+ // recreate when msaa is different
+ if (msaaMode != this->displayMsaa && this->renderTargetInit) {
+ this->destroy_display_buffers();
+ this->destroy_base_shaders();
+ }
+
+ if (!this->renderTargetInit) {
+ ret = this->create_display_buffers(msaaMode);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = this->patch_base_shaders(msaaMode);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int inuse_mem = 0;
+void* GXMContext::alloc(size_t size, size_t align)
+{
+ if (this->cdramPool == nullptr) {
+ this->init_cdram_allocator();
+ }
+ DEBUG_ONLY_PRINTF("cdram_alloc(%d, %d) inuse=%d ", size, align, inuse_mem);
+ void* ptr = tlsf_memalign(this->cdramPool, align, size);
+ DEBUG_ONLY_PRINTF("ptr=%p\n", ptr);
+ inuse_mem += tlsf_block_size(ptr);
+ return ptr;
+}
+
+void GXMContext::free(void* ptr)
+{
+ inuse_mem -= tlsf_block_size(ptr);
+ DEBUG_ONLY_PRINTF("cdram_free(%p)\n", ptr);
+ tlsf_free(this->cdramPool, ptr);
+}
+
+void GXMContext::clear(float r, float g, float b, bool new_scene)
+{
+ new_scene = new_scene && !this->sceneStarted;
+ if (new_scene) {
+ sceGxmBeginScene(
+ this->context,
+ 0,
+ this->renderTarget,
+ nullptr,
+ nullptr,
+ this->displayBuffersSync[this->backBufferIndex],
+ &this->displayBuffersSurface[this->backBufferIndex],
+ &this->depthSurface
+ );
+ this->sceneStarted = true;
+ }
+
+ float color[] = {r, g, b, 1};
+
+ sceGxmSetVertexProgram(this->context, this->planeVertexProgram);
+ sceGxmSetFragmentProgram(this->context, this->colorFragmentProgram);
+
+ void* vertUniforms;
+ void* fragUniforms;
+ sceGxmReserveVertexDefaultUniformBuffer(gxm->context, &vertUniforms);
+ sceGxmReserveFragmentDefaultUniformBuffer(gxm->context, &fragUniforms);
+
+ sceGxmSetVertexStream(gxm->context, 0, this->clearVertices);
+ sceGxmSetUniformDataF(fragUniforms, this->color_uColor, 0, 4, color);
+
+ sceGxmSetFrontDepthFunc(gxm->context, SCE_GXM_DEPTH_FUNC_ALWAYS);
+ sceGxmDraw(gxm->context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->clearIndices, 4);
+ if (new_scene) {
+ sceGxmEndScene(this->context, nullptr, nullptr);
+ this->sceneStarted = false;
+ }
+}
+
+void GXMContext::copy_frontbuffer()
+{
+ SceGxmTexture texture;
+ sceGxmTextureInitLinearStrided(
+ &texture,
+ this->displayBuffers[this->frontBufferIndex],
+ SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR,
+ VITA_GXM_SCREEN_WIDTH,
+ VITA_GXM_SCREEN_HEIGHT,
+ VITA_GXM_SCREEN_STRIDE * 4
+ );
+ sceGxmSetVertexProgram(this->context, this->planeVertexProgram);
+ sceGxmSetFragmentProgram(this->context, this->imageFragmentProgram);
+
+ void* vertUniforms;
+ void* fragUniforms;
+ sceGxmReserveVertexDefaultUniformBuffer(this->context, &vertUniforms);
+ sceGxmReserveFragmentDefaultUniformBuffer(this->context, &fragUniforms);
+
+ sceGxmSetVertexStream(this->context, 0, this->clearVertices);
+ sceGxmSetFragmentTexture(this->context, 0, &texture);
+
+ sceGxmSetFrontDepthFunc(this->context, SCE_GXM_DEPTH_FUNC_ALWAYS);
+ sceGxmDraw(this->context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->clearIndices, 4);
+}
+
+void GXMContext::destroy()
+{
+ sceGxmDisplayQueueFinish();
+ if (gxm->context) {
+ sceGxmFinish(gxm->context);
+ }
+
+ this->destroy_display_buffers();
+ this->destroy_base_shaders();
+
+ sceGxmShaderPatcherDestroy(this->shaderPatcher);
+ sceGxmDestroyContext(this->context);
+ vita_mem_fragment_usse_free(this->fragmentUsseRingBufferUid);
+ vita_mem_free(this->fragmentRingBufferUid);
+ vita_mem_free(this->vertexRingBufferUid);
+ vita_mem_free(this->vdmRingBufferUid);
+ SDL_free(this->contextHostMem);
+}
+
+void GXMContext::swap_display()
+{
+ if (this->sceneStarted) {
+ sceGxmEndScene(gxm->context, nullptr, nullptr);
+ this->sceneStarted = false;
+ }
+
+ SceCommonDialogUpdateParam updateParam;
+ SDL_zero(updateParam);
+ updateParam.renderTarget.colorFormat = VITA_GXM_COLOR_FORMAT;
+ updateParam.renderTarget.surfaceType = SCE_GXM_COLOR_SURFACE_LINEAR;
+ updateParam.renderTarget.width = VITA_GXM_SCREEN_WIDTH;
+ updateParam.renderTarget.height = VITA_GXM_SCREEN_HEIGHT;
+ updateParam.renderTarget.strideInPixels = VITA_GXM_SCREEN_STRIDE;
+ updateParam.renderTarget.colorSurfaceData = this->displayBuffers[this->backBufferIndex];
+ updateParam.displaySyncObject = this->displayBuffersSync[this->backBufferIndex];
+ sceCommonDialogUpdate(&updateParam);
+
+ sceGxmPadHeartbeat(
+ &this->displayBuffersSurface[this->backBufferIndex],
+ this->displayBuffersSync[this->backBufferIndex]
+ );
+
+ // display
+ GXMDisplayData displayData;
+ displayData.address = this->displayBuffers[this->backBufferIndex];
+ displayData.index = this->backBufferIndex;
+ sceGxmDisplayQueueAddEntry(
+ this->displayBuffersSync[this->frontBufferIndex],
+ this->displayBuffersSync[this->backBufferIndex],
+ &displayData
+ );
+
+ this->frontBufferIndex = this->backBufferIndex;
+ this->backBufferIndex = (this->backBufferIndex + 1) % GXM_DISPLAY_BUFFER_COUNT;
+}
diff --git a/miniwin/src/d3drm/backends/gxm/gxm_context.h b/miniwin/src/d3drm/backends/gxm/gxm_context.h
new file mode 100644
index 00000000..3b867f8d
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/gxm_context.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include
+
+#define VITA_GXM_SCREEN_WIDTH 960
+#define VITA_GXM_SCREEN_HEIGHT 544
+#define VITA_GXM_SCREEN_STRIDE 1024
+#define VITA_GXM_PENDING_SWAPS 2
+#define VITA_GXM_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8
+
+#define GXM_DISPLAY_BUFFER_COUNT 3
+
+typedef struct GXMVertex2D {
+ float position[2];
+ float texCoord[2];
+} GXMVertex2D;
+
+typedef struct GXMContext {
+ // context
+ SceUID vdmRingBufferUid;
+ SceUID vertexRingBufferUid;
+ SceUID fragmentRingBufferUid;
+ SceUID fragmentUsseRingBufferUid;
+ size_t fragmentUsseRingBufferOffset;
+
+ void* vdmRingBuffer;
+ void* vertexRingBuffer;
+ void* fragmentRingBuffer;
+ void* fragmentUsseRingBuffer;
+
+ void* contextHostMem;
+ SceGxmContext* context;
+
+ // shader patcher
+ SceUID patcherBufferUid;
+ void* patcherBuffer;
+
+ SceUID patcherVertexUsseUid;
+ size_t patcherVertexUsseOffset;
+ void* patcherVertexUsse;
+ SceUID patcherFragmentUsseUid;
+ size_t patcherFragmentUsseOffset;
+ void* patcherFragmentUsse;
+
+ SceGxmShaderPatcher* shaderPatcher;
+
+ // clear
+ SceGxmShaderPatcherId planeVertexProgramId;
+ SceGxmShaderPatcherId colorFragmentProgramId;
+ SceGxmShaderPatcherId imageFragmentProgramId;
+ SceGxmVertexProgram* planeVertexProgram;
+ SceGxmFragmentProgram* colorFragmentProgram;
+ SceGxmFragmentProgram* imageFragmentProgram;
+ const SceGxmProgramParameter* color_uColor;
+ GXMVertex2D* clearVertices;
+ uint16_t* clearIndices;
+
+ // display
+ SceGxmRenderTarget* renderTarget;
+ bool renderTargetInit = false;
+ void* displayBuffers[GXM_DISPLAY_BUFFER_COUNT];
+ SceUID displayBuffersUid[GXM_DISPLAY_BUFFER_COUNT];
+ SceGxmColorSurface displayBuffersSurface[GXM_DISPLAY_BUFFER_COUNT];
+ SceGxmSyncObject* displayBuffersSync[GXM_DISPLAY_BUFFER_COUNT];
+ int backBufferIndex = 0;
+ int frontBufferIndex = 1;
+ SceGxmMultisampleMode displayMsaa;
+
+ // depth buffer
+ SceUID depthBufferUid;
+ void* depthBufferData;
+ SceUID stencilBufferUid;
+ void* stencilBufferData;
+ SceGxmDepthStencilSurface depthSurface;
+
+ // allocator
+ SceUID cdramUID;
+ void* cdramMem;
+ void* cdramPool;
+
+ bool sceneStarted;
+
+ void swap_display();
+ void copy_frontbuffer();
+ int init(SceGxmMultisampleMode msaaMode);
+ void init_cdram_allocator();
+ int init_context();
+ int create_display_buffers(SceGxmMultisampleMode msaaMode);
+ void init_clear_mesh();
+ void destroy_display_buffers();
+ int register_base_shaders();
+ int patch_base_shaders(SceGxmMultisampleMode msaaMode);
+ void destroy_base_shaders();
+
+ void destroy();
+ void clear(float r, float g, float b, bool new_scene);
+ void* alloc(size_t size, size_t align);
+ void free(void* ptr);
+} GXMContext;
+
+// global so that common dialog can be rendererd without GXMRenderer
+extern GXMContext* gxm;
+
+extern bool with_razor_capture;
+extern bool with_razor_hud;
diff --git a/miniwin/src/d3drm/backends/gxm/gxm_memory.cpp b/miniwin/src/d3drm/backends/gxm/gxm_memory.cpp
new file mode 100644
index 00000000..01313d0a
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/gxm_memory.cpp
@@ -0,0 +1,119 @@
+#include "gxm_memory.h"
+
+#include "tlsf.h"
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+
+void* patcher_host_alloc(void* user_data, unsigned int size)
+{
+ void* mem = SDL_malloc(size);
+ (void) user_data;
+ return mem;
+}
+
+void patcher_host_free(void* user_data, void* mem)
+{
+ (void) user_data;
+ SDL_free(mem);
+}
+
+void* vita_mem_alloc(unsigned int type, size_t size, size_t alignment, int attribs, SceUID* uid, const char* name)
+{
+ void* mem;
+
+ if (type == SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW) {
+ size = ALIGN(size, 256 * 1024);
+ }
+ else if (type == SCE_KERNEL_MEMBLOCK_TYPE_USER_MAIN_PHYCONT_NC_RW) {
+ size = ALIGN(size, 1024 * 1024);
+ }
+ else {
+ size = ALIGN(size, 4 * 1024);
+ }
+
+ *uid = sceKernelAllocMemBlock(name, type, size, NULL);
+
+ if (*uid < 0) {
+ SDL_Log("sceKernelAllocMemBlock: 0x%x", *uid);
+ return NULL;
+ }
+
+ if (sceKernelGetMemBlockBase(*uid, &mem) < 0) {
+ return NULL;
+ }
+
+ if (sceGxmMapMemory(mem, size, (SceGxmMemoryAttribFlags) attribs) < 0) {
+ SDL_Log("sceGxmMapMemory 0x%x 0x%x %d failed", mem, size, attribs);
+ return NULL;
+ }
+
+ return mem;
+}
+
+void vita_mem_free(SceUID uid)
+{
+ void* mem = NULL;
+ if (sceKernelGetMemBlockBase(uid, &mem) < 0) {
+ return;
+ }
+ sceGxmUnmapMemory(mem);
+ sceKernelFreeMemBlock(uid);
+}
+
+void* vita_mem_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;
+}
+
+void vita_mem_vertex_usse_free(SceUID uid)
+{
+ void* mem = NULL;
+ if (sceKernelGetMemBlockBase(uid, &mem) < 0) {
+ return;
+ }
+ sceGxmUnmapVertexUsseMemory(mem);
+ sceKernelFreeMemBlock(uid);
+}
+
+void* vita_mem_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;
+}
+
+void vita_mem_fragment_usse_free(SceUID uid)
+{
+ void* mem = NULL;
+ if (sceKernelGetMemBlockBase(uid, &mem) < 0) {
+ return;
+ }
+ sceGxmUnmapFragmentUsseMemory(mem);
+ sceKernelFreeMemBlock(uid);
+}
diff --git a/miniwin/src/d3drm/backends/gxm/gxm_memory.h b/miniwin/src/d3drm/backends/gxm/gxm_memory.h
new file mode 100644
index 00000000..f169603d
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/gxm_memory.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include
+#include
+
+void* patcher_host_alloc(void* user_data, unsigned int size);
+void patcher_host_free(void* user_data, void* mem);
+
+void* vita_mem_alloc(unsigned int type, size_t size, size_t alignment, int attribs, SceUID* uid, const char* name);
+void vita_mem_free(SceUID uid);
+
+void* vita_mem_vertex_usse_alloc(unsigned int size, SceUID* uid, unsigned int* usse_offset);
+void vita_mem_vertex_usse_free(SceUID uid);
+void* vita_mem_fragment_usse_alloc(unsigned int size, SceUID* uid, unsigned int* usse_offset);
+void vita_mem_fragment_usse_free(SceUID uid);
diff --git a/miniwin/src/d3drm/backends/gxm/gxm_renderer.cpp b/miniwin/src/d3drm/backends/gxm/gxm_renderer.cpp
new file mode 100644
index 00000000..efb25bdd
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/gxm_renderer.cpp
@@ -0,0 +1,1101 @@
+#include "d3drmrenderer_gxm.h"
+#include "gxm_context.h"
+#include "gxm_memory.h"
+#include "meshutils.h"
+#include "razor.h"
+#include "shaders/gxm_shaders.h"
+#include "tlsf.h"
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// from isleapp
+extern bool g_dpadUp;
+extern bool g_dpadDown;
+extern bool g_dpadLeft;
+extern bool g_dpadRight;
+
+typedef struct GXMVertex {
+ float position[3];
+ float normal[3];
+ float texCoord[2];
+} GXMVertex;
+
+Direct3DRMRenderer* GXMRenderer::Create(DWORD width, DWORD height, DWORD msaaSamples)
+{
+ int ret = gxm_library_init();
+ if (ret < 0) {
+ return nullptr;
+ }
+
+ SceGxmMultisampleMode msaaMode = SCE_GXM_MULTISAMPLE_NONE;
+ if (msaaSamples == 2) {
+ msaaMode = SCE_GXM_MULTISAMPLE_2X;
+ }
+ if (msaaSamples == 4) {
+ msaaMode = SCE_GXM_MULTISAMPLE_4X;
+ }
+
+ if (!gxm) {
+ gxm = (GXMContext*) SDL_malloc(sizeof(GXMContext));
+ memset(gxm, 0, sizeof(GXMContext));
+ }
+ ret = SCE_ERR(gxm->init, msaaMode);
+ if (ret < 0) {
+ return nullptr;
+ }
+
+ return new GXMRenderer(width, height, msaaMode);
+}
+
+GXMRenderer::GXMRenderer(DWORD width, DWORD height, SceGxmMultisampleMode msaaMode)
+{
+ m_width = VITA_GXM_SCREEN_WIDTH;
+ m_height = VITA_GXM_SCREEN_HEIGHT;
+ m_virtualWidth = width;
+ m_virtualHeight = height;
+
+ // register shader programs
+ int ret = SCE_ERR(
+ sceGxmShaderPatcherRegisterProgram,
+ gxm->shaderPatcher,
+ mainVertexProgramGxp,
+ &this->mainVertexProgramId
+ );
+ if (ret < 0) {
+ return;
+ }
+
+ ret = SCE_ERR(
+ sceGxmShaderPatcherRegisterProgram,
+ gxm->shaderPatcher,
+ mainColorFragmentProgramGxp,
+ &this->mainColorFragmentProgramId
+ );
+ if (ret < 0) {
+ return;
+ }
+
+ ret = SCE_ERR(
+ sceGxmShaderPatcherRegisterProgram,
+ gxm->shaderPatcher,
+ mainTextureFragmentProgramGxp,
+ &this->mainTextureFragmentProgramId
+ );
+ if (ret < 0) {
+ return;
+ }
+
+ // main shader
+ {
+ GET_SHADER_PARAM(positionAttribute, mainVertexProgramGxp, "aPosition", );
+ GET_SHADER_PARAM(normalAttribute, mainVertexProgramGxp, "aNormal", );
+ GET_SHADER_PARAM(texCoordAttribute, mainVertexProgramGxp, "aTexCoord", );
+
+ SceGxmVertexAttribute vertexAttributes[3];
+ SceGxmVertexStream vertexStreams[1];
+
+ // position
+ vertexAttributes[0].streamIndex = 0;
+ vertexAttributes[0].offset = 0;
+ vertexAttributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
+ vertexAttributes[0].componentCount = 3;
+ vertexAttributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(positionAttribute);
+
+ // normal
+ vertexAttributes[1].streamIndex = 0;
+ vertexAttributes[1].offset = 12;
+ vertexAttributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
+ vertexAttributes[1].componentCount = 3;
+ vertexAttributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(normalAttribute);
+
+ vertexAttributes[2].streamIndex = 0;
+ vertexAttributes[2].offset = 24;
+ vertexAttributes[2].format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
+ vertexAttributes[2].componentCount = 2;
+ vertexAttributes[2].regIndex = sceGxmProgramParameterGetResourceIndex(texCoordAttribute);
+
+ vertexStreams[0].stride = sizeof(GXMVertex);
+ vertexStreams[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT;
+
+ ret = SCE_ERR(
+ sceGxmShaderPatcherCreateVertexProgram,
+ gxm->shaderPatcher,
+ this->mainVertexProgramId,
+ vertexAttributes,
+ 3,
+ vertexStreams,
+ 1,
+ &this->mainVertexProgram
+ );
+ if (ret < 0) {
+ return;
+ }
+ }
+
+ // main color opaque
+ ret = SCE_ERR(
+ sceGxmShaderPatcherCreateFragmentProgram,
+ gxm->shaderPatcher,
+ this->mainColorFragmentProgramId,
+ SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
+ msaaMode,
+ &blendInfoOpaque,
+ mainVertexProgramGxp,
+ &this->opaqueColorFragmentProgram
+ );
+ if (ret < 0) {
+ return;
+ }
+
+ // main color blended
+ ret = SCE_ERR(
+ sceGxmShaderPatcherCreateFragmentProgram,
+ gxm->shaderPatcher,
+ this->mainColorFragmentProgramId,
+ SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
+ msaaMode,
+ &blendInfoTransparent,
+ mainVertexProgramGxp,
+ &this->blendedColorFragmentProgram
+ );
+ if (ret < 0) {
+ return;
+ }
+
+ // main texture opaque
+ ret = SCE_ERR(
+ sceGxmShaderPatcherCreateFragmentProgram,
+ gxm->shaderPatcher,
+ this->mainTextureFragmentProgramId,
+ SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
+ msaaMode,
+ &blendInfoOpaque,
+ mainVertexProgramGxp,
+ &this->opaqueTextureFragmentProgram
+ );
+ if (ret < 0) {
+ return;
+ }
+
+ // main texture transparent
+ ret = SCE_ERR(
+ sceGxmShaderPatcherCreateFragmentProgram,
+ gxm->shaderPatcher,
+ this->mainTextureFragmentProgramId,
+ SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
+ msaaMode,
+ &blendInfoTransparent,
+ mainVertexProgramGxp,
+ &this->blendedTextureFragmentProgram
+ );
+ if (ret < 0) {
+ return;
+ }
+
+ // vertex uniforms
+ this->uModelViewMatrix = sceGxmProgramFindParameterByName(mainVertexProgramGxp, "uModelViewMatrix");
+ this->uNormalMatrix = sceGxmProgramFindParameterByName(mainVertexProgramGxp, "uNormalMatrix");
+ this->uProjectionMatrix = sceGxmProgramFindParameterByName(mainVertexProgramGxp, "uProjectionMatrix");
+
+ // fragment uniforms
+ this->uLights = sceGxmProgramFindParameterByName(mainColorFragmentProgramGxp, "uLights"); // SceneLight[2]
+ this->uAmbientLight = sceGxmProgramFindParameterByName(mainColorFragmentProgramGxp, "uAmbientLight"); // vec3
+ this->uShininess = sceGxmProgramFindParameterByName(mainColorFragmentProgramGxp, "uShininess"); // float
+ this->uColor = sceGxmProgramFindParameterByName(mainColorFragmentProgramGxp, "uColor"); // vec4
+
+ for (int i = 0; i < GXM_FRAGMENT_BUFFER_COUNT; i++) {
+ this->lights[i] = static_cast(gxm->alloc(sizeof(GXMSceneLightUniform), 4));
+ }
+ for (int i = 0; i < GXM_VERTEX_BUFFER_COUNT; i++) {
+ this->quadVertices[i] = static_cast(gxm->alloc(sizeof(GXMVertex2D) * 4 * 50, 4));
+ }
+ this->quadIndices = static_cast(gxm->alloc(sizeof(uint16_t) * 4, 4));
+ this->quadIndices[0] = 0;
+ this->quadIndices[1] = 1;
+ this->quadIndices[2] = 2;
+ this->quadIndices[3] = 3;
+
+ volatile uint32_t* notificationMem = sceGxmGetNotificationRegion();
+ for (uint32_t i = 0; i < GXM_FRAGMENT_BUFFER_COUNT; i++) {
+ this->fragmentNotifications[i].address = notificationMem++;
+ this->fragmentNotifications[i].value = 0;
+ }
+ this->currentFragmentBufferIndex = 0;
+
+ for (uint32_t i = 0; i < GXM_VERTEX_BUFFER_COUNT; i++) {
+ this->vertexNotifications[i].address = notificationMem++;
+ this->vertexNotifications[i].value = 0;
+ }
+ this->currentVertexBufferIndex = 0;
+ m_initialized = true;
+}
+
+GXMRenderer::~GXMRenderer()
+{
+ for (int i = 0; i < GXM_FRAGMENT_BUFFER_COUNT; i++) {
+ if (this->lights[i]) {
+ gxm->free(this->lights[i]);
+ }
+ }
+ for (int i = 0; i < GXM_VERTEX_BUFFER_COUNT; i++) {
+ if (this->quadVertices[i]) {
+ gxm->free(this->quadVertices[i]);
+ }
+ }
+ if (this->quadIndices) {
+ gxm->free(this->quadIndices);
+ }
+}
+
+void GXMRenderer::PushLights(const SceneLight* lightsArray, size_t count)
+{
+ if (count > 3) {
+ SDL_Log("Unsupported number of lights (%d)", static_cast(count));
+ count = 3;
+ }
+
+ m_lights.assign(lightsArray, lightsArray + count);
+}
+
+void GXMRenderer::SetFrustumPlanes(const Plane* frustumPlanes)
+{
+}
+
+void GXMRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back)
+{
+ memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D));
+}
+
+struct TextureDestroyContextGXM {
+ GXMRenderer* renderer;
+ Uint32 textureId;
+};
+
+void GXMRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture)
+{
+ auto* ctx = new TextureDestroyContextGXM{this, id};
+ texture->AddDestroyCallback(
+ [](IDirect3DRMObject* obj, void* arg) {
+ auto* ctx = static_cast(arg);
+ auto& cache = ctx->renderer->m_textures[ctx->textureId];
+ ctx->renderer->m_textures_delete[gxm->backBufferIndex].push_back(cache.gxmTexture);
+ cache.texture = nullptr;
+ memset(&cache.gxmTexture, 0, sizeof(SceGxmTexture));
+ delete ctx;
+ },
+ ctx
+ );
+}
+
+void GXMRenderer::DeferredDelete(int index)
+{
+ for (auto& del : this->m_textures_delete[index]) {
+ void* textureData = sceGxmTextureGetData(&del);
+ gxm->free(textureData);
+ }
+ this->m_textures_delete[index].clear();
+
+ for (auto& del : this->m_buffers_delete[index]) {
+ gxm->free(del);
+ }
+ this->m_buffers_delete[index].clear();
+}
+
+static int calculateMipLevels(int width, int height)
+{
+ if (width <= 0 || height <= 0) {
+ return 1;
+ }
+ int maxDim = (width > height) ? width : height;
+ return floor(log2(maxDim)) + 1;
+}
+
+static int nextPowerOf2(int n)
+{
+ if (n <= 0) {
+ return 1;
+ }
+ n--;
+ n |= n >> 1;
+ n |= n >> 2;
+ n |= n >> 4;
+ n |= n >> 8;
+ n |= n >> 16;
+ n++;
+ return n;
+}
+
+static void convertTextureMetadata(
+ SDL_Surface* surface,
+ bool isUi,
+ bool* supportedFormat,
+ SceGxmTextureFormat* gxmTextureFormat,
+ size_t* textureSize, // size in bytes
+ size_t* textureAlignment, // alignment in bytes
+ size_t* textureStride, // stride in bytes
+ size_t* paletteOffset, // offset from textureData in bytes
+ size_t* mipLevels
+)
+{
+ int bytesPerPixel;
+ size_t paletteSize = 0;
+ *mipLevels = 1;
+
+ switch (surface->format) {
+ case SDL_PIXELFORMAT_INDEX8: {
+ *supportedFormat = true;
+ *gxmTextureFormat = SCE_GXM_TEXTURE_FORMAT_P8_ABGR;
+ *textureAlignment = SCE_GXM_PALETTE_ALIGNMENT;
+ bytesPerPixel = 1;
+ if (!isUi) {
+ *mipLevels = calculateMipLevels(surface->w, surface->h);
+ }
+ paletteSize = 256 * 4; // palette
+ break;
+ }
+ case SDL_PIXELFORMAT_ABGR8888: {
+ *supportedFormat = true;
+ *gxmTextureFormat = SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR;
+ *textureAlignment = SCE_GXM_TEXTURE_ALIGNMENT;
+ bytesPerPixel = 4;
+ if (!isUi) {
+ *mipLevels = calculateMipLevels(surface->w, surface->h);
+ }
+ break;
+ }
+ default: {
+ *supportedFormat = false;
+ *gxmTextureFormat = SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR;
+ *textureAlignment = SCE_GXM_TEXTURE_ALIGNMENT;
+ bytesPerPixel = 4;
+ break;
+ }
+ }
+ *textureStride = ALIGN(surface->w, 8) * bytesPerPixel;
+
+ *mipLevels = 1; // look weird
+
+ size_t totalSize = 0;
+ int currentW = surface->w;
+ int currentH = surface->h;
+
+ // top mip
+ totalSize += (ALIGN(currentW, 8) * currentH) * bytesPerPixel;
+
+ for (size_t i = 1; i < *mipLevels; ++i) {
+ currentW = currentW > 1 ? currentW / 2 : 1;
+ currentH = currentH > 1 ? currentH / 2 : 1;
+ int po2W = nextPowerOf2(currentW * 2);
+ int po2H = nextPowerOf2(currentH * 2);
+ if (po2W < 8) {
+ po2W = 8;
+ }
+ totalSize += po2W * po2H * bytesPerPixel;
+ }
+
+ if (paletteSize != 0) {
+ int alignBytes = ALIGNMENT(totalSize, SCE_GXM_PALETTE_ALIGNMENT);
+ totalSize += alignBytes;
+ *paletteOffset = totalSize;
+ totalSize += paletteSize;
+ }
+
+ *textureSize = totalSize;
+}
+
+void copySurfaceToGxmARGB888(SDL_Surface* src, uint8_t* textureData, size_t dstStride, size_t mipLevels)
+{
+ uint8_t* currentLevelData = textureData;
+ uint32_t currentLevelWidth = src->w;
+ uint32_t currentLevelHeight = src->h;
+ size_t bytesPerPixel = 4;
+
+ // copy top level mip (cant use transfer because this isnt gpu mapped)
+ size_t topLevelStride = ALIGN(currentLevelWidth, 8) * bytesPerPixel;
+ for (int y = 0; y < currentLevelHeight; y++) {
+ uint8_t* srcRow = (uint8_t*) src->pixels + (y * src->pitch);
+ uint8_t* dstRow = textureData + (y * topLevelStride);
+ memcpy(dstRow, srcRow, currentLevelWidth * bytesPerPixel);
+ }
+
+ for (size_t i = 1; i < mipLevels; ++i) {
+ uint32_t currentLevelSrcStride = SDL_max(currentLevelWidth, 8) * bytesPerPixel;
+ uint32_t currentLevelDestStride = SDL_max((currentLevelWidth / 2), 8) * bytesPerPixel;
+ uint8_t* nextLevelData = currentLevelData + (currentLevelSrcStride * currentLevelHeight);
+
+ sceGxmTransferDownscale(
+ SCE_GXM_TRANSFER_FORMAT_U8U8U8U8_ABGR, // src format
+ currentLevelData, // src address
+ 0,
+ 0,
+ currentLevelWidth,
+ currentLevelHeight, // x,y,w,h
+ currentLevelSrcStride, // stride
+ SCE_GXM_TRANSFER_FORMAT_U8U8U8U8_ABGR, // dst format
+ nextLevelData, // dst address
+ 0,
+ 0, // x,y
+ currentLevelDestStride, // stride
+ NULL, // sync
+ SCE_GXM_TRANSFER_FRAGMENT_SYNC, // flag
+ NULL // notification
+ );
+
+ currentLevelData = nextLevelData;
+ currentLevelWidth = currentLevelWidth / 2;
+ currentLevelHeight = currentLevelHeight / 2;
+ }
+}
+
+void copySurfaceToGxmIndexed8(
+ DirectDrawSurfaceImpl* surface,
+ SDL_Surface* src,
+ uint8_t* textureData,
+ size_t dstStride,
+ uint8_t* paletteData,
+ size_t mipLevels
+)
+{
+ LPDIRECTDRAWPALETTE _palette;
+ surface->GetPalette(&_palette);
+ auto palette = static_cast(_palette);
+
+ // copy palette
+ memcpy(paletteData, palette->m_palette->colors, 256 * 4);
+
+ uint8_t* currentLevelData = textureData;
+ uint32_t currentLevelWidth = src->w;
+ uint32_t currentLevelHeight = src->h;
+ size_t bytesPerPixel = 1;
+
+ // copy top level mip (cant use transfer because this isnt gpu mapped)
+ size_t topLevelStride = ALIGN(currentLevelWidth, 8) * bytesPerPixel;
+ for (int y = 0; y < currentLevelHeight; y++) {
+ uint8_t* srcRow = (uint8_t*) src->pixels + (y * src->pitch);
+ uint8_t* dstRow = textureData + (y * topLevelStride);
+ memcpy(dstRow, srcRow, currentLevelWidth * bytesPerPixel);
+ }
+
+ for (size_t i = 1; i < mipLevels; ++i) {
+ uint32_t currentLevelSrcStride = SDL_max(currentLevelWidth, 8) * bytesPerPixel;
+ uint32_t currentLevelDestStride = SDL_max((currentLevelWidth / 2), 8) * bytesPerPixel;
+ uint8_t* nextLevelData = currentLevelData + (currentLevelSrcStride * currentLevelHeight);
+
+ sceGxmTransferDownscale(
+ SCE_GXM_TRANSFER_FORMAT_U8_R, // src format
+ currentLevelData, // src address
+ 0,
+ 0,
+ currentLevelWidth,
+ currentLevelHeight, // x,y,w,h
+ currentLevelSrcStride, // stride
+ SCE_GXM_TRANSFER_FORMAT_U8_R, // dst format
+ nextLevelData, // dst address
+ 0,
+ 0, // x,y
+ currentLevelDestStride, // stride
+ NULL, // sync
+ SCE_GXM_TRANSFER_FRAGMENT_SYNC, // flag
+ NULL // notification
+ );
+
+ currentLevelData = nextLevelData;
+ currentLevelWidth = currentLevelWidth / 2;
+ currentLevelHeight = currentLevelHeight / 2;
+ }
+
+ palette->Release();
+}
+
+void copySurfaceToGxm(
+ DirectDrawSurfaceImpl* surface,
+ uint8_t* textureData,
+ size_t dstStride,
+ size_t paletteOffset,
+ size_t mipLevels
+)
+{
+ SDL_Surface* src = surface->m_surface;
+
+ switch (src->format) {
+ case SDL_PIXELFORMAT_ABGR8888: {
+ copySurfaceToGxmARGB888(src, textureData, dstStride, mipLevels);
+ break;
+ }
+ case SDL_PIXELFORMAT_INDEX8: {
+ copySurfaceToGxmIndexed8(surface, src, textureData, dstStride, textureData + paletteOffset, mipLevels);
+ break;
+ }
+ default: {
+ DEBUG_ONLY_PRINTF("unsupported format %d\n", SDL_GetPixelFormatName(src->format));
+ SDL_Surface* dst = SDL_CreateSurfaceFrom(src->w, src->h, SDL_PIXELFORMAT_ABGR8888, textureData, src->w * 4);
+ SDL_BlitSurface(src, nullptr, dst, nullptr);
+ SDL_DestroySurface(dst);
+ break;
+ }
+ }
+}
+
+Uint32 GXMRenderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi, float scaleX, float scaleY)
+{
+ auto texture = static_cast(iTexture);
+ auto surface = static_cast(texture->m_surface);
+
+ bool supportedFormat;
+ SceGxmTextureFormat gxmTextureFormat;
+ size_t textureSize;
+ size_t textureAlignment;
+ size_t textureStride;
+ size_t paletteOffset;
+ size_t mipLevels;
+
+ int textureWidth = surface->m_surface->w;
+ int textureHeight = surface->m_surface->h;
+
+ convertTextureMetadata(
+ surface->m_surface,
+ isUi,
+ &supportedFormat,
+ &gxmTextureFormat,
+ &textureSize,
+ &textureAlignment,
+ &textureStride,
+ &paletteOffset,
+ &mipLevels
+ );
+
+ if (!supportedFormat) {
+ return NO_TEXTURE_ID;
+ }
+
+ for (Uint32 i = 0; i < m_textures.size(); ++i) {
+ auto& tex = m_textures[i];
+ if (tex.texture == texture) {
+ if (tex.version != texture->m_version) {
+ sceGxmNotificationWait(tex.notification);
+ tex.notification = &this->fragmentNotifications[this->currentFragmentBufferIndex];
+ uint8_t* textureData = (uint8_t*) sceGxmTextureGetData(&tex.gxmTexture);
+ copySurfaceToGxm(surface, textureData, textureStride, paletteOffset, mipLevels);
+ tex.version = texture->m_version;
+ }
+ return i;
+ }
+ }
+
+ DEBUG_ONLY_PRINTF(
+ "Create Texture %s w=%d h=%d s=%d size=%d align=%d mips=%d\n",
+ SDL_GetPixelFormatName(surface->m_surface->format),
+ textureWidth,
+ textureHeight,
+ textureStride,
+ textureSize,
+ textureAlignment,
+ mipLevels
+ );
+
+ // allocate gpu memory
+ uint8_t* textureData = (uint8_t*) gxm->alloc(textureSize, textureAlignment);
+ copySurfaceToGxm(surface, textureData, textureStride, paletteOffset, mipLevels);
+
+ SceGxmTexture gxmTexture;
+ SCE_ERR(
+ sceGxmTextureInitLinear,
+ &gxmTexture,
+ textureData,
+ gxmTextureFormat,
+ textureWidth,
+ textureHeight,
+ mipLevels
+ );
+ if (isUi) {
+ sceGxmTextureSetMinFilter(&gxmTexture, SCE_GXM_TEXTURE_FILTER_POINT);
+ sceGxmTextureSetMagFilter(&gxmTexture, SCE_GXM_TEXTURE_FILTER_POINT);
+ sceGxmTextureSetUAddrMode(&gxmTexture, SCE_GXM_TEXTURE_ADDR_CLAMP);
+ sceGxmTextureSetVAddrMode(&gxmTexture, SCE_GXM_TEXTURE_ADDR_CLAMP);
+ }
+ else {
+ sceGxmTextureSetMinFilter(&gxmTexture, SCE_GXM_TEXTURE_FILTER_LINEAR);
+ sceGxmTextureSetMagFilter(&gxmTexture, SCE_GXM_TEXTURE_FILTER_LINEAR);
+ sceGxmTextureSetUAddrMode(&gxmTexture, SCE_GXM_TEXTURE_ADDR_REPEAT);
+ sceGxmTextureSetVAddrMode(&gxmTexture, SCE_GXM_TEXTURE_ADDR_REPEAT);
+ }
+ if (gxmTextureFormat == SCE_GXM_TEXTURE_FORMAT_P8_ABGR) {
+ sceGxmTextureSetPalette(&gxmTexture, textureData + paletteOffset);
+ }
+
+ for (Uint32 i = 0; i < m_textures.size(); ++i) {
+ auto& tex = m_textures[i];
+ if (!tex.texture) {
+ memset(&tex, 0, sizeof(tex));
+ tex.texture = texture;
+ tex.version = texture->m_version;
+ tex.gxmTexture = gxmTexture;
+ tex.notification = &this->fragmentNotifications[this->currentFragmentBufferIndex];
+ AddTextureDestroyCallback(i, texture);
+ return i;
+ }
+ }
+
+ GXMTextureCacheEntry tex;
+ memset(&tex, 0, sizeof(tex));
+ tex.texture = texture;
+ tex.version = texture->m_version;
+ tex.gxmTexture = gxmTexture;
+ tex.notification = &this->fragmentNotifications[this->currentFragmentBufferIndex];
+ m_textures.push_back(tex);
+ Uint32 textureId = (Uint32) (m_textures.size() - 1);
+ AddTextureDestroyCallback(textureId, texture);
+ return textureId;
+}
+
+const SceGxmTexture* GXMRenderer::UseTexture(GXMTextureCacheEntry& texture)
+{
+ texture.notification = &this->fragmentNotifications[this->currentFragmentBufferIndex];
+ sceGxmSetFragmentTexture(gxm->context, 0, &texture.gxmTexture);
+ return &texture.gxmTexture;
+}
+
+GXMMeshCacheEntry GXMRenderer::GXMUploadMesh(const MeshGroup& meshGroup)
+{
+ GXMMeshCacheEntry cache{&meshGroup, meshGroup.version};
+
+ cache.flat = meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT;
+
+ std::vector vertices;
+ std::vector indices;
+ if (cache.flat) {
+ FlattenSurfaces(
+ meshGroup.vertices.data(),
+ meshGroup.vertices.size(),
+ meshGroup.indices.data(),
+ meshGroup.indices.size(),
+ meshGroup.texture != nullptr,
+ vertices,
+ indices
+ );
+ }
+ else {
+ vertices = meshGroup.vertices;
+ indices.resize(meshGroup.indices.size());
+ std::transform(meshGroup.indices.begin(), meshGroup.indices.end(), indices.begin(), [](DWORD index) {
+ return static_cast(index);
+ });
+ }
+
+ size_t vertexBufferSize = sizeof(GXMVertex) * vertices.size();
+ size_t indexBufferSize = sizeof(uint16_t) * indices.size();
+ void* meshData = gxm->alloc(vertexBufferSize + indexBufferSize, 4);
+
+ GXMVertex* vertexBuffer = (GXMVertex*) meshData;
+ uint16_t* indexBuffer = (uint16_t*) ((uint8_t*) meshData + vertexBufferSize);
+
+ for (int i = 0; i < vertices.size(); i++) {
+ D3DRMVERTEX vertex = vertices.data()[i];
+ vertexBuffer[i] = GXMVertex{
+ .position =
+ {
+ vertex.position.x,
+ vertex.position.y,
+ vertex.position.z,
+ },
+ .normal =
+ {
+ vertex.normal.x,
+ vertex.normal.y,
+ vertex.normal.z,
+ },
+ .texCoord =
+ {
+ vertex.tu,
+ vertex.tv,
+ }
+ };
+ }
+ memcpy(indexBuffer, indices.data(), indices.size() * sizeof(uint16_t));
+
+ cache.meshData = meshData;
+ cache.vertexBuffer = vertexBuffer;
+ cache.indexBuffer = indexBuffer;
+ cache.indexCount = indices.size();
+ return cache;
+}
+
+struct GXMMeshDestroyContext {
+ GXMRenderer* renderer;
+ Uint32 id;
+};
+
+void GXMRenderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh)
+{
+ auto* ctx = new GXMMeshDestroyContext{this, id};
+ mesh->AddDestroyCallback(
+ [](IDirect3DRMObject*, void* arg) {
+ auto* ctx = static_cast(arg);
+ auto& cache = ctx->renderer->m_meshes[ctx->id];
+ cache.meshGroup = nullptr;
+ ctx->renderer->m_buffers_delete[gxm->backBufferIndex].push_back(cache.meshData);
+ cache.meshData = nullptr;
+ cache.indexBuffer = nullptr;
+ cache.vertexBuffer = nullptr;
+ cache.indexCount = 0;
+ delete ctx;
+ },
+ ctx
+ );
+}
+
+Uint32 GXMRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup)
+{
+ for (Uint32 i = 0; i < m_meshes.size(); ++i) {
+ auto& cache = m_meshes[i];
+ if (cache.meshGroup == meshGroup) {
+ if (cache.version != meshGroup->version) {
+ cache = std::move(this->GXMUploadMesh(*meshGroup));
+ }
+ return i;
+ }
+ }
+
+ auto newCache = this->GXMUploadMesh(*meshGroup);
+
+ for (Uint32 i = 0; i < m_meshes.size(); ++i) {
+ auto& cache = m_meshes[i];
+ if (!cache.meshGroup) {
+ cache = std::move(newCache);
+ AddMeshDestroyCallback(i, mesh);
+ return i;
+ }
+ }
+
+ m_meshes.push_back(std::move(newCache));
+ AddMeshDestroyCallback((Uint32) (m_meshes.size() - 1), mesh);
+ return (Uint32) (m_meshes.size() - 1);
+}
+
+bool razor_live_started = false;
+bool razor_display_enabled = false;
+
+void GXMRenderer::StartScene()
+{
+ if (gxm->sceneStarted) {
+ return;
+ }
+
+ this->DeferredDelete(gxm->frontBufferIndex);
+
+#ifdef GXM_WITH_RAZOR
+ bool dpad_up_clicked = !this->last_dpad_up && g_dpadUp;
+ bool dpad_down_clicked = !this->last_dpad_down && g_dpadDown;
+ bool dpad_left_clicked = !this->last_dpad_left && g_dpadLeft;
+ bool dpad_right_clicked = !this->last_dpad_right && g_dpadRight;
+ this->last_dpad_up = g_dpadUp;
+ this->last_dpad_down = g_dpadDown;
+ this->last_dpad_left = g_dpadLeft;
+ this->last_dpad_right = g_dpadRight;
+
+ if (with_razor_hud) {
+ if (dpad_up_clicked) {
+ razor_display_enabled = !razor_display_enabled;
+ sceRazorHudSetDisplayEnabled(razor_display_enabled);
+ }
+ if (dpad_left_clicked) {
+ if (razor_live_started) {
+ sceRazorGpuLiveStop();
+ }
+ else {
+ sceRazorGpuLiveStart();
+ }
+ razor_live_started = !razor_live_started;
+ }
+ if (dpad_right_clicked) {
+ sceRazorGpuTraceTrigger();
+ }
+ }
+ if (with_razor_capture) {
+ if (dpad_down_clicked) {
+ sceRazorGpuCaptureSetTriggerNextFrame("ux0:/data/capture.sgx");
+ }
+ }
+#endif
+
+ sceGxmBeginScene(
+ gxm->context,
+ SCE_GXM_SCENE_FRAGMENT_TRANSFER_SYNC,
+ gxm->renderTarget,
+ nullptr,
+ nullptr,
+ gxm->displayBuffersSync[gxm->backBufferIndex],
+ &gxm->displayBuffersSurface[gxm->backBufferIndex],
+ &gxm->depthSurface
+ );
+ sceGxmSetCullMode(gxm->context, SCE_GXM_CULL_CCW);
+ gxm->sceneStarted = true;
+ this->quadsUsed = 0;
+ this->cleared = false;
+
+ sceGxmNotificationWait(&this->vertexNotifications[this->currentVertexBufferIndex]);
+ sceGxmNotificationWait(&this->fragmentNotifications[this->currentFragmentBufferIndex]);
+}
+
+HRESULT GXMRenderer::BeginFrame()
+{
+ this->transparencyEnabled = false;
+ this->StartScene();
+
+ auto lightData = this->LightsBuffer();
+ int i = 0;
+ for (const auto& light : m_lights) {
+ if (!light.directional && !light.positional) {
+ lightData->ambientLight[0] = light.color.r;
+ lightData->ambientLight[1] = light.color.g;
+ lightData->ambientLight[2] = light.color.b;
+ continue;
+ }
+ if (i == 2) {
+ sceClibPrintf("light overflow\n");
+ continue;
+ }
+
+ lightData->lights[i].color[0] = light.color.r;
+ lightData->lights[i].color[1] = light.color.g;
+ lightData->lights[i].color[2] = light.color.b;
+ lightData->lights[i].color[3] = light.color.a;
+
+ bool isDirectional = light.directional == 1.0;
+ if (isDirectional) {
+ lightData->lights[i].vec[0] = light.direction.x;
+ lightData->lights[i].vec[1] = light.direction.y;
+ lightData->lights[i].vec[2] = light.direction.z;
+ }
+ else {
+ lightData->lights[i].vec[0] = light.position.x;
+ lightData->lights[i].vec[1] = light.position.y;
+ lightData->lights[i].vec[2] = light.position.z;
+ }
+ lightData->lights[i].isDirectional = isDirectional;
+ i++;
+ }
+ sceGxmSetFragmentUniformBuffer(gxm->context, 0, lightData);
+
+ return DD_OK;
+}
+
+void GXMRenderer::EnableTransparency()
+{
+ this->transparencyEnabled = true;
+}
+
+void GXMRenderer::SubmitDraw(
+ DWORD meshId,
+ const D3DRMMATRIX4D& modelViewMatrix,
+ const D3DRMMATRIX4D& worldMatrix,
+ const D3DRMMATRIX4D& viewMatrix,
+ const Matrix3x3& normalMatrix,
+ const Appearance& appearance
+)
+{
+ auto& mesh = m_meshes[meshId];
+
+#ifdef DEBUG
+ char marker[256];
+ snprintf(marker, sizeof(marker), "SubmitDraw: %d", meshId);
+ sceGxmPushUserMarker(gxm->context, marker);
+#endif
+
+ bool textured = appearance.textureId != NO_TEXTURE_ID;
+ const SceGxmFragmentProgram* fragmentProgram;
+ if (this->transparencyEnabled) {
+ fragmentProgram = textured ? this->blendedTextureFragmentProgram : this->blendedColorFragmentProgram;
+ }
+ else {
+ fragmentProgram = textured ? this->opaqueTextureFragmentProgram : this->opaqueColorFragmentProgram;
+ }
+ sceGxmSetVertexProgram(gxm->context, this->mainVertexProgram);
+ sceGxmSetFragmentProgram(gxm->context, fragmentProgram);
+
+ void* vertUniforms;
+ void* fragUniforms;
+ sceGxmReserveVertexDefaultUniformBuffer(gxm->context, &vertUniforms);
+ sceGxmReserveFragmentDefaultUniformBuffer(gxm->context, &fragUniforms);
+
+ // vertex uniforms
+ sceGxmSetUniformDataF(vertUniforms, this->uModelViewMatrix, 0, 4 * 4, &modelViewMatrix[0][0]);
+ sceGxmSetUniformDataF(vertUniforms, this->uNormalMatrix, 0, 3 * 3, &normalMatrix[0][0]);
+ sceGxmSetUniformDataF(vertUniforms, this->uProjectionMatrix, 0, 4 * 4, &this->m_projection[0][0]);
+
+ // fragment uniforms
+ float color[4] = {
+ appearance.color.r / 255.0f,
+ appearance.color.g / 255.0f,
+ appearance.color.b / 255.0f,
+ appearance.color.a / 255.0f
+ };
+ sceGxmSetUniformDataF(fragUniforms, this->uColor, 0, 4, color);
+ sceGxmSetUniformDataF(fragUniforms, this->uShininess, 0, 1, &appearance.shininess);
+
+ if (textured) {
+ auto& texture = m_textures[appearance.textureId];
+ this->UseTexture(texture);
+ }
+ sceGxmSetVertexStream(gxm->context, 0, mesh.vertexBuffer);
+ sceGxmSetFrontDepthFunc(gxm->context, SCE_GXM_DEPTH_FUNC_LESS_EQUAL);
+ sceGxmDraw(gxm->context, SCE_GXM_PRIMITIVE_TRIANGLES, SCE_GXM_INDEX_FORMAT_U16, mesh.indexBuffer, mesh.indexCount);
+
+#ifdef DEBUG
+ sceGxmPopUserMarker(gxm->context);
+#endif
+}
+
+HRESULT GXMRenderer::FinalizeFrame()
+{
+ return DD_OK;
+}
+
+void GXMRenderer::Resize(int width, int height, const ViewportTransform& viewportTransform)
+{
+ m_width = width;
+ m_height = height;
+ m_viewportTransform = viewportTransform;
+}
+
+void GXMRenderer::Clear(float r, float g, float b)
+{
+ this->StartScene();
+ gxm->clear(r, g, b, false);
+ this->cleared = true;
+}
+
+void GXMRenderer::Flip()
+{
+ if (!gxm->sceneStarted) {
+ return;
+ }
+
+ ++this->vertexNotifications[this->currentVertexBufferIndex].value;
+ ++this->fragmentNotifications[this->currentFragmentBufferIndex].value;
+ sceGxmEndScene(
+ gxm->context,
+ &this->vertexNotifications[this->currentVertexBufferIndex],
+ &this->fragmentNotifications[this->currentFragmentBufferIndex]
+ );
+ gxm->sceneStarted = false;
+
+ this->currentVertexBufferIndex = (this->currentVertexBufferIndex + 1) % GXM_VERTEX_BUFFER_COUNT;
+ this->currentFragmentBufferIndex = (this->currentFragmentBufferIndex + 1) % GXM_FRAGMENT_BUFFER_COUNT;
+ gxm->swap_display();
+}
+
+void GXMRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect, FColor color)
+{
+ this->StartScene();
+ if (!this->cleared) {
+ gxm->clear(0, 0, 0, false);
+ this->cleared = true;
+ }
+
+#ifdef DEBUG
+ char marker[256];
+ snprintf(marker, sizeof(marker), "Draw2DImage: %d", textureId);
+ sceGxmPushUserMarker(gxm->context, marker);
+#endif
+
+ sceGxmSetVertexProgram(gxm->context, gxm->planeVertexProgram);
+ if (textureId != NO_TEXTURE_ID) {
+ sceGxmSetFragmentProgram(gxm->context, gxm->imageFragmentProgram);
+ }
+ else {
+ sceGxmSetFragmentProgram(gxm->context, gxm->colorFragmentProgram);
+ }
+
+ void* vertUniforms;
+ void* fragUniforms;
+ sceGxmReserveVertexDefaultUniformBuffer(gxm->context, &vertUniforms);
+ sceGxmReserveFragmentDefaultUniformBuffer(gxm->context, &fragUniforms);
+
+ float left = -this->m_viewportTransform.offsetX / this->m_viewportTransform.scale;
+ float right = (this->m_width - this->m_viewportTransform.offsetX) / this->m_viewportTransform.scale;
+ float top = -this->m_viewportTransform.offsetY / this->m_viewportTransform.scale;
+ float bottom = (this->m_height - this->m_viewportTransform.offsetY) / this->m_viewportTransform.scale;
+
+#define virtualToNDCX(x) (((x - left) / (right - left)) * 2 - 1);
+#define virtualToNDCY(y) -(((y - top) / (bottom - top)) * 2 - 1);
+
+ float x1_virtual = static_cast(dstRect.x);
+ float y1_virtual = static_cast(dstRect.y);
+ float x2_virtual = x1_virtual + dstRect.w;
+ float y2_virtual = y1_virtual + dstRect.h;
+
+ float x1 = virtualToNDCX(x1_virtual);
+ float y1 = virtualToNDCY(y1_virtual);
+ float x2 = virtualToNDCX(x2_virtual);
+ float y2 = virtualToNDCY(y2_virtual);
+
+ float u1 = 0.0;
+ float v1 = 0.0;
+ float u2 = 0.0;
+ float v2 = 0.0;
+
+ if (textureId != NO_TEXTURE_ID) {
+ GXMTextureCacheEntry& texture = m_textures[textureId];
+ const SceGxmTexture* gxmTexture = this->UseTexture(texture);
+ float texW = sceGxmTextureGetWidth(gxmTexture);
+ float texH = sceGxmTextureGetHeight(gxmTexture);
+
+ u1 = static_cast(srcRect.x) / texW;
+ v1 = static_cast(srcRect.y) / texH;
+ u2 = static_cast(srcRect.x + srcRect.w) / texW;
+ v2 = static_cast(srcRect.y + srcRect.h) / texH;
+ }
+ else {
+ SET_UNIFORM(fragUniforms, gxm->color_uColor, color);
+ }
+
+ GXMVertex2D* quadVertices = this->QuadVerticesBuffer();
+ quadVertices[0] = GXMVertex2D{.position = {x1, y1}, .texCoord = {u1, v1}};
+ quadVertices[1] = GXMVertex2D{.position = {x2, y1}, .texCoord = {u2, v1}};
+ quadVertices[2] = GXMVertex2D{.position = {x1, y2}, .texCoord = {u1, v2}};
+ quadVertices[3] = GXMVertex2D{.position = {x2, y2}, .texCoord = {u2, v2}};
+
+ sceGxmSetVertexStream(gxm->context, 0, quadVertices);
+
+ sceGxmSetFrontDepthWriteEnable(gxm->context, SCE_GXM_DEPTH_WRITE_DISABLED);
+ sceGxmSetFrontDepthFunc(gxm->context, SCE_GXM_DEPTH_FUNC_ALWAYS);
+ sceGxmDraw(gxm->context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->quadIndices, 4);
+ sceGxmSetFrontDepthWriteEnable(gxm->context, SCE_GXM_DEPTH_WRITE_ENABLED);
+
+#ifdef DEBUG
+ sceGxmPopUserMarker(gxm->context);
+#endif
+}
+
+void GXMRenderer::SetDither(bool dither)
+{
+}
+
+void GXMRenderer::Download(SDL_Surface* target)
+{
+ SDL_Rect srcRect = {
+ static_cast(m_viewportTransform.offsetX),
+ static_cast(m_viewportTransform.offsetY),
+ static_cast(target->w * m_viewportTransform.scale),
+ static_cast(target->h * m_viewportTransform.scale),
+ };
+ SDL_Surface* src = SDL_CreateSurfaceFrom(
+ VITA_GXM_SCREEN_WIDTH,
+ VITA_GXM_SCREEN_HEIGHT,
+ SDL_PIXELFORMAT_ABGR8888,
+ gxm->displayBuffers[gxm->frontBufferIndex],
+ VITA_GXM_SCREEN_STRIDE * 4
+ );
+ SDL_BlitSurfaceScaled(src, &srcRect, target, nullptr, SDL_SCALEMODE_NEAREST);
+ SDL_DestroySurface(src);
+}
diff --git a/miniwin/src/d3drm/backends/gxm/razor.h b/miniwin/src/d3drm/backends/gxm/razor.h
new file mode 100644
index 00000000..d09260e7
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/razor.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include
+#include
+
+extern "C"
+{
+ extern int sceRazorGpuCaptureSetTrigger(int frames, const char* path);
+
+ extern int sceRazorGpuTraceTrigger();
+ extern int sceRazorGpuTraceSetFilename(const char* filename, int counter);
+ extern int sceRazorHudSetDisplayEnabled(bool enable);
+}
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/.gitignore b/miniwin/src/d3drm/backends/gxm/shaders/.gitignore
new file mode 100644
index 00000000..21ce5a1f
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/.gitignore
@@ -0,0 +1,2 @@
+*.exe
+cache/
\ No newline at end of file
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/CMakeLists.txt b/miniwin/src/d3drm/backends/gxm/shaders/CMakeLists.txt
new file mode 100644
index 00000000..8d6da2ee
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/CMakeLists.txt
@@ -0,0 +1,106 @@
+cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR)
+
+enable_language(ASM)
+
+find_program(PSP2CGC NAMES ${CMAKE_CURRENT_SOURCE_DIR}/psp2cgc.exe psp2cgc.exe psp2cgc)
+find_program(PSP2SHADERPERF NAMES ${CMAKE_CURRENT_SOURCE_DIR}/psp2shaderperf.exe psp2shaderperf.exe psp2shaderperf)
+
+list(APPEND CGC_COMMON_FLAGS "-Wperf" "-cache" "-cachedir" "${CMAKE_CURRENT_BINARY_DIR}/cache" "-W4" "-Wsuppress=5206,5203")
+
+make_directory("${CMAKE_CURRENT_BINARY_DIR}/cache")
+
+# compile .cg to .gxp if psp2cgc is found
+macro(COMPILE_SHADER INPUT_CG OUTPUT_GXP PROFILE)
+ set(EXTRA_FLAGS "${ARGN}")
+
+ set(INPUT_CG_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_CG}")
+ set(OUTPUT_GXP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_GXP}")
+ set(TRACKED_GXP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${OUTPUT_GXP}")
+
+ if(PSP2CGC)
+ if(EXISTS ${TRACKED_GXP_PATH})
+ file(COPY ${TRACKED_GXP_PATH} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+ endif()
+ add_custom_command(
+ OUTPUT "${OUTPUT_GXP_PATH}"
+ COMMAND "${PSP2CGC}" ${CGC_COMMON_FLAGS} -profile "${PROFILE}" ${EXTRA_FLAGS} "${INPUT_CG_PATH}" -o "${OUTPUT_GXP_PATH}"
+ COMMAND ${CMAKE_COMMAND} -E copy "${OUTPUT_GXP_PATH}" "${TRACKED_GXP_PATH}"
+ DEPENDS "${INPUT_CG_PATH}"
+ COMMENT "Compiling ${INPUT_CG} -> ${OUTPUT_GXP} ${PROFILE}"
+ )
+ else()
+ if(NOT EXISTS "${TRACKED_GXP_PATH}")
+ message(FATAL_ERROR "missing shader ${TRACKED_GXP_PATH} ${INPUT_CG}, but psp2cgc.exe not found")
+ endif()
+ add_custom_command(
+ OUTPUT "${OUTPUT_GXP_PATH}"
+ COMMAND /usr/bin/env bash
+ ARGS "-c"
+ "if [ \"${INPUT_CG_PATH}\" -nt \"${TRACKED_GXP_PATH}\" ]; then
+ echo 'warning: ${INPUT_CG} changed but dont have psp2cgc, cant recompile' >&2;
+ fi"
+ COMMAND ${CMAKE_COMMAND} -E copy "${TRACKED_GXP_PATH}" "${OUTPUT_GXP_PATH}"
+ DEPENDS "${INPUT_CG_PATH}" "${TRACKED_GXP_PATH}"
+ VERBATIM
+ COMMENT "Copy ${TRACKED_GXP_PATH} -> ${OUTPUT_GXP} ${PROFILE}"
+ )
+ endif()
+ set(TARGET_NAME "compile_${OUTPUT_GXP}")
+ string(REPLACE "." "_" TARGET_NAME "${TARGET_NAME}")
+ add_custom_target("${TARGET_NAME}" DEPENDS "${OUTPUT_GXP_PATH}")
+ add_dependencies(gxm_shaders_compile "${TARGET_NAME}")
+endmacro()
+
+
+# analyze gxp to create a .perf.txt
+macro(GENERATE_PERF GXP_FILE)
+ set(GXP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${GXP_FILE}")
+ string(REPLACE ".gxp" ".perf.txt" PERF_FILE_NAME "${GXP_FILE}")
+ set(PERF_FILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${PERF_FILE_NAME}")
+
+ add_custom_command(
+ OUTPUT "${PERF_FILE_PATH}"
+ COMMAND "${PSP2SHADERPERF}" -stats -symbols -disasm "${GXP_PATH}" > "${PERF_FILE_PATH}"
+ DEPENDS "${GXP_PATH}"
+ COMMENT "Generating performance analysis for ${GXP_FILE}"
+ )
+ set(TARGET_NAME "perf_${PERF_FILE_NAME}")
+ string(REPLACE "." "_" TARGET_NAME "${TARGET_NAME}")
+ add_custom_target("${TARGET_NAME}" DEPENDS "${PERF_FILE_PATH}")
+ add_dependencies(gxm_perfs "${TARGET_NAME}" )
+endmacro()
+
+
+
+add_library(gxm_shaders STATIC gxm_shaders.s)
+
+target_include_directories(gxm_shaders PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+add_custom_target(gxm_shaders_compile)
+add_dependencies(gxm_shaders gxm_shaders_compile)
+
+COMPILE_SHADER(plane.vert.cg plane.vert.gxp sce_vp_psp2)
+COMPILE_SHADER(color.frag.cg color.frag.gxp sce_fp_psp2)
+COMPILE_SHADER(image.frag.cg image.frag.gxp sce_fp_psp2)
+COMPILE_SHADER(main.vert.cg main.vert.gxp sce_vp_psp2)
+COMPILE_SHADER(main.frag.cg main.color.frag.gxp sce_fp_psp2)
+COMPILE_SHADER(main.frag.cg main.texture.frag.gxp sce_fp_psp2 -DTEXTURED=1)
+
+
+# .perf.txt
+if(PSP2SHADERPERF)
+ add_custom_target(gxm_perfs)
+ add_dependencies(gxm_shaders gxm_perfs)
+ GENERATE_PERF(plane.vert.gxp)
+ GENERATE_PERF(color.frag.gxp)
+ GENERATE_PERF(image.frag.gxp)
+ GENERATE_PERF(main.vert.gxp)
+ GENERATE_PERF(main.color.frag.gxp)
+ GENERATE_PERF(main.texture.frag.gxp)
+else()
+ message(STATUS "psp2shaderperf not found")
+endif()
+
+
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/color.frag.cg b/miniwin/src/d3drm/backends/gxm/shaders/color.frag.cg
new file mode 100644
index 00000000..67533b51
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/color.frag.cg
@@ -0,0 +1,6 @@
+void main(
+ uniform float4 uColor : COLOR,
+ out float4 outColor : COLOR
+) {
+ outColor = uColor;
+}
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/color.frag.gxp b/miniwin/src/d3drm/backends/gxm/shaders/color.frag.gxp
new file mode 100644
index 00000000..dea3d7eb
Binary files /dev/null and b/miniwin/src/d3drm/backends/gxm/shaders/color.frag.gxp differ
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/color.frag.perf.txt b/miniwin/src/d3drm/backends/gxm/shaders/color.frag.perf.txt
new file mode 100644
index 00000000..cabaa25d
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/color.frag.perf.txt
@@ -0,0 +1,26 @@
+Total estimated cost: 1 cycles, parallel mode
+Register count: 2 PAs, 0 temps, 4 SAs *
+Texture reads: 0 non-dependent, dependent: 0 unconditional, 0 conditional
+
+High level analysis:
+No warnings.
+
+* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
+
+Instruction statistics:
+Number of alu ops: 1
+Number of mem ops: 0
+Number of tex ops: 0
+Number of floating point ops: 0
+Number of integer ops: 0
+Number of pack ops: 1
+Number of mov ops: 0
+Number of nop ops: 0
+
+Constants:
+[DEFAULT + 0 ] sa0 = (float4) uColor
+
+
+Primary program:
+ 0: pack.f16.f32 pa0.xyzw, sa0.xyzw
+
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.h b/miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.h
new file mode 100644
index 00000000..0fa9ab41
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.h
@@ -0,0 +1,32 @@
+#include
+
+#define GXP(sym) \
+ extern uint8_t _inc_##sym[]; \
+ static const SceGxmProgram* sym = (const SceGxmProgram*) _inc_##sym;
+
+GXP(mainVertexProgramGxp);
+GXP(mainColorFragmentProgramGxp);
+GXP(mainTextureFragmentProgramGxp);
+GXP(planeVertexProgramGxp);
+GXP(imageFragmentProgramGxp);
+GXP(colorFragmentProgramGxp);
+
+static const SceGxmBlendInfo blendInfoOpaque = {
+ .colorMask = SCE_GXM_COLOR_MASK_ALL,
+ .colorFunc = SCE_GXM_BLEND_FUNC_NONE,
+ .alphaFunc = SCE_GXM_BLEND_FUNC_NONE,
+ .colorSrc = SCE_GXM_BLEND_FACTOR_ZERO,
+ .colorDst = SCE_GXM_BLEND_FACTOR_ZERO,
+ .alphaSrc = SCE_GXM_BLEND_FACTOR_ZERO,
+ .alphaDst = SCE_GXM_BLEND_FACTOR_ZERO,
+};
+
+static const SceGxmBlendInfo blendInfoTransparent = {
+ .colorMask = SCE_GXM_COLOR_MASK_ALL,
+ .colorFunc = SCE_GXM_BLEND_FUNC_ADD,
+ .alphaFunc = SCE_GXM_BLEND_FUNC_ADD,
+ .colorSrc = SCE_GXM_BLEND_FACTOR_SRC_ALPHA,
+ .colorDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .alphaSrc = SCE_GXM_BLEND_FACTOR_ONE,
+ .alphaDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+};
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.s b/miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.s
new file mode 100644
index 00000000..2421947d
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/gxm_shaders.s
@@ -0,0 +1,15 @@
+.section .rodata, "a", %progbits
+
+.macro embed file, symbol
+.global _inc_\symbol
+.align 16
+_inc_\symbol:
+ .incbin "\file"
+.endm
+
+embed "main.vert.gxp", mainVertexProgramGxp
+embed "main.color.frag.gxp", mainColorFragmentProgramGxp
+embed "main.texture.frag.gxp", mainTextureFragmentProgramGxp
+embed "plane.vert.gxp", planeVertexProgramGxp
+embed "image.frag.gxp", imageFragmentProgramGxp
+embed "color.frag.gxp", colorFragmentProgramGxp
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/image.frag.cg b/miniwin/src/d3drm/backends/gxm/shaders/image.frag.cg
new file mode 100644
index 00000000..b3bc6b02
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/image.frag.cg
@@ -0,0 +1,10 @@
+void main(
+ float4 vPosition : POSITION,
+ float2 vTexCoord : TEXCOORD0,
+
+ uniform sampler2D uTexture,
+
+ out float4 outColor : COLOR
+) {
+ outColor = tex2D(uTexture, vTexCoord);
+}
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/image.frag.gxp b/miniwin/src/d3drm/backends/gxm/shaders/image.frag.gxp
new file mode 100644
index 00000000..fefde5f0
Binary files /dev/null and b/miniwin/src/d3drm/backends/gxm/shaders/image.frag.gxp differ
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/image.frag.perf.txt b/miniwin/src/d3drm/backends/gxm/shaders/image.frag.perf.txt
new file mode 100644
index 00000000..4b69dc4b
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/image.frag.perf.txt
@@ -0,0 +1,29 @@
+Total estimated cost: 0 cycles, parallel mode
+Register count: 2 PAs, 0 temps, 0 SAs *
+Texture reads: 1 non-dependent, dependent: 0 unconditional, 0 conditional
+
+High level analysis:
+No warnings.
+
+* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
+
+Instruction statistics:
+Number of alu ops: 0
+Number of mem ops: 0
+Number of tex ops: 0
+Number of floating point ops: 0
+Number of integer ops: 0
+Number of pack ops: 0
+Number of mov ops: 0
+Number of nop ops: 0
+
+
+Samplers:
+TEXUNIT0 = uTexture
+
+Iterators:
+
+
+Primary program:
+pa0 = tex2D(uTexture, TEXCOORD0.xy)
+
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/main.color.frag.gxp b/miniwin/src/d3drm/backends/gxm/shaders/main.color.frag.gxp
new file mode 100644
index 00000000..713e10c9
Binary files /dev/null and b/miniwin/src/d3drm/backends/gxm/shaders/main.color.frag.gxp differ
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/main.color.frag.perf.txt b/miniwin/src/d3drm/backends/gxm/shaders/main.color.frag.perf.txt
new file mode 100644
index 00000000..5292cb7a
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/main.color.frag.perf.txt
@@ -0,0 +1,134 @@
+Total estimated cost: 63.5 cycles, parallel mode
+Register count: 8 PAs, 16 temps, 38 SAs *
+Texture reads: 0 non-dependent, dependent: 0 unconditional, 0 conditional
+
+High level analysis:
+No warnings.
+
+* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
+
+Instruction statistics:
+Number of alu ops: 63
+Number of mem ops: 0
+Number of tex ops: 0
+Number of floating point ops: 43
+Number of integer ops: 1
+Number of pack ops: 1
+Number of mov ops: 15
+Number of nop ops: 2
+
+Constants:
+[DEFAULT + 0 ] sa0 = (float1) uShininess
+[DEFAULT + 2 ] sa2 = (float4) uColor
+[BUFFER0 + 0 ] = (float4) uLights[0].color
+[BUFFER0 + 4 ] = (float4) uLights[0].vec
+[BUFFER0 + 8 ] = (float1) uLights[0].isDirectional
+[BUFFER0 + 10 ] = (float4) uLights[1].color
+[BUFFER0 + 14 ] = (float4) uLights[1].vec
+[BUFFER0 + 18 ] = (float1) uLights[1].isDirectional
+[BUFFER0 + 20 ] = (float3) uAmbientLight
+[LITERAL + 1 ] sa7 = 0xffffffff (-1.#QNAN0f) (-1.#QNANh, -1.#QNANh)
+[LITERAL + 2 ] sa8 = 0x3f800000 (1.000000f) (0.00000h, 1.87500h)
+[BUFFER0 ] sa6 = buffer base address
+
+Iterators:
+pa0 = (float4) TEXCOORD1
+pa4 = (float4) TEXCOORD2
+
+
+Secondary program:
+ 0 : lda32 sa1, [sa6, 0x9]
+ 1 : lda32.fetch3 sa9, [sa6, 0x4]
+ 2 : mul.f32 sa32.xy, -sa8.yz, sa0.yy
+ 3 : mul.f32 sa34.x, -sa10.y, sa0.y
+ 4 : lda32.fetch4 sa12, [sa6, 0x0]
+ 5 : mov.f32 sa36.xy, {0, 0}
+ 6 : mov.f32 sa30.x, {0}
+ 7 : lda32 sa15, [sa6, 0x13]
+ 8 : lda32.fetch3 sa16, [sa6, 0xe]
+ 9 : mul.f32 sa26.xy, -sa16.xy, sa14.yy
+ 10: mul.f32 sa28.x, -sa18.x, sa14.y
+ 11: lda32.fetch4 sa19, [sa6, 0xa]
+ 12: lda32.fetch3 sa22, [sa6, 0x14]
+ 13: nop
+
+Primary program:
+ 0 : cmp.gt.f32 p0, sa0.x, {0}
+ 1 : mov.f32 i0.xyz, pa0.xyz
+ 2 : add.f32 i1.xyz, sa8.yzw, -i0.xyz
+ 2 : +dot.f32 pa3.x, i0.xyz, i0.xyz
+ 3 : mov.f32 i2.xyz, sa32.xyz
+ 4 : mad.f32 i2.xyz, -sa0.yyy, i1.xyz, i2.xyz
+ 5 : add.f32 i1.xyz, i2.xyz, i1.xyz
+ 5 : +rsq.f32 pa3.x, pa3.x
+ 6 : dot.f32 i2.x, i1.xyz, i1.xyz
+ 7 : rsq.f32 pa3.x, i2.x
+ 7 : +mul.f32 i0.xyz, -i0.xyz, pa2.yyy
+ 8 : mad.f32 i2.xyz, pa2.yyy, i1.xyz, i0.xyz
+ 8 : +mov.f32 r6.xy, i0.xy
+ 9 : mov.f32 r8.x, i0.z
+ 10: dot.f32 i0.x, i2.xyz, i2.xyz
+ 10: +mov.f32 r2.xy, i2.xy
+ 11: mov.f32 r4.x, i2.z
+ 12: rsq.f32 pa3.x, i0.x
+ 12: +mul.f32 i0.xyz, i1.xyz, pa2.yyy
+ 13: dot.f32 i0.x, pa4.xyz, i0.xyz
+ 14: max.f32 r14.x, i0.x, {0}
+ 15: p0 br #19
+ 16: mov.f32 r10.xy, sa36.xy
+ 17: or.u32 r12.x, sa30.x, 0x0
+ 18: br #30
+#19: cmov.lezero.f32 r0.x, r14.x, {0}, sa8.x
+ 20: cmov.ltzero.f32 i0.x, r14.x, sa8.x, {0}
+ 21: mad.f32 i0.x, r0.x, {1}, -i0.x
+ 22: mul.f32 i1.xyz, r2.xyz, pa2.yyy
+ 23: dot.f32 i1.x, pa4.xyz, i1.xyz
+ 24: max.f32 i1.x, i1.x, {0}
+ 25: mul.f32 i0.xyz, sa12.xyz, i0.xxx
+ 25: +log.f32 pa3.x, i1.x
+ 26: mul.f32 i1.x, sa0.x, pa2.y
+ 27: exp.f32 i1.x, i1.x
+ 28: mad.f32 r10.xy, i0.xy, i1.xx, {0, 0}
+ 29: mul.f32 r12.x, i0.z, i1.x
+#30: nop
+ 31: mov.f32 i0.xyz, sa16.xyz
+ 32: mad.f32 i0.xyz, -pa0.xyz, {1, 1, 1}, i0.xyz
+ 33: mov.f32 i1.xyz, sa26.xyz
+ 34: mad.f32 i1.xyz, -sa14.yyy, i0.xyz, i1.xyz
+ 35: add.f32 i0.xyz, i1.xyz, i0.xyz
+ 35: +mov.f32 i2.xyz, r6.xyz
+ 36: dot.f32 i1.x, i0.xyz, i0.xyz
+ 37: rsq.f32 i1.x, i1.x
+ 38: mad.f32 i2.xyz, i1.xxx, i0.xyz, i2.xyz
+ 39: mul.f32 i0.xyz, i0.xyz, i1.xxx
+ 39: +mov.f32 r0.xy, i2.xy
+ 40: dot.f32 i0.x, pa4.xyz, i0.xyz
+ 41: max.f32 pa2.x, i0.x, {0}
+ 42: dot.f32 i0.x, i2.xyz, i2.xyz
+ 43: mov.f32 r2.x, i2.z
+ 44: rsq.f32 pa0.-y, i0.x
+ 45: !p0 br #57
+ 46: cmov.lezero.f32 pa0.x, pa2.x, {0}, sa8.x
+ 47: cmov.ltzero.f32 i0.x, pa2.x, sa8.x, {0}
+ 48: mad.f32 i0.x, pa0.x, {1}, -i0.x
+ 49: mul.f32 i1.xyz, r0.xyz, pa0.yyy
+ 50: dot.f32 i1.x, pa4.xyz, i1.xyz
+ 51: max.f32 i1.x, i1.x, {0}
+ 52: mul.f32 i0.xyz, sa18.yzw, i0.xxx
+ 52: +log.f32 pa0.x, i1.x
+ 53: mul.f32 i1.x, sa0.x, pa0.x
+ 54: exp.f32 i1.x, i1.x
+ 55: mad.f32 r10.xy, i0.xy, i1.xx, r10.xy
+ 56: mad.f32 r12.x, i0.z, i1.x, r12.x
+#57: nop
+ 58: mad.f32 i0.xy, r14.xx, sa12.xy, sa22.xy
+ 59: mad.f32 i0.--z, r14.--x, sa14.--x, sa24.--x
+ 60: mov.f32 i1.xyz, sa18.yzw
+ 61: mad.f32 i0.xyz, pa2.xxx, i1.xyz, i0.xyz
+ 62: mov.f32 i1.xyz, r10.xyz
+ 63: mov.f32 i2.xyzw, sa2.xyzw
+ 64: mad.f32 i0.xyz, i2.xyz, i0.xyz, i1.xyz
+ 65: min.f32 i0.xyz, i0.xyz, {1, 1, 1}
+ 66: max.f32 i2.xyz, i0.xyz, {0, 0, 0}
+ 67: pack.f16.f32 pa0.xyzw, i2.xyzw
+
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/main.frag.cg b/miniwin/src/d3drm/backends/gxm/shaders/main.frag.cg
new file mode 100644
index 00000000..6960a5f1
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/main.frag.cg
@@ -0,0 +1,54 @@
+struct SceneLight {
+ float4 color;
+ float4 vec;
+ float isDirectional;
+};
+
+void main(
+ float4 vPosition : POSITION,
+ float2 vTexCoord : TEXCOORD0,
+ float3 vViewPos : TEXCOORD1,
+ float3 vNormal : TEXCOORD2,
+
+ uniform __nostrip SceneLight uLights[2] : BUFFER[0],
+ uniform __nostrip float3 uAmbientLight : BUFFER[0],
+ uniform __nostrip float uShininess,
+ uniform __nostrip float4 uColor,
+ uniform __nostrip sampler2D uTexture,
+
+ out float4 outColor : COLOR
+) {
+ float3 diffuse = float3(0.0, 0.0, 0.0);
+ float3 specular = float3(0.0, 0.0, 0.0);
+
+ float3 viewVec = normalize(-vViewPos); // Assuming camera at origin
+
+ diffuse += uAmbientLight;
+
+ for (int i = 0; i < 2; ++i) {
+ float3 lightColor = uLights[i].color.rgb;
+ float isDirectional = uLights[i].isDirectional;
+
+ float3 lightVec = normalize(lerp(uLights[i].vec.xyz - vViewPos, -uLights[i].vec.xyz, isDirectional));
+
+ float3 halfVec = normalize(viewVec + lightVec);
+ float dotNL = max(dot(vNormal, lightVec), 0.0);
+ float dotNH = max(dot(vNormal, halfVec), 0.0);
+
+ // Diffuse contribution
+ diffuse += dotNL * lightColor;
+
+ // Specular
+ float spec = pow(dotNH, uShininess);
+ if(uShininess > 0.0) {
+ specular += spec * lightColor * sign(dotNL);
+ }
+ }
+
+ outColor.rgb = clamp(diffuse * uColor.rgb + specular, 0.0, 1.0);
+ outColor.a = uColor.a;
+#if TEXTURED
+ float4 texel = tex2D(uTexture, vTexCoord);
+ outColor.rgb *= texel.rgb;
+#endif
+}
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/main.texture.frag.gxp b/miniwin/src/d3drm/backends/gxm/shaders/main.texture.frag.gxp
new file mode 100644
index 00000000..501bfea4
Binary files /dev/null and b/miniwin/src/d3drm/backends/gxm/shaders/main.texture.frag.gxp differ
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/main.texture.frag.perf.txt b/miniwin/src/d3drm/backends/gxm/shaders/main.texture.frag.perf.txt
new file mode 100644
index 00000000..ef379b18
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/main.texture.frag.perf.txt
@@ -0,0 +1,139 @@
+Total estimated cost: 64.5 cycles, parallel mode
+Register count: 12 PAs, 16 temps, 38 SAs *
+Texture reads: 1 non-dependent, dependent: 0 unconditional, 0 conditional
+
+High level analysis:
+No warnings.
+
+* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
+
+Instruction statistics:
+Number of alu ops: 64
+Number of mem ops: 0
+Number of tex ops: 0
+Number of floating point ops: 44
+Number of integer ops: 1
+Number of pack ops: 1
+Number of mov ops: 15
+Number of nop ops: 2
+
+Constants:
+[DEFAULT + 0 ] sa0 = (float1) uShininess
+[DEFAULT + 2 ] sa2 = (float4) uColor
+[BUFFER0 + 0 ] = (float4) uLights[0].color
+[BUFFER0 + 4 ] = (float4) uLights[0].vec
+[BUFFER0 + 8 ] = (float1) uLights[0].isDirectional
+[BUFFER0 + 10 ] = (float4) uLights[1].color
+[BUFFER0 + 14 ] = (float4) uLights[1].vec
+[BUFFER0 + 18 ] = (float1) uLights[1].isDirectional
+[BUFFER0 + 20 ] = (float3) uAmbientLight
+[LITERAL + 1 ] sa7 = 0xffffffff (-1.#QNAN0f) (-1.#QNANh, -1.#QNANh)
+[LITERAL + 2 ] sa8 = 0x3f800000 (1.000000f) (0.00000h, 1.87500h)
+[BUFFER0 ] sa6 = buffer base address
+
+Samplers:
+TEXUNIT0 = uTexture
+
+Iterators:
+pa0 = (float4) TEXCOORD1
+pa8 = (float4) TEXCOORD2
+
+
+Secondary program:
+ 0 : lda32 sa1, [sa6, 0x9]
+ 1 : lda32.fetch3 sa9, [sa6, 0x4]
+ 2 : mul.f32 sa32.xy, -sa8.yz, sa0.yy
+ 3 : mul.f32 sa34.x, -sa10.y, sa0.y
+ 4 : lda32.fetch4 sa12, [sa6, 0x0]
+ 5 : mov.f32 sa36.xy, {0, 0}
+ 6 : mov.f32 sa30.x, {0}
+ 7 : lda32 sa15, [sa6, 0x13]
+ 8 : lda32.fetch3 sa16, [sa6, 0xe]
+ 9 : mul.f32 sa26.xy, -sa16.xy, sa14.yy
+ 10: mul.f32 sa28.x, -sa18.x, sa14.y
+ 11: lda32.fetch4 sa19, [sa6, 0xa]
+ 12: lda32.fetch3 sa22, [sa6, 0x14]
+ 13: nop
+
+Primary program:
+pa4 = tex2D(uTexture, TEXCOORD0.xy)
+ 0 : cmp.gt.f32 p0, sa0.x, {0}
+ 1 : mov.f32 i0.xyz, pa0.xyz
+ 2 : add.f32 i1.xyz, sa8.yzw, -i0.xyz
+ 2 : +dot.f32 pa3.x, i0.xyz, i0.xyz
+ 3 : mov.f32 i2.xyz, sa32.xyz
+ 4 : mad.f32 i2.xyz, -sa0.yyy, i1.xyz, i2.xyz
+ 5 : add.f32 i1.xyz, i2.xyz, i1.xyz
+ 5 : +rsq.f32 pa3.x, pa3.x
+ 6 : dot.f32 i2.x, i1.xyz, i1.xyz
+ 7 : rsq.f32 pa3.x, i2.x
+ 7 : +mul.f32 i0.xyz, -i0.xyz, pa2.yyy
+ 8 : mad.f32 i2.xyz, pa2.yyy, i1.xyz, i0.xyz
+ 8 : +mov.f32 r6.xy, i0.xy
+ 9 : mov.f32 r8.x, i0.z
+ 10: dot.f32 i0.x, i2.xyz, i2.xyz
+ 10: +mov.f32 r2.xy, i2.xy
+ 11: mov.f32 r4.x, i2.z
+ 12: rsq.f32 pa3.x, i0.x
+ 12: +mul.f32 i0.xyz, i1.xyz, pa2.yyy
+ 13: dot.f32 i0.x, pa8.xyz, i0.xyz
+ 14: max.f32 r14.x, i0.x, {0}
+ 15: p0 br #19
+ 16: mov.f32 r10.xy, sa36.xy
+ 17: or.u32 r12.x, sa30.x, 0x0
+ 18: br #30
+#19: cmov.lezero.f32 r0.x, r14.x, {0}, sa8.x
+ 20: cmov.ltzero.f32 i0.x, r14.x, sa8.x, {0}
+ 21: mad.f32 i0.x, r0.x, {1}, -i0.x
+ 22: mul.f32 i1.xyz, r2.xyz, pa2.yyy
+ 23: dot.f32 i1.x, pa8.xyz, i1.xyz
+ 24: max.f32 i1.x, i1.x, {0}
+ 25: mul.f32 i0.xyz, sa12.xyz, i0.xxx
+ 25: +log.f32 pa3.x, i1.x
+ 26: mul.f32 i1.x, sa0.x, pa2.y
+ 27: exp.f32 i1.x, i1.x
+ 28: mad.f32 r10.xy, i0.xy, i1.xx, {0, 0}
+ 29: mul.f32 r12.x, i0.z, i1.x
+#30: nop
+ 31: mov.f32 i0.xyz, sa16.xyz
+ 32: mad.f32 i0.xyz, -pa0.xyz, {1, 1, 1}, i0.xyz
+ 33: mov.f32 i1.xyz, sa26.xyz
+ 34: mad.f32 i1.xyz, -sa14.yyy, i0.xyz, i1.xyz
+ 35: add.f32 i0.xyz, i1.xyz, i0.xyz
+ 35: +mov.f32 i2.xyz, r6.xyz
+ 36: dot.f32 i1.x, i0.xyz, i0.xyz
+ 37: rsq.f32 i1.x, i1.x
+ 38: mad.f32 i2.xyz, i1.xxx, i0.xyz, i2.xyz
+ 39: mul.f32 i0.xyz, i0.xyz, i1.xxx
+ 39: +mov.f32 r0.xy, i2.xy
+ 40: dot.f32 i0.x, pa8.xyz, i0.xyz
+ 41: max.f32 pa2.x, i0.x, {0}
+ 42: dot.f32 i0.x, i2.xyz, i2.xyz
+ 43: mov.f32 r2.x, i2.z
+ 44: rsq.f32 pa0.-y, i0.x
+ 45: !p0 br #57
+ 46: cmov.lezero.f32 pa0.x, pa2.x, {0}, sa8.x
+ 47: cmov.ltzero.f32 i0.x, pa2.x, sa8.x, {0}
+ 48: mad.f32 i0.x, pa0.x, {1}, -i0.x
+ 49: mul.f32 i1.xyz, r0.xyz, pa0.yyy
+ 50: dot.f32 i1.x, pa8.xyz, i1.xyz
+ 51: max.f32 i1.x, i1.x, {0}
+ 52: mul.f32 i0.xyz, sa18.yzw, i0.xxx
+ 52: +log.f32 pa0.x, i1.x
+ 53: mul.f32 i1.x, sa0.x, pa0.x
+ 54: exp.f32 i1.x, i1.x
+ 55: mad.f32 r10.xy, i0.xy, i1.xx, r10.xy
+ 56: mad.f32 r12.x, i0.z, i1.x, r12.x
+#57: nop
+ 58: mad.f32 i0.xy, r14.xx, sa12.xy, sa22.xy
+ 59: mad.f32 i0.--z, r14.--x, sa14.--x, sa24.--x
+ 60: mov.f32 i1.xyz, sa18.yzw
+ 61: mad.f32 i0.xyz, pa2.xxx, i1.xyz, i0.xyz
+ 62: mov.f32 i1.xyz, r10.xyz
+ 63: mov.f32 i2.xyzw, sa2.xyzw
+ 64: mad.f32 i0.xyz, i2.xyz, i0.xyz, i1.xyz
+ 65: min.f32 i0.xyz, i0.xyz, {1, 1, 1}
+ 66: max.f32 i0.xyz, i0.xyz, {0, 0, 0}
+ 67: mad.f32 i2.xyz, pa4.xyz, i0.xyz, {0, 0, 0}
+ 68: pack.f16.f32 pa0.xyzw, i2.xyzw
+
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/main.vert.cg b/miniwin/src/d3drm/backends/gxm/shaders/main.vert.cg
new file mode 100644
index 00000000..2f74fee6
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/main.vert.cg
@@ -0,0 +1,20 @@
+void main(
+ float3 aPosition : POSITION,
+ float3 aNormal : NORMAL,
+ float2 aTexCoord : TEXCOORD0,
+
+ column_major uniform float4x4 uModelViewMatrix,
+ column_major uniform float3x3 uNormalMatrix,
+ column_major uniform float4x4 uProjectionMatrix,
+
+ out float4 vPosition : POSITION,
+ out float2 vTexCoord : TEXCOORD0,
+ out float3 vViewPos : TEXCOORD1,
+ out float3 vNormal : TEXCOORD2
+) {
+ float4 viewPos = mul(uModelViewMatrix, float4(aPosition, 1.0));
+ vPosition = mul(uProjectionMatrix, viewPos);
+ vViewPos = viewPos.xyz;
+ vNormal = normalize(mul(uNormalMatrix, aNormal));
+ vTexCoord = aTexCoord;
+}
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/main.vert.gxp b/miniwin/src/d3drm/backends/gxm/shaders/main.vert.gxp
new file mode 100644
index 00000000..b4d3427d
Binary files /dev/null and b/miniwin/src/d3drm/backends/gxm/shaders/main.vert.gxp differ
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/main.vert.perf.txt b/miniwin/src/d3drm/backends/gxm/shaders/main.vert.perf.txt
new file mode 100644
index 00000000..ab3c679e
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/main.vert.perf.txt
@@ -0,0 +1,87 @@
+Total estimated cost: 18 cycles, parallel mode
+Register count: 12 PAs, 0 temps, 56 SAs *
+Texture reads: 0 non-dependent, dependent: 0 unconditional, 0 conditional
+
+High level analysis:
+- One or multiple vertex outputs are misaligned due to a three coefficient TEXCOORD vertex output. This will lead to additional data movement instructions being generated. If only one three coefficient TEXCOORD output is present, assigning it the coordinate with the greatest index will allow the previous coordinates to be aligned.
+
+* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
+
+Instruction statistics:
+Number of alu ops: 19
+Number of mem ops: 0
+Number of tex ops: 0
+Number of floating point ops: 14
+Number of integer ops: 0
+Number of pack ops: 0
+Number of mov ops: 4
+Number of nop ops: 0
+
+Constants:
+[DEFAULT + 0 ] sa0 = (float4) uModelViewMatrix[4]
+[DEFAULT + 32 ] sa32 = (float3) uNormalMatrix[3]
+[DEFAULT + 16 ] sa16 = (float4) uProjectionMatrix[4]
+
+Vertex attributes:
+pa0 = (float4) aPosition
+pa4 = (float4) aNormal
+pa8 = (float4) aTexCoord
+
+
+Secondary program:
+ 0 : nop
+ 1 : mov.f32 i0.xyzw, sa8.xyzw
+ 2 : mul.f32 i1.xyzw, sa16.xyzw, i0.xxxx
+ 3 : mad.f32 i1.xyzw, sa20.xyzw, i0.yyyy, i1.xyzw
+ 4 : mad.f32 i1.xyzw, sa24.xyzw, i0.zzzz, i1.xyzw
+ 5 : mad.f32 sa52.xy, sa28.xy, i0.ww, i1.xy
+ 6 : mad.f32 sa54.xy, sa30.xy, i0.ww, i1.zw
+ 7 : mov.f32 i1.xyzw, sa12.xyzw
+ 8 : mul.f32 i2.xyzw, sa28.xyzw, i1.wwww
+ 9 : mad.f32 i2.xyzw, sa24.xyzw, i1.zzzz, i2.xyzw
+ 10: mad.f32 i2.xyzw, sa20.xyzw, i1.yyyy, i2.xyzw
+ 11: mad.f32 sa48.xy, sa16.xy, i1.xx, i2.xy
+ 12: mad.f32 sa50.xy, sa18.xy, i1.xx, i2.zw
+ 13: mov.f32 i2.xyzw, sa4.xyzw
+ 14: mul.f32 i1.xyzw, sa16.xyzw, i2.xxxx
+ 15: mad.f32 i1.xyzw, sa20.xyzw, i2.yyyy, i1.xyzw
+ 16: mad.f32 i1.xyzw, sa24.xyzw, i2.zzzz, i1.xyzw
+ 17: mad.f32 sa44.xy, sa28.xy, i2.ww, i1.xy
+ 18: mad.f32 sa46.xy, sa30.xy, i2.ww, i1.zw
+ 19: mov.f32 i1.xyzw, sa0.xyzw
+ 20: mul.f32 i0.xyzw, sa16.xyzw, i1.xxxx
+ 20: +mov.f32 sa4.xy, i1.yy
+ 21: mad.f32 i0.xyzw, sa20.xyzw, i1.yyyy, i0.xyzw
+ 22: mad.f32 i0.xyzw, sa24.xyzw, i1.zzzz, i0.xyzw
+ 23: mad.f32 sa18.xy, sa28.xy, i1.ww, i0.xy
+ 24: mad.f32 sa16.xy, sa30.xy, i1.ww, i0.zw
+ 25: mov.f32 sa4.-y, i2.-y
+ 26: mov.f32 sa6.xy, (sa8.y, sa12.y)
+ 27: mov.f32 sa0.x, i1.x
+ 28: mov.f32 sa0.-y, i2.-x
+ 29: mov.f32 sa8.-y, sa12.-x
+ 30: mov.f32 sa10.-y, sa14.-x
+ 31: mov.f32 sa2.xy, sa8.xy
+ 32: mov.f32 sa8.x, i1.z
+ 33: mov.f32 sa8.-y, i2.-z
+
+Primary program:
+ 0 : mov.f32 o4.xy, pa8.xy
+ 1 : mov.f32 i0.xyz, pa4.xyz
+ 2 : mad.f32 i1.xyz, sa32.xyz, i0.xxx, {0, 0, 0}
+ 3 : mad.f32 i1.xyz, sa36.xyz, i0.yyy, i1.xyz
+ 4 : mad.f32 i0.xyz, sa40.xyz, i0.zzz, i1.xyz
+ 5 : mov.f32 i1.xyz, pa0.xyz
+ 6 : mov.f32 i2.xyzw, sa48.xyzw
+ 7 : mad.f32 i2.xyzw, sa52.xyzw, i1.zzzz, i2.xyzw
+ 8 : mad.f32 i2.xyzw, sa44.xyzw, i1.yyyy, i2.xyzw
+ 9 : mad.f32 o0.xy, sa18.xy, i1.xx, i2.xy
+ 10: mad.f32 o2.xy, sa16.xy, i1.xx, i2.zw
+ 11: dot.f32 o6.x, sa0.xyzw, i1.xyz1
+ 12: dot.f32 o6.-y, sa4.xyzw, i1.xyz1
+ 13: dot.f32 o6.--z, sa8.xyzw, i1.xyz1
+ 14: dot.f32 i1.x, i0.xyz, i0.xyz
+ 15: rsq.f32 i1.x, i1.x
+ 16: mad.f32 o10.xy, i0.yz, i1.xx, {0, 0}
+ 17: mad.f32 o8.-y, i0.-x, i1.-x, {0, 0}
+
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.cg b/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.cg
new file mode 100644
index 00000000..532431d6
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.cg
@@ -0,0 +1,11 @@
+
+void main(
+ float2 aPosition : POSITION,
+ float2 aTexCoord : TEXCOORD0,
+
+ out float4 vPosition : POSITION,
+ out float2 vTexCoord : TEXCOORD0
+) : POSITION {
+ vPosition = float4(aPosition, 1.f, 1.f);
+ vTexCoord = aTexCoord;
+}
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.gxp b/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.gxp
new file mode 100644
index 00000000..b9a45ae8
Binary files /dev/null and b/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.gxp differ
diff --git a/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.perf.txt b/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.perf.txt
new file mode 100644
index 00000000..60080fde
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/shaders/plane.vert.perf.txt
@@ -0,0 +1,30 @@
+Total estimated cost: 3 cycles, parallel mode
+Register count: 8 PAs, 0 temps, 0 SAs *
+Texture reads: 0 non-dependent, dependent: 0 unconditional, 0 conditional
+
+High level analysis:
+No warnings.
+
+* Please refer to the Razor documentation for details regarding the meaning of these numbers. Decreasing the number of registers used will not necessarily increase performance
+
+Instruction statistics:
+Number of alu ops: 4
+Number of mem ops: 0
+Number of tex ops: 0
+Number of floating point ops: 2
+Number of integer ops: 0
+Number of pack ops: 0
+Number of mov ops: 1
+Number of nop ops: 0
+
+
+Vertex attributes:
+pa0 = (float4) aPosition
+pa4 = (float4) aTexCoord
+
+
+Primary program:
+ 0: mov.f32 o4.xy, pa4.xy
+ 1: mul.f32 o0.xy, pa0.xy, {1, 1}
+ 2: mul.f32 o2.xy, {1, 1}, {1, 1}
+
diff --git a/miniwin/src/d3drm/backends/gxm/tlsf.c b/miniwin/src/d3drm/backends/gxm/tlsf.c
new file mode 100644
index 00000000..31d25bdc
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/tlsf.c
@@ -0,0 +1,1264 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tlsf.h"
+
+#if defined(__cplusplus)
+#define tlsf_decl inline
+#else
+#define tlsf_decl static
+#endif
+
+/*
+** Architecture-specific bit manipulation routines.
+**
+** TLSF achieves O(1) cost for malloc and free operations by limiting
+** the search for a free block to a free list of guaranteed size
+** adequate to fulfill the request, combined with efficient free list
+** queries using bitmasks and architecture-specific bit-manipulation
+** routines.
+**
+** Most modern processors provide instructions to count leading zeroes
+** in a word, find the lowest and highest set bit, etc. These
+** specific implementations will be used when available, falling back
+** to a reasonably efficient generic implementation.
+**
+** NOTE: TLSF spec relies on ffs/fls returning value 0..31.
+** ffs/fls return 1-32 by default, returning 0 for error.
+*/
+
+/*
+** Detect whether or not we are building for a 32- or 64-bit (LP/LLP)
+** architecture. There is no reliable portable method at compile-time.
+*/
+#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) \
+ || defined (_WIN64) || defined (__LP64__) || defined (__LLP64__)
+#define TLSF_64BIT
+#endif
+
+/*
+** gcc 3.4 and above have builtin support, specialized for architecture.
+** Some compilers masquerade as gcc; patchlevel test filters them out.
+*/
+#if defined (__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \
+ && defined (__GNUC_PATCHLEVEL__)
+
+#if defined (__SNC__)
+/* SNC for Playstation 3. */
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __builtin_clz(reverse);
+ return bit - 1;
+}
+
+#else
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ return __builtin_ffs(word) - 1;
+}
+
+#endif
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __builtin_clz(word) : 0;
+ return bit - 1;
+}
+
+#elif defined (_MSC_VER) && (_MSC_VER >= 1400) && (defined (_M_IX86) || defined (_M_X64))
+/* Microsoft Visual C++ support on x86/X64 architectures. */
+
+#include
+
+#pragma intrinsic(_BitScanReverse)
+#pragma intrinsic(_BitScanForward)
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ unsigned long index;
+ return _BitScanReverse(&index, word) ? index : -1;
+}
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ unsigned long index;
+ return _BitScanForward(&index, word) ? index : -1;
+}
+
+#elif defined (_MSC_VER) && defined (_M_PPC)
+/* Microsoft Visual C++ support on PowerPC architectures. */
+
+#include
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = 32 - _CountLeadingZeros(word);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - _CountLeadingZeros(reverse);
+ return bit - 1;
+}
+
+#elif defined (__ARMCC_VERSION)
+/* RealView Compilation Tools for ARM */
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __clz(reverse);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __clz(word) : 0;
+ return bit - 1;
+}
+
+#elif defined (__ghs__)
+/* Green Hills support for PowerPC */
+
+#include
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __CLZ32(reverse);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __CLZ32(word) : 0;
+ return bit - 1;
+}
+
+#else
+/* Fall back to generic implementation. */
+
+tlsf_decl int tlsf_fls_generic(unsigned int word)
+{
+ int bit = 32;
+
+ if (!word) bit -= 1;
+ if (!(word & 0xffff0000)) { word <<= 16; bit -= 16; }
+ if (!(word & 0xff000000)) { word <<= 8; bit -= 8; }
+ if (!(word & 0xf0000000)) { word <<= 4; bit -= 4; }
+ if (!(word & 0xc0000000)) { word <<= 2; bit -= 2; }
+ if (!(word & 0x80000000)) { word <<= 1; bit -= 1; }
+
+ return bit;
+}
+
+/* Implement ffs in terms of fls. */
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ return tlsf_fls_generic(word & (~word + 1)) - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ return tlsf_fls_generic(word) - 1;
+}
+
+#endif
+
+/* Possibly 64-bit version of tlsf_fls. */
+#if defined (TLSF_64BIT)
+tlsf_decl int tlsf_fls_sizet(size_t size)
+{
+ int high = (int)(size >> 32);
+ int bits = 0;
+ if (high)
+ {
+ bits = 32 + tlsf_fls(high);
+ }
+ else
+ {
+ bits = tlsf_fls((int)size & 0xffffffff);
+
+ }
+ return bits;
+}
+#else
+#define tlsf_fls_sizet tlsf_fls
+#endif
+
+#undef tlsf_decl
+
+/*
+** Constants.
+*/
+
+/* Public constants: may be modified. */
+enum tlsf_public
+{
+ /* log2 of number of linear subdivisions of block sizes. Larger
+ ** values require more memory in the control structure. Values of
+ ** 4 or 5 are typical.
+ */
+ SL_INDEX_COUNT_LOG2 = 5,
+};
+
+/* Private constants: do not modify. */
+enum tlsf_private
+{
+#if defined (TLSF_64BIT)
+ /* All allocation sizes and addresses are aligned to 8 bytes. */
+ ALIGN_SIZE_LOG2 = 3,
+#else
+ /* All allocation sizes and addresses are aligned to 4 bytes. */
+ ALIGN_SIZE_LOG2 = 2,
+#endif
+ ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2),
+
+ /*
+ ** We support allocations of sizes up to (1 << FL_INDEX_MAX) bits.
+ ** However, because we linearly subdivide the second-level lists, and
+ ** our minimum size granularity is 4 bytes, it doesn't make sense to
+ ** create first-level lists for sizes smaller than SL_INDEX_COUNT * 4,
+ ** or (1 << (SL_INDEX_COUNT_LOG2 + 2)) bytes, as there we will be
+ ** trying to split size ranges into more slots than we have available.
+ ** Instead, we calculate the minimum threshold size, and place all
+ ** blocks below that size into the 0th first-level list.
+ */
+
+#if defined (TLSF_64BIT)
+ /*
+ ** TODO: We can increase this to support larger sizes, at the expense
+ ** of more overhead in the TLSF structure.
+ */
+ FL_INDEX_MAX = 32,
+#else
+ FL_INDEX_MAX = 30,
+#endif
+ SL_INDEX_COUNT = (1 << SL_INDEX_COUNT_LOG2),
+ FL_INDEX_SHIFT = (SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2),
+ FL_INDEX_COUNT = (FL_INDEX_MAX - FL_INDEX_SHIFT + 1),
+
+ SMALL_BLOCK_SIZE = (1 << FL_INDEX_SHIFT),
+};
+
+/*
+** Cast and min/max macros.
+*/
+
+#define tlsf_cast(t, exp) ((t) (exp))
+#define tlsf_min(a, b) ((a) < (b) ? (a) : (b))
+#define tlsf_max(a, b) ((a) > (b) ? (a) : (b))
+
+/*
+** Set assert macro, if it has not been provided by the user.
+*/
+#if !defined (tlsf_assert)
+#define tlsf_assert assert
+#endif
+
+/*
+** Static assertion mechanism.
+*/
+
+#define _tlsf_glue2(x, y) x ## y
+#define _tlsf_glue(x, y) _tlsf_glue2(x, y)
+#define tlsf_static_assert(exp) \
+ typedef char _tlsf_glue(static_assert, __LINE__) [(exp) ? 1 : -1]
+
+/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */
+tlsf_static_assert(sizeof(int) * CHAR_BIT == 32);
+tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32);
+tlsf_static_assert(sizeof(size_t) * CHAR_BIT <= 64);
+
+/* SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type. */
+tlsf_static_assert(sizeof(unsigned int) * CHAR_BIT >= SL_INDEX_COUNT);
+
+/* Ensure we've properly tuned our sizes. */
+tlsf_static_assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
+
+/*
+** Data structures and associated constants.
+*/
+
+/*
+** Block header structure.
+**
+** There are several implementation subtleties involved:
+** - The prev_phys_block field is only valid if the previous block is free.
+** - The prev_phys_block field is actually stored at the end of the
+** previous block. It appears at the beginning of this structure only to
+** simplify the implementation.
+** - The next_free / prev_free fields are only valid if the block is free.
+*/
+typedef struct block_header_t
+{
+ /* Points to the previous physical block. */
+ struct block_header_t* prev_phys_block;
+
+ /* The size of this block, excluding the block header. */
+ size_t size;
+
+ /* Next and previous free blocks. */
+ struct block_header_t* next_free;
+ struct block_header_t* prev_free;
+} block_header_t;
+
+/*
+** Since block sizes are always at least a multiple of 4, the two least
+** significant bits of the size field are used to store the block status:
+** - bit 0: whether block is busy or free
+** - bit 1: whether previous block is busy or free
+*/
+static const size_t block_header_free_bit = 1 << 0;
+static const size_t block_header_prev_free_bit = 1 << 1;
+
+/*
+** The size of the block header exposed to used blocks is the size field.
+** The prev_phys_block field is stored *inside* the previous free block.
+*/
+static const size_t block_header_overhead = sizeof(size_t);
+
+/* User data starts directly after the size field in a used block. */
+static const size_t block_start_offset =
+ offsetof(block_header_t, size) + sizeof(size_t);
+
+/*
+** A free block must be large enough to store its header minus the size of
+** the prev_phys_block field, and no larger than the number of addressable
+** bits for FL_INDEX.
+*/
+static const size_t block_size_min =
+ sizeof(block_header_t) - sizeof(block_header_t*);
+static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX;
+
+
+/* The TLSF control structure. */
+typedef struct control_t
+{
+ /* Empty lists point at this block to indicate they are free. */
+ block_header_t block_null;
+
+ /* Bitmaps for free lists. */
+ unsigned int fl_bitmap;
+ unsigned int sl_bitmap[FL_INDEX_COUNT];
+
+ /* Head of free lists. */
+ block_header_t* blocks[FL_INDEX_COUNT][SL_INDEX_COUNT];
+} control_t;
+
+/* A type used for casting when doing pointer arithmetic. */
+typedef ptrdiff_t tlsfptr_t;
+
+/*
+** block_header_t member functions.
+*/
+
+static size_t block_size(const block_header_t* block)
+{
+ return block->size & ~(block_header_free_bit | block_header_prev_free_bit);
+}
+
+static void block_set_size(block_header_t* block, size_t size)
+{
+ const size_t oldsize = block->size;
+ block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit));
+}
+
+static int block_is_last(const block_header_t* block)
+{
+ return block_size(block) == 0;
+}
+
+static int block_is_free(const block_header_t* block)
+{
+ return tlsf_cast(int, block->size & block_header_free_bit);
+}
+
+static void block_set_free(block_header_t* block)
+{
+ block->size |= block_header_free_bit;
+}
+
+static void block_set_used(block_header_t* block)
+{
+ block->size &= ~block_header_free_bit;
+}
+
+static int block_is_prev_free(const block_header_t* block)
+{
+ return tlsf_cast(int, block->size & block_header_prev_free_bit);
+}
+
+static void block_set_prev_free(block_header_t* block)
+{
+ block->size |= block_header_prev_free_bit;
+}
+
+static void block_set_prev_used(block_header_t* block)
+{
+ block->size &= ~block_header_prev_free_bit;
+}
+
+static block_header_t* block_from_ptr(const void* ptr)
+{
+ return tlsf_cast(block_header_t*,
+ tlsf_cast(unsigned char*, ptr) - block_start_offset);
+}
+
+static void* block_to_ptr(const block_header_t* block)
+{
+ return tlsf_cast(void*,
+ tlsf_cast(unsigned char*, block) + block_start_offset);
+}
+
+/* Return location of next block after block of given size. */
+static block_header_t* offset_to_block(const void* ptr, size_t size)
+{
+ return tlsf_cast(block_header_t*, tlsf_cast(tlsfptr_t, ptr) + size);
+}
+
+/* Return location of previous block. */
+static block_header_t* block_prev(const block_header_t* block)
+{
+ tlsf_assert(block_is_prev_free(block) && "previous block must be free");
+ return block->prev_phys_block;
+}
+
+/* Return location of next existing block. */
+static block_header_t* block_next(const block_header_t* block)
+{
+ block_header_t* next = offset_to_block(block_to_ptr(block),
+ block_size(block) - block_header_overhead);
+ tlsf_assert(!block_is_last(block));
+ return next;
+}
+
+/* Link a new block with its physical neighbor, return the neighbor. */
+static block_header_t* block_link_next(block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ next->prev_phys_block = block;
+ return next;
+}
+
+static void block_mark_as_free(block_header_t* block)
+{
+ /* Link the block to the next block, first. */
+ block_header_t* next = block_link_next(block);
+ block_set_prev_free(next);
+ block_set_free(block);
+}
+
+static void block_mark_as_used(block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ block_set_prev_used(next);
+ block_set_used(block);
+}
+
+static size_t align_up(size_t x, size_t align)
+{
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return (x + (align - 1)) & ~(align - 1);
+}
+
+static size_t align_down(size_t x, size_t align)
+{
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return x - (x & (align - 1));
+}
+
+static void* align_ptr(const void* ptr, size_t align)
+{
+ const tlsfptr_t aligned =
+ (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1);
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return tlsf_cast(void*, aligned);
+}
+
+/*
+** Adjust an allocation size to be aligned to word size, and no smaller
+** than internal minimum.
+*/
+static size_t adjust_request_size(size_t size, size_t align)
+{
+ size_t adjust = 0;
+ if (size)
+ {
+ const size_t aligned = align_up(size, align);
+
+ /* aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap */
+ if (aligned < block_size_max)
+ {
+ adjust = tlsf_max(aligned, block_size_min);
+ }
+ }
+ return adjust;
+}
+
+/*
+** TLSF utility functions. In most cases, these are direct translations of
+** the documentation found in the white paper.
+*/
+
+static void mapping_insert(size_t size, int* fli, int* sli)
+{
+ int fl, sl;
+ if (size < SMALL_BLOCK_SIZE)
+ {
+ /* Store small blocks in first list. */
+ fl = 0;
+ sl = tlsf_cast(int, size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
+ }
+ else
+ {
+ fl = tlsf_fls_sizet(size);
+ sl = tlsf_cast(int, size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2);
+ fl -= (FL_INDEX_SHIFT - 1);
+ }
+ *fli = fl;
+ *sli = sl;
+}
+
+/* This version rounds up to the next block size (for allocations) */
+static void mapping_search(size_t size, int* fli, int* sli)
+{
+ if (size >= SMALL_BLOCK_SIZE)
+ {
+ const size_t round = (1 << (tlsf_fls_sizet(size) - SL_INDEX_COUNT_LOG2)) - 1;
+ size += round;
+ }
+ mapping_insert(size, fli, sli);
+}
+
+static block_header_t* search_suitable_block(control_t* control, int* fli, int* sli)
+{
+ int fl = *fli;
+ int sl = *sli;
+
+ /*
+ ** First, search for a block in the list associated with the given
+ ** fl/sl index.
+ */
+ unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl);
+ if (!sl_map)
+ {
+ /* No block exists. Search in the next largest first-level list. */
+ const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1));
+ if (!fl_map)
+ {
+ /* No free blocks available, memory has been exhausted. */
+ return 0;
+ }
+
+ fl = tlsf_ffs(fl_map);
+ *fli = fl;
+ sl_map = control->sl_bitmap[fl];
+ }
+ tlsf_assert(sl_map && "internal error - second level bitmap is null");
+ sl = tlsf_ffs(sl_map);
+ *sli = sl;
+
+ /* Return the first block in the free list. */
+ return control->blocks[fl][sl];
+}
+
+/* Remove a free block from the free list.*/
+static void remove_free_block(control_t* control, block_header_t* block, int fl, int sl)
+{
+ block_header_t* prev = block->prev_free;
+ block_header_t* next = block->next_free;
+ tlsf_assert(prev && "prev_free field can not be null");
+ tlsf_assert(next && "next_free field can not be null");
+ next->prev_free = prev;
+ prev->next_free = next;
+
+ /* If this block is the head of the free list, set new head. */
+ if (control->blocks[fl][sl] == block)
+ {
+ control->blocks[fl][sl] = next;
+
+ /* If the new head is null, clear the bitmap. */
+ if (next == &control->block_null)
+ {
+ control->sl_bitmap[fl] &= ~(1U << sl);
+
+ /* If the second bitmap is now empty, clear the fl bitmap. */
+ if (!control->sl_bitmap[fl])
+ {
+ control->fl_bitmap &= ~(1U << fl);
+ }
+ }
+ }
+}
+
+/* Insert a free block into the free block list. */
+static void insert_free_block(control_t* control, block_header_t* block, int fl, int sl)
+{
+ block_header_t* current = control->blocks[fl][sl];
+ tlsf_assert(current && "free list cannot have a null entry");
+ tlsf_assert(block && "cannot insert a null entry into the free list");
+ block->next_free = current;
+ block->prev_free = &control->block_null;
+ current->prev_free = block;
+
+ tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE)
+ && "block not aligned properly");
+ /*
+ ** Insert the new block at the head of the list, and mark the first-
+ ** and second-level bitmaps appropriately.
+ */
+ control->blocks[fl][sl] = block;
+ control->fl_bitmap |= (1U << fl);
+ control->sl_bitmap[fl] |= (1U << sl);
+}
+
+/* Remove a given block from the free list. */
+static void block_remove(control_t* control, block_header_t* block)
+{
+ int fl, sl;
+ mapping_insert(block_size(block), &fl, &sl);
+ remove_free_block(control, block, fl, sl);
+}
+
+/* Insert a given block into the free list. */
+static void block_insert(control_t* control, block_header_t* block)
+{
+ int fl, sl;
+ mapping_insert(block_size(block), &fl, &sl);
+ insert_free_block(control, block, fl, sl);
+}
+
+static int block_can_split(block_header_t* block, size_t size)
+{
+ return block_size(block) >= sizeof(block_header_t) + size;
+}
+
+/* Split a block into two, the second of which is free. */
+static block_header_t* block_split(block_header_t* block, size_t size)
+{
+ /* Calculate the amount of space left in the remaining block. */
+ block_header_t* remaining =
+ offset_to_block(block_to_ptr(block), size - block_header_overhead);
+
+ const size_t remain_size = block_size(block) - (size + block_header_overhead);
+
+ tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE)
+ && "remaining block not aligned properly");
+
+ tlsf_assert(block_size(block) == remain_size + size + block_header_overhead);
+ block_set_size(remaining, remain_size);
+ tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size");
+
+ block_set_size(block, size);
+ block_mark_as_free(remaining);
+
+ return remaining;
+}
+
+/* Absorb a free block's storage into an adjacent previous free block. */
+static block_header_t* block_absorb(block_header_t* prev, block_header_t* block)
+{
+ tlsf_assert(!block_is_last(prev) && "previous block can't be last");
+ /* Note: Leaves flags untouched. */
+ prev->size += block_size(block) + block_header_overhead;
+ block_link_next(prev);
+ return prev;
+}
+
+/* Merge a just-freed block with an adjacent previous free block. */
+static block_header_t* block_merge_prev(control_t* control, block_header_t* block)
+{
+ if (block_is_prev_free(block))
+ {
+ block_header_t* prev = block_prev(block);
+ tlsf_assert(prev && "prev physical block can't be null");
+ tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such");
+ block_remove(control, prev);
+ block = block_absorb(prev, block);
+ }
+
+ return block;
+}
+
+/* Merge a just-freed block with an adjacent free block. */
+static block_header_t* block_merge_next(control_t* control, block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ tlsf_assert(next && "next physical block can't be null");
+
+ if (block_is_free(next))
+ {
+ tlsf_assert(!block_is_last(block) && "previous block can't be last");
+ block_remove(control, next);
+ block = block_absorb(block, next);
+ }
+
+ return block;
+}
+
+/* Trim any trailing block space off the end of a block, return to pool. */
+static void block_trim_free(control_t* control, block_header_t* block, size_t size)
+{
+ tlsf_assert(block_is_free(block) && "block must be free");
+ if (block_can_split(block, size))
+ {
+ block_header_t* remaining_block = block_split(block, size);
+ block_link_next(block);
+ block_set_prev_free(remaining_block);
+ block_insert(control, remaining_block);
+ }
+}
+
+/* Trim any trailing block space off the end of a used block, return to pool. */
+static void block_trim_used(control_t* control, block_header_t* block, size_t size)
+{
+ tlsf_assert(!block_is_free(block) && "block must be used");
+ if (block_can_split(block, size))
+ {
+ /* If the next block is free, we must coalesce. */
+ block_header_t* remaining_block = block_split(block, size);
+ block_set_prev_used(remaining_block);
+
+ remaining_block = block_merge_next(control, remaining_block);
+ block_insert(control, remaining_block);
+ }
+}
+
+static block_header_t* block_trim_free_leading(control_t* control, block_header_t* block, size_t size)
+{
+ block_header_t* remaining_block = block;
+ if (block_can_split(block, size))
+ {
+ /* We want the 2nd block. */
+ remaining_block = block_split(block, size - block_header_overhead);
+ block_set_prev_free(remaining_block);
+
+ block_link_next(block);
+ block_insert(control, block);
+ }
+
+ return remaining_block;
+}
+
+static block_header_t* block_locate_free(control_t* control, size_t size)
+{
+ int fl = 0, sl = 0;
+ block_header_t* block = 0;
+
+ if (size)
+ {
+ mapping_search(size, &fl, &sl);
+
+ /*
+ ** mapping_search can futz with the size, so for excessively large sizes it can sometimes wind up
+ ** with indices that are off the end of the block array.
+ ** So, we protect against that here, since this is the only callsite of mapping_search.
+ ** Note that we don't need to check sl, since it comes from a modulo operation that guarantees it's always in range.
+ */
+ if (fl < FL_INDEX_COUNT)
+ {
+ block = search_suitable_block(control, &fl, &sl);
+ }
+ }
+
+ if (block)
+ {
+ tlsf_assert(block_size(block) >= size);
+ remove_free_block(control, block, fl, sl);
+ }
+
+ return block;
+}
+
+static void* block_prepare_used(control_t* control, block_header_t* block, size_t size)
+{
+ void* p = 0;
+ if (block)
+ {
+ tlsf_assert(size && "size must be non-zero");
+ block_trim_free(control, block, size);
+ block_mark_as_used(block);
+ p = block_to_ptr(block);
+ }
+ return p;
+}
+
+/* Clear structure and point all empty lists at the null block. */
+static void control_construct(control_t* control)
+{
+ int i, j;
+
+ control->block_null.next_free = &control->block_null;
+ control->block_null.prev_free = &control->block_null;
+
+ control->fl_bitmap = 0;
+ for (i = 0; i < FL_INDEX_COUNT; ++i)
+ {
+ control->sl_bitmap[i] = 0;
+ for (j = 0; j < SL_INDEX_COUNT; ++j)
+ {
+ control->blocks[i][j] = &control->block_null;
+ }
+ }
+}
+
+/*
+** Debugging utilities.
+*/
+
+typedef struct integrity_t
+{
+ int prev_status;
+ int status;
+} integrity_t;
+
+#define tlsf_insist(x) { tlsf_assert(x); if (!(x)) { status--; } }
+
+static void integrity_walker(void* ptr, size_t size, int used, void* user)
+{
+ block_header_t* block = block_from_ptr(ptr);
+ integrity_t* integ = tlsf_cast(integrity_t*, user);
+ const int this_prev_status = block_is_prev_free(block) ? 1 : 0;
+ const int this_status = block_is_free(block) ? 1 : 0;
+ const size_t this_block_size = block_size(block);
+
+ int status = 0;
+ (void)used;
+ tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect");
+ tlsf_insist(size == this_block_size && "block size incorrect");
+
+ integ->prev_status = this_status;
+ integ->status += status;
+}
+
+int tlsf_check(tlsf_t tlsf)
+{
+ int i, j;
+
+ control_t* control = tlsf_cast(control_t*, tlsf);
+ int status = 0;
+
+ /* Check that the free lists and bitmaps are accurate. */
+ for (i = 0; i < FL_INDEX_COUNT; ++i)
+ {
+ for (j = 0; j < SL_INDEX_COUNT; ++j)
+ {
+ const int fl_map = control->fl_bitmap & (1U << i);
+ const int sl_list = control->sl_bitmap[i];
+ const int sl_map = sl_list & (1U << j);
+ const block_header_t* block = control->blocks[i][j];
+
+ /* Check that first- and second-level lists agree. */
+ if (!fl_map)
+ {
+ tlsf_insist(!sl_map && "second-level map must be null");
+ }
+
+ if (!sl_map)
+ {
+ tlsf_insist(block == &control->block_null && "block list must be null");
+ continue;
+ }
+
+ /* Check that there is at least one free block. */
+ tlsf_insist(sl_list && "no free blocks in second-level map");
+ tlsf_insist(block != &control->block_null && "block should not be null");
+
+ while (block != &control->block_null)
+ {
+ int fli, sli;
+ tlsf_insist(block_is_free(block) && "block should be free");
+ tlsf_insist(!block_is_prev_free(block) && "blocks should have coalesced");
+ tlsf_insist(!block_is_free(block_next(block)) && "blocks should have coalesced");
+ tlsf_insist(block_is_prev_free(block_next(block)) && "block should be free");
+ tlsf_insist(block_size(block) >= block_size_min && "block not minimum size");
+
+ mapping_insert(block_size(block), &fli, &sli);
+ tlsf_insist(fli == i && sli == j && "block size indexed in wrong list");
+ block = block->next_free;
+ }
+ }
+ }
+
+ return status;
+}
+
+#undef tlsf_insist
+
+static void default_walker(void* ptr, size_t size, int used, void* user)
+{
+ (void)user;
+ printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr));
+}
+
+void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user)
+{
+ tlsf_walker pool_walker = walker ? walker : default_walker;
+ block_header_t* block =
+ offset_to_block(pool, -(int)block_header_overhead);
+
+ while (block && !block_is_last(block))
+ {
+ pool_walker(
+ block_to_ptr(block),
+ block_size(block),
+ !block_is_free(block),
+ user);
+ block = block_next(block);
+ }
+}
+
+size_t tlsf_block_size(void* ptr)
+{
+ size_t size = 0;
+ if (ptr)
+ {
+ const block_header_t* block = block_from_ptr(ptr);
+ size = block_size(block);
+ }
+ return size;
+}
+
+int tlsf_check_pool(pool_t pool)
+{
+ /* Check that the blocks are physically correct. */
+ integrity_t integ = { 0, 0 };
+ tlsf_walk_pool(pool, integrity_walker, &integ);
+
+ return integ.status;
+}
+
+/*
+** Size of the TLSF structures in a given memory block passed to
+** tlsf_create, equal to the size of a control_t
+*/
+size_t tlsf_size(void)
+{
+ return sizeof(control_t);
+}
+
+size_t tlsf_align_size(void)
+{
+ return ALIGN_SIZE;
+}
+
+size_t tlsf_block_size_min(void)
+{
+ return block_size_min;
+}
+
+size_t tlsf_block_size_max(void)
+{
+ return block_size_max;
+}
+
+/*
+** Overhead of the TLSF structures in a given memory block passed to
+** tlsf_add_pool, equal to the overhead of a free block and the
+** sentinel block.
+*/
+size_t tlsf_pool_overhead(void)
+{
+ return 2 * block_header_overhead;
+}
+
+size_t tlsf_alloc_overhead(void)
+{
+ return block_header_overhead;
+}
+
+pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes)
+{
+ block_header_t* block;
+ block_header_t* next;
+
+ const size_t pool_overhead = tlsf_pool_overhead();
+ const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE);
+
+ if (((ptrdiff_t)mem % ALIGN_SIZE) != 0)
+ {
+ printf("tlsf_add_pool: Memory must be aligned by %u bytes.\n",
+ (unsigned int)ALIGN_SIZE);
+ return 0;
+ }
+
+ if (pool_bytes < block_size_min || pool_bytes > block_size_max)
+ {
+#if defined (TLSF_64BIT)
+ printf("tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min),
+ (unsigned int)((pool_overhead + block_size_max) / 256));
+#else
+ printf("tlsf_add_pool: Memory size must be between %u and %u bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min),
+ (unsigned int)(pool_overhead + block_size_max));
+#endif
+ return 0;
+ }
+
+ /*
+ ** Create the main free block. Offset the start of the block slightly
+ ** so that the prev_phys_block field falls outside of the pool -
+ ** it will never be used.
+ */
+ block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead);
+ block_set_size(block, pool_bytes);
+ block_set_free(block);
+ block_set_prev_used(block);
+ block_insert(tlsf_cast(control_t*, tlsf), block);
+
+ /* Split the block to create a zero-size sentinel block. */
+ next = block_link_next(block);
+ block_set_size(next, 0);
+ block_set_used(next);
+ block_set_prev_free(next);
+
+ return mem;
+}
+
+void tlsf_remove_pool(tlsf_t tlsf, pool_t pool)
+{
+ control_t* control = tlsf_cast(control_t*, tlsf);
+ block_header_t* block = offset_to_block(pool, -(int)block_header_overhead);
+
+ int fl = 0, sl = 0;
+
+ tlsf_assert(block_is_free(block) && "block should be free");
+ tlsf_assert(!block_is_free(block_next(block)) && "next block should not be free");
+ tlsf_assert(block_size(block_next(block)) == 0 && "next block size should be zero");
+
+ mapping_insert(block_size(block), &fl, &sl);
+ remove_free_block(control, block, fl, sl);
+}
+
+/*
+** TLSF main interface.
+*/
+
+#if _DEBUG
+int test_ffs_fls()
+{
+ /* Verify ffs/fls work properly. */
+ int rv = 0;
+ rv += (tlsf_ffs(0) == -1) ? 0 : 0x1;
+ rv += (tlsf_fls(0) == -1) ? 0 : 0x2;
+ rv += (tlsf_ffs(1) == 0) ? 0 : 0x4;
+ rv += (tlsf_fls(1) == 0) ? 0 : 0x8;
+ rv += (tlsf_ffs(0x80000000) == 31) ? 0 : 0x10;
+ rv += (tlsf_ffs(0x80008000) == 15) ? 0 : 0x20;
+ rv += (tlsf_fls(0x80000008) == 31) ? 0 : 0x40;
+ rv += (tlsf_fls(0x7FFFFFFF) == 30) ? 0 : 0x80;
+
+#if defined (TLSF_64BIT)
+ rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100;
+ rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200;
+ rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400;
+#endif
+
+ if (rv)
+ {
+ printf("test_ffs_fls: %x ffs/fls tests failed.\n", rv);
+ }
+ return rv;
+}
+#endif
+
+tlsf_t tlsf_create(void* mem)
+{
+#if _DEBUG
+ if (test_ffs_fls())
+ {
+ return 0;
+ }
+#endif
+
+ if (((tlsfptr_t)mem % ALIGN_SIZE) != 0)
+ {
+ printf("tlsf_create: Memory must be aligned to %u bytes.\n",
+ (unsigned int)ALIGN_SIZE);
+ return 0;
+ }
+
+ control_construct(tlsf_cast(control_t*, mem));
+
+ return tlsf_cast(tlsf_t, mem);
+}
+
+tlsf_t tlsf_create_with_pool(void* mem, size_t bytes)
+{
+ tlsf_t tlsf = tlsf_create(mem);
+ tlsf_add_pool(tlsf, (char*)mem + tlsf_size(), bytes - tlsf_size());
+ return tlsf;
+}
+
+void tlsf_destroy(tlsf_t tlsf)
+{
+ /* Nothing to do. */
+ (void)tlsf;
+}
+
+pool_t tlsf_get_pool(tlsf_t tlsf)
+{
+ return tlsf_cast(pool_t, (char*)tlsf + tlsf_size());
+}
+
+void* tlsf_malloc(tlsf_t tlsf, size_t size)
+{
+ control_t* control = tlsf_cast(control_t*, tlsf);
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+ block_header_t* block = block_locate_free(control, adjust);
+ return block_prepare_used(control, block, adjust);
+}
+
+void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size)
+{
+ control_t* control = tlsf_cast(control_t*, tlsf);
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+
+ /*
+ ** We must allocate an additional minimum block size bytes so that if
+ ** our free block will leave an alignment gap which is smaller, we can
+ ** trim a leading free block and release it back to the pool. We must
+ ** do this because the previous physical block is in use, therefore
+ ** the prev_phys_block field is not valid, and we can't simply adjust
+ ** the size of that block.
+ */
+ const size_t gap_minimum = sizeof(block_header_t);
+ const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align);
+
+ /*
+ ** If alignment is less than or equals base alignment, we're done.
+ ** If we requested 0 bytes, return null, as tlsf_malloc(0) does.
+ */
+ const size_t aligned_size = (adjust && align > ALIGN_SIZE) ? size_with_gap : adjust;
+
+ block_header_t* block = block_locate_free(control, aligned_size);
+
+ /* This can't be a static assert. */
+ tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead);
+
+ if (block)
+ {
+ void* ptr = block_to_ptr(block);
+ void* aligned = align_ptr(ptr, align);
+ size_t gap = tlsf_cast(size_t,
+ tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
+
+ /* If gap size is too small, offset to next aligned boundary. */
+ if (gap && gap < gap_minimum)
+ {
+ const size_t gap_remain = gap_minimum - gap;
+ const size_t offset = tlsf_max(gap_remain, align);
+ const void* next_aligned = tlsf_cast(void*,
+ tlsf_cast(tlsfptr_t, aligned) + offset);
+
+ aligned = align_ptr(next_aligned, align);
+ gap = tlsf_cast(size_t,
+ tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
+ }
+
+ if (gap)
+ {
+ tlsf_assert(gap >= gap_minimum && "gap size too small");
+ block = block_trim_free_leading(control, block, gap);
+ }
+ }
+
+ return block_prepare_used(control, block, adjust);
+}
+
+void tlsf_free(tlsf_t tlsf, void* ptr)
+{
+ /* Don't attempt to free a NULL pointer. */
+ if (ptr)
+ {
+ control_t* control = tlsf_cast(control_t*, tlsf);
+ block_header_t* block = block_from_ptr(ptr);
+ tlsf_assert(!block_is_free(block) && "block already marked as free");
+ block_mark_as_free(block);
+ block = block_merge_prev(control, block);
+ block = block_merge_next(control, block);
+ block_insert(control, block);
+ }
+}
+
+/*
+** The TLSF block information provides us with enough information to
+** provide a reasonably intelligent implementation of realloc, growing or
+** shrinking the currently allocated block as required.
+**
+** This routine handles the somewhat esoteric edge cases of realloc:
+** - a non-zero size with a null pointer will behave like malloc
+** - a zero size with a non-null pointer will behave like free
+** - a request that cannot be satisfied will leave the original buffer
+** untouched
+** - an extended buffer size will leave the newly-allocated area with
+** contents undefined
+*/
+void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size)
+{
+ control_t* control = tlsf_cast(control_t*, tlsf);
+ void* p = 0;
+
+ /* Zero-size requests are treated as free. */
+ if (ptr && size == 0)
+ {
+ tlsf_free(tlsf, ptr);
+ }
+ /* Requests with NULL pointers are treated as malloc. */
+ else if (!ptr)
+ {
+ p = tlsf_malloc(tlsf, size);
+ }
+ else
+ {
+ block_header_t* block = block_from_ptr(ptr);
+ block_header_t* next = block_next(block);
+
+ const size_t cursize = block_size(block);
+ const size_t combined = cursize + block_size(next) + block_header_overhead;
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+
+ tlsf_assert(!block_is_free(block) && "block already marked as free");
+
+ /*
+ ** If the next block is used, or when combined with the current
+ ** block, does not offer enough space, we must reallocate and copy.
+ */
+ if (adjust > cursize && (!block_is_free(next) || adjust > combined))
+ {
+ p = tlsf_malloc(tlsf, size);
+ if (p)
+ {
+ const size_t minsize = tlsf_min(cursize, size);
+ memcpy(p, ptr, minsize);
+ tlsf_free(tlsf, ptr);
+ }
+ }
+ else
+ {
+ /* Do we need to expand to the next block? */
+ if (adjust > cursize)
+ {
+ block_merge_next(control, block);
+ block_mark_as_used(block);
+ }
+
+ /* Trim the resulting block and return the original pointer. */
+ block_trim_used(control, block, adjust);
+ p = ptr;
+ }
+ }
+
+ return p;
+}
\ No newline at end of file
diff --git a/miniwin/src/d3drm/backends/gxm/tlsf.h b/miniwin/src/d3drm/backends/gxm/tlsf.h
new file mode 100644
index 00000000..9f890175
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/tlsf.h
@@ -0,0 +1,91 @@
+#ifndef INCLUDED_tlsf
+#define INCLUDED_tlsf
+
+/*
+** Two Level Segregated Fit memory allocator, version 3.1.
+** Written by Matthew Conte
+** http://tlsf.baisoku.org
+**
+** Based on the original documentation by Miguel Masmano:
+** http://www.gii.upv.es/tlsf/main/docs
+**
+** This implementation was written to the specification
+** of the document, therefore no GPL restrictions apply.
+**
+** Copyright (c) 2006-2016, Matthew Conte
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of the copyright holder nor the
+** names of its contributors may be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY
+** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+ /* tlsf_t: a TLSF structure. Can contain 1 to N pools. */
+ /* pool_t: a block of memory that TLSF can manage. */
+ typedef void* tlsf_t;
+ typedef void* pool_t;
+
+ /* Create/destroy a memory pool. */
+ tlsf_t tlsf_create(void* mem);
+ tlsf_t tlsf_create_with_pool(void* mem, size_t bytes);
+ void tlsf_destroy(tlsf_t tlsf);
+ pool_t tlsf_get_pool(tlsf_t tlsf);
+
+ /* Add/remove memory pools. */
+ pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes);
+ void tlsf_remove_pool(tlsf_t tlsf, pool_t pool);
+
+ /* malloc/memalign/realloc/free replacements. */
+ void* tlsf_malloc(tlsf_t tlsf, size_t bytes);
+ void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t bytes);
+ void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size);
+ void tlsf_free(tlsf_t tlsf, void* ptr);
+
+ /* Returns internal block size, not original request size */
+ size_t tlsf_block_size(void* ptr);
+
+ /* Overheads/limits of internal structures. */
+ size_t tlsf_size(void);
+ size_t tlsf_align_size(void);
+ size_t tlsf_block_size_min(void);
+ size_t tlsf_block_size_max(void);
+ size_t tlsf_pool_overhead(void);
+ size_t tlsf_alloc_overhead(void);
+
+ /* Debugging. */
+ typedef void (*tlsf_walker)(void* ptr, size_t size, int used, void* user);
+ void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user);
+ /* Returns nonzero if any internal consistency check fails. */
+ int tlsf_check(tlsf_t tlsf);
+ int tlsf_check_pool(pool_t pool);
+
+#if defined(__cplusplus)
+};
+#endif
+
+#endif
diff --git a/miniwin/src/d3drm/backends/gxm/utils.h b/miniwin/src/d3drm/backends/gxm/utils.h
new file mode 100644
index 00000000..18fe5dc1
--- /dev/null
+++ b/miniwin/src/d3drm/backends/gxm/utils.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include
+#include
+#include
+
+#ifdef DEBUG
+#define DEBUG_ONLY_PRINTF(...) sceClibPrintf(__VA_ARGS__)
+#else
+#define DEBUG_ONLY_PRINTF(...)
+#endif
+
+#define SCE_ERR(func, ...) \
+ ({ \
+ DEBUG_ONLY_PRINTF("%s\n", #func); \
+ int __sce_err_ret_val = func(__VA_ARGS__); \
+ if (__sce_err_ret_val < 0) { \
+ sceClibPrintf(#func " error: 0x%x\n", __sce_err_ret_val); \
+ } \
+ __sce_err_ret_val; \
+ })
+
+#define ALIGN(x, a) (((x) + ((a) -1)) & ~((a) -1))
+
+#define ALIGNMENT(n, a) (((a) - ((n) % (a))) % (a))
+
+#define SET_UNIFORM(buffer, param, value) \
+ do { \
+ size_t __offset = sceGxmProgramParameterGetResourceIndex(param); \
+ void* __dst = (uint8_t*) (buffer) + (__offset * sizeof(uint32_t)); \
+ memcpy(__dst, reinterpret_cast(&(value)), sizeof(value)); \
+ } while (0)
+
+#define GET_SHADER_PARAM(var, gxp, name, ret) \
+ const SceGxmProgramParameter* var = sceGxmProgramFindParameterByName(gxp, name); \
+ if (!var) { \
+ SDL_Log("Failed to find param %s", name); \
+ return ret; \
+ }
+
+static void printMatrix4x4(const float mat[4][4])
+{
+ sceClibPrintf("mat4{\n");
+ for (int i = 0; i < 4; i++) {
+ sceClibPrintf("%f %f %f %f\n", mat[i][0], mat[i][1], mat[i][2], mat[i][3]);
+ }
+ sceClibPrintf("}\n");
+}
diff --git a/miniwin/src/d3drm/d3drmrenderer.cpp b/miniwin/src/d3drm/d3drmrenderer.cpp
index f1e1fb8b..b30c5b23 100644
--- a/miniwin/src/d3drm/d3drmrenderer.cpp
+++ b/miniwin/src/d3drm/d3drmrenderer.cpp
@@ -20,6 +20,9 @@
#ifdef USE_SOFTWARE_RENDER
#include "d3drmrenderer_software.h"
#endif
+#ifdef USE_GXM
+#include "d3drmrenderer_gxm.h"
+#endif
Direct3DRMRenderer* CreateDirect3DRMRenderer(
const IDirect3DMiniwin* d3d,
@@ -66,6 +69,11 @@ Direct3DRMRenderer* CreateDirect3DRMRenderer(
if (SDL_memcmp(guid, &DirectX9_GUID, sizeof(GUID)) == 0) {
return DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
}
+#endif
+#ifdef USE_GXM
+ if (SDL_memcmp(guid, &GXM_GUID, sizeof(GUID)) == 0) {
+ return GXMRenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight, d3d->GetMSAASamples());
+ }
#endif
return nullptr;
}
@@ -93,4 +101,7 @@ void Direct3DRMRenderer_EnumDevices(const IDirect3DMiniwin* d3d, LPD3DENUMDEVICE
#ifdef USE_SOFTWARE_RENDER
Direct3DRMSoftware_EnumDevice(cb, ctx);
#endif
+#ifdef USE_GXM
+ GXMRenderer_EnumDevice(cb, ctx);
+#endif
}
diff --git a/miniwin/src/internal/d3drmrenderer_gxm.h b/miniwin/src/internal/d3drmrenderer_gxm.h
new file mode 100644
index 00000000..1a8fe16c
--- /dev/null
+++ b/miniwin/src/internal/d3drmrenderer_gxm.h
@@ -0,0 +1,181 @@
+#pragma once
+
+#include "../d3drm/backends/gxm/gxm_context.h"
+#include "d3drmrenderer.h"
+#include "d3drmtexture_impl.h"
+#include "ddpalette_impl.h"
+#include "ddraw_impl.h"
+
+#include
+#include
+#include
+#include
+#include
+
+DEFINE_GUID(GXM_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x58, 0x4D);
+
+#define GXM_VERTEX_BUFFER_COUNT 2
+#define GXM_FRAGMENT_BUFFER_COUNT 3
+
+#define GXM_WITH_RAZOR DEBUG
+
+struct GXMTextureCacheEntry {
+ IDirect3DRMTexture* texture;
+ Uint32 version;
+ SceGxmTexture gxmTexture;
+ SceGxmNotification* notification; // latest frame it was used in
+};
+
+struct GXMMeshCacheEntry {
+ const MeshGroup* meshGroup;
+ int version;
+ bool flat;
+
+ void* meshData;
+ void* vertexBuffer;
+ void* indexBuffer;
+ uint16_t indexCount;
+};
+
+struct SceneLightGXM {
+ float color[4];
+ float vec[4];
+ float isDirectional;
+ float _align;
+};
+
+struct GXMSceneLightUniform {
+ SceneLightGXM lights[2];
+ float ambientLight[3];
+};
+
+class GXMRenderer : public Direct3DRMRenderer {
+public:
+ static Direct3DRMRenderer* Create(DWORD width, DWORD height, DWORD msaaSamples);
+ GXMRenderer(DWORD width, DWORD height, SceGxmMultisampleMode msaaMode);
+ ~GXMRenderer() override;
+
+ void PushLights(const SceneLight* lightsArray, size_t count) override;
+ void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override;
+ void SetFrustumPlanes(const Plane* frustumPlanes) override;
+ Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi, float scaleX, float scaleY) override;
+ Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override;
+ HRESULT BeginFrame() override;
+ void EnableTransparency() override;
+ void SubmitDraw(
+ DWORD meshId,
+ const D3DRMMATRIX4D& modelViewMatrix,
+ const D3DRMMATRIX4D& worldMatrix,
+ const D3DRMMATRIX4D& viewMatrix,
+ const Matrix3x3& normalMatrix,
+ const Appearance& appearance
+ ) override;
+ HRESULT FinalizeFrame() override;
+ void Resize(int width, int height, const ViewportTransform& viewportTransform) override;
+ void Clear(float r, float g, float b) override;
+ void Flip() override;
+ void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect, FColor color) override;
+ void Download(SDL_Surface* target) override;
+ void SetDither(bool dither) override;
+
+ void DeferredDelete(int index);
+
+private:
+ void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture);
+ void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh);
+
+ GXMMeshCacheEntry GXMUploadMesh(const MeshGroup& meshGroup);
+
+ void StartScene();
+ const SceGxmTexture* UseTexture(GXMTextureCacheEntry& texture);
+
+ inline GXMVertex2D* QuadVerticesBuffer()
+ {
+ GXMVertex2D* verts = &this->quadVertices[this->currentVertexBufferIndex][this->quadsUsed * 4];
+ this->quadsUsed += 1;
+ if (this->quadsUsed >= 50) {
+ SDL_Log("QuadVerticesBuffer overflow");
+ this->quadsUsed = 0; // declare bankruptcy
+ }
+ return verts;
+ }
+
+ inline GXMSceneLightUniform* LightsBuffer() { return this->lights[this->currentFragmentBufferIndex]; }
+
+ std::vector m_textures;
+ std::vector m_meshes;
+ D3DRMMATRIX4D m_projection;
+ std::vector m_lights;
+ std::vector m_textures_delete[GXM_FRAGMENT_BUFFER_COUNT];
+ std::vector m_buffers_delete[GXM_FRAGMENT_BUFFER_COUNT];
+
+ bool transparencyEnabled = false;
+
+ // shader
+ SceGxmShaderPatcherId mainVertexProgramId;
+ SceGxmShaderPatcherId mainColorFragmentProgramId;
+ SceGxmShaderPatcherId mainTextureFragmentProgramId;
+
+ SceGxmVertexProgram* mainVertexProgram;
+ SceGxmFragmentProgram* opaqueColorFragmentProgram;
+ SceGxmFragmentProgram* blendedColorFragmentProgram;
+ SceGxmFragmentProgram* opaqueTextureFragmentProgram;
+ SceGxmFragmentProgram* blendedTextureFragmentProgram;
+
+ // main shader vertex uniforms
+ const SceGxmProgramParameter* uModelViewMatrix;
+ const SceGxmProgramParameter* uNormalMatrix;
+ const SceGxmProgramParameter* uProjectionMatrix;
+
+ // main shader fragment uniforms
+ const SceGxmProgramParameter* uShininess;
+ const SceGxmProgramParameter* uColor;
+ const SceGxmProgramParameter* uLights;
+ const SceGxmProgramParameter* uAmbientLight;
+
+ // uniforms / quad meshes
+ GXMSceneLightUniform* lights[GXM_FRAGMENT_BUFFER_COUNT];
+ GXMVertex2D* quadVertices[GXM_VERTEX_BUFFER_COUNT];
+ uint16_t* quadIndices;
+ int quadsUsed = 0;
+ bool cleared = false;
+
+ SceGxmNotification vertexNotifications[GXM_VERTEX_BUFFER_COUNT];
+ SceGxmNotification fragmentNotifications[GXM_FRAGMENT_BUFFER_COUNT];
+ int currentFragmentBufferIndex = 0;
+ int currentVertexBufferIndex = 0;
+
+#ifdef GXM_WITH_RAZOR
+ bool last_dpad_up;
+ bool last_dpad_down;
+ bool last_dpad_left;
+ bool last_dpad_right;
+#endif
+
+ bool m_initialized = false;
+};
+
+int gxm_library_init();
+
+inline static void GXMRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
+{
+ D3DDEVICEDESC halDesc = {};
+ halDesc.dcmColorModel = D3DCOLORMODEL::RGB;
+ halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH;
+ halDesc.dwDeviceZBufferBitDepth = DDBD_16;
+ halDesc.dwDeviceZBufferBitDepth |= DDBD_32;
+ halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE;
+ halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND;
+ halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR;
+
+ D3DDEVICEDESC helDesc = {};
+ helDesc.dwDeviceRenderBitDepth = DDBD_32;
+
+ int ret = gxm_library_init();
+ if (ret < 0) {
+ SDL_Log("gxm_library_init failed: %08x", ret);
+ return;
+ }
+
+ EnumDevice(cb, ctx, "GXM HAL", &halDesc, &helDesc, GXM_GUID);
+}
diff --git a/packaging/vita/sce_sys/icon0.png b/packaging/vita/sce_sys/icon0.png
new file mode 100644
index 00000000..fe68bd7c
Binary files /dev/null and b/packaging/vita/sce_sys/icon0.png differ
diff --git a/packaging/vita/sce_sys/livearea/contents/bg.png b/packaging/vita/sce_sys/livearea/contents/bg.png
new file mode 100644
index 00000000..5e742ac3
Binary files /dev/null and b/packaging/vita/sce_sys/livearea/contents/bg.png differ
diff --git a/packaging/vita/sce_sys/livearea/contents/configure.png b/packaging/vita/sce_sys/livearea/contents/configure.png
new file mode 100644
index 00000000..df07b590
Binary files /dev/null and b/packaging/vita/sce_sys/livearea/contents/configure.png differ
diff --git a/packaging/vita/sce_sys/livearea/contents/gate.png b/packaging/vita/sce_sys/livearea/contents/gate.png
new file mode 100644
index 00000000..a7ce0290
Binary files /dev/null and b/packaging/vita/sce_sys/livearea/contents/gate.png differ
diff --git a/packaging/vita/sce_sys/livearea/contents/logo.png b/packaging/vita/sce_sys/livearea/contents/logo.png
new file mode 100644
index 00000000..cf942db5
Binary files /dev/null and b/packaging/vita/sce_sys/livearea/contents/logo.png differ
diff --git a/packaging/vita/sce_sys/livearea/contents/template.xml b/packaging/vita/sce_sys/livearea/contents/template.xml
new file mode 100644
index 00000000..f1c36678
--- /dev/null
+++ b/packaging/vita/sce_sys/livearea/contents/template.xml
@@ -0,0 +1,23 @@
+
+
+
+ bg.png
+
+
+ gate.png
+
+
+
+ logo.png
+
+
+
+
+ psla:-config
+ configure.png
+
+ Configure
+
+
+
+
\ No newline at end of file
diff --git a/packaging/vita/sce_sys/manual/001.png b/packaging/vita/sce_sys/manual/001.png
new file mode 100644
index 00000000..5bfb0923
Binary files /dev/null and b/packaging/vita/sce_sys/manual/001.png differ
diff --git a/packaging/vita/sce_sys/manual/002.png b/packaging/vita/sce_sys/manual/002.png
new file mode 100644
index 00000000..8323ba95
Binary files /dev/null and b/packaging/vita/sce_sys/manual/002.png differ
diff --git a/packaging/vita/sce_sys/manual/003.png b/packaging/vita/sce_sys/manual/003.png
new file mode 100644
index 00000000..6948e4cb
Binary files /dev/null and b/packaging/vita/sce_sys/manual/003.png differ
diff --git a/packaging/vita/sce_sys/manual/004.png b/packaging/vita/sce_sys/manual/004.png
new file mode 100644
index 00000000..05ba6671
Binary files /dev/null and b/packaging/vita/sce_sys/manual/004.png differ
diff --git a/packaging/vita/sce_sys/manual/005.png b/packaging/vita/sce_sys/manual/005.png
new file mode 100644
index 00000000..e93792d2
Binary files /dev/null and b/packaging/vita/sce_sys/manual/005.png differ
diff --git a/packaging/vita/sce_sys/manual/006.png b/packaging/vita/sce_sys/manual/006.png
new file mode 100644
index 00000000..77c537f3
Binary files /dev/null and b/packaging/vita/sce_sys/manual/006.png differ
diff --git a/packaging/vita/sce_sys/manual/007.png b/packaging/vita/sce_sys/manual/007.png
new file mode 100644
index 00000000..89e2ba2c
Binary files /dev/null and b/packaging/vita/sce_sys/manual/007.png differ
diff --git a/packaging/vita/sce_sys/manual/008.png b/packaging/vita/sce_sys/manual/008.png
new file mode 100644
index 00000000..cc72e8b3
Binary files /dev/null and b/packaging/vita/sce_sys/manual/008.png differ
diff --git a/packaging/vita/sce_sys/manual/009.png b/packaging/vita/sce_sys/manual/009.png
new file mode 100644
index 00000000..4043bb04
Binary files /dev/null and b/packaging/vita/sce_sys/manual/009.png differ
diff --git a/packaging/vita/sce_sys/manual/010.png b/packaging/vita/sce_sys/manual/010.png
new file mode 100644
index 00000000..4118af1b
Binary files /dev/null and b/packaging/vita/sce_sys/manual/010.png differ
diff --git a/packaging/vita/sce_sys/manual/011.png b/packaging/vita/sce_sys/manual/011.png
new file mode 100644
index 00000000..7f5b86b1
Binary files /dev/null and b/packaging/vita/sce_sys/manual/011.png differ
diff --git a/packaging/vita/sce_sys/manual/012.png b/packaging/vita/sce_sys/manual/012.png
new file mode 100644
index 00000000..33a134ac
Binary files /dev/null and b/packaging/vita/sce_sys/manual/012.png differ
diff --git a/packaging/vita/sce_sys/manual/013.png b/packaging/vita/sce_sys/manual/013.png
new file mode 100644
index 00000000..3ac2e8e7
Binary files /dev/null and b/packaging/vita/sce_sys/manual/013.png differ
diff --git a/packaging/vita/sce_sys/manual/014.png b/packaging/vita/sce_sys/manual/014.png
new file mode 100644
index 00000000..ff254bf6
Binary files /dev/null and b/packaging/vita/sce_sys/manual/014.png differ
diff --git a/packaging/vita/sce_sys/manual/015.png b/packaging/vita/sce_sys/manual/015.png
new file mode 100644
index 00000000..f6b7a304
Binary files /dev/null and b/packaging/vita/sce_sys/manual/015.png differ
diff --git a/packaging/vita/sce_sys/manual/016.png b/packaging/vita/sce_sys/manual/016.png
new file mode 100644
index 00000000..6dce107c
Binary files /dev/null and b/packaging/vita/sce_sys/manual/016.png differ
diff --git a/packaging/vita/sce_sys/manual/017.png b/packaging/vita/sce_sys/manual/017.png
new file mode 100644
index 00000000..f571e9ba
Binary files /dev/null and b/packaging/vita/sce_sys/manual/017.png differ
diff --git a/packaging/vita/sce_sys/manual/018.png b/packaging/vita/sce_sys/manual/018.png
new file mode 100644
index 00000000..29d5910b
Binary files /dev/null and b/packaging/vita/sce_sys/manual/018.png differ
diff --git a/packaging/vita/sce_sys/manual/019.png b/packaging/vita/sce_sys/manual/019.png
new file mode 100644
index 00000000..644fa94b
Binary files /dev/null and b/packaging/vita/sce_sys/manual/019.png differ
diff --git a/packaging/vita/sce_sys/manual/020.png b/packaging/vita/sce_sys/manual/020.png
new file mode 100644
index 00000000..b78c121c
Binary files /dev/null and b/packaging/vita/sce_sys/manual/020.png differ
diff --git a/packaging/vita/sce_sys/manual/021.png b/packaging/vita/sce_sys/manual/021.png
new file mode 100644
index 00000000..87d5e451
Binary files /dev/null and b/packaging/vita/sce_sys/manual/021.png differ
diff --git a/packaging/vita/sce_sys/manual/022.png b/packaging/vita/sce_sys/manual/022.png
new file mode 100644
index 00000000..4d0e10f2
Binary files /dev/null and b/packaging/vita/sce_sys/manual/022.png differ
diff --git a/packaging/vita/sce_sys/manual/023.png b/packaging/vita/sce_sys/manual/023.png
new file mode 100644
index 00000000..127be7bf
Binary files /dev/null and b/packaging/vita/sce_sys/manual/023.png differ
diff --git a/packaging/vita/sce_sys/manual/024.png b/packaging/vita/sce_sys/manual/024.png
new file mode 100644
index 00000000..b43c2b0d
Binary files /dev/null and b/packaging/vita/sce_sys/manual/024.png differ
diff --git a/packaging/vita/sce_sys/manual/025.png b/packaging/vita/sce_sys/manual/025.png
new file mode 100644
index 00000000..53bb020a
Binary files /dev/null and b/packaging/vita/sce_sys/manual/025.png differ
diff --git a/packaging/vita/sce_sys/manual/026.png b/packaging/vita/sce_sys/manual/026.png
new file mode 100644
index 00000000..391b725c
Binary files /dev/null and b/packaging/vita/sce_sys/manual/026.png differ
diff --git a/packaging/vita/sce_sys/manual/027.png b/packaging/vita/sce_sys/manual/027.png
new file mode 100644
index 00000000..1ca2d096
Binary files /dev/null and b/packaging/vita/sce_sys/manual/027.png differ
diff --git a/packaging/vita/sce_sys/manual/028.png b/packaging/vita/sce_sys/manual/028.png
new file mode 100644
index 00000000..fd1fe0b5
Binary files /dev/null and b/packaging/vita/sce_sys/manual/028.png differ
diff --git a/packaging/vita/sce_sys/pic0.png b/packaging/vita/sce_sys/pic0.png
new file mode 100644
index 00000000..bc699fbf
Binary files /dev/null and b/packaging/vita/sce_sys/pic0.png differ
diff --git a/util/compat.h b/util/compat.h
index e3b2aca9..77a1f392 100644
--- a/util/compat.h
+++ b/util/compat.h
@@ -21,6 +21,8 @@
// See: https://github.com/libsdl-org/SDL/issues/12943
#ifdef __EMSCRIPTEN__
#define Any_ShowSimpleMessageBox Emscripten_ShowSimpleMessageBox
+#elif defined(__vita__)
+#define Any_ShowSimpleMessageBox Vita_ShowSimpleMessageBox
#else
#define Any_ShowSimpleMessageBox SDL_ShowSimpleMessageBox
#endif