This commit is contained in:
olebeck 2025-09-26 19:19:49 +02:00
parent 8f9f7d55fe
commit 214f4f8584
41 changed files with 1561 additions and 3329 deletions

View File

@ -48,7 +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: '--preset vita'}
- { 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 }}
@ -122,7 +122,7 @@ jobs:
export VITASDK=/usr/local/vitasdk
export PATH=$VITASDK/bin:$PATH
echo "VITASDK=/usr/local/vitasdk" >> $GITHUB_ENV
echo "PATH=$VITASDK/bin:$PATH" >> $GITHUB_ENV
echo "$VITASDK/bin" >> $GITHUB_PATH
./install-all.sh
- uses: actions/checkout@v4

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR)
project(isle LANGUAGES CXX C VERSION 0.1)
project(isle LANGUAGES CXX C ASM VERSION 0.1)
if (IOS)
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
@ -903,7 +903,6 @@ if(VITA)
set(VPK_FILE_ARGS "")
file(GLOB_RECURSE SCE_SYS_FILES packaging/vita/sce_sys/*)
file(GLOB_RECURSE SCE_MODULE_FILES packaging/vita/sce_module/*)
foreach(FILE ${SCE_SYS_FILES} ${SCE_MODULE_FILES})
file(RELATIVE_PATH REL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/packaging/vita/ ${FILE})
list(APPEND VPK_FILE_ARGS "FILE")

View File

@ -1,50 +0,0 @@
{
"version": 5,
"cmakeMinimumRequired": {
"major": 3,
"minor": 22,
"patch": 0
},
"configurePresets": [
{
"name": "base",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"hidden": true
},
{
"name": "release",
"inherits": ["base"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "debug",
"inherits": ["base"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "mingw64",
"inherits": ["release"],
"toolchainFile": "CMake/cmake-toolchain-mingw64-x86_64.cmake"
},
{
"name": "mingw64-debug",
"inherits": ["debug", "mingw64"]
},
{
"name": "vita",
"inherits": ["release"],
"toolchainFile": "$env{VITASDK}/share/vita.toolchain.cmake"
},
{
"name": "vita-debug",
"inherits": ["debug", "vita"]
}
]
}

View File

@ -13,31 +13,24 @@ FetchContent_Declare(
)
FetchContent_MakeAvailable(ScePaf_External)
add_library(iniparser_paf
iniparser_paf/src/dictionary.c
iniparser_paf/src/iniparser.c
)
target_compile_options(iniparser_paf PRIVATE -O3)
target_link_libraries(iniparser_paf
ScePafStdc_stub
SceLibKernel_stub
SceLibc_stub
SceFios2_stub
)
target_include_directories(iniparser_paf PUBLIC
iniparser_paf/src/
)
target_compile_options(iniparser_paf PRIVATE
-Wl,-q -Wall -fno-builtin -fshort-wchar -Wno-unused-function -Wno-sign-compare
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
src/main.cpp
)
set_target_properties(isle-config PROPERTIES
@ -66,7 +59,7 @@ target_link_libraries(isle-config PRIVATE
SceFios2_stub
SceLibc_stub
iniparser_paf
iniparser_paf-static
)
vita_create_self(isle-config.self isle-config

View File

@ -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 <inttypes.h>
#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)

View File

@ -1,381 +0,0 @@
/*-------------------------------------------------------------------------*/
/**
@file dictionary.c
@author N. Devillard
@brief Implements a dictionary for string variables.
This module implements a simple dictionary object, i.e. a list
of string/string associations. This object is useful to store e.g.
informations retrieved from a configuration file (ini files).
*/
/*--------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
Includes
---------------------------------------------------------------------------*/
#include "dictionary.h"
#include "pafstd.h"
/** Minimal allocated number of entries in a dictionary */
#define DICTMINSZ 128
/*---------------------------------------------------------------------------
Private functions
---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
@brief Duplicate a string
@param s String to duplicate
@return Pointer to a newly allocated string, to be freed with free()
This is a replacement for strdup(). This implementation is provided
for systems that do not have it.
*/
/*--------------------------------------------------------------------------*/
static char * xstrdup(const char * s)
{
char * t ;
size_t len ;
if (!s)
return NULL ;
len = strlen(s) + 1 ;
t = (char*) malloc(len) ;
if (t) {
memcpy(t, s, len) ;
}
return t ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Double the size of the dictionary
@param d Dictionary to grow
@return This function returns non-zero in case of failure
*/
/*--------------------------------------------------------------------------*/
static int dictionary_grow(dictionary * d)
{
char ** new_val ;
char ** new_key ;
unsigned * new_hash ;
new_val = (char**) calloc(d->size * 2, sizeof *d->val);
new_key = (char**) calloc(d->size * 2, sizeof *d->key);
new_hash = (unsigned*) calloc(d->size * 2, sizeof *d->hash);
if (!new_val || !new_key || !new_hash) {
/* An allocation failed, leave the dictionary unchanged */
if (new_val)
free(new_val);
if (new_key)
free(new_key);
if (new_hash)
free(new_hash);
return -1 ;
}
/* Initialize the newly allocated space */
memcpy(new_val, d->val, d->size * sizeof(char *));
memcpy(new_key, d->key, d->size * sizeof(char *));
memcpy(new_hash, d->hash, d->size * sizeof(unsigned));
/* Delete previous data */
free(d->val);
free(d->key);
free(d->hash);
/* Actually update the dictionary */
d->size *= 2 ;
d->val = new_val;
d->key = new_key;
d->hash = new_hash;
return 0 ;
}
/*---------------------------------------------------------------------------
Function codes
---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
@brief Compute the hash key for a string.
@param key Character string to use for key.
@return 1 unsigned int on at least 32 bits.
This hash function has been taken from an Article in Dr Dobbs Journal.
This is normally a collision-free function, distributing keys evenly.
The key is stored anyway in the struct so that collision can be avoided
by comparing the key itself in last resort.
*/
/*--------------------------------------------------------------------------*/
unsigned dictionary_hash(const char * key)
{
size_t len ;
unsigned hash ;
size_t i ;
if (!key)
return 0 ;
len = strlen(key);
for (hash=0, i=0 ; i<len ; i++) {
hash += (unsigned)key[i] ;
hash += (hash<<10);
hash ^= (hash>>6) ;
}
hash += (hash <<3);
hash ^= (hash >>11);
hash += (hash <<15);
return hash ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Create a new dictionary object.
@param size Optional initial size of the dictionary.
@return 1 newly allocated dictionary object.
This function allocates a new dictionary object of given size and returns
it. If you do not know in advance (roughly) the number of entries in the
dictionary, give size=0.
*/
/*-------------------------------------------------------------------------*/
dictionary * dictionary_new(size_t size)
{
dictionary * d ;
/* If no size was specified, allocate space for DICTMINSZ */
if (size<DICTMINSZ) size=DICTMINSZ ;
d = (dictionary*) calloc(1, sizeof *d) ;
if (d) {
d->size = size ;
d->val = (char**) calloc(size, sizeof *d->val);
d->key = (char**) calloc(size, sizeof *d->key);
d->hash = (unsigned*) calloc(size, sizeof *d->hash);
if (!d->size || !d->val || !d->hash) {
free((void *) d->size);
free((void *) d->val);
free((void *) d->hash);
free(d);
d = NULL;
}
}
return d ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Delete a dictionary object
@param d dictionary object to deallocate.
@return void
Deallocate a dictionary object and all memory associated to it.
*/
/*--------------------------------------------------------------------------*/
void dictionary_del(dictionary * d)
{
size_t i ;
if (d==NULL) return ;
for (i=0 ; i<d->size ; i++) {
if (d->key[i]!=NULL)
free(d->key[i]);
if (d->val[i]!=NULL)
free(d->val[i]);
}
free(d->val);
free(d->key);
free(d->hash);
free(d);
return ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Get a value from a dictionary.
@param d dictionary object to search.
@param key Key to look for in the dictionary.
@param def Default value to return if key not found.
@return 1 pointer to internally allocated character string.
This function locates a key in a dictionary and returns a pointer to its
value, or the passed 'def' pointer if no such key can be found in
dictionary. The returned character pointer points to data internal to the
dictionary object, you should not try to free it or modify it.
*/
/*--------------------------------------------------------------------------*/
const char * dictionary_get(const dictionary * d, const char * key, const char * def)
{
unsigned hash ;
size_t i ;
if(d == NULL || key == NULL)
return def ;
hash = dictionary_hash(key);
for (i=0 ; i<d->size ; i++) {
if (d->key[i]==NULL)
continue ;
/* Compare hash */
if (hash==d->hash[i]) {
/* Compare string, to avoid hash collisions */
if (!strcmp(key, d->key[i])) {
return d->val[i] ;
}
}
}
return def ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Set a value in a dictionary.
@param d dictionary object to modify.
@param key Key to modify or add.
@param val Value to add.
@return int 0 if Ok, anything else otherwise
If the given key is found in the dictionary, the associated value is
replaced by the provided one. If the key cannot be found in the
dictionary, it is added to it.
It is Ok to provide a NULL value for val, but NULL values for the dictionary
or the key are considered as errors: the function will return immediately
in such a case.
Notice that if you dictionary_set a variable to NULL, a call to
dictionary_get will return a NULL value: the variable will be found, and
its value (NULL) is returned. In other words, setting the variable
content to NULL is equivalent to deleting the variable from the
dictionary. It is not possible (in this implementation) to have a key in
the dictionary without value.
This function returns non-zero in case of failure.
*/
/*--------------------------------------------------------------------------*/
int dictionary_set(dictionary * d, const char * key, const char * val)
{
size_t i ;
unsigned hash ;
if (d==NULL || key==NULL) return -1 ;
/* Compute hash for this key */
hash = dictionary_hash(key) ;
/* Find if value is already in dictionary */
if (d->n>0) {
for (i=0 ; i<d->size ; i++) {
if (d->key[i]==NULL)
continue ;
if (hash==d->hash[i]) { /* Same hash value */
if (!strcmp(key, d->key[i])) { /* Same key */
/* Found a value: modify and return */
if (d->val[i]!=NULL)
free(d->val[i]);
d->val[i] = (val ? xstrdup(val) : NULL);
/* Value has been modified: return */
return 0 ;
}
}
}
}
/* Add a new value */
/* See if dictionary needs to grow */
if (d->n==d->size) {
/* Reached maximum size: reallocate dictionary */
if (dictionary_grow(d) != 0)
return -1;
}
/* Insert key in the first empty slot. Start at d->n and wrap at
d->size. Because d->n < d->size this will necessarily
terminate. */
for (i=d->n ; d->key[i] ; ) {
if(++i == d->size) i = 0;
}
/* Copy key */
d->key[i] = xstrdup(key);
d->val[i] = (val ? xstrdup(val) : NULL) ;
d->hash[i] = hash;
d->n ++ ;
return 0 ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Delete a key in a dictionary
@param d dictionary object to modify.
@param key Key to remove.
@return void
This function deletes a key in a dictionary. Nothing is done if the
key cannot be found.
*/
/*--------------------------------------------------------------------------*/
void dictionary_unset(dictionary * d, const char * key)
{
unsigned hash ;
size_t i ;
if (key == NULL || d == NULL) {
return;
}
hash = dictionary_hash(key);
for (i=0 ; i<d->size ; i++) {
if (d->key[i]==NULL)
continue ;
/* Compare hash */
if (hash==d->hash[i]) {
/* Compare string, to avoid hash collisions */
if (!strcmp(key, d->key[i])) {
/* Found key */
break ;
}
}
}
if (i>=d->size)
/* Key not found */
return ;
free(d->key[i]);
d->key[i] = NULL ;
if (d->val[i]!=NULL) {
free(d->val[i]);
d->val[i] = NULL ;
}
d->hash[i] = 0 ;
d->n -- ;
return ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Dump a dictionary to an opened file pointer.
@param d Dictionary to dump
@param f Opened file pointer.
@return void
Dumps a dictionary onto an opened file pointer. Key pairs are printed out
as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
output file pointers.
*/
/*--------------------------------------------------------------------------*/
void dictionary_dump(const dictionary * d, FILE * out)
{
size_t i ;
if (d==NULL || out==NULL) return ;
if (d->n<1) {
fprintf(out, "empty dictionary\n");
return ;
}
for (i=0 ; i<d->size ; i++) {
if (d->key[i]) {
fprintf(out, "%20s\t[%s]\n",
d->key[i],
d->val[i] ? d->val[i] : "UNDEF");
}
}
return ;
}

View File

@ -1,167 +0,0 @@
/*-------------------------------------------------------------------------*/
/**
@file dictionary.h
@author N. Devillard
@brief Implements a dictionary for string variables.
This module implements a simple dictionary object, i.e. a list
of string/string associations. This object is useful to store e.g.
informations retrieved from a configuration file (ini files).
*/
/*--------------------------------------------------------------------------*/
#ifndef _DICTIONARY_H_
#define _DICTIONARY_H_
/*---------------------------------------------------------------------------
Includes
---------------------------------------------------------------------------*/
#include "pafstd.h"
#ifdef __cplusplus
extern "C"
{
#endif
/*---------------------------------------------------------------------------
New types
---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
@brief Dictionary object
This object contains a list of string/string associations. Each
association is identified by a unique string key. Looking up values
in the dictionary is speeded up by the use of a (hopefully collision-free)
hash function.
*/
/*-------------------------------------------------------------------------*/
typedef struct _dictionary_ {
unsigned n; /** Number of entries in dictionary */
size_t size; /** Storage size */
char** val; /** List of string values */
char** key; /** List of string keys */
unsigned* hash; /** List of hash values for keys */
} dictionary;
/*---------------------------------------------------------------------------
Function prototypes
---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
@brief Compute the hash key for a string.
@param key Character string to use for key.
@return 1 unsigned int on at least 32 bits.
This hash function has been taken from an Article in Dr Dobbs Journal.
This is normally a collision-free function, distributing keys evenly.
The key is stored anyway in the struct so that collision can be avoided
by comparing the key itself in last resort.
*/
/*--------------------------------------------------------------------------*/
unsigned dictionary_hash(const char* key);
/*-------------------------------------------------------------------------*/
/**
@brief Create a new dictionary object.
@param size Optional initial size of the dictionary.
@return 1 newly allocated dictionary object.
This function allocates a new dictionary object of given size and returns
it. If you do not know in advance (roughly) the number of entries in the
dictionary, give size=0.
*/
/*--------------------------------------------------------------------------*/
dictionary* dictionary_new(size_t size);
/*-------------------------------------------------------------------------*/
/**
@brief Delete a dictionary object
@param d dictionary object to deallocate.
@return void
Deallocate a dictionary object and all memory associated to it.
*/
/*--------------------------------------------------------------------------*/
void dictionary_del(dictionary* vd);
/*-------------------------------------------------------------------------*/
/**
@brief Get a value from a dictionary.
@param d dictionary object to search.
@param key Key to look for in the dictionary.
@param def Default value to return if key not found.
@return 1 pointer to internally allocated character string.
This function locates a key in a dictionary and returns a pointer to its
value, or the passed 'def' pointer if no such key can be found in
dictionary. The returned character pointer points to data internal to the
dictionary object, you should not try to free it or modify it.
*/
/*--------------------------------------------------------------------------*/
const char* dictionary_get(const dictionary* d, const char* key, const char* def);
/*-------------------------------------------------------------------------*/
/**
@brief Set a value in a dictionary.
@param d dictionary object to modify.
@param key Key to modify or add.
@param val Value to add.
@return int 0 if Ok, anything else otherwise
If the given key is found in the dictionary, the associated value is
replaced by the provided one. If the key cannot be found in the
dictionary, it is added to it.
It is Ok to provide a NULL value for val, but NULL values for the dictionary
or the key are considered as errors: the function will return immediately
in such a case.
Notice that if you dictionary_set a variable to NULL, a call to
dictionary_get will return a NULL value: the variable will be found, and
its value (NULL) is returned. In other words, setting the variable
content to NULL is equivalent to deleting the variable from the
dictionary. It is not possible (in this implementation) to have a key in
the dictionary without value.
This function returns non-zero in case of failure.
*/
/*--------------------------------------------------------------------------*/
int dictionary_set(dictionary* vd, const char* key, const char* val);
/*-------------------------------------------------------------------------*/
/**
@brief Delete a key in a dictionary
@param d dictionary object to modify.
@param key Key to remove.
@return void
This function deletes a key in a dictionary. Nothing is done if the
key cannot be found.
*/
/*--------------------------------------------------------------------------*/
void dictionary_unset(dictionary* d, const char* key);
/*-------------------------------------------------------------------------*/
/**
@brief Dump a dictionary to an opened file pointer.
@param d Dictionary to dump
@param f Opened file pointer.
@return void
Dumps a dictionary onto an opened file pointer. Key pairs are printed out
as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
output file pointers.
*/
/*--------------------------------------------------------------------------*/
void dictionary_dump(const dictionary* d, FILE* out);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,958 +0,0 @@
/*-------------------------------------------------------------------------*/
/**
@file iniparser.c
@author N. Devillard
@brief Parser for ini files.
*/
/*--------------------------------------------------------------------------*/
/*---------------------------- Includes ------------------------------------*/
#include <ctype.h>
#include "pafstd.h"
#include <stdint.h>
#include <stdarg.h>
#include "iniparser.h"
/*---------------------------- Defines -------------------------------------*/
#define ASCIILINESZ (1024)
#define INI_INVALID_KEY ((char*)-1)
/*---------------------------------------------------------------------------
Private to this module
---------------------------------------------------------------------------*/
/**
* This enum stores the status for each parsed line (internal use only).
*/
typedef enum _line_status_ {
LINE_UNPROCESSED,
LINE_ERROR,
LINE_EMPTY,
LINE_COMMENT,
LINE_SECTION,
LINE_VALUE
} line_status ;
/*-------------------------------------------------------------------------*/
/**
@brief Convert a string to lowercase.
@param in String to convert.
@param out Output buffer.
@param len Size of the out buffer.
@return ptr to the out buffer or NULL if an error occured.
This function convert a string into lowercase.
At most len - 1 elements of the input string will be converted.
*/
/*--------------------------------------------------------------------------*/
static const char * strlwc(const char * in, char *out, unsigned len)
{
unsigned i ;
if (in==NULL || out == NULL || len==0) return NULL ;
i=0 ;
while (in[i] != '\0' && i < len-1) {
out[i] = (char)tolower((int)in[i]);
i++ ;
}
out[i] = '\0';
return out ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Duplicate a string
@param s String to duplicate
@return Pointer to a newly allocated string, to be freed with free()
This is a replacement for strdup(). This implementation is provided
for systems that do not have it.
*/
/*--------------------------------------------------------------------------*/
static char * xstrdup(const char * s)
{
char * t ;
size_t len ;
if (!s)
return NULL ;
len = strlen(s) + 1 ;
t = (char*) malloc(len) ;
if (t) {
memcpy(t, s, len) ;
}
return t ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Remove blanks at the beginning and the end of a string.
@param str String to parse and alter.
@return unsigned New size of the string.
*/
/*--------------------------------------------------------------------------*/
static unsigned strstrip(char * s)
{
char *last = NULL ;
char *dest = s;
if (s==NULL) return 0;
last = s + strlen(s);
while (isspace((unsigned char)*s) && *s) s++;
while (last > s) {
if (!isspace((unsigned char)*(last-1)))
break ;
last -- ;
}
*last = (char)0;
memmove(dest,s,last - s + 1);
return last - s;
}
/*-------------------------------------------------------------------------*/
/**
@brief Default error callback for iniparser: wraps `fprintf(stderr, ...)`.
*/
/*--------------------------------------------------------------------------*/
static int default_error_callback(const char *format, ...)
{
int ret = 0;
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
return ret;
}
static int (*iniparser_error_callback)(const char*, ...) = default_error_callback;
/*-------------------------------------------------------------------------*/
/**
@brief Configure a function to receive the error messages.
@param errback Function to call.
By default, the error will be printed on stderr. If a null pointer is passed
as errback the error callback will be switched back to default.
*/
/*--------------------------------------------------------------------------*/
void iniparser_set_error_callback(int (*errback)(const char *, ...))
{
if (errback) {
iniparser_error_callback = errback;
} else {
iniparser_error_callback = default_error_callback;
}
}
/*-------------------------------------------------------------------------*/
/**
@brief Get number of sections in a dictionary
@param d Dictionary to examine
@return int Number of sections found in dictionary
This function returns the number of sections found in a dictionary.
The test to recognize sections is done on the string stored in the
dictionary: a section name is given as "section" whereas a key is
stored as "section:key", thus the test looks for entries that do not
contain a colon.
This clearly fails in the case a section name contains a colon, but
this should simply be avoided.
This function returns -1 in case of error.
*/
/*--------------------------------------------------------------------------*/
int iniparser_getnsec(const dictionary * d)
{
size_t i ;
int nsec ;
if (d==NULL) return -1 ;
nsec=0 ;
for (i=0 ; i<d->size ; i++) {
if (d->key[i]==NULL)
continue ;
if (strchr(d->key[i], ':')==NULL) {
nsec ++ ;
}
}
return nsec ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Get name for section n in a dictionary.
@param d Dictionary to examine
@param n Section number (from 0 to nsec-1).
@return Pointer to char string
This function locates the n-th section in a dictionary and returns
its name as a pointer to a string statically allocated inside the
dictionary. Do not free or modify the returned string!
This function returns NULL in case of error.
*/
/*--------------------------------------------------------------------------*/
const char * iniparser_getsecname(const dictionary * d, int n)
{
size_t i ;
int foundsec ;
if (d==NULL || n<0) return NULL ;
foundsec=0 ;
for (i=0 ; i<d->size ; i++) {
if (d->key[i]==NULL)
continue ;
if (strchr(d->key[i], ':')==NULL) {
foundsec++ ;
if (foundsec>n)
break ;
}
}
if (foundsec<=n) {
return NULL ;
}
return d->key[i] ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Dump a dictionary to an opened file pointer.
@param d Dictionary to dump.
@param f Opened file pointer to dump to.
@return void
This function prints out the contents of a dictionary, one element by
line, onto the provided file pointer. It is OK to specify @c stderr
or @c stdout as output files. This function is meant for debugging
purposes mostly.
*/
/*--------------------------------------------------------------------------*/
void iniparser_dump(const dictionary * d, FILE * f)
{
size_t i ;
if (d==NULL || f==NULL) return ;
for (i=0 ; i<d->size ; i++) {
if (d->key[i]==NULL)
continue ;
if (d->val[i]!=NULL) {
fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
} else {
fprintf(f, "[%s]=UNDEF\n", d->key[i]);
}
}
return ;
}
static void escape_value(char *escaped, char *value) {
char c;
int v = 0;
int e = 0;
if(!escaped || !value)
return;
while((c = value[v]) != '\0') {
if(c == '\\' || c == '"') {
escaped[e] = '\\';
e++;
}
escaped[e] = c;
v++;
e++;
}
escaped[e] = '\0';
}
/*-------------------------------------------------------------------------*/
/**
@brief Save a dictionary to a loadable ini file
@param d Dictionary to dump
@param f Opened file pointer to dump to
@return void
This function dumps a given dictionary into a loadable ini file.
It is Ok to specify @c stderr or @c stdout as output files.
*/
/*--------------------------------------------------------------------------*/
void iniparser_dump_ini(const dictionary * d, FILE * f)
{
size_t i ;
size_t nsec ;
const char * secname ;
char escaped[(ASCIILINESZ * 2) + 2] = "";
if (d==NULL || f==NULL) return ;
nsec = iniparser_getnsec(d);
if (nsec<1) {
/* No section in file: dump all keys as they are */
for (i=0 ; i<d->size ; i++) {
if (d->key[i]==NULL)
continue ;
escape_value(escaped, d->val[i]);
fprintf(f, "%s = \"%s\"\n", d->key[i], escaped);
}
return ;
}
for (i=0 ; i<nsec ; i++) {
secname = iniparser_getsecname(d, i) ;
iniparser_dumpsection_ini(d, secname, f);
}
fprintf(f, "\n");
return ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Save a dictionary section to a loadable ini file
@param d Dictionary to dump
@param s Section name of dictionary to dump
@param f Opened file pointer to dump to
@return void
This function dumps a given section of a given dictionary into a loadable ini
file. It is Ok to specify @c stderr or @c stdout as output files.
*/
/*--------------------------------------------------------------------------*/
void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f)
{
size_t j ;
char keym[ASCIILINESZ+1];
int seclen ;
char escaped[(ASCIILINESZ * 2) + 2] = "";
if (d==NULL || f==NULL) return;
if (! iniparser_find_entry(d, s)) return;
if (strlen(s) > sizeof(keym)) return;
seclen = (int)strlen(s);
fprintf(f, "\n[%s]\n", s);
sprintf(keym, "%s:", s);
for (j=0 ; j<d->size ; j++) {
if (d->key[j]==NULL)
continue ;
if (!strncmp(d->key[j], keym, seclen+1)) {
escape_value(escaped, d->val[j]);
fprintf(f, "%-30s = \"%s\"\n", d->key[j]+seclen+1, escaped);
}
}
fprintf(f, "\n");
return ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Get the number of keys in a section of a dictionary.
@param d Dictionary to examine
@param s Section name of dictionary to examine
@return Number of keys in section
*/
/*--------------------------------------------------------------------------*/
int iniparser_getsecnkeys(const dictionary * d, const char * s)
{
int seclen, nkeys ;
char keym[ASCIILINESZ+1];
size_t j ;
nkeys = 0;
if (d==NULL) return nkeys;
if (! iniparser_find_entry(d, s)) return nkeys;
seclen = (int)strlen(s);
strlwc(s, keym, sizeof(keym));
keym[seclen] = ':';
for (j=0 ; j<d->size ; j++) {
if (d->key[j]==NULL)
continue ;
if (!strncmp(d->key[j], keym, seclen+1))
nkeys++;
}
return nkeys;
}
/*-------------------------------------------------------------------------*/
/**
@brief Get the number of keys in a section of a dictionary.
@param d Dictionary to examine
@param s Section name of dictionary to examine
@param keys Already allocated array to store the keys in
@return The pointer passed as `keys` argument or NULL in case of error
This function queries a dictionary and finds all keys in a given section.
The keys argument should be an array of pointers which size has been
determined by calling `iniparser_getsecnkeys` function prior to this one.
Each pointer in the returned char pointer-to-pointer is pointing to
a string allocated in the dictionary; do not free or modify them.
*/
/*--------------------------------------------------------------------------*/
const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys)
{
size_t i, j, seclen ;
char keym[ASCIILINESZ+1];
if (d==NULL || keys==NULL) return NULL;
if (! iniparser_find_entry(d, s)) return NULL;
seclen = strlen(s);
strlwc(s, keym, sizeof(keym));
keym[seclen] = ':';
i = 0;
for (j=0 ; j<d->size ; j++) {
if (d->key[j]==NULL)
continue ;
if (!strncmp(d->key[j], keym, seclen+1)) {
keys[i] = d->key[j];
i++;
}
}
return keys;
}
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key
@param d Dictionary to search
@param key Key string to look for
@param def Default value to return if key not found.
@return pointer to statically allocated character string
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the pointer passed as 'def' is returned.
The returned char pointer is pointing to a string allocated in
the dictionary, do not free or modify it.
*/
/*--------------------------------------------------------------------------*/
const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)
{
const char * lc_key ;
const char * sval ;
char tmp_str[ASCIILINESZ+1];
if (d==NULL || key==NULL)
return def ;
lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
sval = dictionary_get(d, lc_key, def);
return sval ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to an long int
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return long integer
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
Supported values for integers include the usual C notation
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
are supported. Examples:
"42" -> 42
"042" -> 34 (octal -> decimal)
"0x42" -> 66 (hexa -> decimal)
Warning: the conversion may overflow in various ways. Conversion is
totally outsourced to strtol(), see the associated man page for overflow
handling.
Credits: Thanks to A. Becker for suggesting strtol()
*/
/*--------------------------------------------------------------------------*/
long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
{
const char * str ;
str = iniparser_getstring(d, key, INI_INVALID_KEY);
if (str==NULL || str==INI_INVALID_KEY) return notfound ;
return strtol(str, NULL, 0);
}
int64_t iniparser_getint64(const dictionary * d, const char * key, int64_t notfound)
{
const char * str ;
str = iniparser_getstring(d, key, INI_INVALID_KEY);
if (str==NULL || str==INI_INVALID_KEY) return notfound ;
return strtoimax(str, NULL, 0);
}
uint64_t iniparser_getuint64(const dictionary * d, const char * key, uint64_t notfound)
{
const char * str ;
str = iniparser_getstring(d, key, INI_INVALID_KEY);
if (str==NULL || str==INI_INVALID_KEY) return notfound ;
return strtoumax(str, NULL, 0);
}
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to an int
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return integer
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
Supported values for integers include the usual C notation
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
are supported. Examples:
"42" -> 42
"042" -> 34 (octal -> decimal)
"0x42" -> 66 (hexa -> decimal)
Warning: the conversion may overflow in various ways. Conversion is
totally outsourced to strtol(), see the associated man page for overflow
handling.
Credits: Thanks to A. Becker for suggesting strtol()
*/
/*--------------------------------------------------------------------------*/
int iniparser_getint(const dictionary * d, const char * key, int notfound)
{
return (int)iniparser_getlongint(d, key, notfound);
}
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to a double
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return double
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
*/
/*--------------------------------------------------------------------------*/
double iniparser_getdouble(const dictionary * d, const char * key, double notfound)
{
const char * str ;
str = iniparser_getstring(d, key, INI_INVALID_KEY);
if (str==NULL || str==INI_INVALID_KEY) return notfound ;
return atof(str);
}
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to a boolean
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return integer
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
A true boolean is found if one of the following is matched:
- A string starting with 'y'
- A string starting with 'Y'
- A string starting with 't'
- A string starting with 'T'
- A string starting with '1'
A false boolean is found if one of the following is matched:
- A string starting with 'n'
- A string starting with 'N'
- A string starting with 'f'
- A string starting with 'F'
- A string starting with '0'
The notfound value returned if no boolean is identified, does not
necessarily have to be 0 or 1.
*/
/*--------------------------------------------------------------------------*/
int iniparser_getboolean(const dictionary * d, const char * key, int notfound)
{
int ret ;
const char * c ;
c = iniparser_getstring(d, key, INI_INVALID_KEY);
if (c==NULL || c==INI_INVALID_KEY) return notfound ;
if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
ret = 1 ;
} else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
ret = 0 ;
} else {
ret = notfound ;
}
return ret;
}
/*-------------------------------------------------------------------------*/
/**
@brief Finds out if a given entry exists in a dictionary
@param ini Dictionary to search
@param entry Name of the entry to look for
@return integer 1 if entry exists, 0 otherwise
Finds out if a given entry exists in the dictionary. Since sections
are stored as keys with NULL associated values, this is the only way
of querying for the presence of sections in a dictionary.
*/
/*--------------------------------------------------------------------------*/
int iniparser_find_entry(const dictionary * ini, const char * entry)
{
int found=0 ;
if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
found = 1 ;
}
return found ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Set an entry in a dictionary.
@param ini Dictionary to modify.
@param entry Entry to modify (entry name)
@param val New value to associate to the entry.
@return int 0 if Ok, -1 otherwise.
If the given entry can be found in the dictionary, it is modified to
contain the provided value. If it cannot be found, the entry is created.
It is Ok to set val to NULL.
*/
/*--------------------------------------------------------------------------*/
int iniparser_set(dictionary * ini, const char * entry, const char * val)
{
char tmp_key[ASCIILINESZ+1] = {0};
char tmp_val[ASCIILINESZ+1] = {0};
size_t len;
if(val) {
len = strlen(val);
len = len > ASCIILINESZ ? ASCIILINESZ : len;
memcpy(tmp_val, val, len) ;
val = tmp_val;
}
return dictionary_set(ini, strlwc(entry, tmp_key, sizeof(tmp_key)), val);
}
/*-------------------------------------------------------------------------*/
/**
@brief Delete an entry in a dictionary
@param ini Dictionary to modify
@param entry Entry to delete (entry name)
@return void
If the given entry can be found, it is deleted from the dictionary.
*/
/*--------------------------------------------------------------------------*/
void iniparser_unset(dictionary * ini, const char * entry)
{
char tmp_str[ASCIILINESZ+1];
dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));
}
static void parse_quoted_value(char *value, char quote) {
char c;
char *quoted;
int q = 0, v = 0;
int esc = 0;
if(!value)
return;
quoted = xstrdup(value);
if(!quoted) {
iniparser_error_callback("iniparser: memory allocation failure\n");
goto end_of_value;
}
while((c = quoted[q]) != '\0') {
if(!esc) {
if(c == '\\') {
esc = 1;
q++;
continue;
}
if(c == quote) {
goto end_of_value;
}
}
esc = 0;
value[v] = c;
v++;
q++;
}
end_of_value:
value[v] = '\0';
free(quoted);
}
/*-------------------------------------------------------------------------*/
/**
@brief Load a single line from an INI file
@param input_line Input line, may be concatenated multi-line input
@param section Output space to store section
@param key Output space to store key
@param value Output space to store value
@return line_status value
*/
/*--------------------------------------------------------------------------*/
static line_status iniparser_line(
const char * input_line,
char * section,
char * key,
char * value)
{
line_status sta ;
char * line = NULL;
size_t len ;
int d_quote;
line = xstrdup(input_line);
len = strstrip(line);
sta = LINE_UNPROCESSED ;
if (len<1) {
/* Empty line */
sta = LINE_EMPTY ;
} else if (line[0]=='#' || line[0]==';') {
/* Comment line */
sta = LINE_COMMENT ;
} else if (line[0]=='[' && line[len-1]==']') {
/* Section name without opening square bracket */
sscanf(line, "[%[^\n]", section);
len = strlen(section);
/* Section name without closing square bracket */
if(section[len-1] == ']')
{
section[len-1] = '\0';
}
strstrip(section);
strlwc(section, section, len);
sta = LINE_SECTION ;
} else if ((d_quote = sscanf (line, "%[^=] = \"%[^\n]\"", key, value)) == 2
|| sscanf (line, "%[^=] = '%[^\n]'", key, value) == 2) {
/* Usual key=value with quotes, with or without comments */
strstrip(key);
strlwc(key, key, len);
if(d_quote == 2)
parse_quoted_value(value, '"');
else
parse_quoted_value(value, '\'');
/* Don't strip spaces from values surrounded with quotes */
sta = LINE_VALUE ;
} else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
/* Usual key=value without quotes, with or without comments */
strstrip(key);
strlwc(key, key, len);
strstrip(value);
/*
* sscanf cannot handle '' or "" as empty values
* this is done here
*/
if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
value[0]=0 ;
}
sta = LINE_VALUE ;
} else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
|| sscanf(line, "%[^=] %[=]", key, value) == 2) {
/*
* Special cases:
* key=
* key=;
* key=#
*/
strstrip(key);
strlwc(key, key, len);
value[0]=0 ;
sta = LINE_VALUE ;
} else {
/* Generate syntax error */
sta = LINE_ERROR ;
}
free(line);
return sta ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Parse an ini file and return an allocated dictionary object
@param in File to read.
@param ininame Name of the ini file to read (only used for nicer error messages)
@return Pointer to newly allocated dictionary
This is the parser for ini files. This function is called, providing
the file to be read. It returns a dictionary object that should not
be accessed directly, but through accessor functions instead.
The returned dictionary must be freed using iniparser_freedict().
*/
/*--------------------------------------------------------------------------*/
dictionary * iniparser_load_file(FILE * in, const char * ininame)
{
char line [ASCIILINESZ+1] ;
char section [ASCIILINESZ+1] ;
char key [ASCIILINESZ+1] ;
char tmp [(ASCIILINESZ * 2) + 2] ;
char val [ASCIILINESZ+1] ;
int last=0 ;
int len ;
int lineno=0 ;
int errs=0;
int mem_err=0;
dictionary * dict ;
dict = dictionary_new(0) ;
if (!dict) {
return NULL ;
}
memset(line, 0, ASCIILINESZ);
memset(section, 0, ASCIILINESZ);
memset(key, 0, ASCIILINESZ);
memset(val, 0, ASCIILINESZ);
last=0 ;
while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
lineno++ ;
len = (int)strlen(line)-1;
if (len<=0)
continue;
/* Safety check against buffer overflows */
if (line[len]!='\n' && !feof(in)) {
iniparser_error_callback(
"iniparser: input line too long in %s (%d)\n",
ininame,
lineno);
dictionary_del(dict);
return NULL ;
}
/* Get rid of \n and spaces at end of line */
while ((len>=0) &&
((line[len]=='\n') || (isspace((unsigned char)line[len])))) {
line[len]=0 ;
len-- ;
}
if (len < 0) { /* Line was entirely \n and/or spaces */
len = 0;
}
/* Detect multi-line */
if (line[len]=='\\') {
/* Multi-line value */
last=len ;
continue ;
} else {
last=0 ;
}
switch (iniparser_line(line, section, key, val)) {
case LINE_EMPTY:
case LINE_COMMENT:
break ;
case LINE_SECTION:
mem_err = dictionary_set(dict, section, NULL);
break ;
case LINE_VALUE:
sprintf(tmp, "%s:%s", section, key);
mem_err = dictionary_set(dict, tmp, val);
break ;
case LINE_ERROR:
iniparser_error_callback(
"iniparser: syntax error in %s (%d):\n-> %s\n",
ininame,
lineno,
line);
errs++ ;
break;
default:
break ;
}
memset(line, 0, ASCIILINESZ);
last=0;
if (mem_err<0) {
iniparser_error_callback("iniparser: memory allocation failure\n");
break ;
}
}
if (errs) {
dictionary_del(dict);
dict = NULL ;
}
return dict ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Parse an ini file and return an allocated dictionary object
@param ininame Name of the ini file to read.
@return Pointer to newly allocated dictionary
This is the parser for ini files. This function is called, providing
the name of the file to be read. It returns a dictionary object that
should not be accessed directly, but through accessor functions
instead.
The returned dictionary must be freed using iniparser_freedict().
*/
/*--------------------------------------------------------------------------*/
dictionary * iniparser_load(const char * ininame)
{
FILE * in ;
dictionary * dict ;
if ((in=fopen(ininame, "r"))==NULL) {
iniparser_error_callback("iniparser: cannot open %s\n", ininame);
return NULL ;
}
dict = iniparser_load_file(in, ininame);
fclose(in);
return dict ;
}
/*-------------------------------------------------------------------------*/
/**
@brief Free all memory associated to an ini dictionary
@param d Dictionary to free
@return void
Free all memory associated to an ini dictionary.
It is mandatory to call this function before the dictionary object
gets out of the current context.
*/
/*--------------------------------------------------------------------------*/
void iniparser_freedict(dictionary * d)
{
dictionary_del(d);
}

View File

@ -1,443 +0,0 @@
/*-------------------------------------------------------------------------*/
/**
@file iniparser.h
@author N. Devillard
@brief Parser for ini files.
*/
/*--------------------------------------------------------------------------*/
#ifndef _INIPARSER_H_
#define _INIPARSER_H_
/*---------------------------------------------------------------------------
Includes
---------------------------------------------------------------------------*/
#include "dictionary.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
/*-------------------------------------------------------------------------*/
/**
@brief Configure a function to receive the error messages.
@param errback Function to call.
By default, the error will be printed on stderr. If a null pointer is passed
as errback the error callback will be switched back to default.
*/
/*--------------------------------------------------------------------------*/
void iniparser_set_error_callback(int (*errback)(const char*, ...));
/*-------------------------------------------------------------------------*/
/**
@brief Get number of sections in a dictionary
@param d Dictionary to examine
@return int Number of sections found in dictionary
This function returns the number of sections found in a dictionary.
The test to recognize sections is done on the string stored in the
dictionary: a section name is given as "section" whereas a key is
stored as "section:key", thus the test looks for entries that do not
contain a colon.
This clearly fails in the case a section name contains a colon, but
this should simply be avoided.
This function returns -1 in case of error.
*/
/*--------------------------------------------------------------------------*/
int iniparser_getnsec(const dictionary* d);
/*-------------------------------------------------------------------------*/
/**
@brief Get name for section n in a dictionary.
@param d Dictionary to examine
@param n Section number (from 0 to nsec-1).
@return Pointer to char string
This function locates the n-th section in a dictionary and returns
its name as a pointer to a string statically allocated inside the
dictionary. Do not free or modify the returned string!
This function returns NULL in case of error.
*/
/*--------------------------------------------------------------------------*/
const char* iniparser_getsecname(const dictionary* d, int n);
/*-------------------------------------------------------------------------*/
/**
@brief Save a dictionary to a loadable ini file
@param d Dictionary to dump
@param f Opened file pointer to dump to
This function dumps a given dictionary into a loadable ini file.
It is Ok to specify @c stderr or @c stdout as output files.
All values are quoted, these charecters are escaped:
- ' : the quote character (e.g. "String with \"Quotes\"")
- \ : the backslash character (e.g. "C:\\tmp")
*/
/*--------------------------------------------------------------------------*/
void iniparser_dump_ini(const dictionary* d, FILE* f);
/*-------------------------------------------------------------------------*/
/**
@brief Save a dictionary section to a loadable ini file
@param d Dictionary to dump
@param s Section name of dictionary to dump
@param f Opened file pointer to dump to
This function dumps a given section of a given dictionary into a loadable ini
file. It is Ok to specify @c stderr or @c stdout as output files.
*/
/*--------------------------------------------------------------------------*/
void iniparser_dumpsection_ini(const dictionary* d, const char* s, FILE* f);
/*-------------------------------------------------------------------------*/
/**
@brief Dump a dictionary to an opened file pointer.
@param d Dictionary to dump.
@param f Opened file pointer to dump to.
This function prints out the contents of a dictionary, one element by
line, onto the provided file pointer. It is OK to specify @c stderr
or @c stdout as output files. This function is meant for debugging
purposes mostly.
*/
/*--------------------------------------------------------------------------*/
void iniparser_dump(const dictionary* d, FILE* f);
/*-------------------------------------------------------------------------*/
/**
@brief Get the number of keys in a section of a dictionary.
@param d Dictionary to examine
@param s Section name of dictionary to examine
@return Number of keys in section
*/
/*--------------------------------------------------------------------------*/
int iniparser_getsecnkeys(const dictionary* d, const char* s);
/*-------------------------------------------------------------------------*/
/**
@brief Get the number of keys in a section of a dictionary.
@param d Dictionary to examine
@param s Section name of dictionary to examine
@param keys Already allocated array to store the keys in
@return The pointer passed as `keys` argument or NULL in case of error
This function queries a dictionary and finds all keys in a given section.
The keys argument should be an array of pointers which size has been
determined by calling `iniparser_getsecnkeys` function prior to this one.
Each pointer in the returned char pointer-to-pointer is pointing to
a string allocated in the dictionary; do not free or modify them.
*/
/*--------------------------------------------------------------------------*/
const char** iniparser_getseckeys(const dictionary* d, const char* s, const char** keys);
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key
@param d Dictionary to search
@param key Key string to look for
@param def Default value to return if key not found.
@return pointer to statically allocated character string
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the pointer passed as 'def' is returned.
The returned char pointer is pointing to a string allocated in
the dictionary, do not free or modify it.
*/
/*--------------------------------------------------------------------------*/
const char* iniparser_getstring(const dictionary* d, const char* key, const char* def);
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to an int
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return integer
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
Supported values for integers include the usual C notation
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
are supported. Examples:
- "42" -> 42
- "042" -> 34 (octal -> decimal)
- "0x42" -> 66 (hexa -> decimal)
Warning: the conversion may overflow in various ways. Conversion is
totally outsourced to strtol(), see the associated man page for overflow
handling.
Credits: Thanks to A. Becker for suggesting strtol()
*/
/*--------------------------------------------------------------------------*/
int iniparser_getint(const dictionary* d, const char* key, int notfound);
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to an long int
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return integer
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
Supported values for integers include the usual C notation
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
are supported. Examples:
- "42" -> 42
- "042" -> 34 (octal -> decimal)
- "0x42" -> 66 (hexa -> decimal)
Warning: the conversion may overflow in various ways. Conversion is
totally outsourced to strtol(), see the associated man page for overflow
handling.
*/
/*--------------------------------------------------------------------------*/
long int iniparser_getlongint(const dictionary* d, const char* key, long int notfound);
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to an int64_t
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return integer
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
Supported values for integers include the usual C notation
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
are supported. Examples:
- "42" -> 42
- "042" -> 34 (octal -> decimal)
- "0x42" -> 66 (hexa -> decimal)
Warning: the conversion may overflow in various ways. Conversion is
totally outsourced to strtoimax(), see the associated man page for overflow
handling.
This function is usefull on 32bit architectures where `long int` is only
32bit.
*/
/*--------------------------------------------------------------------------*/
int64_t iniparser_getint64(const dictionary* d, const char* key, int64_t notfound);
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to an uint64_t
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return integer
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
Supported values for integers include the usual C notation
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
are supported. Examples:
- "42" -> 42
- "042" -> 34 (octal -> decimal)
- "0x42" -> 66 (hexa -> decimal)
Warning: the conversion may overflow in various ways. Conversion is
totally outsourced to strtoumax(), see the associated man page for overflow
handling.
This function is usefull on 32bit architectures where `long int` is only
32bit.
*/
/*--------------------------------------------------------------------------*/
uint64_t iniparser_getuint64(const dictionary* d, const char* key, uint64_t notfound);
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to a double
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return double
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
*/
/*--------------------------------------------------------------------------*/
double iniparser_getdouble(const dictionary* d, const char* key, double notfound);
/*-------------------------------------------------------------------------*/
/**
@brief Get the string associated to a key, convert to a boolean
@param d Dictionary to search
@param key Key string to look for
@param notfound Value to return in case of error
@return integer
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
A true boolean is found if one of the following is matched:
- A string starting with 'y'
- A string starting with 'Y'
- A string starting with 't'
- A string starting with 'T'
- A string starting with '1'
A false boolean is found if one of the following is matched:
- A string starting with 'n'
- A string starting with 'N'
- A string starting with 'f'
- A string starting with 'F'
- A string starting with '0'
The notfound value returned if no boolean is identified, does not
necessarily have to be 0 or 1.
*/
/*--------------------------------------------------------------------------*/
int iniparser_getboolean(const dictionary* d, const char* key, int notfound);
/*-------------------------------------------------------------------------*/
/**
@brief Set an entry in a dictionary.
@param ini Dictionary to modify.
@param entry Entry to modify (entry name)
@param val New value to associate to the entry.
@return int 0 if Ok, -1 otherwise.
If the given entry can be found in the dictionary, it is modified to
contain the provided value. If it cannot be found, the entry is created.
It is Ok to set val to NULL.
*/
/*--------------------------------------------------------------------------*/
int iniparser_set(dictionary* ini, const char* entry, const char* val);
/*-------------------------------------------------------------------------*/
/**
@brief Delete an entry in a dictionary
@param ini Dictionary to modify
@param entry Entry to delete (entry name)
If the given entry can be found, it is deleted from the dictionary.
*/
/*--------------------------------------------------------------------------*/
void iniparser_unset(dictionary* ini, const char* entry);
/*-------------------------------------------------------------------------*/
/**
@brief Finds out if a given entry exists in a dictionary
@param ini Dictionary to search
@param entry Name of the entry to look for
@return integer 1 if entry exists, 0 otherwise
Finds out if a given entry exists in the dictionary. Since sections
are stored as keys with NULL associated values, this is the only way
of querying for the presence of sections in a dictionary.
*/
/*--------------------------------------------------------------------------*/
int iniparser_find_entry(const dictionary* ini, const char* entry);
/*-------------------------------------------------------------------------*/
/**
@brief Parse an ini file and return an allocated dictionary object
@param ininame Name of the ini file to read.
@return Pointer to newly allocated dictionary
This is the parser for ini files. This function is called, providing
the name of the file to be read. It returns a dictionary object that
should not be accessed directly, but through accessor functions
instead.
Iff the value is a quoted string it supports some escape sequences:
- \" or ' : the quote character
(e.g. 'String with "Quotes"' or "String with 'Quotes'")
- \ : the backslash character (e.g. "C:\tmp")
Escape sequences always start with a backslash. Additional escape sequences
might be added in the future. Backslash characters must be escaped. Any other
sequence then those outlined above is invalid and may lead to unpredictable
results.
The returned dictionary must be freed using iniparser_freedict().
*/
/*--------------------------------------------------------------------------*/
dictionary* iniparser_load(const char* ininame);
/*-------------------------------------------------------------------------*/
/**
@brief Parse an ini file and return an allocated dictionary object
@param in File to read.
@param ininame Name of the ini file to read (only used for nicer error messages)
@return Pointer to newly allocated dictionary
This is the parser for ini files. This function is called, providing
the file to be read. It returns a dictionary object that should not
be accessed directly, but through accessor functions instead.
Iff the value is a quoted string it supports some escape sequences:
- \" or ' : the quote character
(e.g. 'String with "Quotes"' or "String with 'Quotes'")
- \ : the backslash character (e.g. "C:\tmp")
Escape sequences always start with a backslash. Additional escape sequences
might be added in the future. Backslash characters must be escaped. Any other
sequence then those outlined above is invalid and may lead to unpredictable
results.
The returned dictionary must be freed using iniparser_freedict().
*/
/*--------------------------------------------------------------------------*/
dictionary* iniparser_load_file(FILE* in, const char* ininame);
/*-------------------------------------------------------------------------*/
/**
@brief Free all memory associated to an ini dictionary
@param d Dictionary to free
Free all memory associated to an ini dictionary.
It is mandatory to call this function before the dictionary object
gets out of the current context.
*/
/*--------------------------------------------------------------------------*/
void iniparser_freedict(dictionary* d);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,47 +0,0 @@
#ifndef __PAFSTD__
#define __PAFSTD__
#include <inttypes.h>
#include <paf/std/stdc.h>
#define malloc sce_paf_malloc
#define memcpy sce_paf_memcpy
#define memset sce_paf_memset
#define memmove sce_paf_memmove
#define free sce_paf_free
#define calloc sce_paf_calloc
#define strcmp sce_paf_strcmp
#define strlen sce_paf_strlen
#define strncmp sce_paf_strncmp
#define strchr sce_paf_strchr
#define strtol sce_paf_strtol
#define atof sce_paf_atof
// _ctype_ isnt called that in scelibc, so just stub the functions that are needed
inline int _is_space(char c)
{
return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v');
}
inline int _to_lower(int c)
{
if (c >= 'A' && c <= 'Z') {
return c + ('a' - 'A');
}
return c;
}
inline int _is_digit(int c)
{
return (c >= '0' && c <= '9');
}
#undef isspace
#undef tolower
#undef isdigit
#define isspace _is_space
#define tolower _to_lower
#define isdigit _is_digit
#endif

View File

@ -176,16 +176,19 @@ struct Config {
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: {
@ -206,18 +209,21 @@ struct Config {
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: {
@ -384,7 +390,7 @@ void open_settings()
g_appSetIf->ShowFooter();
}
int paf_main(void)
int main()
{
paf::Framework::InitParam fwParam;
fwParam.mode = paf::Framework::Mode_Normal;

View File

@ -1,56 +0,0 @@
#include <paf.h>
#include <psp2/kernel/clib.h>
#include <psp2/kernel/modulemgr.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/sysmodule.h>
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);
}
int paf_main(void);
extern "C" int module_start(SceSize args, void* argp)
{
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
);
}
res = paf_main();
sceKernelExitProcess(res);
return SCE_KERNEL_START_SUCCESS;
}
extern "C" void _start()
{
module_start(0, nullptr);
}

View File

@ -0,0 +1,169 @@
#include <paf.h>
#include <psp2/kernel/clib.h>
#include <psp2/kernel/modulemgr.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/sysmodule.h>
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);
}

View File

@ -70,9 +70,12 @@ if(NOT (VITA OR WINDOWS_STORE))
endif()
if(VITA)
add_subdirectory(src/d3drm/backends/gxm/shaders)
target_sources(miniwin PRIVATE
src/d3drm/backends/gxm/renderer.cpp
src/d3drm/backends/gxm/memory.cpp
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
@ -80,11 +83,10 @@ if(VITA)
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)
# for shaders incbin
target_include_directories(miniwin PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/d3drm/backends/gxm)
endif()
if(NINTENDO_3DS)

View File

@ -0,0 +1,680 @@
#include "gxm_context.h"
#include "gxm_memory.h"
#include "shaders/gxm_shaders.h"
#include "tlsf.h"
#include "utils.h"
#include <SDL3/SDL.h>
#include <psp2/common_dialog.h>
#include <psp2/display.h>
#include <psp2/gxm.h>
#include <psp2/kernel/sysmem.h>
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<GXMVertex2D*>(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<uint16_t*>(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;
}

View File

@ -1,9 +1,13 @@
#pragma once
#include "tlsf.h"
#include <psp2/gxm.h>
#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 {
@ -72,7 +76,7 @@ typedef struct GXMContext {
// allocator
SceUID cdramUID;
void* cdramMem;
tlsf_t cdramPool;
void* cdramPool;
bool sceneStarted;
@ -96,3 +100,6 @@ typedef struct GXMContext {
// global so that common dialog can be rendererd without GXMRenderer
extern GXMContext* gxm;
extern bool with_razor_capture;
extern bool with_razor_hud;

View File

@ -1,4 +1,5 @@
#include "gxm_memory.h"
#include "tlsf.h"
#include "utils.h"
@ -7,12 +8,6 @@
#include <psp2/kernel/clib.h>
#include <psp2/kernel/sysmem.h>
#define CDRAM_POOL_SIZE 64 * 1024 * 1024
static SceUID cdramAllocatorUID = -1;
static tlsf_t cdramAllocator = nullptr;
int inuse_mem = 0;
void* patcher_host_alloc(void* user_data, unsigned int size)
{
void* mem = SDL_malloc(size);

View File

@ -3,6 +3,7 @@
#include "gxm_memory.h"
#include "meshutils.h"
#include "razor.h"
#include "shaders/gxm_shaders.h"
#include "tlsf.h"
#include "utils.h"
@ -15,12 +16,6 @@
#include <psp2/kernel/sysmem.h>
#include <psp2/types.h>
#include <string>
#define INCBIN_PREFIX _inc_
#include "incbin.h"
static bool with_razor_capture = false;
static bool with_razor_hud = false;
static bool gxm_initialized = false;
// from isleapp
extern bool g_dpadUp;
@ -28,748 +23,18 @@ extern bool g_dpadDown;
extern bool g_dpadLeft;
extern bool g_dpadRight;
#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 CDRAM_POOL_SIZE 64 * 1024 * 1024
#define VITA_GXM_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8
#define VITA_GXM_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8
#define SCE_GXM_PRECOMPUTED_ALIGNMENT 16
#define INCSHADER(filename, name) \
INCBIN(name, filename); \
const SceGxmProgram* name = (const SceGxmProgram*) _inc_##name##Data;
INCSHADER("shaders/main.vert.gxp", mainVertexProgramGxp);
INCSHADER("shaders/main.color.frag.gxp", mainColorFragmentProgramGxp);
INCSHADER("shaders/main.texture.frag.gxp", mainTextureFragmentProgramGxp);
INCSHADER("shaders/plane.vert.gxp", planeVertexProgramGxp);
INCSHADER("shaders/image.frag.gxp", imageFragmentProgramGxp);
INCSHADER("shaders/color.frag.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,
};
typedef struct GXMVertex {
float position[3];
float normal[3];
float texCoord[2];
} GXMVertex;
typedef struct GXMDisplayData {
void* address;
int index;
} GXMDisplayData;
static void display_callback(const void* callback_data)
{
const GXMDisplayData* display_data = (const GXMDisplayData*) callback_data;
GXMRenderer* renderer = static_cast<GXMRenderer*>(DDRenderer);
renderer->DeferredDelete(display_data->index);
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
#include <taihen.h>
static int load_skprx(const char* name)
{
int modid = taiLoadKernelModule(name, 0, nullptr);
if (modid < 0) {
sceClibPrintf("%s load: 0x%08x\n", name, modid);
return modid;
}
int status;
int ret = taiStartKernelModule(modid, 0, nullptr, 0, nullptr, &status);
if (ret < 0) {
sceClibPrintf("%s start: 0x%08x\n", name, ret);
}
return ret;
}
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()
{
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<GXMVertex2D*>(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<uint16_t*>(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;
}
Direct3DRMRenderer* GXMRenderer::Create(DWORD width, DWORD height, DWORD msaaSamples)
{
int ret = gxm_library_init();
if (ret < 0) {
return nullptr;
}
return new GXMRenderer(width, height, msaaSamples);
}
GXMRenderer::GXMRenderer(DWORD width, DWORD height, DWORD msaaSamples)
{
m_width = VITA_GXM_SCREEN_WIDTH;
m_height = VITA_GXM_SCREEN_HEIGHT;
m_virtualWidth = width;
m_virtualHeight = height;
SceGxmMultisampleMode msaaMode = SCE_GXM_MULTISAMPLE_NONE;
if (msaaSamples == 2) {
@ -779,18 +44,27 @@ GXMRenderer::GXMRenderer(DWORD width, DWORD height, DWORD msaaSamples)
msaaMode = SCE_GXM_MULTISAMPLE_4X;
}
int ret;
if (!gxm) {
gxm = (GXMContext*) SDL_malloc(sizeof(GXMContext));
memset(gxm, 0, sizeof(GXMContext));
}
ret = SCE_ERR(gxm->init, msaaMode);
if (ret < 0) {
return;
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
ret = SCE_ERR(
int ret = SCE_ERR(
sceGxmShaderPatcherRegisterProgram,
gxm->shaderPatcher,
mainVertexProgramGxp,
@ -1516,6 +790,8 @@ void GXMRenderer::StartScene()
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;

View File

@ -1,415 +0,0 @@
/**
* @file incbin.h
* @author Dale Weiler
* @brief Utility for including binary files
*
* Facilities for including binary files into the current translation unit and
* making use from them externally in other translation units.
*/
#ifndef INCBIN_HDR
#define INCBIN_HDR
#include <limits.h>
#if defined(__AVX512BW__) || defined(__AVX512CD__) || defined(__AVX512DQ__) || defined(__AVX512ER__) || \
defined(__AVX512PF__) || defined(__AVX512VL__) || defined(__AVX512F__)
#define INCBIN_ALIGNMENT_INDEX 6
#elif defined(__AVX__) || defined(__AVX2__)
#define INCBIN_ALIGNMENT_INDEX 5
#elif defined(__SSE__) || defined(__SSE2__) || defined(__SSE3__) || defined(__SSSE3__) || defined(__SSE4_1__) || \
defined(__SSE4_2__) || defined(__neon__) || defined(__ARM_NEON) || defined(__ALTIVEC__)
#define INCBIN_ALIGNMENT_INDEX 4
#elif ULONG_MAX != 0xffffffffu
#define INCBIN_ALIGNMENT_INDEX 3
#else
#define INCBIN_ALIGNMENT_INDEX 2
#endif
/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
#define INCBIN_ALIGN_SHIFT_0 1
#define INCBIN_ALIGN_SHIFT_1 2
#define INCBIN_ALIGN_SHIFT_2 4
#define INCBIN_ALIGN_SHIFT_3 8
#define INCBIN_ALIGN_SHIFT_4 16
#define INCBIN_ALIGN_SHIFT_5 32
#define INCBIN_ALIGN_SHIFT_6 64
/* Actual alignment value */
#define INCBIN_ALIGNMENT INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), INCBIN_ALIGNMENT_INDEX)
/* Stringize */
#define INCBIN_STR(X) #X
#define INCBIN_STRINGIZE(X) INCBIN_STR(X)
/* Concatenate */
#define INCBIN_CAT(X, Y) X##Y
#define INCBIN_CONCATENATE(X, Y) INCBIN_CAT(X, Y)
/* Deferred macro expansion */
#define INCBIN_EVAL(X) X
#define INCBIN_INVOKE(N, ...) INCBIN_EVAL(N(__VA_ARGS__))
/* Variable argument count for overloading by arity */
#define INCBIN_VA_ARG_COUNTER(_1, _2, _3, N, ...) N
#define INCBIN_VA_ARGC(...) INCBIN_VA_ARG_COUNTER(__VA_ARGS__, 3, 2, 1, 0)
/* Green Hills uses a different directive for including binary data */
#if defined(__ghs__)
#if (__ghs_asm == 2)
#define INCBIN_MACRO ".file"
/* Or consider the ".myrawdata" entry in the ld file */
#else
#define INCBIN_MACRO "\tINCBIN"
#endif
#else
#define INCBIN_MACRO ".incbin"
#endif
#ifndef _MSC_VER
#define INCBIN_ALIGN __attribute__((aligned(INCBIN_ALIGNMENT)))
#else
#define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
#endif
#if defined(__arm__) || /* GNU C and RealView */ \
defined(__arm) || /* Diab */ \
defined(_ARM) /* ImageCraft */
#define INCBIN_ARM
#endif
#ifdef __GNUC__
/* Utilize .balign where supported */
#define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
#define INCBIN_ALIGN_BYTE ".balign 1\n"
#elif defined(INCBIN_ARM)
/*
* On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
* the shift count. This is the value passed to `.align'
*/
#define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
#define INCBIN_ALIGN_BYTE ".align 0\n"
#else
/* We assume other inline assembler's treat `.align' as `.balign' */
#define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
#define INCBIN_ALIGN_BYTE ".align 1\n"
#endif
/* INCBIN_CONST is used by incbin.c generated files */
#if defined(__cplusplus)
#define INCBIN_EXTERNAL extern "C"
#define INCBIN_CONST extern const
#else
#define INCBIN_EXTERNAL extern
#define INCBIN_CONST const
#endif
/**
* @brief Optionally override the linker section into which size and data is
* emitted.
*
* @warning If you use this facility, you might have to deal with
* platform-specific linker output section naming on your own.
*/
#if !defined(INCBIN_OUTPUT_SECTION)
#if defined(__APPLE__)
#define INCBIN_OUTPUT_SECTION ".const_data"
#else
#define INCBIN_OUTPUT_SECTION ".rodata"
#endif
#endif
/**
* @brief Optionally override the linker section into which data is emitted.
*
* @warning If you use this facility, you might have to deal with
* platform-specific linker output section naming on your own.
*/
#if !defined(INCBIN_OUTPUT_DATA_SECTION)
#define INCBIN_OUTPUT_DATA_SECTION INCBIN_OUTPUT_SECTION
#endif
/**
* @brief Optionally override the linker section into which size is emitted.
*
* @warning If you use this facility, you might have to deal with
* platform-specific linker output section naming on your own.
*
* @note This is useful for Harvard architectures where program memory cannot
* be directly read from the program without special instructions. With this you
* can chose to put the size variable in RAM rather than ROM.
*/
#if !defined(INCBIN_OUTPUT_SIZE_SECTION)
#define INCBIN_OUTPUT_SIZE_SECTION INCBIN_OUTPUT_SECTION
#endif
#if defined(__APPLE__)
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && !defined(INCBIN_SILENCE_BITCODE_WARNING)
#warning \
"incbin is incompatible with bitcode. Using the library will break upload to App Store if you have bitcode enabled. Add `#define INCBIN_SILENCE_BITCODE_WARNING` before including this header to silence this warning."
#endif
/* The directives are different for Apple branded compilers */
#define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
#define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
#define INCBIN_INT ".long "
#define INCBIN_MANGLE "_"
#define INCBIN_BYTE ".byte "
#define INCBIN_TYPE(...)
#else
#define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
#define INCBIN_GLOBAL(NAME) ".global " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
#if defined(__ghs__)
#define INCBIN_INT ".word "
#else
#define INCBIN_INT ".int "
#endif
#if defined(__USER_LABEL_PREFIX__)
#define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
#else
#define INCBIN_MANGLE ""
#endif
#if defined(INCBIN_ARM)
/* On arm assemblers, `@' is used as a line comment token */
#define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
#elif defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__)
/* Mingw and Cygwin don't support this directive either */
#define INCBIN_TYPE(NAME)
#else
/* It's safe to use `@' on other architectures */
#define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
#endif
#define INCBIN_BYTE ".byte "
#endif
/* List of style types used for symbol names */
#define INCBIN_STYLE_CAMEL 0
#define INCBIN_STYLE_SNAKE 1
/**
* @brief Specify the prefix to use for symbol names.
*
* @note By default this is "g".
*
* @code
* #define INCBIN_PREFIX incbin
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
*
* // Now you have the following symbols instead:
* // const unsigned char incbinFoo<data>[];
* // const unsigned char *const incbinFoo<end>;
* // const unsigned int incbinFoo<size>;
* @endcode
*/
#if !defined(INCBIN_PREFIX)
#define INCBIN_PREFIX g
#endif
/**
* @brief Specify the style used for symbol names.
*
* Possible options are
* - INCBIN_STYLE_CAMEL "CamelCase"
* - INCBIN_STYLE_SNAKE "snake_case"
*
* @note By default this is INCBIN_STYLE_CAMEL
*
* @code
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
* #include "incbin.h"
* INCBIN(foo, "foo.txt");
*
* // Now you have the following symbols:
* // const unsigned char <prefix>foo_data[];
* // const unsigned char *const <prefix>foo_end;
* // const unsigned int <prefix>foo_size;
* @endcode
*/
#if !defined(INCBIN_STYLE)
#define INCBIN_STYLE INCBIN_STYLE_CAMEL
#endif
/* Style lookup tables */
#define INCBIN_STYLE_0_DATA Data
#define INCBIN_STYLE_0_END End
#define INCBIN_STYLE_0_SIZE Size
#define INCBIN_STYLE_1_DATA _data
#define INCBIN_STYLE_1_END _end
#define INCBIN_STYLE_1_SIZE _size
/* Style lookup: returning identifier */
#define INCBIN_STYLE_IDENT(TYPE) \
INCBIN_CONCATENATE(INCBIN_STYLE_, INCBIN_CONCATENATE(INCBIN_EVAL(INCBIN_STYLE), INCBIN_CONCATENATE(_, TYPE)))
/* Style lookup: returning string literal */
#define INCBIN_STYLE_STRING(TYPE) INCBIN_STRINGIZE(INCBIN_STYLE_IDENT(TYPE))
/* Generate the global labels by indirectly invoking the macro with our style
* type and concatenating the name against them. */
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
INCBIN_INVOKE(INCBIN_GLOBAL, INCBIN_CONCATENATE(NAME, INCBIN_INVOKE(INCBIN_STYLE_IDENT, TYPE))) \
INCBIN_INVOKE(INCBIN_TYPE, INCBIN_CONCATENATE(NAME, INCBIN_INVOKE(INCBIN_STYLE_IDENT, TYPE)))
/**
* @brief Externally reference binary data included in another translation unit.
*
* Produces three external symbols that reference the binary data included in
* another translation unit.
*
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
* "Data", as well as "End" and "Size" after. An example is provided below.
*
* @param TYPE Optional array type. Omitting this picks a default of `unsigned char`.
* @param NAME The name given for the binary data
*
* @code
* INCBIN_EXTERN(Foo);
*
* // Now you have the following symbols:
* // extern const unsigned char <prefix>Foo<data>[];
* // extern const unsigned char *const <prefix>Foo<end>;
* // extern const unsigned int <prefix>Foo<size>;
* @endcode
*
* You may specify a custom optional data type as well as the first argument.
* @code
* INCBIN_EXTERN(custom_type, Foo);
*
* // Now you have the following symbols:
* // extern const custom_type <prefix>Foo<data>[];
* // extern const custom_type *const <prefix>Foo<end>;
* // extern const unsigned int <prefix>Foo<size>;
* @endcode
*/
#define INCBIN_EXTERN(...) INCBIN_CONCATENATE(INCBIN_EXTERN_, INCBIN_VA_ARGC(__VA_ARGS__))(__VA_ARGS__)
#define INCBIN_EXTERN_1(NAME, ...) INCBIN_EXTERN_2(unsigned char, NAME)
#define INCBIN_EXTERN_2(TYPE, NAME) \
INCBIN_EXTERNAL const INCBIN_ALIGN TYPE \
INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), INCBIN_STYLE_IDENT(DATA))[]; \
INCBIN_EXTERNAL const INCBIN_ALIGN \
TYPE* const INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), INCBIN_STYLE_IDENT(END)); \
INCBIN_EXTERNAL const unsigned int INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
INCBIN_STYLE_IDENT(SIZE) \
)
/**
* @brief Externally reference textual data included in another translation unit.
*
* Produces three external symbols that reference the textual data included in
* another translation unit.
*
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
* "Data", as well as "End" and "Size" after. An example is provided below.
*
* @param NAME The name given for the textual data
*
* @code
* INCBIN_EXTERN(Foo);
*
* // Now you have the following symbols:
* // extern const char <prefix>Foo<data>[];
* // extern const char *const <prefix>Foo<end>;
* // extern const unsigned int <prefix>Foo<size>;
* @endcode
*/
#define INCTXT_EXTERN(NAME) INCBIN_EXTERN_2(char, NAME)
/**
* @brief Include a binary file into the current translation unit.
*
* Includes a binary file into the current translation unit, producing three symbols
* for objects that encode the data and size respectively.
*
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
* "Data", as well as "End" and "Size" after. An example is provided below.
*
* @param TYPE Optional array type. Omitting this picks a default of `unsigned char`.
* @param NAME The name to associate with this binary data (as an identifier.)
* @param FILENAME The file to include (as a string literal.)
*
* @code
* INCBIN(Icon, "icon.png");
*
* // Now you have the following symbols:
* // const unsigned char <prefix>Icon<data>[];
* // const unsigned char *const <prefix>Icon<end>;
* // const unsigned int <prefix>Icon<size>;
* @endcode
*
* You may specify a custom optional data type as well as the first argument.
* These macros are specialized by arity.
* @code
* INCBIN(custom_type, Icon, "icon.png");
*
* // Now you have the following symbols:
* // const custom_type <prefix>Icon<data>[];
* // const custom_type *const <prefix>Icon<end>;
* // const unsigned int <prefix>Icon<size>;
* @endcode
*
* @warning This must be used in global scope
* @warning The identifiers may be different if INCBIN_STYLE is not default
*
* To externally reference the data included by this in another translation unit
* please @see INCBIN_EXTERN.
*/
#ifdef _MSC_VER
#define INCBIN(NAME, FILENAME) INCBIN_EXTERN(NAME)
#else
#define INCBIN(...) INCBIN_CONCATENATE(INCBIN_, INCBIN_VA_ARGC(__VA_ARGS__))(__VA_ARGS__)
#if defined(__GNUC__)
#define INCBIN_1(...) _Pragma("GCC error \"Single argument INCBIN not allowed\"")
#elif defined(__clang__)
#define INCBIN_1(...) _Pragma("clang error \"Single argument INCBIN not allowed\"")
#else
#define INCBIN_1(...) /* Cannot do anything here */
#endif
#define INCBIN_2(NAME, FILENAME) INCBIN_3(unsigned char, NAME, FILENAME)
#define INCBIN_3(TYPE, NAME, FILENAME) INCBIN_COMMON(TYPE, NAME, FILENAME, /* No terminator for binary data */)
#define INCBIN_COMMON(TYPE, NAME, FILENAME, TERMINATOR) \
__asm__(INCBIN_SECTION INCBIN_GLOBAL_LABELS(NAME, DATA) \
INCBIN_ALIGN_HOST INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA \
) ":\n" INCBIN_MACRO " \"" FILENAME "\"\n" TERMINATOR INCBIN_GLOBAL_LABELS(NAME, END) \
INCBIN_ALIGN_BYTE INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END \
) ":\n" INCBIN_BYTE "1\n" INCBIN_GLOBAL_LABELS(NAME, SIZE) \
INCBIN_ALIGN_HOST INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE \
) ":\n" INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END \
) " - " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA \
) "\n" INCBIN_ALIGN_HOST ".text\n"); \
INCBIN_EXTERN(TYPE, NAME)
#endif
/**
* @brief Include a textual file into the current translation unit.
*
* This behaves the same as INCBIN except it produces char compatible arrays
* and implicitly adds a null-terminator byte, thus the size of data included
* by this is one byte larger than that of INCBIN.
*
* Includes a textual file into the current translation unit, producing three
* symbols for objects that encode the data and size respectively.
*
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
* "Data", as well as "End" and "Size" after. An example is provided below.
*
* @param NAME The name to associate with this binary data (as an identifier.)
* @param FILENAME The file to include (as a string literal.)
*
* @code
* INCTXT(Readme, "readme.txt");
*
* // Now you have the following symbols:
* // const char <prefix>Readme<data>[];
* // const char *const <prefix>Readme<end>;
* // const unsigned int <prefix>Readme<size>;
* @endcode
*
* @warning This must be used in global scope
* @warning The identifiers may be different if INCBIN_STYLE is not default
*
* To externally reference the data included by this in another translation unit
* please @see INCBIN_EXTERN.
*/
#if defined(_MSC_VER)
#define INCTXT(NAME, FILENAME) INCBIN_EXTERN(NAME)
#else
#define INCTXT(NAME, FILENAME) INCBIN_COMMON(char, NAME, FILENAME, INCBIN_BYTE "0\n")
#endif
#endif

View File

@ -1,3 +1,2 @@
*.exe
cache/
*.perf.txt
cache/

View File

@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR)
find_program(PSP2CGC NAMES ${CMAKE_CURRENT_SOURCE_DIR}/psp2cgc.exe psp2cgc.exe psp2cgc REQUIRED)
find_program(PSP2SHADERPERF NAMES ${CMAKE_CURRENT_SOURCE_DIR}/psp2shaderperf.exe psp2shaderperf.exe psp2shaderperf REQUIRED)
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 '${INPUT_CG} changed but dont have psp2cgc, cant compile' >&2;
exit 1;
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()

View File

@ -1,27 +0,0 @@
shaders += plane.vert.gxp color.frag.gxp image.frag.gxp
shaders += main.vert.gxp main.color.frag.gxp main.texture.frag.gxp
PERFS = $(shaders:.gxp=.perf.txt)
all: $(shaders)
perfs: $(PERFS)
CGCFLAGS = -Wperf -cache -cachedir cache -W4 -Wsuppress=5206,5203
cache:
mkdir -p cache
%.vert.gxp: %.vert.cg | cache
./psp2cgc.exe $(CGCFLAGS) -profile sce_vp_psp2 $< -o $@
%.frag.gxp: %.frag.cg | cache
./psp2cgc.exe $(CGCFLAGS) -profile sce_fp_psp2 $< -o $@
main.color.frag.gxp: main.frag.cg | cache
./psp2cgc.exe $(CGCFLAGS) -profile sce_fp_psp2 $< -o $@
main.texture.frag.gxp: main.frag.cg | cache
./psp2cgc.exe $(CGCFLAGS) -DTEXTURED=1 -profile sce_fp_psp2 $< -o $@
%.perf.txt: %.gxp
./psp2shaderperf.exe -stats -symbols -disasm $< > $@

View File

@ -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

View File

@ -0,0 +1,32 @@
#include <psp2/gxm.h>
#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,
};

View File

@ -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

View File

@ -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<half4>(uTexture, TEXCOORD0.xy)

View File

@ -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

View File

@ -6,9 +6,9 @@ struct SceneLight {
void main(
float4 vPosition : POSITION,
float2 vTexCoord : TEXCOORD0,
float3 vViewPos : TEXCOORD1,
float3 vNormal : TEXCOORD2,
float2 vTexCoord : TEXCOORD3,
uniform __nostrip SceneLight uLights[2] : BUFFER[0],
uniform __nostrip float3 uAmbientLight : BUFFER[0],

View File

@ -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<float4>(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

View File

@ -8,9 +8,9 @@ void main(
column_major uniform float4x4 uProjectionMatrix,
out float4 vPosition : POSITION,
out float2 vTexCoord : TEXCOORD0,
out float3 vViewPos : TEXCOORD1,
out float3 vNormal : TEXCOORD2,
out float2 vTexCoord : TEXCOORD3
out float3 vNormal : TEXCOORD2
) {
float4 viewPos = mul(uModelViewMatrix, float4(aPosition, 1.0));
vPosition = mul(uProjectionMatrix, viewPos);

View File

@ -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}

View File

@ -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}

View File

@ -1,10 +1,10 @@
#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 "gxm_context.h"
#include <SDL3/SDL.h>
#include <psp2/gxm.h>
@ -52,7 +52,7 @@ struct GXMSceneLightUniform {
class GXMRenderer : public Direct3DRMRenderer {
public:
static Direct3DRMRenderer* Create(DWORD width, DWORD height, DWORD msaaSamples);
GXMRenderer(DWORD width, DWORD height, DWORD msaaSamples);
GXMRenderer(DWORD width, DWORD height, SceGxmMultisampleMode msaaMode);
~GXMRenderer() override;
void PushLights(const SceneLight* lightsArray, size_t count) override;

Binary file not shown.