diff --git a/assets/plugins/plugin-performance-0.1.5.jar b/assets/plugins/plugin-performance-0.1.5.jar index bebc2084..ee3725be 100644 Binary files a/assets/plugins/plugin-performance-0.1.5.jar and b/assets/plugins/plugin-performance-0.1.5.jar differ diff --git a/hyperlap2d-runtime-libgdx b/hyperlap2d-runtime-libgdx index bd0fc0d4..8457a75a 160000 --- a/hyperlap2d-runtime-libgdx +++ b/hyperlap2d-runtime-libgdx @@ -1 +1 @@ -Subproject commit bd0fc0d4b424470a8eb36ae343509753ebfd8edb +Subproject commit 8457a75aab0f574b2ea9a62bcdd871c71b81671a diff --git a/plugin-performance/src/main/java/games/rednblack/editor/plugin/performance/PerformancePanel.java b/plugin-performance/src/main/java/games/rednblack/editor/plugin/performance/PerformancePanel.java index b9fb6572..13705d39 100644 --- a/plugin-performance/src/main/java/games/rednblack/editor/plugin/performance/PerformancePanel.java +++ b/plugin-performance/src/main/java/games/rednblack/editor/plugin/performance/PerformancePanel.java @@ -9,9 +9,6 @@ import com.kotcrab.vis.ui.widget.VisLabel; import com.kotcrab.vis.ui.widget.VisTable; import games.rednblack.h2d.common.UIDraggablePanel; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryUsage; - public class PerformancePanel extends UIDraggablePanel { private final VisTable mainTable; @@ -99,9 +96,9 @@ public class PerformancePanel extends UIDraggablePanel { entitiesCount.setText(entitySubscription.getEntities().size()); fpsLbl.setText(Gdx.graphics.getFramesPerSecond()); - MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); - long usedMemory = memoryUsage.getUsed() / (1024 * 1024); - long allocatedMemory = memoryUsage.getCommitted() / (1024 * 1024); + long used = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + long usedMemory = used / (1024 * 1024); + long allocatedMemory = Runtime.getRuntime().totalMemory() / (1024 * 1024); memoryLabel.getText().clear(); memoryLabel.getText().append(usedMemory); memoryLabel.getText().append(" of "); diff --git a/src/main/java/com/badlogic/gdx/utils/Pool.java b/src/main/java/com/badlogic/gdx/utils/Pool.java deleted file mode 100644 index 84b55bca..00000000 --- a/src/main/java/com/badlogic/gdx/utils/Pool.java +++ /dev/null @@ -1,144 +0,0 @@ -/******************************************************************************* - * Copyright 2011 See AUTHORS file. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -package com.badlogic.gdx.utils; - -/** A pool of objects that can be reused to avoid allocation. - * @see Pools - * @author Nathan Sweet */ -abstract public class Pool { - /** The maximum number of objects that will be pooled. */ - public final int max; - /** The highest number of free objects. Can be reset any time. */ - public int peak; - - private final Array freeObjects; - - /** Creates a pool with an initial capacity of 16 and no maximum. */ - public Pool () { - this(16, Integer.MAX_VALUE); - } - - /** Creates a pool with the specified initial capacity and no maximum. */ - public Pool (int initialCapacity) { - this(initialCapacity, Integer.MAX_VALUE); - } - - /** @param initialCapacity The initial size of the array supporting the pool. No objects are created/pre-allocated. Use - * {@link #fill(int)} after instantiation if needed. - * @param max The maximum number of free objects to store in this pool. */ - public Pool (int initialCapacity, int max) { - freeObjects = new Array(false, initialCapacity); - this.max = max; - } - - abstract protected T newObject (); - - /** Returns an object from this pool. The object may be new (from {@link #newObject()}) or reused (previously - * {@link #free(Object) freed}). */ - public T obtain () { - synchronized (freeObjects) { - return freeObjects.size == 0 ? newObject() : freeObjects.pop(); - } - } - - /** Puts the specified object in the pool, making it eligible to be returned by {@link #obtain()}. If the pool already contains - * {@link #max} free objects, the specified object is {@link #discard(Object) discarded}, it is not reset and not added to the - * pool. - *

- * The pool does not check if an object is already freed, so the same object must not be freed multiple times. */ - public void free (T object) { - synchronized (freeObjects) { - if (object == null) throw new IllegalArgumentException("object cannot be null."); - if (freeObjects.size < max) { - freeObjects.add(object); - peak = Math.max(peak, freeObjects.size); - reset(object); - } else - discard(object); - } - } - - /** Adds the specified number of new free objects to the pool. Usually called early on as a pre-allocation mechanism but can be - * used at any time. - * - * @param size the number of objects to be added */ - public void fill (int size) { - synchronized (freeObjects) { - for (int i = 0; i < size; i++) - if (freeObjects.size < max) freeObjects.add(newObject()); - peak = Math.max(peak, freeObjects.size); - } - } - - /** Called when an object is freed to clear the state of the object for possible later reuse. The default implementation calls - * {@link Poolable#reset()} if the object is {@link Poolable}. */ - protected void reset (T object) { - if (object instanceof Poolable) ((Poolable)object).reset(); - } - - /** Called when an object is discarded. This is the case when an object is freed, but the maximum capacity of the pool is - * reached, and when the pool is {@link #clear() cleared} */ - protected void discard (T object) { - reset(object); - } - - /** Puts the specified objects in the pool. Null objects within the array are silently ignored. - *

- * The pool does not check if an object is already freed, so the same object must not be freed multiple times. - * @see #free(Object) */ - public void freeAll (Array objects) { - synchronized (freeObjects) { - if (objects == null) throw new IllegalArgumentException("objects cannot be null."); - Array freeObjects = this.freeObjects; - int max = this.max; - for (int i = 0, n = objects.size; i < n; i++) { - T object = objects.get(i); - if (object == null) continue; - if (freeObjects.size < max) { - freeObjects.add(object); - reset(object); - } else { - discard(object); - } - } - peak = Math.max(peak, freeObjects.size); - } - } - - /** Removes and discards all free objects from this pool. */ - public void clear () { - synchronized (freeObjects) { - Array freeObjects = this.freeObjects; - for (int i = 0, n = freeObjects.size; i < n; i++) - discard(freeObjects.get(i)); - freeObjects.clear(); - } - } - - /** The number of objects available to be obtained. */ - public int getFree () { - synchronized (freeObjects) { - return freeObjects.size; - } - } - - /** Objects implementing this interface will have {@link #reset()} called when passed to {@link Pool#free(Object)}. */ - static public interface Poolable { - /** Resets the object for reuse. Object references should be nulled and fields may be set to default values. */ - public void reset (); - } -} diff --git a/src/main/java/com/badlogic/gdx/utils/Pools.java b/src/main/java/com/badlogic/gdx/utils/Pools.java new file mode 100644 index 00000000..28af8944 --- /dev/null +++ b/src/main/java/com/badlogic/gdx/utils/Pools.java @@ -0,0 +1,74 @@ +package com.badlogic.gdx.utils; + +public class Pools { + static private final ThreadLocal> typePools = new ThreadLocal>() { + protected ObjectMap initialValue () { + return new ObjectMap(); + }; + }; + + /** Returns a new or existing pool for the specified type, stored in a Class to {@link Pool} map. Note the max size is ignored + * if this is not the first time this pool has been requested. */ + static public Pool get (Class type, int max) { + Pool pool = typePools.get().get(type); + if (pool == null) { + pool = new ReflectionPool(type, 4, max); + typePools.get().put(type, pool); + } + return pool; + } + + /** Returns a new or existing pool for the specified type, stored in a Class to {@link Pool} map. The max size of the pool used + * is 100. */ + static public Pool get (Class type) { + return get(type, 100); + } + + /** Sets an existing pool for the specified type, stored in a Class to {@link Pool} map. */ + static public void set (Class type, Pool pool) { + typePools.get().put(type, pool); + } + + /** Obtains an object from the {@link #get(Class) pool}. */ + static public T obtain (Class type) { + return get(type).obtain(); + } + + /** Frees an object from the {@link #get(Class) pool}. */ + static public void free (Object object) { + if (object == null) throw new IllegalArgumentException("object cannot be null."); + Pool pool = typePools.get().get(object.getClass()); + if (pool == null) return; // Ignore freeing an object that was never retained. + pool.free(object); + } + + /** Frees the specified objects from the {@link #get(Class) pool}. Null objects within the array are silently ignored. Objects + * don't need to be from the same pool. */ + static public void freeAll (Array objects) { + freeAll(objects, false); + } + + /** Frees the specified objects from the {@link #get(Class) pool}. Null objects within the array are silently ignored. + * @param samePool If true, objects don't need to be from the same pool but the pool must be looked up for each object. */ + static public void freeAll (Array objects, boolean samePool) { + if (objects == null) throw new IllegalArgumentException("objects cannot be null."); + Pool pool = null; + for (int i = 0, n = objects.size; i < n; i++) { + Object object = objects.get(i); + if (object == null) continue; + if (pool == null) { + pool = typePools.get().get(object.getClass()); + if (pool == null) continue; // Ignore freeing an object that was never retained. + } + pool.free(object); + if (!samePool) pool = null; + } + } + + public static String name() { + return "ThreadSafe Pools"; + } + + private Pools () { + } +} diff --git a/src/main/java/games/rednblack/editor/HyperLap2D.java b/src/main/java/games/rednblack/editor/HyperLap2D.java index a6457128..9bb5a298 100644 --- a/src/main/java/games/rednblack/editor/HyperLap2D.java +++ b/src/main/java/games/rednblack/editor/HyperLap2D.java @@ -91,9 +91,11 @@ public class HyperLap2D implements IProxy, ApplicationListener, Lwjgl3WindowList facade.sendNotification(MsgAPI.RESUME); } + float[] delta = new float[1]; @Override public void render() { - facade.sendNotification(MsgAPI.RENDER, Math.min(Gdx.graphics.getDeltaTime(), 0.1f)); + delta[0] = Math.min(Gdx.graphics.getDeltaTime(), 0.1f); + facade.sendNotification(MsgAPI.RENDER, delta); } @Override diff --git a/src/main/java/games/rednblack/editor/controller/BootstrapInfoCommand.java b/src/main/java/games/rednblack/editor/controller/BootstrapInfoCommand.java index 24fe49e7..bcb308e7 100644 --- a/src/main/java/games/rednblack/editor/controller/BootstrapInfoCommand.java +++ b/src/main/java/games/rednblack/editor/controller/BootstrapInfoCommand.java @@ -2,6 +2,7 @@ package games.rednblack.editor.controller; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.utils.Pools; import games.rednblack.editor.utils.AppConfig; import games.rednblack.h2d.common.HyperLog; import games.rednblack.puremvc.commands.SimpleCommand; @@ -21,5 +22,7 @@ public class BootstrapInfoCommand extends SimpleCommand { HyperLog.info("Shaders version " + gl20.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION)); HyperLog.info("JVM Version: " + System.getProperty("java.version") + " (" + System.getProperty("java.vendor") + ")"); + + HyperLog.info(Pools.name()); } } diff --git a/src/main/java/games/rednblack/editor/proxy/ResourceManager.java b/src/main/java/games/rednblack/editor/proxy/ResourceManager.java index f0057345..7c7dd8a9 100644 --- a/src/main/java/games/rednblack/editor/proxy/ResourceManager.java +++ b/src/main/java/games/rednblack/editor/proxy/ResourceManager.java @@ -91,18 +91,18 @@ public class ResourceManager extends Proxy implements IResourceRetriever { packer.setTransparentColor(Color.WHITE); packer.getTransparentColor().a = 0; - FreeTypeFontGenerator dejaVuSansGenerator = new FreeTypeFontGenerator(Gdx.files.internal("freetypefonts/DejaVuSans.ttf")) { + FreeTypeFontGenerator dejaVuSansGenerator = new FreeTypeFontGenerator(Gdx.files.internal("freetypefonts/DejaVuSans.ttf")) /*{ @Override protected BitmapFont newBitmapFont(BitmapFont.BitmapFontData data, Array pageRegions, boolean integer) { return new ThreadSafeBitmapFont(data, pageRegions, integer); } - }; - FreeTypeFontGenerator monoGenerator = new FreeTypeFontGenerator(Gdx.files.internal("freetypefonts/FiraCode-Regular.ttf")){ + }*/; + FreeTypeFontGenerator monoGenerator = new FreeTypeFontGenerator(Gdx.files.internal("freetypefonts/FiraCode-Regular.ttf"))/*{ @Override protected BitmapFont newBitmapFont(BitmapFont.BitmapFontData data, Array pageRegions, boolean integer) { return new ThreadSafeBitmapFont(data, pageRegions, integer); } - }; + }*/; FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter(); parameter.characters += "⌘⇧⌥\u25CF\u2022"; diff --git a/src/main/java/games/rednblack/editor/utils/AssetsUtils.java b/src/main/java/games/rednblack/editor/utils/AssetsUtils.java index f9a755aa..0b88a5b5 100644 --- a/src/main/java/games/rednblack/editor/utils/AssetsUtils.java +++ b/src/main/java/games/rednblack/editor/utils/AssetsUtils.java @@ -3,6 +3,7 @@ package games.rednblack.editor.utils; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntArray; import com.kotcrab.vis.ui.widget.file.FileTypeFilter; import org.apache.commons.io.FileUtils; @@ -10,7 +11,6 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.util.Arrays; public class AssetsUtils { @@ -64,38 +64,45 @@ public class AssetsUtils { return fileTypeFilter; } - public static boolean isAnimationSequence(String[] names) { - if (names.length < 2) return false; - int[] sequenceArray = new int[names.length]; - for (int i = 0; i < names.length; i++) { - String name = names[i]; + public static IntArray sequenceArray = new IntArray(); + + public static boolean isAnimationSequence(Array files) { + if (files.size < 2) return false; + sequenceArray.clear(); + sequenceArray.ensureCapacity(files.size); + sequenceArray.size = files.size; + + for (int i = 0; i < files.size; i++) { + String name = files.get(i); // try to remove extension if any if (name.indexOf(".") > 0) name = name.substring(0, name.indexOf(".")); try { int intValue = Integer.parseInt(name.replaceAll("(.+)_", "")); - sequenceArray[i] = intValue; + sequenceArray.insert(i, intValue); } catch (Exception e) { - sequenceArray[i] = -10; + sequenceArray.insert(i, -1); } } - Arrays.sort(sequenceArray); - if (sequenceArray[0] == 0 && sequenceArray[sequenceArray.length - 1] == sequenceArray.length - 1) { + sequenceArray.sort(); + if (sequenceArray.get(0) == 0 && sequenceArray.get(sequenceArray.size - 1) == sequenceArray.size - 1) { return true; } - return sequenceArray[0] == 1 && sequenceArray[sequenceArray.length - 1] == sequenceArray.length; + return sequenceArray.get(0) == 1 && sequenceArray.get(sequenceArray.size - 1) == sequenceArray.size; } + private final static Array tmpNames = new Array<>(); + public static boolean isAtlasAnimationSequence(Array regions) { if (regions.size < 2) return false; + tmpNames.clear(); //Check old .atlas format - String[] regionNames = new String[regions.size]; for (int i = 0; i < regions.size; i++) { - regionNames[i] = regions.get(i).name; + tmpNames.add(regions.get(i).name); } - if (isAnimationSequence(regionNames)) + if (isAnimationSequence(tmpNames)) return true; //New .atlas format diff --git a/src/main/java/games/rednblack/editor/utils/asset/Asset.java b/src/main/java/games/rednblack/editor/utils/asset/Asset.java index e3c95c95..f7c8e3cf 100644 --- a/src/main/java/games/rednblack/editor/utils/asset/Asset.java +++ b/src/main/java/games/rednblack/editor/utils/asset/Asset.java @@ -60,13 +60,15 @@ public abstract class Asset implements IAsset { // save before importing SceneVO vo = Sandbox.getInstance().sceneVoFromItems(); - projectManager.saveCurrentProject(vo); + if (!skipRepack) //Skip saving if internal resource + projectManager.saveCurrentProject(vo); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(() -> importAsset(files, progressHandler, skipRepack)); executor.execute(() -> { progressHandler.progressChanged(100); - projectManager.saveCurrentProject(); + if (!skipRepack) //Skip saving if internal resource + projectManager.saveCurrentProject(); try { Thread.sleep(300); } catch (InterruptedException e) { diff --git a/src/main/java/games/rednblack/editor/utils/asset/impl/HyperLap2DLibraryAsset.java b/src/main/java/games/rednblack/editor/utils/asset/impl/HyperLap2DLibraryAsset.java index 9a198060..02f075bb 100644 --- a/src/main/java/games/rednblack/editor/utils/asset/impl/HyperLap2DLibraryAsset.java +++ b/src/main/java/games/rednblack/editor/utils/asset/impl/HyperLap2DLibraryAsset.java @@ -134,6 +134,7 @@ public class HyperLap2DLibraryAsset extends Asset { ResolutionManager resolutionManager = Facade.getInstance().retrieveProxy(ResolutionManager.NAME); resolutionManager.rePackProjectImagesForAllResolutionsSync(); + projectManager.saveCurrentProject(); progressHandler.progressChanged(100); diff --git a/src/main/java/games/rednblack/editor/utils/asset/impl/ImageAsset.java b/src/main/java/games/rednblack/editor/utils/asset/impl/ImageAsset.java index 5c1df741..b1d48b79 100644 --- a/src/main/java/games/rednblack/editor/utils/asset/impl/ImageAsset.java +++ b/src/main/java/games/rednblack/editor/utils/asset/impl/ImageAsset.java @@ -26,11 +26,13 @@ import java.util.function.Consumer; public class ImageAsset extends Asset { + private final Array names = new Array<>(); + @Override public int matchType(Array files) { - String[] names = new String[files.size]; + names.clear(); for (int i = 0; i < files.size; i++) { - names[i] = files.get(i).nameWithoutExtension(); + names.add(files.get(i).nameWithoutExtension()); } return AssetsUtils.isAnimationSequence(names) ? AssetsUtils.TYPE_UNKNOWN : super.matchType(files); diff --git a/src/main/java/games/rednblack/editor/utils/asset/impl/SpriteAnimationSequenceAsset.java b/src/main/java/games/rednblack/editor/utils/asset/impl/SpriteAnimationSequenceAsset.java index b671badc..67b96031 100644 --- a/src/main/java/games/rednblack/editor/utils/asset/impl/SpriteAnimationSequenceAsset.java +++ b/src/main/java/games/rednblack/editor/utils/asset/impl/SpriteAnimationSequenceAsset.java @@ -15,11 +15,13 @@ import java.util.Objects; public class SpriteAnimationSequenceAsset extends SpriteAnimationAtlasAsset { + private final Array names = new Array<>(); + @Override public int matchType(Array files) { - String[] names = new String[files.size]; + names.clear(); for (int i = 0; i < files.size; i++) { - names[i] = files.get(i).nameWithoutExtension(); + names.add(files.get(i).nameWithoutExtension()); } return AssetsUtils.isAnimationSequence(names) ? getType() : AssetsUtils.TYPE_UNKNOWN; } diff --git a/src/main/java/games/rednblack/editor/view/HyperLap2DScreenMediator.java b/src/main/java/games/rednblack/editor/view/HyperLap2DScreenMediator.java index ae2b01d7..66874d8b 100644 --- a/src/main/java/games/rednblack/editor/view/HyperLap2DScreenMediator.java +++ b/src/main/java/games/rednblack/editor/view/HyperLap2DScreenMediator.java @@ -80,7 +80,8 @@ public class HyperLap2DScreenMediator extends Mediator { viewComponent.resume(); break; case MsgAPI.RENDER: - viewComponent.render(notification.getBody()); + float[] delta = notification.getBody(); + viewComponent.render(delta[0]); break; case MsgAPI.RESIZE: int[] data = notification.getBody(); diff --git a/src/main/java/games/rednblack/editor/view/ui/followers/PolygonFollower.java b/src/main/java/games/rednblack/editor/view/ui/followers/PolygonFollower.java index e0480f06..e4d875e2 100644 --- a/src/main/java/games/rednblack/editor/view/ui/followers/PolygonFollower.java +++ b/src/main/java/games/rednblack/editor/view/ui/followers/PolygonFollower.java @@ -1,7 +1,6 @@ package games.rednblack.editor.view.ui.followers; import com.artemis.ComponentMapper; -import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.OrthographicCamera; diff --git a/src/main/java/games/rednblack/editor/view/ui/widget/actors/polygon/PolyLine.java b/src/main/java/games/rednblack/editor/view/ui/widget/actors/polygon/PolyLine.java index 5d6fe3e4..235e3bf1 100644 --- a/src/main/java/games/rednblack/editor/view/ui/widget/actors/polygon/PolyLine.java +++ b/src/main/java/games/rednblack/editor/view/ui/widget/actors/polygon/PolyLine.java @@ -1,12 +1,9 @@ package games.rednblack.editor.view.ui.widget.actors.polygon; import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.math.Intersector; -import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Touchable; -import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Pool; import space.earlygrey.shapedrawer.ShapeDrawer;