From c667c1b0d11cb6134eee10227857168df96ade36 Mon Sep 17 00:00:00 2001 From: zetaPRIME Date: Tue, 9 May 2017 05:21:52 -0400 Subject: [PATCH] profiler; rsf, makefile and image loading tweaks; pngcrush'd largest images; removed leftover debug text from asset loading --- .../source/starlight/ThemeManager.cpp | 45 ++++++++++++------ .../source/starlight/gfx/RenderCore.cpp | 2 +- .../source/starlight/ui/DebugConsole.cpp | 1 + libstarlight/source/starlight/ui/Label.cpp | 1 + .../source/starlight/util/Profiler.cpp | 19 ++++++++ libstarlight/source/starlight/util/Profiler.h | 22 +++++++++ libstarlight/todo.txt | 4 ++ testbed/Makefile | 2 +- testbed/resources/starlight-testbed.rsf | 6 +++ testbed/source/Core.cpp | 2 + .../default/decorations/generic backdrop.png | Bin 4277 -> 2565 bytes themes/default/decorations/osk.background.png | Bin 8034 -> 5309 bytes 12 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 libstarlight/source/starlight/util/Profiler.cpp create mode 100644 libstarlight/source/starlight/util/Profiler.h diff --git a/libstarlight/source/starlight/ThemeManager.cpp b/libstarlight/source/starlight/ThemeManager.cpp index 9da5194..e966922 100644 --- a/libstarlight/source/starlight/ThemeManager.cpp +++ b/libstarlight/source/starlight/ThemeManager.cpp @@ -25,6 +25,10 @@ #include "starlight/util/JsonConversions.h" +#include "starlight/util/Profiler.h" +using starlight::util::Profiler; +#include + using std::string; using std::shared_ptr; using std::make_shared; @@ -75,40 +79,48 @@ namespace { } CTexture* LoadPNG(const std::string& path, bool isPremult = false) { + //Profiler::TaskStart(); unsigned char* imgbuf; unsigned width, height; + lodepng::State state; lodepng_decode32_file(&imgbuf, &width, &height, path.c_str()); + if (state.info_png.color.colortype != 6) isPremult = true; // expect no alpha if not rgba + /*{ + std::stringstream ss; ss << "loaded png, "; + ss << width; ss << "x"; ss << height; + Profiler::TaskFinish(ss.str()); + }*/ unsigned bw = NextPow2(width), bh = NextPow2(height); u8* gpubuf = static_cast(linearAlloc(bw*bh*4)); + //Profiler::TaskStart(); + //memset(gpubuf, 0, bw*bh*4); + //Profiler::TaskFinish("cleared canvas"); - u8* src = static_cast(imgbuf); u8* dst = static_cast(gpubuf); + //Profiler::TaskStart(); if (isPremult) { + u32* src = reinterpret_cast(imgbuf); u32* dst = reinterpret_cast(gpubuf); // just convert endianness for(unsigned iy = 0; iy < height; iy++) { for (unsigned ix = 0; ix < width; ix++) { - int r = *src++; - int g = *src++; - int b = *src++; - int a = *src++; + u32 clr = *src; + *dst = __builtin_bswap32(clr); - *dst++ = a; - *dst++ = b; - *dst++ = g; - *dst++ = r; + src+=4; dst+=4; } dst += (bw - width) * 4; // skip the difference } } else { + u8* src = static_cast(imgbuf); u8* dst = static_cast(gpubuf); // convert and premultiply for(unsigned iy = 0; iy < height; iy++) { for (unsigned ix = 0; ix < width; ix++) { - int r = *src++; - int g = *src++; - int b = *src++; - int a = *src++; + u8 r = *src++; + u8 g = *src++; + u8 b = *src++; + u8 a = *src++; float aa = (1.0f / 255.0f) * a; @@ -120,11 +132,14 @@ namespace { dst += (bw - width) * 4; // skip the difference } } + //Profiler::TaskFinish("made into canvas, flipped and premult"); // completely skipping over the difference instead of erasing might eventually lead to garbage outside of frame, // but meh; that'll only be visible if you intentionally push the UVs outside the image proper + //Profiler::TaskStart(); CTexture* tx = RenderCore::LoadTexture(static_cast(gpubuf), bw, bh); tx->size = Vector2(width, height); // and for now just fix the size after the fact + //Profiler::TaskFinish("copied into linear"); std::free(imgbuf); linearFree(gpubuf); @@ -216,7 +231,7 @@ shared_ptr ThemeManager::LoadAsset(string& path, ThemeRefContainer nulldrw = make_shared(); string ext = FindExtension(path); - printf("load: %s (%s)\n", path.c_str(), ext.c_str()); + //printf("load: %s (%s)\n", path.c_str(), ext.c_str()); /**/ if (ext == "png") { return make_shared(LoadPNG(path)); } @@ -227,7 +242,7 @@ shared_ptr ThemeManager::LoadAsset(string& path, ThemeRefContainer> j; } auto st = j.dump(); - printf("file contents: %s\n", st.c_str()); + //printf("file contents: %s\n", st.c_str()); string type = j["assetType"]; /**/ if (type == "ninepatch") { diff --git a/libstarlight/source/starlight/gfx/RenderCore.cpp b/libstarlight/source/starlight/gfx/RenderCore.cpp index 0ee7149..9852f45 100644 --- a/libstarlight/source/starlight/gfx/RenderCore.cpp +++ b/libstarlight/source/starlight/gfx/RenderCore.cpp @@ -264,7 +264,7 @@ CTexture* RenderCore::LoadTexture(void* src, int width, int height) { C3D_TexBind(0, tex->texture); - printf("loaded image w %i (%i) h %i (%i)\n", width, owidth, height, oheight); + //printf("loaded image w %i (%i) h %i (%i)\n", width, owidth, height, oheight); return tex; } diff --git a/libstarlight/source/starlight/ui/DebugConsole.cpp b/libstarlight/source/starlight/ui/DebugConsole.cpp index dace710..5971f54 100644 --- a/libstarlight/source/starlight/ui/DebugConsole.cpp +++ b/libstarlight/source/starlight/ui/DebugConsole.cpp @@ -88,6 +88,7 @@ void DebugConsole::PreDraw() { if (!buffer) buffer = std::make_unique(rect.size + Vector2(0, 8)); buffer->Clear(); GFXManager::PushContext(buffer.get()); + GFXManager::PrepareForDrawing(); // force clear even if nothing to write textConfig.Print(buffer->rect, text); GFXManager::PopContext(); } diff --git a/libstarlight/source/starlight/ui/Label.cpp b/libstarlight/source/starlight/ui/Label.cpp index a70e437..749cbfd 100644 --- a/libstarlight/source/starlight/ui/Label.cpp +++ b/libstarlight/source/starlight/ui/Label.cpp @@ -53,6 +53,7 @@ void Label::PreDraw() { buffer = std::make_unique(rect.size + Vector2(0, 8)); buffer->Clear(); GFXManager::PushContext(buffer.get()); + GFXManager::PrepareForDrawing(); // force clear even if nothing to write textConfig.ROGet().Print(buffer->rect, text); GFXManager::PopContext(); } diff --git a/libstarlight/source/starlight/util/Profiler.cpp b/libstarlight/source/starlight/util/Profiler.cpp new file mode 100644 index 0000000..a2f870f --- /dev/null +++ b/libstarlight/source/starlight/util/Profiler.cpp @@ -0,0 +1,19 @@ +#include "Profiler.h" + +#include + +#include "3ds.h" + +using starlight::util::Profiler; + +Profiler::TickCount Profiler::tc = Profiler::TickCount(); + +void Profiler::TaskStart() { + osTickCounterUpdate(reinterpret_cast(&tc)); +} + +void Profiler::TaskFinish(const std::string& msg) { + osTickCounterUpdate(reinterpret_cast(&tc)); + double tm = osTickCounterRead(reinterpret_cast(&tc)); + printf("T:%f - %s\n", tm, msg.c_str()); +} diff --git a/libstarlight/source/starlight/util/Profiler.h b/libstarlight/source/starlight/util/Profiler.h new file mode 100644 index 0000000..32f55de --- /dev/null +++ b/libstarlight/source/starlight/util/Profiler.h @@ -0,0 +1,22 @@ +#pragma once +#include "starlight/_global.h" + +#include + +namespace starlight { + namespace util { + class Profiler { + private: + struct TickCount { + unsigned long long int elapsed; + unsigned long long int ref; + }; + static TickCount tc; + Profiler() {}; + + public: + static void TaskStart(); + static void TaskFinish(const std::string& msg); + }; + } +} diff --git a/libstarlight/todo.txt b/libstarlight/todo.txt index ca565d9..9a95f00 100644 --- a/libstarlight/todo.txt +++ b/libstarlight/todo.txt @@ -13,6 +13,9 @@ roadmap to v0.5.1 { - libctru console as ui element + pngcrush the biggest assets (default and osk backdrops etc.) + - profile image loading (for large images, loading the png and spanned copy/premult take about the same time; memset to preclear is only ~2ms with a 512x512) + - fix the hang on osk when pressing (L|R)+up+left figure out what (else) to put on the left side of the keyboard (opposite backspace and enter) temporary drawable loading, local themeref, discard etc. @@ -28,6 +31,7 @@ roadmap to v0.5.1 { ...and some mechanism for allowing it to opt out of the rest of the cycle Trackable sideclass for threads; float progress 0..1, etc. + ^ make progress bar and use it for a progress/"please wait" dialog - MAKE THREADS END CLEANLY ^ observed a single instance of being stalled on redscreen, not really sure what that was about lambda task thread diff --git a/testbed/Makefile b/testbed/Makefile index 694c993..fc52a9a 100644 --- a/testbed/Makefile +++ b/testbed/Makefile @@ -173,7 +173,7 @@ send: $(BUILD) #--------------------------------------------------------------------------------- send-cia: $(TARGET)-strip.elf @makerom -f cia -o $(TARGET).cia -elf $(TARGET)-strip.elf -rsf resources/$(TARGET).rsf -icon resources/icon.icn -banner resources/banner.bnr -exefslogo -target t - @sockme $(TARGET).cia $(3DSIP) + @sockme $(TARGET).cia $(3DSIP) || sockme $(TARGET).cia $(3DSIP) || sockme $(TARGET).cia $(3DSIP) #--------------------------------------------------------------------------------- run: $(BUILD) @citra $(TARGET).3dsx diff --git a/testbed/resources/starlight-testbed.rsf b/testbed/resources/starlight-testbed.rsf index 98ec6ba..dfd5ae1 100644 --- a/testbed/resources/starlight-testbed.rsf +++ b/testbed/resources/starlight-testbed.rsf @@ -91,6 +91,12 @@ AccessControlInfo: Priority : 16 MaxCpu : 0x9E # Default + + # New3DS Exclusive Process Settings + SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 268MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true DisableDebug : true EnableForceDebug : false diff --git a/testbed/source/Core.cpp b/testbed/source/Core.cpp index 8bf6c1d..5688629 100644 --- a/testbed/source/Core.cpp +++ b/testbed/source/Core.cpp @@ -11,6 +11,7 @@ #include "starlight/gfx/RenderCore.h" #include "starlight/util/Path.h" +#include "starlight/util/Profiler.h" #include "starlight/ui/ParallaxLayer.h" #include "starlight/ui/ScrollField.h" @@ -36,6 +37,7 @@ using starlight::GFXManager; using starlight::gfx::RenderCore; using starlight::util::Path; +using starlight::util::Profiler; using starlight::Application; diff --git a/themes/default/decorations/generic backdrop.png b/themes/default/decorations/generic backdrop.png index dd84cdd652adb35aa597356bf6b91986953dd1e7..29b8cafaf29b1803988e41594636116edec86f06 100644 GIT binary patch literal 2565 zcmcguX;hQv5{?ulQYchJpj6oeOF%HPC_#3R9Tx&YAw`ye>_UQsT?C7=g{1@to1!5l z5hMyIf}|n@Fo{qWSsTO{A^~I*WzP-h!QOj%?>+aPp8j~xcV^ysXP#%~%r|r6w1cIn z@OQ!h06^5*%KR(DXIg&6fPOQ}#HYU%)4w8R5N!V356x*ZToz&v;o)mZvJ&PuH1&^YsP8>eT6o!n zcV-ceBOwbG9Z!v6B0fddy;Ps09^jc9wsUz8ohL)(wZK2>$|}uxDLUzL zy|OKVHi;EL#?CMlQ%<%BTAZ&`Tkyfbq8p_>sEf~6jmM#|EZaV>gr6&O2e@73Lz#vm782q0)!d);wH}^;ERRU%ngAgy~8R zl<2i!+Yx_*+7%zPWc!DKcm^@XeE-K3O1mV16E4?9#a3lDFtWD=v(!_ zJihjn4_r`T7TPX_+G#2-pt(DgyUcczVRt`=v1kUbCq?6$ofg39q&?Iy2li#6TwZeI zVFE}WL#hg{ohsw@*7s{n_M#)Nj29s}WmS(%IVi8h#42N;DL?_ZBWZ1~g6x)Gpb1d^ zO&f4It~z!#FWyjv^6)r$!taJKa=tsMm~Bozq`8DM^iU$(+@FZxnvEYDYz$(!>%P%A9*-EzbSy6a`coKjQ8@Q}$dikwzlDwe!Rq;>3 z6XN0Ru@t!^LNyI7X2i3>G~m>e3EoXqIOjQQ@7Y}JN_f$zTid((oY;G($ehHtcmou< zzo&9wV7}^};=A1S$ok-O`!7-4iy_J)yl49#3v3el5`Y(>5YI1)pu&~U5cmu?g>C9+ zh4`)9{+s*Ho;KfP@|rR!H`TVGZ-@C8@BeHdu$j{@;kO@s%KTPVz8Gv(;|uuzj_J=y z{Q>kJ4E`=$rjG$vgJ^fHf`ItZsmFD=8O zM*6(SbZm+7xlH8v(#IfywZwSD_49%$cHr_ci9%^dkM5UxbwnJt!+G7h9y8f6GN1K8e7pPFPksCP z3vVNfc34XVClBeqQ=>9Qr)fl~iTUb~n~q%1BanwW2O3OR360F}4<< zaILEP9-W-WlX{#N7Vyw(FD9rzX>PeM5hnA0fF{5blJf=RF%P3>JaCPNhTTx-p2E6z zb8rpgmAH4%-RywzZ>IbfeEq3Jd#A;bhL%Z3ykZ@M9Gbk%=;EV# z8d4eTe&YQp8)eUS@OhS7fpx7i!#1@7pKo=xV*ey=M(MrcuweKZy&QjSNykGSXX&~)OJIH?Yx}zK6 zMtr=c*gqfE!NzA`q0Zl&Xf%VFD?Es&`;UQtphn)((V^Aw@||84g>@pb>kA^{hNffS zlqbmGB9GuwM2Qn-A!g8|VR3oBY&?(XtDuEyJz}yoNPLb`?Rw^LYlIst)ij28bD4Du zeu?HvT0EXvAgx)Lp7jx&1(IC^cPI(spF)$y1@s^@7*PkJ13hcVE}Ed|2f+mvo~8e6 zWDl`b*J|||cfRv`1xTFL_bn!WbKr-FrA4kWkFK~fzEdKNL%0m--10PVR3On0d1lCp z{PTdeEXjxQ;6XmAyyW+U$biwaq&deTz2fDvH?zCo;LW9Dcxfs62(#H$i`jceE^W_b zsn;Uh_jp;`nQ6+zh5f~@KVYdYVc&;jPe@}XTp=mb7&dd>pk&wqUJ{jKNagM#veMIK z9V<(4*Wqczi+%5on8HLTW?Ss}Ym@vPt&LspXwgSRy#k1v8=19*gL#$dg{%Jn1ZFCS literal 4277 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 diff --git a/themes/default/decorations/osk.background.png b/themes/default/decorations/osk.background.png index ac72c66670ab9cd2ff3c1a6d9a13c4b955749978..cdbe2bfe8dac0e7c46a9ea7eb6c26d4e8fea7d9e 100644 GIT binary patch literal 5309 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 W*Q*16i~hMu)Ohe%wOrXEEZ;`E6AI%F9NgVKV@ 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 G#r;2-ee$XR