Added timers to see what's taking so long. It's the fonts - 11 seconds to load Chinese characters.

Specifically, putGlyph. Which makes sense, since it's basically rendering 3000 images into the memory for Chinese.
This commit is contained in:
Yair Morgenstern 2019-11-28 22:10:26 +02:00
parent 3816014ae4
commit c1d3ac2604
7 changed files with 300 additions and 300 deletions

View file

@ -39,7 +39,6 @@ class UncivGame(val version: String) : Game() {
override fun create() {
Current = this
if(Gdx.app.type!= Application.ApplicationType.Desktop)
viewEntireMapForDebug=false
Gdx.input.setCatchKey(Input.Keys.BACK, true)

View file

@ -38,6 +38,8 @@ object GameBasics {
}
init {
val gameBasicsStartTime = System.currentTimeMillis()
val techColumns = getFromJson(Array<TechColumn>::class.java, "Techs")
for (techColumn in techColumns) {
for (tech in techColumn.techs) {
@ -77,11 +79,14 @@ object GameBasics {
Difficulties += createHashmap(getFromJson(Array<Difficulty>::class.java, "Difficulties"))
val gameBasicsLoadTime = System.currentTimeMillis() - gameBasicsStartTime
println("Loading game basics - "+gameBasicsLoadTime+"ms")
// Apparently you can't iterate over the files in a directory when running out of a .jar...
// https://www.badlogicgames.com/forum/viewtopic.php?f=11&t=27250
// which means we need to list everything manually =/
val translationStart = System.currentTimeMillis()
val translationFileNames = listOf("Buildings","Diplomacy,Trade,Nations",
"NewGame,SaveGame,LoadGame,Options", "Notifications","Other","Policies","Techs",
"Terrains,Resources,Improvements","Units,Promotions")
@ -92,6 +97,8 @@ object GameBasics {
Translations.add(file.readString(Charsets.UTF_8.name()))
}
}
val translationFilesTime = System.currentTimeMillis() - translationStart
println("Loading translation files - "+translationFilesTime+"ms")
}
}

View file

@ -62,6 +62,8 @@ open class CameraStageBaseScreen : Screen {
}
fun resetFonts(){
val startTime = System.currentTimeMillis()
skin.get(TextButton.TextButtonStyle::class.java).font = Fonts().getFont(45).apply { data.setScale(20/45f) }
skin.get(CheckBox.CheckBoxStyle::class.java).font= Fonts().getFont(45).apply { data.setScale(20/45f) }
skin.get(Label.LabelStyle::class.java).apply {
@ -72,6 +74,10 @@ open class CameraStageBaseScreen : Screen {
skin.get(SelectBox.SelectBoxStyle::class.java).font = Fonts().getFont(45).apply { data.setScale(20/45f) }
skin.get(SelectBox.SelectBoxStyle::class.java).listStyle.font = Fonts().getFont(45).apply { data.setScale(20/45f) }
skin.get(CheckBox.CheckBoxStyle::class.java).fontColor= Color.WHITE
val resetFontsTime = System.currentTimeMillis() - startTime
println("Resetting fonts - "+resetFontsTime+"ms")
}
internal var batch: Batch = SpriteBatch()
}

View file

@ -17,6 +17,8 @@ class Fonts {
fun getCharactersForFont(language:String=""): String {
if (characterSetCache.containsKey(language)) return characterSetCache[language]!!
val startTime = System.currentTimeMillis()
val defaultText = "AÀÁBCČĆDĐEÈÉFGHIÌÍÏJKLMNOÒÓÖPQRSŠTUÙÚÜVWXYZŽaäàâăbcčćçdđeéèfghiìîjklmnoòöpqrsșštțuùüvwxyzž" +
"АБВГҐДЂЕЁЄЖЗЅИІЇЙЈКЛЉМНЊОПРСТЋУЎФХЦЧЏШЩЪЫЬЭЮЯабвгґдђеёєжзѕиіїйјклљмнњопрстћуўфхцчџшщъыьэюя" + // Russian
"ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωάßΆέΈέΉίϊΐΊόΌύΰϋΎΫΏ" + // Greek
@ -43,6 +45,10 @@ class Fonts {
}
val characterSetString = charSet.joinToString("")
characterSetCache[language]=characterSetString
val totalTime = System.currentTimeMillis() - startTime
println("Loading characters for font - "+totalTime+"ms")
return characterSetString
}
@ -55,7 +61,11 @@ class Fonts {
val font=NativeFont(NativeFontPaint(size))
val charsForFont = getCharactersForFont(if(isUniqueFont) language else "")
font.appendText(charsForFont)
fontCache[keyForFont] = font
return font
}

View file

@ -1,299 +0,0 @@
package core.java.nativefont;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.PixmapPacker;
import com.badlogic.gdx.graphics.g2d.PixmapPacker.Page;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
/**
* Created by tian on 2016/10/2.
*/
public class NativeFont extends BitmapFont {
private static NativeFontListener listener;
private static boolean robovm;
private Set<String> charSet;
private BitmapFontData data;
private HashMap<String, EmojiDate> emojiSet;
private Texture.TextureFilter magFilter;
private Texture.TextureFilter minFilter;
private PixmapPacker packer;
private int pageWidth;
private NativeFontPaint paint;
private int size;
public class EmojiDate {
public String path;
public int size;
public EmojiDate(String path, int size) {
this.path = path;
this.size = size;
}
}
public NativeFont() {
this(new NativeFontPaint());
}
public NativeFont(NativeFontPaint paint) {
super(new BitmapFontData(), new TextureRegion(), false);
this.pageWidth = 512;
this.paint = new NativeFontPaint();
this.charSet = new HashSet();
this.packer = null;
this.minFilter = Texture.TextureFilter.Linear;
this.magFilter = Texture.TextureFilter.Linear;
this.emojiSet = new HashMap();
updataSize(paint.getTextSize());
if (listener == null) createListener();
this.paint = paint;
}
private void createListener() {
String className = "core.java.nativefont.NativeFont";
if (Gdx.app.getType() == Application.ApplicationType.Desktop) {
className += "Desktop";
} else if (Gdx.app.getType() == Application.ApplicationType.Android) {
className += "Android";
} else if (Gdx.app.getType() == Application.ApplicationType.iOS) {
if (robovm)
className += "IOS";
else
className += "IOSMoe";
}else if (Gdx.app.getType() == Application.ApplicationType.WebGL){
className += "Html";
}
try {
Class<? extends NativeFontListener> claz = (Class<? extends NativeFontListener>) Gdx.app.getClass().getClassLoader().loadClass(className);
listener = claz.newInstance();
} catch (Exception e) {
throw new GdxRuntimeException("Class Not Found:" + e.getMessage());
}
}
public void updataSize(int newSize) {
this.data = getData();
this.size = Math.max(newSize, this.size);
this.data.down = (float) (-this.size);
this.data.ascent = (float) (-this.size);
this.data.capHeight = (float) this.size;
this.data.lineHeight = (float) this.size;
}
public NativeFont setTextColor(Color color) {
this.paint.setColor(color);
return this;
}
public NativeFont setStrokeColor(Color color) {
this.paint.setStrokeColor(color);
return this;
}
public NativeFont setStrokeWidth(int width) {
this.paint.setStrokeWidth(width);
return this;
}
public NativeFont setSize(int size) {
this.paint.setTextSize(size);
return this;
}
public NativeFont setBold(boolean istrue) {
this.paint.setFakeBoldText(istrue);
return this;
}
public NativeFont setUnderline(boolean istrue) {
this.paint.setUnderlineText(istrue);
return this;
}
public NativeFont setStrikeThru(boolean istrue) {
this.paint.setStrikeThruText(istrue);
return this;
}
public NativeFont setPaint(NativeFontPaint paint) {
this.paint = paint;
return this;
}
public NativeFont addEmojiPath(String emojiKey, String imgPath, int size) {
this.emojiSet.put(emojiKey, new EmojiDate(imgPath, size));
return this;
}
public NativeFont appendEmoji(String txt, String imgname, int size) {
Pixmap pixmap = new Pixmap(Gdx.files.internal(imgname));
// Pixmap.setFilter(Pixmap.Filter.BiLinear);
Pixmap pixmap2 = new Pixmap(size, size, Pixmap.Format.RGBA8888);
pixmap2.setFilter(Pixmap.Filter.BiLinear);
pixmap2.drawPixmap(pixmap, 0, 0, pixmap.getWidth(), pixmap.getHeight(), 0, 0, size, size);
pixmap.dispose();
appendEmoji(txt, pixmap2);
return this;
}
public NativeFont appendEmoji(String txt, Pixmap pixmap) {
if (this.charSet.add(txt)) {
if (this.packer == null) {
this.packer = new PixmapPacker(this.pageWidth, this.pageWidth, Pixmap.Format.RGBA8888, 2, false);
}
putGlyph(txt.charAt(0), pixmap);
updataSize(pixmap.getHeight());
upData();
}
return this;
}
public NativeFont createText(String characters) {
if (!(characters == null || characters.length() == 0)) {
create(characters, true);
end();
}
return this;
}
public NativeFont appendText(String characters) {
if (!(characters == null || characters.length() == 0)) {
create(characters, false);
}
return this;
}
private void create(String characters, boolean haveMinPageSize) {
char c;
characters = characters.replaceAll("[\\t\\n\\x0B\\f\\r]", "");
Array<String> cs = new Array<String>();
for (char c2 : characters.toCharArray()) {
if (this.charSet.add((String.valueOf(c2)))) {
cs.add((String.valueOf(c2)));
}
}
if (haveMinPageSize) {
this.pageWidth = (this.paint.getTextSize() + 2) * ((int) (Math.sqrt((double) cs.size) + 1.0d));
}
if (this.packer == null) {
this.packer = new PixmapPacker(this.pageWidth, this.pageWidth, Pixmap.Format.RGBA8888, 2, false);
}
char c2;
for (int i = 0; i < cs.size; i++) {
String txt = cs.get(i);
c2 = txt.charAt(0);
String css = String.valueOf(c2);
if (this.emojiSet.get(css) != null) {
this.charSet.remove(css);
EmojiDate date = this.emojiSet.get(css);
appendEmoji(c2 + "", date.path, date.size);
} else {
putGlyph(c2, listener.getFontPixmap(txt, this.paint));
}
}
updataSize(this.size);
upData();
if (getRegions().size == 1) {
setOwnsTexture(true);
} else {
setOwnsTexture(false);
}
}
private void putGlyph(char c, Pixmap pixmap) {
Rectangle rect = this.packer.pack(String.valueOf(c), pixmap);
pixmap.dispose();
int pIndex = this.packer.getPageIndex((String.valueOf(c)));
Glyph glyph = new Glyph();
glyph.id = c;
glyph.page = pIndex;
glyph.srcX = (int) rect.x;
glyph.srcY = (int) rect.y;
glyph.width = (int) rect.width;
glyph.height = (int) rect.height;
glyph.xadvance = glyph.width;
this.data.setGlyph(c, glyph);
}
private void upData() {
Glyph spaceGlyph = this.data.getGlyph(' ');
if (spaceGlyph == null) {
spaceGlyph = new Glyph();
Glyph xadvanceGlyph = this.data.getGlyph('l');
if (xadvanceGlyph == null) {
xadvanceGlyph = this.data.getFirstGlyph();
}
spaceGlyph.xadvance = xadvanceGlyph.xadvance;
spaceGlyph.id = 32;
this.data.setGlyph(32, spaceGlyph);
}
this.data.spaceXadvance = (float) (spaceGlyph.xadvance + spaceGlyph.width);
Array<Page> pages = this.packer.getPages();
Array<TextureRegion> regions = getRegions();
int regSize = regions.size - 1;
for (int i = 0; i < pages.size; i++) {
Page p = pages.get(i);
if (i > regSize) {
p.updateTexture(this.minFilter, this.magFilter, false);
regions.add(new TextureRegion(p.getTexture()));
} else {
if (p.updateTexture(this.minFilter, this.magFilter, false)) {
regions.set(i, new TextureRegion(p.getTexture()));
}
}
}
for (Glyph[] page : this.data.glyphs) {
if (page == null) continue;
for (Glyph glyph : page) {
if (glyph != null) {
TextureRegion region = getRegions().get(glyph.page);
if (region == null) {
throw new IllegalArgumentException("BitmapFont texture region array cannot contain null elements.");
}
this.data.setGlyphRegion(glyph, region);
}
}
}
}
public NativeFont end() {
this.paint = null;
this.charSet.clear();
this.charSet = null;
this.packer.dispose();
this.packer = null;
return this;
}
public void dispose() {
end();
super.dispose();
}
public static void setRobovm() {
robovm = true;
}
public static NativeFontListener getListener() {
return listener;
}
}

View file

@ -0,0 +1,272 @@
package core.java.nativefont
import com.badlogic.gdx.Application
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture.TextureFilter
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.PixmapPacker
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.utils.Array
import com.badlogic.gdx.utils.GdxRuntimeException
import java.util.*
/**
* Created by tian on 2016/10/2.
*/
class NativeFont @JvmOverloads constructor(paint: NativeFontPaint = NativeFontPaint()) : BitmapFont(BitmapFontData(), TextureRegion(), false) {
private var charSet: MutableSet<String?>?
private val emojiSet: HashMap<String?, EmojiDate?>
private val magFilter: TextureFilter
private val minFilter: TextureFilter
private var packer: PixmapPacker?
private var pageWidth = 512
private var paint: NativeFontPaint?
private var size = 0
inner class EmojiDate(var path: String, var size: Int)
private fun createListener() {
var className = "core.java.nativefont.NativeFont"
if (Gdx.app.type == Application.ApplicationType.Desktop) {
className += "Desktop"
} else if (Gdx.app.type == Application.ApplicationType.Android) {
className += "Android"
} else if (Gdx.app.type == Application.ApplicationType.iOS) {
className += if (robovm) "IOS" else "IOSMoe"
} else if (Gdx.app.type == Application.ApplicationType.WebGL) {
className += "Html"
}
listener = try {
val claz = Gdx.app.javaClass.classLoader.loadClass(className) as Class<out NativeFontListener>
claz.newInstance()
} catch (e: Exception) {
throw GdxRuntimeException("Class Not Found:" + e.message)
}
}
fun updataSize(newSize: Int) {
size = Math.max(newSize, size)
this.data.down = (-size).toFloat()
this.data.ascent = (-size).toFloat()
this.data.capHeight = size.toFloat()
this.data.lineHeight = size.toFloat()
}
fun setTextColor(color: Color?): NativeFont {
paint!!.color = color
return this
}
fun setStrokeColor(color: Color?): NativeFont {
paint!!.strokeColor = color
return this
}
fun setStrokeWidth(width: Int): NativeFont {
paint!!.strokeWidth = width
return this
}
fun setSize(size: Int): NativeFont {
paint!!.textSize = size
return this
}
fun setBold(istrue: Boolean): NativeFont {
paint!!.fakeBoldText = istrue
return this
}
fun setUnderline(istrue: Boolean): NativeFont {
paint!!.underlineText = istrue
return this
}
fun setStrikeThru(istrue: Boolean): NativeFont {
paint!!.strikeThruText = istrue
return this
}
fun setPaint(paint: NativeFontPaint?): NativeFont {
this.paint = paint
return this
}
fun addEmojiPath(emojiKey: String?, imgPath: String, size: Int): NativeFont {
emojiSet[emojiKey] = EmojiDate(imgPath, size)
return this
}
fun appendEmoji(txt: String, imgname: String?, size: Int): NativeFont {
val pixmap = Pixmap(Gdx.files.internal(imgname))
// Pixmap.setFilter(Pixmap.Filter.BiLinear);
val pixmap2 = Pixmap(size, size, Pixmap.Format.RGBA8888)
pixmap2.filter = Pixmap.Filter.BiLinear
pixmap2.drawPixmap(pixmap, 0, 0, pixmap.width, pixmap.height, 0, 0, size, size)
pixmap.dispose()
appendEmoji(txt, pixmap2)
return this
}
fun appendEmoji(txt: String, pixmap: Pixmap): NativeFont {
if (charSet!!.add(txt)) {
if (packer == null) {
packer = PixmapPacker(pageWidth, pageWidth, Pixmap.Format.RGBA8888, 2, false)
}
putGlyph(txt[0], pixmap)
updataSize(pixmap.height)
upData()
}
return this
}
fun createText(characters: String?): NativeFont {
if (!(characters == null || characters.length == 0)) {
create(characters, true)
end()
}
return this
}
fun appendText(characters: String?): NativeFont {
if (!(characters == null || characters.length == 0)) {
create(characters, false)
}
return this
}
private fun create(characters: String, haveMinPageSize: Boolean) {
var characters = characters
characters = characters.replace("[\\t\\n\\x0B\\f\\r]".toRegex(), "")
val arrayOfCharsAsStrings = Array<String>()
for (c2 in characters.toCharArray()) {
if (charSet!!.add(c2.toString())) {
arrayOfCharsAsStrings.add(c2.toString())
}
}
if (haveMinPageSize) {
pageWidth = (paint!!.textSize + 2) * (Math.sqrt(arrayOfCharsAsStrings.size.toDouble()) + 1.0).toInt()
}
if (packer == null) {
packer = PixmapPacker(pageWidth, pageWidth, Pixmap.Format.RGBA8888, 2, false)
}
val putGlyphStartTime = System.currentTimeMillis()
for (i in 0 until arrayOfCharsAsStrings.size) {
val txt = arrayOfCharsAsStrings[i]
val c2 = txt[0]
val css = c2.toString()
if (emojiSet[css] != null) {
charSet!!.remove(css)
val date = emojiSet[css]
appendEmoji(c2.toString() + "", date!!.path, date.size)
} else {
putGlyph(c2, listener!!.getFontPixmap(txt, paint))
}
}
val putGlyphTime = System.currentTimeMillis() - putGlyphStartTime
println("Putting glyphs - "+putGlyphTime+"ms")
updataSize(size)
upData()
if (regions.size == 1) {
setOwnsTexture(true)
} else {
setOwnsTexture(false)
}
}
private fun putGlyph(c: Char, pixmap: Pixmap) {
val rect = packer!!.pack(c.toString(), pixmap)
pixmap.dispose()
val pIndex = packer!!.getPageIndex(c.toString())
val glyph = Glyph()
glyph.id = c.toInt()
glyph.page = pIndex
glyph.srcX = rect.x.toInt()
glyph.srcY = rect.y.toInt()
glyph.width = rect.width.toInt()
glyph.height = rect.height.toInt()
glyph.xadvance = glyph.width
this.data!!.setGlyph(c.toInt(), glyph)
}
private fun upData() {
var spaceGlyph = this.data!!.getGlyph(' ')
if (spaceGlyph == null) {
spaceGlyph = Glyph()
var xadvanceGlyph = this.data!!.getGlyph('l')
if (xadvanceGlyph == null) {
xadvanceGlyph = this.data!!.firstGlyph
}
spaceGlyph.xadvance = xadvanceGlyph!!.xadvance
spaceGlyph.id = 32
this.data!!.setGlyph(32, spaceGlyph)
}
this.data!!.spaceXadvance = (spaceGlyph.xadvance + spaceGlyph.width).toFloat()
val pages = packer!!.pages
val regions = regions
val regSize = regions.size - 1
for (i in 0 until pages.size) {
val p = pages[i]
if (i > regSize) {
p.updateTexture(minFilter, magFilter, false)
regions.add(TextureRegion(p.texture))
} else {
if (p.updateTexture(minFilter, magFilter, false)) {
regions[i] = TextureRegion(p.texture)
}
}
}
for (page in this.data!!.glyphs) {
if (page == null) continue
for (glyph in page) {
if (glyph != null) {
val region = getRegions()[glyph.page]
?: throw IllegalArgumentException("BitmapFont texture region array cannot contain null elements.")
this.data!!.setGlyphRegion(glyph, region)
}
}
}
}
fun end(): NativeFont {
paint = null
charSet!!.clear()
charSet = null
packer!!.dispose()
packer = null
return this
}
override fun dispose() {
end()
super.dispose()
}
companion object {
var listener: NativeFontListener? = null
private set
private var robovm = false
fun setRobovm() {
robovm = true
}
}
init {
this.paint = NativeFontPaint()
charSet = HashSet()
packer = null
minFilter = TextureFilter.Linear
magFilter = TextureFilter.Linear
emojiSet = HashMap()
updataSize(paint.textSize)
if (listener == null) createListener()
this.paint = paint
}
}

View file

@ -32,6 +32,8 @@ internal object DesktopLauncher {
}
private fun packImages() {
val startTime = System.currentTimeMillis()
val settings = TexturePacker.Settings()
// Apparently some chipsets, like NVIDIA Tegra 3 graphics chipset (used in Asus TF700T tablet),
// don't support non-power-of-two texture sizes - kudos @yuroller!
@ -46,6 +48,9 @@ internal object DesktopLauncher {
settings.filterMag = Texture.TextureFilter.MipMapLinearLinear
settings.filterMin = Texture.TextureFilter.MipMapLinearLinear
TexturePacker.process(settings, "../Images", ".", "game")
val texturePackingTime = System.currentTimeMillis() - startTime
println("Packing textures - "+texturePackingTime+"ms")
}
private fun tryActivateDiscord(game: UncivGame) {