Add optional colour correction + interframe blending

This commit is contained in:
jdgleaver 2020-09-21 17:50:21 +01:00
parent 4a2848af48
commit aa7feb70ca
11 changed files with 8964 additions and 470 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ gpsp.gpe
tags
cscope.out
pandora/linux
tools/generate_cc_lut

View File

@ -13,7 +13,8 @@ SOURCES_C := $(CORE_DIR)/main.c \
$(CORE_DIR)/sound.c \
$(CORE_DIR)/cheats.c \
$(CORE_DIR)/libretro.c \
$(CORE_DIR)/libco/libco.c
$(CORE_DIR)/libco/libco.c \
$(CORE_DIR)/gba_cc_lut.c
ifeq ($(HAVE_DYNAREC), 1)

6558
gba_cc_lut.c Normal file

File diff suppressed because it is too large Load Diff

8
gba_cc_lut.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef __CC_LUT_H__
#define __CC_LUT_H__
#include "common.h"
extern const u16 gba_cc_lut[];
#endif /* __CC_LUT_H__ */

View File

@ -6,9 +6,11 @@
#include "common.h"
#include "libco.h"
#include "libretro.h"
#include "libretro_core_options.h"
#include "memmap.h"
#include "gba_memory.h"
#include "gba_cc_lut.h"
#if defined(VITA) && defined(HAVE_DYNAREC)
#include <psp2/kernel/sysmem.h>
@ -81,6 +83,13 @@ u32 iwram_stack_optimize = 1;
u32 translation_gate_target_pc[MAX_TRANSLATION_GATES];
u32 translation_gate_targets = 0;
static u16 *gba_screen_pixels_prev = NULL;
static u16 *gba_processed_pixels = NULL;
static void (*video_post_process)(void) = NULL;
static bool post_process_cc = false;
static bool post_process_mix = false;
void switch_to_main_thread(void)
{
co_switch(main_thread);
@ -123,7 +132,178 @@ static uint32_t next_pow2(uint32_t v)
return v;
}
/* Video post processing START */
/* Note: This code is intentionally W.E.T.
* (Write Everything Twice). These functions
* are performance critical, and we cannot
* afford to do unnecessary comparisons/switches
* inside the inner for loops */
static void video_post_process_cc(void)
{
uint16_t *src = gba_screen_pixels;
uint16_t *dst = gba_processed_pixels;
size_t x, y;
for (y = 0; y < GBA_SCREEN_HEIGHT; y++)
{
for (x = 0; x < GBA_SCREEN_PITCH; x++)
{
u16 src_color = *(src + x);
/* Convert colour to RGB555 and perform lookup */
*(dst + x) = *(gba_cc_lut + (((src_color & 0xFFC0) >> 1) | (src_color & 0x1F)));
}
src += GBA_SCREEN_PITCH;
dst += GBA_SCREEN_PITCH;
}
}
static void video_post_process_mix(void)
{
uint16_t *src_curr = gba_screen_pixels;
uint16_t *src_prev = gba_screen_pixels_prev;
uint16_t *dst = gba_processed_pixels;
size_t x, y;
for (y = 0; y < GBA_SCREEN_HEIGHT; y++)
{
for (x = 0; x < GBA_SCREEN_PITCH; x++)
{
/* Get colours from current + previous frames (RGB565) */
uint16_t rgb_curr = *(src_curr + x);
uint16_t rgb_prev = *(src_prev + x);
uint16_t r_curr = rgb_curr >> 11 & 0x1F;
uint16_t g_curr = rgb_curr >> 6 & 0x1F;
uint16_t b_curr = rgb_curr & 0x1F;
uint16_t r_prev = rgb_prev >> 11 & 0x1F;
uint16_t g_prev = rgb_prev >> 6 & 0x1F;
uint16_t b_prev = rgb_prev & 0x1F;
/* Store colours for next frame */
*(src_prev + x) = rgb_curr;
/* Mix colours */
uint16_t r_mix = (r_curr >> 1) + (r_prev >> 1) + (((r_curr & 0x1) + (r_prev & 0x1)) >> 1);
uint16_t g_mix = (g_curr >> 1) + (g_prev >> 1) + (((g_curr & 0x1) + (g_prev & 0x1)) >> 1);
uint16_t b_mix = (b_curr >> 1) + (b_prev >> 1) + (((b_curr & 0x1) + (b_prev & 0x1)) >> 1);
/* Convert back to RGB565 and assign
* to current frame */
*(dst + x) = r_mix << 11 | g_mix << 6 | b_mix;
}
src_curr += GBA_SCREEN_PITCH;
src_prev += GBA_SCREEN_PITCH;
dst += GBA_SCREEN_PITCH;
}
}
static void video_post_process_cc_mix(void)
{
uint16_t *src_curr = gba_screen_pixels;
uint16_t *src_prev = gba_screen_pixels_prev;
uint16_t *dst = gba_processed_pixels;
size_t x, y;
for (y = 0; y < GBA_SCREEN_HEIGHT; y++)
{
for (x = 0; x < GBA_SCREEN_PITCH; x++)
{
/* Get colours from current + previous frames (RGB565) */
uint16_t rgb_curr = *(src_curr + x);
uint16_t rgb_prev = *(src_prev + x);
uint16_t r_curr = rgb_curr >> 11 & 0x1F;
uint16_t g_curr = rgb_curr >> 6 & 0x1F;
uint16_t b_curr = rgb_curr & 0x1F;
uint16_t r_prev = rgb_prev >> 11 & 0x1F;
uint16_t g_prev = rgb_prev >> 6 & 0x1F;
uint16_t b_prev = rgb_prev & 0x1F;
/* Store colours for next frame */
*(src_prev + x) = rgb_curr;
/* Mix colours */
uint16_t r_mix = (r_curr >> 1) + (r_prev >> 1) + (((r_curr & 0x1) + (r_prev & 0x1)) >> 1);
uint16_t g_mix = (g_curr >> 1) + (g_prev >> 1) + (((g_curr & 0x1) + (g_prev & 0x1)) >> 1);
uint16_t b_mix = (b_curr >> 1) + (b_prev >> 1) + (((b_curr & 0x1) + (b_prev & 0x1)) >> 1);
/* Convert colour to RGB555, perform lookup
* and assign to current frame */
*(dst + x) = *(gba_cc_lut + (r_mix << 10 | g_mix << 5 | b_mix));
}
src_curr += GBA_SCREEN_PITCH;
src_prev += GBA_SCREEN_PITCH;
dst += GBA_SCREEN_PITCH;
}
}
static void init_post_processing(void)
{
size_t buf_size = GBA_SCREEN_PITCH * GBA_SCREEN_HEIGHT * sizeof(u16);
video_post_process = NULL;
/* If post processing is disabled, return
* immediately */
if (!post_process_cc && !post_process_mix)
return;
/* Initialise output buffer, if required */
if (!gba_processed_pixels &&
(post_process_cc || post_process_mix))
{
#ifdef _3DS
gba_processed_pixels = (u16*)linearMemAlign(buf_size, 128);
#else
gba_processed_pixels = (u16*)malloc(buf_size);
#endif
if (!gba_processed_pixels)
return;
memset(gba_processed_pixels, 0xFFFF, buf_size);
}
/* Initialise 'history' buffer, if required */
if (!gba_screen_pixels_prev &&
post_process_mix)
{
gba_screen_pixels_prev = (u16*)malloc(buf_size);
if (!gba_screen_pixels_prev)
return;
memset(gba_screen_pixels_prev, 0xFFFF, buf_size);
}
/* Assign post processing function */
if (post_process_cc && post_process_mix)
video_post_process = video_post_process_cc_mix;
else if (post_process_cc)
video_post_process = video_post_process_cc;
else if (post_process_mix)
video_post_process = video_post_process_mix;
}
/* Video post processing END */
static void video_run(void) {
u16 *gba_screen_pixels_buf = gba_screen_pixels;
if (video_post_process)
{
video_post_process();
gba_screen_pixels_buf = gba_processed_pixels;
}
#if defined(PSP)
static unsigned int __attribute__((aligned(16))) d_list[32];
void* texture_vram_p = NULL;
@ -131,12 +311,12 @@ static void video_run(void) {
texture_vram_p = (void*) (0x44200000 - texture_size); /* max VRAM address - frame size */
sceKernelDcacheWritebackRange(gba_screen_pixels, texture_size);
sceKernelDcacheWritebackRange(gba_screen_pixels_buf, texture_size);
sceGuStart(GU_DIRECT, d_list);
sceGuTexMode(GU_PSM_5650, 0, 0, GU_FALSE);
sceGuCopyImage(GU_PSM_5650, 0, 0, GBA_SCREEN_WIDTH, GBA_SCREEN_HEIGHT, GBA_SCREEN_WIDTH,
gba_screen_pixels, 0, 0, GBA_SCREEN_WIDTH, texture_vram_p);
gba_screen_pixels_buf, 0, 0, GBA_SCREEN_WIDTH, texture_vram_p);
sceGuTexImage(0, next_pow2(GBA_SCREEN_WIDTH), next_pow2(GBA_SCREEN_HEIGHT), GBA_SCREEN_WIDTH, texture_vram_p);
sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB);
sceGuDisable(GU_BLEND);
@ -146,7 +326,7 @@ static void video_run(void) {
video_cb(texture_vram_p, GBA_SCREEN_WIDTH, GBA_SCREEN_HEIGHT,
GBA_SCREEN_PITCH * 2);
#else
video_cb(gba_screen_pixels, GBA_SCREEN_WIDTH, GBA_SCREEN_HEIGHT,
video_cb(gba_screen_pixels_buf, GBA_SCREEN_WIDTH, GBA_SCREEN_HEIGHT,
GBA_SCREEN_PITCH * 2);
#endif
}
@ -197,7 +377,6 @@ void retro_get_system_av_info(struct retro_system_av_info* info)
void retro_init(void)
{
#if defined(_3DS) && defined(HAVE_DYNAREC)
if (__ctr_svchax && !translation_caches_inited)
{
@ -311,10 +490,20 @@ void retro_deinit(void)
#ifdef _3DS
linearFree(gba_screen_pixels);
if (gba_processed_pixels)
linearFree(gba_processed_pixels);
#else
free(gba_screen_pixels);
if (gba_processed_pixels)
free(gba_processed_pixels);
#endif
gba_screen_pixels = NULL;
if (gba_screen_pixels_prev)
free(gba_screen_pixels_prev);
gba_screen_pixels = NULL;
gba_processed_pixels = NULL;
gba_screen_pixels_prev = NULL;
video_post_process = NULL;
}
static retro_time_t retro_perf_dummy_get_time_usec() { return 0; }
@ -327,25 +516,6 @@ void retro_set_environment(retro_environment_t cb)
{
struct retro_log_callback log;
static struct retro_variable vars[] = {
#ifdef HAVE_DYNAREC
{ "gpsp_drc", "Dynamic recompiler (restart); enabled|disabled" },
#endif
{ "gpsp_frameskip_type", "Frameskip type; off|manual|automatic" },
{ "gpsp_frameskip_value", "Frameskip value; 1|2|3|4|5|6|7|8|9" },
{ "gpsp_frameskip_variation", "Frameskip variation; uniform|random" },
{ "gpsp_save_method", "Backup Save Method (Restart); gpSP|libretro" },
{ NULL, NULL },
};
#if defined(_3DS) && (HAVE_DYNAREC)
if(!__ctr_svchax)
{
vars[0].key = 0;
vars[0].value = NULL;
}
#endif
environ_cb = cb;
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
@ -363,7 +533,8 @@ void retro_set_environment(retro_environment_t cb)
retro_perf_dummy_log,
};
environ_cb(RETRO_ENVIRONMENT_GET_PERF_INTERFACE, &perf_cb);
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
libretro_set_core_options(environ_cb);
}
void retro_set_video_refresh(retro_video_refresh_t cb)
@ -446,6 +617,8 @@ static void extract_directory(char* buf, const char* path, size_t size)
static void check_variables(int started_from_load)
{
struct retro_variable var;
bool post_process_cc_prev;
bool post_process_mix_prev;
#ifdef HAVE_DYNAREC
var.key = "gpsp_drc";
@ -466,7 +639,7 @@ static void check_variables(int started_from_load)
#endif
var.key = "gpsp_frameskip_value";
var.value = 0;
var.value = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
frameskip_value = strtol(var.value, NULL, 10);
@ -492,6 +665,34 @@ static void check_variables(int started_from_load)
random_skip = 1;
}
var.key = "gpsp_color_correction";
var.value = NULL;
post_process_cc_prev = post_process_cc;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (strcmp(var.value, "disabled") == 0)
post_process_cc = false;
else if (strcmp(var.value, "enabled") == 0)
post_process_cc = true;
}
var.key = "gpsp_frame_mixing";
var.value = NULL;
post_process_mix_prev = post_process_mix;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (strcmp(var.value, "disabled") == 0)
post_process_mix = false;
else if (strcmp(var.value, "enabled") == 0)
post_process_mix = true;
}
/* Check whether post processing options
* have changed */
if ((post_process_cc != post_process_cc_prev) ||
(post_process_mix != post_process_mix_prev))
init_post_processing();
if (started_from_load)
{
var.key = "gpsp_save_method";

1986
libretro.h

File diff suppressed because it is too large Load Diff

351
libretro_core_options.h Normal file
View File

@ -0,0 +1,351 @@
#ifndef LIBRETRO_CORE_OPTIONS_H__
#define LIBRETRO_CORE_OPTIONS_H__
#include <stdlib.h>
#include <string.h>
#include <libretro.h>
#include <retro_inline.h>
#ifndef HAVE_NO_LANGEXTRA
#include "libretro_core_options_intl.h"
#endif
#if defined(_3DS) && defined(HAVE_DYNAREC)
#include "3ds/3ds_utils.h"
#endif
/*
********************************
* VERSION: 1.3
********************************
*
* - 1.3: Move translations to libretro_core_options_intl.h
* - libretro_core_options_intl.h includes BOM and utf-8
* fix for MSVC 2010-2013
* - Added HAVE_NO_LANGEXTRA flag to disable translations
* on platforms/compilers without BOM support
* - 1.2: Use core options v1 interface when
* RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1
* (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)
* - 1.1: Support generation of core options v0 retro_core_option_value
* arrays containing options with a single value
* - 1.0: First commit
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
********************************
* Core Option Definitions
********************************
*/
/* RETRO_LANGUAGE_ENGLISH */
/* Default language:
* - All other languages must include the same keys and values
* - Will be used as a fallback in the event that frontend language
* is not available
* - Will be used as a fallback for any missing entries in
* frontend language definition */
struct retro_core_option_definition option_defs_us[] = {
{
"gpsp_frameskip_type",
"Frameskip Type",
"Skip frames to improve performance at the expense of visual smoothness. 'Manual' skips frames at an interval set by 'Frameskip Value' and 'Frameskip Variation'. 'Automatic' attempts to adjust frame skipping based on CPU load.",
{
{ "off", "disabled" },
{ "manual", "Manual" },
{ "automatic", "Automatic" },
{ NULL, NULL },
},
"off"
},
{
"gpsp_frameskip_value",
"Frameskip Value",
"When 'Frameskip Type' is 'Manual', the value set here is the number of frames omitted after a frame is rendered - i.e. '1' = 30fps, '2' = 15fps, etc.",
{
{ "1", NULL },
{ "2", NULL },
{ "3", NULL },
{ "4", NULL },
{ "5", NULL },
{ "6", NULL },
{ "7", NULL },
{ "8", NULL },
{ "9", NULL },
{ NULL, NULL },
},
"1"
},
{
"gpsp_frameskip_variation",
"Frameskip Variation",
"When 'Frameskip Type' is 'Manual', specifies whether frame skipping should occur at regular ('Uniform') intervals, or with an element of 'Random' variation (may help reduce the visual impact of stuttering).",
{
{ "uniform", "Uniform" },
{ "random", "Random" },
{ NULL, NULL },
},
"uniform"
},
{
"gpsp_color_correction",
"Color Correction",
"Adjusts output colors to match the display of real GBA hardware.",
{
{ "enabled", NULL },
{ "disabled", NULL },
{ NULL, NULL },
},
"disabled"
},
{
"gpsp_frame_mixing",
"Interframe Blending",
"Simulates LCD ghosting effects by performing a 50:50 mix of the current and previous frames. Required for correct operation when playing games that exploit LCD ghosting for transparency effects (F-Zero, the Boktai series, etc.).",
{
{ "enabled", NULL },
{ "disabled", NULL },
{ NULL, NULL },
},
"disabled"
},
{
"gpsp_save_method",
"Backup Save Method (Restart)",
"Choose the data format used for cartridge save files. 'gpSP' can be used for compatibility with the stand-alone version of gpSP. 'libretro' provides better integration with the frontend.",
{
{ "gpSP", NULL },
{ "libretro", NULL },
{ NULL, NULL },
},
"gpSP"
},
#if defined(HAVE_DYNAREC)
{
"gpsp_drc",
"Dynamic Recompiler (Restart)",
"Dynamically recompile CPU instructions to native instructions. Greatly improves performance, but may reduce accuracy.",
{
{ "enabled", NULL },
{ "disabled", NULL },
{ NULL, NULL },
},
"enabled"
},
#endif
{ NULL, NULL, NULL, {{0}}, NULL },
};
/*
********************************
* Language Mapping
********************************
*/
#ifndef HAVE_NO_LANGEXTRA
struct retro_core_option_definition *option_defs_intl[RETRO_LANGUAGE_LAST] = {
option_defs_us, /* RETRO_LANGUAGE_ENGLISH */
NULL, /* RETRO_LANGUAGE_JAPANESE */
NULL, /* RETRO_LANGUAGE_FRENCH */
NULL, /* RETRO_LANGUAGE_SPANISH */
NULL, /* RETRO_LANGUAGE_GERMAN */
NULL, /* RETRO_LANGUAGE_ITALIAN */
NULL, /* RETRO_LANGUAGE_DUTCH */
NULL, /* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */
NULL, /* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */
NULL, /* RETRO_LANGUAGE_RUSSIAN */
NULL, /* RETRO_LANGUAGE_KOREAN */
NULL, /* RETRO_LANGUAGE_CHINESE_TRADITIONAL */
NULL, /* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */
NULL, /* RETRO_LANGUAGE_ESPERANTO */
NULL, /* RETRO_LANGUAGE_POLISH */
NULL, /* RETRO_LANGUAGE_VIETNAMESE */
NULL, /* RETRO_LANGUAGE_ARABIC */
NULL, /* RETRO_LANGUAGE_GREEK */
NULL, /* RETRO_LANGUAGE_TURKISH */
NULL, /* RETRO_LANGUAGE_SLOVAK */
NULL, /* RETRO_LANGUAGE_PERSIAN */
NULL, /* RETRO_LANGUAGE_HEBREW */
NULL, /* RETRO_LANGUAGE_ASTURIAN */
};
#endif
/*
********************************
* Functions
********************************
*/
/* Handles configuration/setting of core options.
* Should be called as early as possible - ideally inside
* retro_set_environment(), and no later than retro_load_game()
* > We place the function body in the header to avoid the
* necessity of adding more .c files (i.e. want this to
* be as painless as possible for core devs)
*/
static INLINE void libretro_set_core_options(retro_environment_t environ_cb)
{
unsigned version = 0;
if (!environ_cb)
return;
#if defined(_3DS) && (HAVE_DYNAREC)
if(!__ctr_svchax)
{
/* Critical error - dynarec is force
* disabled, so remove 'gpsp_drc' option */
option_defs_us[6].key = NULL;
option_defs_us[6].desc = NULL;
option_defs_us[6].info = NULL;
option_defs_us[6].default_value = NULL;
}
#endif
if (environ_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version) && (version >= 1))
{
#ifndef HAVE_NO_LANGEXTRA
struct retro_core_options_intl core_options_intl;
unsigned language = 0;
core_options_intl.us = option_defs_us;
core_options_intl.local = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) &&
(language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH))
core_options_intl.local = option_defs_intl[language];
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_intl);
#else
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS, &option_defs_us);
#endif
}
else
{
size_t i;
size_t num_options = 0;
struct retro_variable *variables = NULL;
char **values_buf = NULL;
/* Determine number of options */
for (;;)
{
if (!option_defs_us[num_options].key)
break;
num_options++;
}
/* Allocate arrays */
variables = (struct retro_variable *)calloc(num_options + 1, sizeof(struct retro_variable));
values_buf = (char **)calloc(num_options, sizeof(char *));
if (!variables || !values_buf)
goto error;
/* Copy parameters from option_defs_us array */
for (i = 0; i < num_options; i++)
{
const char *key = option_defs_us[i].key;
const char *desc = option_defs_us[i].desc;
const char *default_value = option_defs_us[i].default_value;
struct retro_core_option_value *values = option_defs_us[i].values;
size_t buf_len = 3;
size_t default_index = 0;
values_buf[i] = NULL;
if (desc)
{
size_t num_values = 0;
/* Determine number of values */
for (;;)
{
if (!values[num_values].value)
break;
/* Check if this is the default value */
if (default_value)
if (strcmp(values[num_values].value, default_value) == 0)
default_index = num_values;
buf_len += strlen(values[num_values].value);
num_values++;
}
/* Build values string */
if (num_values > 0)
{
size_t j;
buf_len += num_values - 1;
buf_len += strlen(desc);
values_buf[i] = (char *)calloc(buf_len, sizeof(char));
if (!values_buf[i])
goto error;
strcpy(values_buf[i], desc);
strcat(values_buf[i], "; ");
/* Default value goes first */
strcat(values_buf[i], values[default_index].value);
/* Add remaining values */
for (j = 0; j < num_values; j++)
{
if (j != default_index)
{
strcat(values_buf[i], "|");
strcat(values_buf[i], values[j].value);
}
}
}
}
variables[i].key = key;
variables[i].value = values_buf[i];
}
/* Set variables */
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables);
error:
/* Clean up */
if (values_buf)
{
for (i = 0; i < num_options; i++)
{
if (values_buf[i])
{
free(values_buf[i]);
values_buf[i] = NULL;
}
}
free(values_buf);
values_buf = NULL;
}
if (variables)
{
free(variables);
variables = NULL;
}
}
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,88 @@
#ifndef LIBRETRO_CORE_OPTIONS_INTL_H__
#define LIBRETRO_CORE_OPTIONS_INTL_H__
#if defined(_MSC_VER) && (_MSC_VER >= 1500 && _MSC_VER < 1900)
/* https://support.microsoft.com/en-us/kb/980263 */
#pragma execution_character_set("utf-8")
#pragma warning(disable:4566)
#endif
#include <libretro.h>
/*
********************************
* VERSION: 1.3
********************************
*
* - 1.3: Move translations to libretro_core_options_intl.h
* - libretro_core_options_intl.h includes BOM and utf-8
* fix for MSVC 2010-2013
* - Added HAVE_NO_LANGEXTRA flag to disable translations
* on platforms/compilers without BOM support
* - 1.2: Use core options v1 interface when
* RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1
* (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)
* - 1.1: Support generation of core options v0 retro_core_option_value
* arrays containing options with a single value
* - 1.0: First commit
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
********************************
* Core Option Definitions
********************************
*/
/* RETRO_LANGUAGE_JAPANESE */
/* RETRO_LANGUAGE_FRENCH */
/* RETRO_LANGUAGE_SPANISH */
/* RETRO_LANGUAGE_GERMAN */
/* RETRO_LANGUAGE_ITALIAN */
/* RETRO_LANGUAGE_DUTCH */
/* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */
/* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */
/* RETRO_LANGUAGE_RUSSIAN */
/* RETRO_LANGUAGE_KOREAN */
/* RETRO_LANGUAGE_CHINESE_TRADITIONAL */
/* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */
/* RETRO_LANGUAGE_ESPERANTO */
/* RETRO_LANGUAGE_POLISH */
/* RETRO_LANGUAGE_VIETNAMESE */
/* RETRO_LANGUAGE_ARABIC */
/* RETRO_LANGUAGE_GREEK */
/* RETRO_LANGUAGE_TURKISH */
/* RETRO_LANGUAGE_SLOVAK */
/* RETRO_LANGUAGE_PERSIAN */
/* RETRO_LANGUAGE_HEBREW */
/* RETRO_LANGUAGE_ASTURIAN */
#ifdef __cplusplus
}
#endif
#endif

39
retro_inline.h Normal file
View File

@ -0,0 +1,39 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_inline.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_INLINE_H
#define __LIBRETRO_SDK_INLINE_H
#ifndef INLINE
#if defined(_WIN32) || defined(__INTEL_COMPILER)
#define INLINE __inline
#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L
#define INLINE inline
#elif defined(__GNUC__)
#define INLINE __inline__
#else
#define INLINE
#endif
#endif
#endif

12
tools/Makefile Normal file
View File

@ -0,0 +1,12 @@
CC = gcc
CFLAGS = -Wall
TARGET = generate_cc_lut
all: $(TARGET)
$(TARGET): $(TARGET).c
$(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c -lm
clean:
$(RM) $(TARGET)

135
tools/generate_cc_lut.c Normal file
View File

@ -0,0 +1,135 @@
#include <stdio.h>
#include <stdint.h>
#include <math.h>
/* gpsp targets devices that are too slow to generate
* a colour correction table at runtime. We therefore
* have to pre-generate the lookup table array... */
/* Colour correction defines */
#define CC_TARGET_GAMMA 2.2f
#define CC_RGB_MAX 31.0f
#define CC_LUM 0.94f
#define CC_R 0.82f
#define CC_G 0.665f
#define CC_B 0.73f
#define CC_RG 0.125f
#define CC_RB 0.195f
#define CC_GR 0.24f
#define CC_GB 0.075f
#define CC_BR -0.06f
#define CC_BG 0.21f
#define CC_GAMMA_ADJ 1.0f
/* Output video is RGB565. This is 16bit,
* but only 15 bits are actually used
* (i.e. 'G' is the highest 5 bits of the
* 6bit component). To save memory, we
* only include 15bit compound values
* and convert RGB565 video to 15bit when
* using the lookup table */
#define CC_LUT_SIZE 32768
static uint16_t c_lut[CC_LUT_SIZE] = {0};
void init_lut(void)
{
size_t color;
float display_gamma_inv = 1.0f / CC_TARGET_GAMMA;
float rgb_max_inv = 1.0f / CC_RGB_MAX;
float adjusted_gamma = CC_TARGET_GAMMA + CC_GAMMA_ADJ;
/* Populate colour correction look-up table */
for (color = 0; color < CC_LUT_SIZE; color++)
{
unsigned r_final = 0;
unsigned g_final = 0;
unsigned b_final = 0;
/* Extract values from RGB555 input */
const unsigned r = color >> 10 & 0x1F;
const unsigned g = color >> 5 & 0x1F;
const unsigned b = color & 0x1F;
/* Perform gamma expansion */
float r_float = pow((float)r * rgb_max_inv, adjusted_gamma);
float g_float = pow((float)g * rgb_max_inv, adjusted_gamma);
float b_float = pow((float)b * rgb_max_inv, adjusted_gamma);
/* Perform colour mangling */
float r_correct = CC_LUM * ((CC_R * r_float) + (CC_GR * g_float) + (CC_BR * b_float));
float g_correct = CC_LUM * ((CC_RG * r_float) + (CC_G * g_float) + (CC_BG * b_float));
float b_correct = CC_LUM * ((CC_RB * r_float) + (CC_GB * g_float) + (CC_B * b_float));
/* Range check... */
r_correct = r_correct > 0.0f ? r_correct : 0.0f;
g_correct = g_correct > 0.0f ? g_correct : 0.0f;
b_correct = b_correct > 0.0f ? b_correct : 0.0f;
/* Perform gamma compression */
r_correct = pow(r_correct, display_gamma_inv);
g_correct = pow(g_correct, display_gamma_inv);
b_correct = pow(b_correct, display_gamma_inv);
/* Range check... */
r_correct = r_correct > 1.0f ? 1.0f : r_correct;
g_correct = g_correct > 1.0f ? 1.0f : g_correct;
b_correct = b_correct > 1.0f ? 1.0f : b_correct;
/* Convert to RGB565 */
r_final = (unsigned)((r_correct * CC_RGB_MAX) + 0.5f) & 0x1F;
g_final = (unsigned)((g_correct * CC_RGB_MAX) + 0.5f) & 0x1F;
b_final = (unsigned)((b_correct * CC_RGB_MAX) + 0.5f) & 0x1F;
c_lut[color] = r_final << 11 | g_final << 6 | b_final;
}
}
int main(int argc, char *argv[])
{
FILE *file = NULL;
size_t i;
/* Populate lookup table */
init_lut();
/* Write header file */
file = fopen("../gba_cc_lut.h", "w");
if (!file)
return 1;
fprintf(file,
"#ifndef __CC_LUT_H__\n"
"#define __CC_LUT_H__\n\n"
"#include \"common.h\"\n\n"
"extern const u16 gba_cc_lut[];\n\n"
"#endif /* __CC_LUT_H__ */\n");
fclose(file);
file = NULL;
/* Write source file */
file = fopen("../gba_cc_lut.c", "w");
if (!file)
return 1;
fprintf(file,
"#include \"gba_cc_lut.h\"\n\n"
"const u16 gba_cc_lut[] = {\n");
for (i = 0; i < CC_LUT_SIZE; i++)
{
fprintf(file, " 0x%04x", c_lut[i]);
if (i == CC_LUT_SIZE - 1)
fprintf(file, "\n");
else
{
if ((i + 1) % 5 == 0)
fprintf(file, ",\n");
else
fprintf(file, ",");
}
}
fprintf(file, "};\n");
fclose(file);
file = NULL;
return 0;
}