From e5befdd02e57b9910e183fadbf5008e9fc1f0762 Mon Sep 17 00:00:00 2001 From: AShiningRay Date: Sun, 10 Nov 2024 14:32:44 -0300 Subject: [PATCH] Introduce FreeJ2ME's first Speed Hack! (No Alpha on Blank Images) This option makes FreeJ2ME create all blank images without an alpha channel, cutting out alpha processing on all of them, which of course also includes the emulated "LCD" screen. Despite its name and function, it appears to be relatively safe despite providing a ridiculous speed up that often goes from 50% to 150%(!) faster as far as rendering goes, with the slower cases being software 3D rasterization that often doesn't rely much on Java Image APIs in general. Will be relegated to a hack because no matter how unlikely, there's bound to be a jar that creates a blank image just to paint it with transparency at some point... and it also deviates from J2ME spec. --- src/libretro/freej2me_libretro.c | 28 ++++++++++----- src/libretro/freej2me_libretro.h | 38 +++++++++++++++++++-- src/org/recompile/freej2me/AWTGUI.java | 22 +++++++++++- src/org/recompile/freej2me/Anbu.java | 4 +++ src/org/recompile/freej2me/Config.java | 10 ++++++ src/org/recompile/freej2me/FreeJ2ME.java | 4 +++ src/org/recompile/freej2me/Libretro.java | 36 +++++++++++++------ src/org/recompile/mobile/Mobile.java | 5 ++- src/org/recompile/mobile/PlatformImage.java | 4 ++- 9 files changed, 127 insertions(+), 24 deletions(-) diff --git a/src/libretro/freej2me_libretro.c b/src/libretro/freej2me_libretro.c index 0b82ecb1..47d3d96a 100755 --- a/src/libretro/freej2me_libretro.c +++ b/src/libretro/freej2me_libretro.c @@ -158,6 +158,9 @@ unsigned int pointerInnerColor = 0x000000; unsigned int pointerOutlineColor = 0xFFFFFF; unsigned int pointerClickedColor = 0xFFFF00; +/* Speed Hack section */ +unsigned int spdHackNoAlpha = 0; // Boolean + /* Libretro exposed config variables END */ /* First byte is the identifier, next four are width and height, and next four are vibration */ @@ -445,11 +448,18 @@ static void check_variables(bool first_time_startup) else if (!strcmp(var.value, "Black")) { pointerClickedColor = 0x000000; } } + var.key = "freej2me_spdhacknoalpha"; + if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (!strcmp(var.value, "off")) { spdHackNoAlpha = 0; } + else if (!strcmp(var.value, "on")) { spdHackNoAlpha = 1; } + } + /* Prepare a string to pass those core options to the Java app */ options_update = malloc(sizeof(char) * PIPE_MAX_LEN); - snprintf(options_update, PIPE_MAX_LEN, "FJ2ME_LR_OPTS:|%lux%lu|%d|%d|%d|%d|%d|%d|%d", screenRes[0], screenRes[1], rotateScreen, phoneType, gameFPS, soundEnabled, customMidi, dumpAudioStreams, loggingLevel); + snprintf(options_update, PIPE_MAX_LEN, "FJ2ME_LR_OPTS:|%lux%lu|%d|%d|%d|%d|%d|%d|%d|%d", screenRes[0], screenRes[1], rotateScreen, phoneType, gameFPS, soundEnabled, customMidi, dumpAudioStreams, loggingLevel, spdHackNoAlpha); optstrlen = strlen(options_update); /* 0xD = 13, which is the special case where the java app will receive the updated configs */ @@ -495,7 +505,7 @@ void retro_init(void) /* Check variables and set parameters */ check_variables(true); - char resArg[2][4], rotateArg[2], phoneArg[2], fpsArg[3], soundArg[2], midiArg[2], dumpAudioArg[2], logLevelArg[2]; + char resArg[2][4], rotateArg[2], phoneArg[2], fpsArg[3], soundArg[2], midiArg[2], dumpAudioArg[2], logLevelArg[2], spdHackNoAlphaArg[2]; sprintf(resArg[0], "%lu", screenRes[0]); sprintf(resArg[1], "%lu", screenRes[1]); sprintf(rotateArg, "%d", rotateScreen); @@ -505,6 +515,7 @@ void retro_init(void) sprintf(midiArg, "%d", customMidi); sprintf(dumpAudioArg, "%d", dumpAudioStreams); sprintf(logLevelArg, "%d", loggingLevel); + sprintf(spdHackNoAlphaArg, "%d", spdHackNoAlpha); /* We need to clean up any argument memory from the previous launch arguments in order to load up updated ones */ if (restarting) @@ -540,7 +551,8 @@ void retro_init(void) params[9] = strdup(midiArg); params[10] = strdup(dumpAudioArg); params[11] = strdup(logLevelArg); - params[12] = NULL; // Null-terminate the array + params[12] = strdup(spdHackNoAlphaArg); + params[13] = NULL; // Null-terminate the array log_fn(RETRO_LOG_INFO, "Preparing to open FreeJ2ME-Plus' Java app.\n"); @@ -1061,8 +1073,8 @@ pid_t javaOpen(char *cmd, char **params) log_fn(RETRO_LOG_INFO, "Setting up java app's process and pipes...\n"); log_fn(RETRO_LOG_INFO, "Opening: %s %s %s ...\n", *(params+0), *(params+1), *(params+2)); - log_fn(RETRO_LOG_INFO, "Params: %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3), - *(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11)); + log_fn(RETRO_LOG_INFO, "Params: %s | %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3), + *(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11), *(params+12)); } else { log_fn(RETRO_LOG_INFO, "Restarting FreeJ2ME.\n"); restarting = false; } @@ -1191,7 +1203,7 @@ void javaOpen(char *cmd, char **params) sprintf(cmdWin, "javaw -jar %s", cmd); log_fn(RETRO_LOG_INFO, "Opening: %s \n", cmdWin); - for (int i = 3; i < 12; i++) + for (int i = 3; i < 13; i++) { //log_fn(RETRO_LOG_INFO, "Processing arg %d: %s \n", i, *(params+i)); sprintf(cmdWin, "%s %s", cmdWin, *(params+i)); @@ -1204,8 +1216,8 @@ void javaOpen(char *cmd, char **params) log_fn(RETRO_LOG_INFO, "Setting up java app's process and pipes...\n"); log_fn(RETRO_LOG_INFO, "Opening: %s ...\n", cmdWin); - log_fn(RETRO_LOG_INFO, "Params: %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3), - *(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11)); + log_fn(RETRO_LOG_INFO, "Params: %s | %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3), + *(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11), *(params+12)); } else { log_fn(RETRO_LOG_INFO, "Restarting FreeJ2ME.\n"); restarting = false; } diff --git a/src/libretro/freej2me_libretro.h b/src/libretro/freej2me_libretro.h index 2b3bd86f..c0b872dc 100644 --- a/src/libretro/freej2me_libretro.h +++ b/src/libretro/freej2me_libretro.h @@ -81,6 +81,11 @@ struct retro_core_option_v2_category option_categories[] = "Advanced Settings", "Options related to FreeJ2ME's libretro core, such as the on-screen pointer type and speed, as well as logging." }, + { + "speed_hacks", + "Speed Hacks", + "Options that can increase FreeJ2ME's performance in exchange for lower compatibility by going out of J2ME specifications." + }, }; /* Core config options if running on a frontend with support for config version 2 */ @@ -340,6 +345,20 @@ struct retro_core_option_v2_definition core_options[] = }, "Yellow" }, + { + "freej2me_spdhacknoalpha", + "Speed Hacks > No Alpha on Blank Images (Restart Required)", + "No Alpha on Blank Images (Restart Required)", + "J2ME dictates that all images, including fully blank ones, have to be created with an alpha channel, and this includes the virtual phone's LCD screen. However, FreeJ2ME can create those without an alpha channel instead, cutting back on alpha processing for those images that usually are always fully painted with not transparency. Provides a Moderate to Large performance boost depending with little to no side effects", + "J2ME dictates that all images, including fully blank ones, have to be created with an alpha channel, and this includes the virtual phone's LCD screen. However, FreeJ2ME can create those without an alpha channel instead, cutting back on alpha processing for those images that usually are always fully painted with not transparency. Provides a Moderate to Large performance boost depending with little to no side effects", + "speed_hacks", + { + { "on", "Enabled" }, + { "off", "Disabled (Default)" }, + { NULL, NULL }, + }, + "off" + }, { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL }, }; @@ -568,6 +587,17 @@ struct retro_core_option_definition core_options_v1 [] = }, "Yellow" }, + { + "freej2me_spdhacknoalpha", + "No Alpha on Blank Images (Restart Required)", + "J2ME dictates that all images, including fully blank ones, have to be created with an alpha channel, and this includes the virtual phone's LCD screen. However, FreeJ2ME can create those without an alpha channel instead, cutting back on alpha processing for those images that usually are always fully painted with not transparency. Provides a Moderate to Large performance boost depending with little to no side effects", + { + { "on", "Enabled" }, + { "off", "Disabled (Default)" }, + { NULL, NULL }, + }, + "off" + }, { NULL, NULL, NULL, {{0}}, NULL }, }; @@ -603,11 +633,11 @@ static const struct retro_variable vars[] = }, { /* Logging Level */ "freej2me_logginglevel", - "Dump Audio Streams: 0|1|2|3|4" + "Dump Audio Streams; 0|1|2|3|4" }, { /* Dump Audio Streams */ "freej2me_dumpaudiostreams", - "Dump Audio Streams: off|on" + "Dump Audio Streams; off|on" }, { /* Pointer Type */ "freej2me_pointertype", @@ -633,5 +663,9 @@ static const struct retro_variable vars[] = "freej2me_pointerclickcolor", "Pointer Click Indicator Color; Yellow|Black|Red|Green|Blue|Pink|Cyan|White" }, + { /* No Alpha on Blank Images speed hack */ + "freej2me_spdhacknoalpha", + "No Alpha on Blank Images(SpeedHack); off|on", + }, { NULL, NULL }, }; diff --git a/src/org/recompile/freej2me/AWTGUI.java b/src/org/recompile/freej2me/AWTGUI.java index 4b3e1b6b..8e3baa15 100644 --- a/src/org/recompile/freej2me/AWTGUI.java +++ b/src/org/recompile/freej2me/AWTGUI.java @@ -50,7 +50,8 @@ public final class AWTGUI /* MenuBar's menus */ Menu fileMenu = new Menu("File"); - Menu optionMenu = new Menu("Settings"); + Menu optionMenu = new Menu("Settings"); + Menu speedHackMenu = new Menu("SpeedHacks"); Menu debugMenu = new Menu("Debug"); /* Sub menus (for now, all of them are located in "Settings") */ @@ -170,6 +171,8 @@ public final class AWTGUI final CheckboxMenuItem fpsCap30 = new CheckboxMenuItem("30 FPS", false); final CheckboxMenuItem fpsCap15 = new CheckboxMenuItem("15 FPS", false); + final CheckboxMenuItem noAlphaOnBlankImages = new CheckboxMenuItem("No alpha on blank images"); + final CheckboxMenuItem logDisabled = new CheckboxMenuItem("Disabled", false); final CheckboxMenuItem logDebug = new CheckboxMenuItem("Debug", false); final CheckboxMenuItem logInfo = new CheckboxMenuItem("Info", false); @@ -416,6 +419,18 @@ public void itemStateChanged(ItemEvent e) } }); + noAlphaOnBlankImages.addItemListener(new ItemListener() + { + public void itemStateChanged(ItemEvent e) + { + if(noAlphaOnBlankImages.getState()){ config.updateAlphaSpeedHack("on"); hasPendingChange = true; } + else{ config.updateAlphaSpeedHack("off"); hasPendingChange = true; } + + restartRequiredDialog.setLocationRelativeTo(main); + restartRequiredDialog.setVisible(true); + } + }); + stdLayout.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) @@ -808,6 +823,7 @@ private void buildMenuBar() optionMenu.add(phoneType); optionMenu.add(fpsCap); optionMenu.add(mapInputs); + optionMenu.add(speedHackMenu); logLevel.add(logDisabled); logLevel.add(logDebug); @@ -842,6 +858,8 @@ private void buildMenuBar() fpsCap.add(fpsCap60); fpsCap.add(fpsCap30); fpsCap.add(fpsCap15); + + speedHackMenu.add(noAlphaOnBlankImages); // add menus to menubar menuBar.add(fileMenu); @@ -869,6 +887,8 @@ public void updateOptions() lgLayout.setState(config.settings.get("phone").equals("LG")); motorolaLayout.setState(config.settings.get("phone").equals("Motorola")); + noAlphaOnBlankImages.setState(config.settings.get("spdhacknoalpha").equals("on")); + resChoice.select(""+ Integer.parseInt(config.settings.get("width")) + "x" + ""+ Integer.parseInt(config.settings.get("height"))); /* We only need to do this call once, when the jar first loads */ diff --git a/src/org/recompile/freej2me/Anbu.java b/src/org/recompile/freej2me/Anbu.java index e2b930da..a761d1df 100644 --- a/src/org/recompile/freej2me/Anbu.java +++ b/src/org/recompile/freej2me/Anbu.java @@ -759,5 +759,9 @@ void settingsChanged() if(midiSoundfont.equals("Custom")) { Manager.useCustomMidi = true; } else if(midiSoundfont.equals("Default")) { Manager.useCustomMidi = false; } + String speedHackNoAlpha = config.settings.get("spdhacknoalpha"); + if(speedHackNoAlpha.equals("on")) { Mobile.noAlphaOnBlankImages = true; } + else if (speedHackNoAlpha.equals("off")) { Mobile.noAlphaOnBlankImages = false; }; + } } diff --git a/src/org/recompile/freej2me/Config.java b/src/org/recompile/freej2me/Config.java index f3710fcf..87d1815d 100644 --- a/src/org/recompile/freej2me/Config.java +++ b/src/org/recompile/freej2me/Config.java @@ -96,6 +96,7 @@ public void init() settings.put("rotate", "off"); settings.put("fps", "0"); settings.put("soundfont", "Default"); + settings.put("spdhacknoalpha", "off"); saveConfig(); } } @@ -136,6 +137,7 @@ public void init() if(!settings.containsKey("rotate")) { settings.put("rotate", "off"); } if(!settings.containsKey("fps")) { settings.put("fps", "0"); } if(!settings.containsKey("soundfont")) { settings.put("soundfont", "Default"); } + if(!settings.containsKey("spdhacknoalpha")) { settings.put("spdhacknoalpha", "off"); } } catch (Exception e) @@ -217,4 +219,12 @@ public void updateSoundfont(String value) onChange.run(); } + public void updateAlphaSpeedHack(String value) + { + Mobile.log(Mobile.LOG_DEBUG, Config.class.getPackage().getName() + "." + Config.class.getSimpleName() + ": " + "Config: spdhacknoalpha "+value); + settings.put("spdhacknoalpha", value); + saveConfig(); + onChange.run(); + } + } diff --git a/src/org/recompile/freej2me/FreeJ2ME.java b/src/org/recompile/freej2me/FreeJ2ME.java index 0cde4856..7a393fd4 100644 --- a/src/org/recompile/freej2me/FreeJ2ME.java +++ b/src/org/recompile/freej2me/FreeJ2ME.java @@ -388,6 +388,10 @@ private void settingsChanged() if(midiSoundfont.equals("Custom")) { Manager.useCustomMidi = true; } else if(midiSoundfont.equals("Default")) { Manager.useCustomMidi = false; } + String speedHackNoAlpha = config.settings.get("spdhacknoalpha"); + if(speedHackNoAlpha.equals("on")) { Mobile.noAlphaOnBlankImages = true; } + else if (speedHackNoAlpha.equals("off")) { Mobile.noAlphaOnBlankImages = false; }; + // Create a standard size LCD if not rotated, else invert window's width and height. if(!rotateDisplay) { diff --git a/src/org/recompile/freej2me/Libretro.java b/src/org/recompile/freej2me/Libretro.java index 60306625..c69d0c23 100644 --- a/src/org/recompile/freej2me/Libretro.java +++ b/src/org/recompile/freej2me/Libretro.java @@ -141,6 +141,10 @@ public Libretro(String args[]) if(Integer.parseInt(args[8]) == 0) { Mobile.logging = false; } else { Mobile.logging = true; Mobile.minLogLevel = (byte) (Integer.parseInt(args[8])-1); } + /* No Alpha on Blank Images SpeedHack is a per-game config */ + if(Integer.parseInt(args[9]) == 0) { Mobile.noAlphaOnBlankImages = false; } + else { Mobile.noAlphaOnBlankImages = true; } + /* Once it finishes parsing all arguments, it's time to set up freej2me-lr */ surface = new BufferedImage(lcdWidth, lcdHeight, BufferedImage.TYPE_3BYTE_BGR); // libretro display @@ -299,15 +303,15 @@ public void run() if(rotateDisplay) { config.settings.put("rotate", "on"); } if(!rotateDisplay) { config.settings.put("rotate", "off"); } - if(Mobile.lg) { config.settings.put("phone", "LG"); } - else if(Mobile.motorola) { config.settings.put("phone", "Motorola"); } - else if(Mobile.motoTriplets) { config.settings.put("phone", "MotoTriplets"); } - else if(Mobile.motoV8) { config.settings.put("phone", "MotoV8"); } - else if(Mobile.nokia) { config.settings.put("phone", "Nokia"); } - else if(Mobile.nokiaKeyboard) { config.settings.put("phone", "NokiaKeyboard"); } - else if(Mobile.sagem) { config.settings.put("phone", "Sagem"); } - else if(Mobile.siemens) { config.settings.put("phone", "Siemens"); } - else { config.settings.put("phone", "Standard"); } + if(Mobile.lg) { config.settings.put("phone", "LG"); } + else if(Mobile.motorola) { config.settings.put("phone", "Motorola"); } + else if(Mobile.motoTriplets) { config.settings.put("phone", "MotoTriplets"); } + else if(Mobile.motoV8) { config.settings.put("phone", "MotoV8"); } + else if(Mobile.nokia) { config.settings.put("phone", "Nokia"); } + else if(Mobile.nokiaKeyboard) { config.settings.put("phone", "NokiaKeyboard"); } + else if(Mobile.sagem) { config.settings.put("phone", "Sagem"); } + else if(Mobile.siemens) { config.settings.put("phone", "Siemens"); } + else { config.settings.put("phone", "Standard"); } if(soundEnabled) { config.settings.put("sound", "on"); } if(!soundEnabled) { config.settings.put("sound", "off"); } @@ -315,8 +319,11 @@ public void run() config.settings.put("fps", "" + Mobile.limitFPS); if(!Manager.useCustomMidi) { config.settings.put("soundfont", "Default"); } - else { config.settings.put("soundfont", "Custom"); } - + else { config.settings.put("soundfont", "Custom"); } + + if(!Mobile.noAlphaOnBlankImages) { config.settings.put("spdhacknoalpha", "off"); } + else { config.settings.put("spdhacknoalpha", "on"); } + config.saveConfig(); settingsChanged(); @@ -386,6 +393,9 @@ public void run() if(Integer.parseInt(cfgtokens[9])==0) { Mobile.logging = false; } else { Mobile.logging = true; Mobile.minLogLevel = (byte) (Integer.parseInt(cfgtokens[9])-1); } + if(Integer.parseInt(cfgtokens[10])==0) { Mobile.noAlphaOnBlankImages = false; } + else { Mobile.noAlphaOnBlankImages = true; } + config.saveConfig(); settingsChanged(); break; @@ -467,6 +477,10 @@ private void settingsChanged() if(midiSoundfont.equals("Custom")) { Manager.useCustomMidi = true; } else if(midiSoundfont.equals("Default")) { Manager.useCustomMidi = false; } + String speedHackNoAlpha = config.settings.get("spdhacknoalpha"); + if(speedHackNoAlpha.equals("on")) { Mobile.noAlphaOnBlankImages = true; } + else if (speedHackNoAlpha.equals("off")) { Mobile.noAlphaOnBlankImages = false; }; + if(lcdWidth != w || lcdHeight != h) { lcdWidth = w; diff --git a/src/org/recompile/mobile/Mobile.java b/src/org/recompile/mobile/Mobile.java index bd5eff81..87fedd4b 100644 --- a/src/org/recompile/mobile/Mobile.java +++ b/src/org/recompile/mobile/Mobile.java @@ -44,12 +44,15 @@ public class Mobile private static Graphics3D graphics3d; + // Mobile should contain flags to any and all "speedhacks" present in FreeJ2ME + public static boolean noAlphaOnBlankImages = true; + // Enable/disable logging to the console and optionally to a file public static boolean logging = true; private static final String LOG_FILE = "freej2me_system" + File.separatorChar + "FreeJ2ME.log"; public static byte minLogLevel = 1; - //Log Levels + // Log Levels public static final byte LOG_DEBUG = 0; public static final byte LOG_INFO = 1; public static final byte LOG_WARNING = 2; diff --git a/src/org/recompile/mobile/PlatformImage.java b/src/org/recompile/mobile/PlatformImage.java index 13d039c7..0ac92261 100644 --- a/src/org/recompile/mobile/PlatformImage.java +++ b/src/org/recompile/mobile/PlatformImage.java @@ -61,7 +61,9 @@ public PlatformImage(int Width, int Height) width = Width; height = Height; - canvas = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_ARGB); + if(Mobile.noAlphaOnBlankImages) { canvas = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB); } + else { canvas = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_ARGB); } + createGraphics(); gc.setColor(0xFFFFFF);