refactor libgdx static objects to be disposed

-fixes memory leaks since these objects are not properly disposed by the GC
-Textures, BitmapFonts and others should be disposed manually if not loaded in the assetmanager
This commit is contained in:
Anthony Calosa
2022-07-09 15:53:19 +08:00
parent 2d81a1ad08
commit 4b5ebf2575
8 changed files with 70 additions and 60 deletions

View File

@@ -55,7 +55,6 @@ import java.io.File;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -104,8 +103,7 @@ public class Forge implements ApplicationListener {
public static boolean enablePreloadExtendedArt = false;
public static boolean isTabletDevice = false;
public static String locale = "en-US";
public Assets cardAssets;
public Assets otherAssets;
public Assets assets;
public static boolean hdbuttons = false;
public static boolean hdstart = false;
public static boolean isPortraitMode = false;
@@ -166,8 +164,7 @@ public class Forge implements ApplicationListener {
// don't allow to read and process
ForgeConstants.SPRITE_CARDBG_FILE = "";
}
cardAssets = new Assets();
otherAssets = new Assets();
assets = new Assets();
graphics = new Graphics();
splashScreen = new SplashScreen();
frameRate = new FrameRate();
@@ -954,8 +951,7 @@ public class Forge implements ApplicationListener {
currentScreen.onClose(null);
currentScreen = null;
}
cardAssets.dispose();
otherAssets.dispose();
assets.dispose();
Dscreens.clear();
graphics.dispose();
SoundSystem.instance.dispose();
@@ -967,11 +963,8 @@ public class Forge implements ApplicationListener {
/** Retrieve assets.
* @param other if set to true returns otherAssets otherwise returns cardAssets
*/
public static Assets getAssets(boolean other) {
if (other)
return ((Forge)Gdx.app.getApplicationListener()).otherAssets;
else
return ((Forge)Gdx.app.getApplicationListener()).cardAssets;
public static Assets getAssets() {
return ((Forge)Gdx.app.getApplicationListener()).assets;
}
public static boolean switchScene(Scene newScene) {
if (currentScene != null) {

View File

@@ -120,11 +120,11 @@ public class Config {
public TextureAtlas getAtlas(String spriteAtlas) {
String fileName = getFile(spriteAtlas).path();
if (!Forge.getAssets(true).manager.contains(fileName, TextureAtlas.class)) {
Forge.getAssets(true).manager.load(fileName, TextureAtlas.class);
Forge.getAssets(true).manager.finishLoadingAsset(fileName);
if (!Forge.getAssets().others.contains(fileName, TextureAtlas.class)) {
Forge.getAssets().others.load(fileName, TextureAtlas.class);
Forge.getAssets().others.finishLoadingAsset(fileName);
}
return Forge.getAssets(true).manager.get(fileName);
return Forge.getAssets().others.get(fileName);
}
public SettingData getSettingData()
{

View File

@@ -2,12 +2,28 @@ package forge.assets;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.resolvers.AbsoluteFileHandleResolver;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.ObjectMap;
import java.util.HashMap;
public class Assets implements Disposable {
public AssetManager manager = new AssetManager(new AbsoluteFileHandleResolver());
public AssetManager cards = new AssetManager(new AbsoluteFileHandleResolver());
public AssetManager others = new AssetManager(new AbsoluteFileHandleResolver());
public HashMap<Integer, FSkinFont> fonts = new HashMap<>();
public ObjectMap<Integer, BitmapFont> counterFonts = new ObjectMap<>();
public ObjectMap<String, Texture> generatedCards = new ObjectMap<>(512);
@Override
public void dispose() {
manager.dispose();
cards.dispose();
others.dispose();
for (BitmapFont bitmapFont : counterFonts.values())
bitmapFont.dispose();
for (Texture texture : generatedCards.values())
texture.dispose();
for (FSkinFont fSkinFont : fonts.values())
fSkinFont.font.dispose();
}
}

View File

@@ -88,7 +88,7 @@ public class FSkin {
* the skin name
*/
public static void loadLight(String skinName, final SplashScreen splashScreen) {
AssetManager manager = Forge.getAssets(true).manager;
AssetManager manager = Forge.getAssets().others;
preferredName = skinName.toLowerCase().replace(' ', '_');
//reset hd buttons/icons
@@ -236,7 +236,7 @@ public class FSkin {
parameter.magFilter = Texture.TextureFilter.Linear;
}
AssetManager manager = Forge.getAssets(true).manager;
AssetManager manager = Forge.getAssets().others;
// Grab and test various sprite files.
final FileHandle f1 = getDefaultSkinFile(SourceFile.ICONS.getFilename());

View File

@@ -3,7 +3,6 @@ package forge.assets;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
@@ -39,7 +38,6 @@ public class FSkinFont {
private static final int MAX_FONT_SIZE_MANY_GLYPHS = 36;
private static final String TTF_FILE = "font1.ttf";
private static final HashMap<Integer, FSkinFont> fonts = new HashMap<>();
private static final String commonCharacterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"
+ "nopqrstuvwxyz1234567890\"!?'.,;:()[]{}<>|/@\\^$-%+=#_&*\u2014"
@@ -58,10 +56,10 @@ public class FSkinFont {
return _get((int)Utils.scale(unscaledSize));
}
public static FSkinFont _get(final int scaledSize) {
FSkinFont skinFont = fonts.get(scaledSize);
FSkinFont skinFont = Forge.getAssets().fonts.get(scaledSize);
if (skinFont == null) {
skinFont = new FSkinFont(scaledSize);
fonts.put(scaledSize, skinFont);
Forge.getAssets().fonts.put(scaledSize, skinFont);
}
return skinFont;
}
@@ -97,14 +95,14 @@ public class FSkinFont {
}
public static void updateAll() {
for (FSkinFont skinFont : fonts.values()) {
for (FSkinFont skinFont : Forge.getAssets().fonts.values()) {
skinFont.updateFont();
}
}
private final int fontSize;
private final float scale;
private BitmapFont font;
BitmapFont font;
private FSkinFont(int fontSize0) {
if (fontSize0 > MAX_FONT_SIZE) {
@@ -400,16 +398,13 @@ public class FSkinFont {
if (fontFile != null && fontFile.exists()) {
final BitmapFontData data = new BitmapFontData(fontFile, false);
String finalFontName = fontName;
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() { //font must be initialized on UI thread
try {
font = new BitmapFont(data, (TextureRegion) null, true);
found[0] = true;
} catch (Exception e) {
e.printStackTrace();
found[0] = false;
}
FThreads.invokeInEdtNowOrLater(() -> { //font must be initialized on UI thread
try {
font = new BitmapFont(data, (TextureRegion) null, true);
found[0] = true;
} catch (Exception e) {
e.printStackTrace();
found[0] = false;
}
});
}

View File

@@ -131,9 +131,9 @@ public enum FSkinTexture implements FImage {
if (preferredFile.path().contains("fallback_skin")) {
texture = new Texture(preferredFile);
} else {
Forge.getAssets(true).manager.load(preferredFile.path(), Texture.class);
Forge.getAssets(true).manager.finishLoadingAsset(preferredFile.path());
texture = Forge.getAssets(true).manager.get(preferredFile.path(), Texture.class);
Forge.getAssets().others.load(preferredFile.path(), Texture.class);
Forge.getAssets().others.finishLoadingAsset(preferredFile.path());
texture = Forge.getAssets().others.get(preferredFile.path(), Texture.class);
}
}
catch (final Exception e) {
@@ -155,9 +155,9 @@ public enum FSkinTexture implements FImage {
if (defaultFile.path().contains("fallback_skin")) {
texture = new Texture(defaultFile);
} else {
Forge.getAssets(true).manager.load(defaultFile.path(), Texture.class);
Forge.getAssets(true).manager.finishLoadingAsset(defaultFile.path());
texture = Forge.getAssets(true).manager.get(defaultFile.path(), Texture.class);
Forge.getAssets().others.load(defaultFile.path(), Texture.class);
Forge.getAssets().others.finishLoadingAsset(defaultFile.path());
texture = Forge.getAssets().others.get(defaultFile.path(), Texture.class);
}
}
catch (final Exception e) {

View File

@@ -97,7 +97,6 @@ public class ImageCache {
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;
public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE;
private static final ObjectMap<String, Pair<String, Boolean>> imageBorder = new ObjectMap<>(1024);
private static final ObjectMap<String, Texture> generatedCards = new ObjectMap<>(512);
private static boolean imageLoaded, delayLoadRequested;
public static void allowSingleLoad() {
@@ -121,11 +120,11 @@ public class ImageCache {
ImageKeys.clearMissingCards();
}
public static void clearGeneratedCards() {
generatedCards.clear();
Forge.getAssets().generatedCards.clear();
}
public static void disposeTextures(){
CardRenderer.clearcardArtCache();
Forge.getAssets(false).manager.clear();
Forge.getAssets().cards.clear();
}
public static Texture getImage(InventoryItem ii) {
@@ -134,7 +133,9 @@ public class ImageCache {
if(imageKey.startsWith(ImageKeys.CARD_PREFIX) || imageKey.startsWith(ImageKeys.TOKEN_PREFIX))
return getImage(ii.getImageKey(false), true, false);
}
return getImage(ii.getImageKey(false), true, true);
boolean useDefaultNotFound = imageKey != null && !(imageKey.startsWith(ImageKeys.PRECON_PREFIX) || imageKey.startsWith(ImageKeys.FATPACK_PREFIX)
|| imageKey.startsWith(ImageKeys.BOOSTERBOX_PREFIX) || imageKey.startsWith(ImageKeys.BOOSTER_PREFIX) || imageKey.startsWith(ImageKeys.TOURNAMENTPACK_PREFIX));
return getImage(ii.getImageKey(false), useDefaultNotFound, true);
}
/**
@@ -265,33 +266,40 @@ public class ImageCache {
if (file == null)
return null;
if (!otherCache && Forge.enableUIMask.equals("Full") && isBorderless(imageKey))
return generatedCards.get(imageKey);
return Forge.getAssets(otherCache).manager.get(file.getPath(), Texture.class, false);
return Forge.getAssets().generatedCards.get(imageKey);
if (otherCache)
return Forge.getAssets().others.get(file.getPath(), Texture.class, false);
return Forge.getAssets().cards.get(file.getPath(), Texture.class, false);
}
static Texture loadAsset(String imageKey, File file, boolean otherCache) {
if (file == null)
return null;
syncQ.add(file.getPath());
if (!otherCache && Forge.getAssets(false).manager.getLoadedAssets() > maxCardCapacity) {
unloadCardTextures(Forge.getAssets(false).manager);
if (!otherCache && Forge.getAssets().cards.getLoadedAssets() > maxCardCapacity) {
unloadCardTextures(Forge.getAssets().cards);
return null;
}
String fileName = file.getPath();
//load to assetmanager
Forge.getAssets(otherCache).manager.load(fileName, Texture.class, Forge.isTextureFilteringEnabled() ? filtered : defaultParameter);
Forge.getAssets(otherCache).manager.finishLoadingAsset(fileName);
if (otherCache) {
Forge.getAssets().others.load(fileName, Texture.class, Forge.isTextureFilteringEnabled() ? filtered : defaultParameter);
Forge.getAssets().others.finishLoadingAsset(fileName);
} else {
Forge.getAssets().cards.load(fileName, Texture.class, Forge.isTextureFilteringEnabled() ? filtered : defaultParameter);
Forge.getAssets().cards.finishLoadingAsset(fileName);
}
//return loaded assets
if (otherCache) {
return Forge.getAssets(true).manager.get(fileName, Texture.class, false);
return Forge.getAssets().others.get(fileName, Texture.class, false);
} else {
Texture t = Forge.getAssets(false).manager.get(fileName, Texture.class, false);
Texture t = Forge.getAssets().cards.get(fileName, Texture.class, false);
//if full bordermasking is enabled, update the border color
if (Forge.enableUIMask.equals("Full")) {
boolean borderless = isBorderless(imageKey);
updateBorders(t.toString(), borderless ? Pair.of(Color.valueOf("#171717").toString(), false): isCloserToWhite(getpixelColor(t)));
//if borderless, generate new texture from the asset and store
if (borderless) {
generatedCards.put(imageKey, generateTexture(new FileHandle(file), t, Forge.isTextureFilteringEnabled()));
Forge.getAssets().generatedCards.put(imageKey, generateTexture(new FileHandle(file), t, Forge.isTextureFilteringEnabled()));
}
}
return t;

View File

@@ -104,8 +104,6 @@ public class CardRenderer {
private static final float BORDER_THICKNESS = Utils.scale(1);
public static final float PADDING_MULTIPLIER = 0.021f;
public static final float CROP_MULTIPLIER = 0.96f;
private static Map<Integer, BitmapFont> counterFonts = new HashMap<>();
private static final Color counterBackgroundColor = new Color(0f, 0f, 0f, 0.9f);
private static final Map<CounterType, Color> counterColorCache = new HashMap<>();
private static final GlyphLayout layout = new GlyphLayout();
@@ -1101,7 +1099,7 @@ public class CardRenderer {
private static void drawCounterTabs(final CardView card, final Graphics g, final float x, final float y, final float w, final float h) {
int fontSize = Math.max(11, Math.min(22, (int) (h * 0.08)));
BitmapFont font = counterFonts.get(fontSize);
BitmapFont font = Forge.getAssets().counterFonts.get(fontSize);
final float additionalXOffset = 3f * ((fontSize - 11) / 11f);
final float variableWidth = ((fontSize - 11) / 11f) * 44f;
@@ -1218,7 +1216,7 @@ public class CardRenderer {
private static void drawMarkersTabs(final List<String> markers, final Graphics g, final float x, final float y, final float w, final float h, boolean larger) {
int fontSize = larger ? Math.max(9, Math.min(22, (int) (h * 0.08))) : Math.max(8, Math.min(22, (int) (h * 0.05)));
BitmapFont font = counterFonts.get(fontSize);
BitmapFont font = Forge.getAssets().counterFonts.get(fontSize);
final float additionalXOffset = 3f * ((fontSize - 8) / 8f);
@@ -1410,7 +1408,7 @@ public class CardRenderer {
textureRegions.add(new TextureRegion(texture));
}
counterFonts.put(fontSize, new BitmapFont(fontData, textureRegions, true));
Forge.getAssets().counterFonts.put(fontSize, new BitmapFont(fontData, textureRegions, true));
generator.dispose();
packer.dispose();