Skip to content

Commit

Permalink
Introduce FreeJ2ME's first Speed Hack! (No Alpha on Blank Images)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
AShiningRay committed Nov 10, 2024
1 parent ea7344d commit e5befdd
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 24 deletions.
28 changes: 20 additions & 8 deletions src/libretro/freej2me_libretro.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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; }

Expand Down Expand Up @@ -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));
Expand All @@ -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; }

Expand Down
38 changes: 36 additions & 2 deletions src/libretro/freej2me_libretro.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 },
};

Expand Down Expand Up @@ -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 },
};

Expand Down Expand Up @@ -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",
Expand All @@ -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 },
};
22 changes: 21 additions & 1 deletion src/org/recompile/freej2me/AWTGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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") */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */
Expand Down
4 changes: 4 additions & 0 deletions src/org/recompile/freej2me/Anbu.java
Original file line number Diff line number Diff line change
Expand Up @@ -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; };

}
}
10 changes: 10 additions & 0 deletions src/org/recompile/freej2me/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public void init()
settings.put("rotate", "off");
settings.put("fps", "0");
settings.put("soundfont", "Default");
settings.put("spdhacknoalpha", "off");
saveConfig();
}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
}

}
4 changes: 4 additions & 0 deletions src/org/recompile/freej2me/FreeJ2ME.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
36 changes: 25 additions & 11 deletions src/org/recompile/freej2me/Libretro.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -299,24 +303,27 @@ 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"); }

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();

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/org/recompile/mobile/Mobile.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion src/org/recompile/mobile/PlatformImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit e5befdd

Please sign in to comment.