From febf506a5e3504e98b7dc488d6d3906c292b7b4a Mon Sep 17 00:00:00 2001 From: tobid7 Date: Thu, 27 Nov 2025 13:32:44 +0100 Subject: [PATCH] # Changes - Add Debugstuff to main - Add IsType and AutoLoad to AssetManager - Add ID class for at compile time hashing - Add A8 to image (but not supported yet) - Fix error in Vertex constructor - Add PathClear to PathFill func - Add pCheckSize to check for overflows - Make Tex in Texture a pointer ref - Add default uv to texture - Add own c++ exception - Add FNV32 Hash func (compile and runtime) - Fix Power2 check in texture loader - Load Shader manualy in iron (cause it seems not working correctly with files) --- CMakeLists.txt | 7 ++-- example/source/main.cpp | 80 ++++++++++++++++++++++++++++++++++-- include/amethyst/app.hpp | 1 + include/amethyst/assets.hpp | 39 ++++++++++++++---- include/amethyst/c3d.hpp | 2 +- include/amethyst/id.hpp | 42 +++++++++++++++++++ include/amethyst/image.hpp | 1 + include/amethyst/iron.hpp | 11 +++-- include/amethyst/texture.hpp | 6 +-- include/amethyst/utils.hpp | 19 ++++++++- source/app.cpp | 1 - source/assets.cpp | 21 ++++++++++ source/c3d.cpp | 9 ++-- source/image.cpp | 5 ++- source/iron/iron.cpp | 37 +++++++++++++++-- source/texture.cpp | 34 +++++++-------- source/utils.cpp | 8 ---- 17 files changed, 265 insertions(+), 58 deletions(-) create mode 100644 include/amethyst/id.hpp create mode 100644 source/assets.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a3f2a2c..f1b5e7c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED true) option(AMY_BUILD_3DS "Build for 3ds" ON) option(AMY_GOD_DEV "Turn this on if you think you are god" OFF) -add_subdirectory(vendor/libpicasso) +# add_subdirectory(vendor/libpicasso) add_library(${PROJECT_NAME} STATIC source/app.cpp + source/assets.cpp source/amethyst.cpp source/image.cpp source/renderer.cpp @@ -24,10 +25,10 @@ add_library(${PROJECT_NAME} STATIC source/maths/mat.cpp ) target_include_directories(${PROJECT_NAME} PUBLIC include) -target_link_libraries(${PROJECT_NAME} PUBLIC pica::pica) +# target_link_libraries(${PROJECT_NAME} PUBLIC pica::pica) if(${AMY_BUILD_3DS}) target_link_libraries(${PROJECT_NAME} PUBLIC m z ctru citro3d) - target_compile_definitions(${PROJECT_NAME} PUBLIC AMY_3DS) + target_compile_definitions(${PROJECT_NAME} PUBLIC AMY_3DS ) endif() add_subdirectory(example) diff --git a/example/source/main.cpp b/example/source/main.cpp index b5ca86d..7f43d64 100755 --- a/example/source/main.cpp +++ b/example/source/main.cpp @@ -1,18 +1,68 @@ #include +#include #include +#include #include "amethyst/iron.hpp" +struct memory_metrics { + uint64_t t_TotalAllocated = 0; ///< Total Allocated Memory + uint64_t t_TotalFreed = 0; ///< Total Deleted Memory + /// @brief Gets the Currently Allocated Memory + uint32_t t_CurrentlyAllocated() { return t_TotalAllocated - t_TotalFreed; } +}; + +static memory_metrics metrics; + +bool rd7i_enable_memtrack = true; + +void* operator new(size_t size) { + void* ptr = malloc(size); + if (rd7i_enable_memtrack) metrics.t_TotalAllocated += size; + return ptr; +} + +void operator delete(void* memory, size_t size) { + if (rd7i_enable_memtrack) metrics.t_TotalFreed += size; + free(memory); +} + +size_t GetTotalAllocated() { return metrics.t_TotalAllocated; } + +size_t GetTotalFreed() { return metrics.t_TotalFreed; } + +size_t GetCurrent() { return metrics.t_CurrentlyAllocated(); } + +std::string FormatBytes(unsigned long long bytes) { + static const std::vector endings = { + "B", "KB", "MB", "GB", "TB", "Unk", + }; + int i = 0; + double b = bytes; + while (b > 1024) { + i++; + b /= 1024; + } + if (i >= (int)endings.size()) { + i = (int)endings.size() - 1; + } + return std::format("{:.1f} {}", b, endings[i]); +} + class example : public Amy::App { public: example() { Amy::Ctr::Init(); consoleInit(GFX_BOTTOM, NULL); Amy::C3D::Init(); - m_top = Amy::C3D::CreateScreen(GFX_TOP, GFX_LEFT); + m_top = Amy::C3D::CreateScreen(GFX_TOP); Amy::Iron::Init(); dl = new Amy::Iron::Drawlist(); dl->DrawSolid(); + mgr.AutoLoad("shader", "romfs:/shaders/shader2d.shbin"); + mgr.Get("shader")->Input(GPU_FLOAT, 3); + mgr.Get("shader")->Input(GPU_FLOAT, 3); + mgr.AutoLoad("icon", "romfs:/icon.png"); }; ~example() { Amy::C3D::DeleteScreen(m_top); @@ -20,22 +70,44 @@ class example : public Amy::App { }; void Main() override { + std::cout << std::format("\x1b[1;1HDelta: {:.3f} -> {:.3} FPS\x1b[K", this->Delta(), 1000.0/this->Delta()); + std::cout << std::format("\x1b[2;1HTime: {:.3f}\x1b[K", this->Time()); + std::cout << std::format( + "\x1b[3;1HMem: {}\n +{}\n -{}\nLin: {}\x1b[K", + FormatBytes(GetCurrent()), FormatBytes(GetTotalAllocated()), + FormatBytes(GetTotalFreed()), FormatBytes(linearSpaceFree())); + Amy::C3D::StartFrame(); m_top->Use(); m_top->Clear(); - dl->DrawRectFilled(0, 50, 0xff00ff00); + dl->DrawTex(mgr.Get("icon")); + dl->DrawRectFilled(100, 48, 0xffffffff); + dl->DrawSolid(); + dl->DrawRectFilled(0, 50, 0xffffffff); Amy::Iron::NewFrame(); Amy::Iron::DrawOn(m_top); Amy::Iron::Draw(*dl); dl->Clear(); + mgr.Get("shader")->Use(); + mgr.Get("shader")->SetMat4(0, mtx); + C3D_ImmDrawBegin(GPU_TRIANGLES); + C3D_ImmSendAttrib(200, 50, 0, 0); + C3D_ImmSendAttrib(1, 0, 0, 1); + C3D_ImmSendAttrib(300, 190, 0, 0); + C3D_ImmSendAttrib(0, 1, 0, 1); + C3D_ImmSendAttrib(100, 190, 0, 0); + C3D_ImmSendAttrib(0, 0, 1, 1); + C3D_ImmDrawEnd(); Amy::C3D::EndFrame(); - std::cout << std::format("\x1b[1;1HDelta: {:.3f}\x1b[K", this->Delta()); - std::cout << std::format("\x1b[2;1HTime: {:.2f}\x1b[K", this->Time()); } private: Amy::C3D::Screen* m_top = nullptr; Amy::Iron::Drawlist* dl = nullptr; + Amy::C3D::Shader* test = nullptr; + Amy::AssetMgr mgr; + Amy::mat4 mtx = + Amy::mat4::identity() * Amy::mat4::ortho(0, 400, 240, 0, 0, 1); }; int main() { diff --git a/include/amethyst/app.hpp b/include/amethyst/app.hpp index 9f40df9..f046c60 100644 --- a/include/amethyst/app.hpp +++ b/include/amethyst/app.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace Amy { class App { diff --git a/include/amethyst/assets.hpp b/include/amethyst/assets.hpp index dccafc2..7488198 100755 --- a/include/amethyst/assets.hpp +++ b/include/amethyst/assets.hpp @@ -1,34 +1,55 @@ #pragma once +#include #include #include namespace Amy { -class Assets { +class AssetMgr { public: - Assets() = default; - ~Assets() = default; + AssetMgr() = default; + ~AssetMgr() = default; - void add(ksr id, Asset* v) { pAssets[id] = v; } - void remove(ksr id) { + void AutoLoad(const ID& name, ksr path); + + void Add(const ID& id, Asset* v) { + if (pAssets.count(id)) { + throw std::runtime_error("[amy]: assets: " + id.GetName() + + " already exists!"); + } + pAssets[id] = v; + } + + void Remove(const ID& id) { if (pAssets.count(id)) { pAssets.erase(id); } } + template - T* get(ksr id) { + T* Get(const ID& id) { auto r = pAssets.find(id); if (r == pAssets.end()) { - throw std::runtime_error("[amy] assets: unable to find " + id); + throw std::runtime_error("[amy] assets: unable to find " + id.GetName()); } if (auto v = dynamic_cast(r->second)) { return v; } else { - throw std::runtime_error(id + " is not of type " + typeid(T).name()); + throw std::runtime_error(id.GetName() + " is not of type " + + typeid(T).name()); } } + template + bool IsType(const ID& id) { + auto r = pAssets.find(id); + if (r == pAssets.end()) { + throw std::runtime_error("[amy] assets: unable to find " + id.GetName()); + } + return dynamic_cast(r->second) != nullptr; + } + private: - std::map pAssets; + std::map pAssets; }; } // namespace Amy \ No newline at end of file diff --git a/include/amethyst/c3d.hpp b/include/amethyst/c3d.hpp index 42724b0..5e6fc5f 100755 --- a/include/amethyst/c3d.hpp +++ b/include/amethyst/c3d.hpp @@ -53,7 +53,7 @@ class C3D { void SetMat4(int loc, const mat4& m); int loc(ksr name); - private: + // private: C3D_AttrInfo pInfo; shaderProgram_s pProgram; DVLB_s* pCode; diff --git a/include/amethyst/id.hpp b/include/amethyst/id.hpp new file mode 100644 index 0000000..64e44dc --- /dev/null +++ b/include/amethyst/id.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +namespace Amy { +class ID { + public: + constexpr ID(const ui& id) { pID = id; } + constexpr ID(std::string name) { + pID = Utils::FNV32(name); +#ifdef AMY_KEEP_STR_ID + pName = name; +#endif + } + constexpr ID(const char* name) { + pID = Utils::FNV32(name); +#ifdef AMY_KEEP_STR_ID + pName = name; +#endif + } + ~ID() {} + + ui GetID() { return pID; } + std::string GetName() const { +#ifdef AMY_KEEP_STR_ID + return pName; +#else + return std::format("hash({:#08x})", pID); +#endif + } + + operator ui() const { return pID; } + operator std::string() const { return GetName(); } + + private: + ui pID; +#ifdef AMY_KEEP_STR_ID + str pName; +#endif +}; +} // namespace Amy \ No newline at end of file diff --git a/include/amethyst/image.hpp b/include/amethyst/image.hpp index 074494f..a366b42 100755 --- a/include/amethyst/image.hpp +++ b/include/amethyst/image.hpp @@ -12,6 +12,7 @@ class Image { BGR, // bpp == 3 ABGR, // bpp == 4 BGRA, // bpp == 4 + A8, // bpp == 1 (not supported in laoding) }; Image() = default; Image(ksr path) { this->Load(path); } diff --git a/include/amethyst/iron.hpp b/include/amethyst/iron.hpp index 93cfce8..3a9761e 100644 --- a/include/amethyst/iron.hpp +++ b/include/amethyst/iron.hpp @@ -15,8 +15,8 @@ class Iron { Vertex(float x, float y, float u, float v, ui clr) { pos.x = x; pos.y = y; - uv.x = x; - uv.y = y; + uv.x = u; + uv.y = v; color = clr; } Vertex(const fvec2& pos, const fvec2& uv, ui clr) { @@ -33,6 +33,7 @@ class Iron { class Command { public: Command() = default; + ~Command() = default; using ref = up; Command& Add(const u16& idx) { IndexBuf.push_back(VertexBuf.size() + idx); @@ -100,7 +101,10 @@ class Iron { DrawPolyLine(pPath, color, flags, thickness); PathClear(); } - void PathFill(ui color) { DrawConvexPolyFilled(pPath, color); } + void PathFill(ui color) { + DrawConvexPolyFilled(pPath, color); + PathClear(); + } void PathArcToN(const fvec2& c, float radius, float a_min, float a_max, int segments); void PathFastArcToN(const fvec2& c, float radius, float a_min, float a_max, @@ -152,6 +156,7 @@ class Iron { static void pSetupShader(); static void pFragConfig(); static void pInitSolidTex(); + static bool pCheckSize(size_t idx, size_t vtx); static std::vector> m_vbuf; static std::vector> m_ibuf; diff --git a/include/amethyst/texture.hpp b/include/amethyst/texture.hpp index 27727f4..b63bac6 100755 --- a/include/amethyst/texture.hpp +++ b/include/amethyst/texture.hpp @@ -26,14 +26,14 @@ class Texture : public Asset { ivec2& Size() { return pSize; } Rect& Uv() { return pUv; } - C3D_Tex* Ptr() { return pLoaded ? &pTex : nullptr; } + C3D_Tex* Ptr() { return pLoaded ? pTex : nullptr; } void Bind(int reg = 0); private: - C3D_Tex pTex; + C3D_Tex* pTex = nullptr; ivec2 pSize; - Rect pUv; + Rect pUv = fvec4(0, 1, 1, 0); bool pLoaded = false; }; } // namespace Amy \ No newline at end of file diff --git a/include/amethyst/utils.hpp b/include/amethyst/utils.hpp index a741871..cc53a73 100755 --- a/include/amethyst/utils.hpp +++ b/include/amethyst/utils.hpp @@ -3,8 +3,11 @@ #include namespace Amy { +class Error : public std::runtime_error { + public: + Error(ksr str) : std::runtime_error("[amy] " + str) {} +}; namespace Utils { -ui FastHash(ksr s); vec LoadFile2Mem(ksr path); ui HashMemory(kvr data); ui NextPow2(ui v); @@ -12,6 +15,20 @@ bool IsSingleBitNum(ui v); ull GetTimeNano(); ull GetTimeMicro(); ull GetTimeMilli(); +/** + * FNV Hash functiom (32 Bit) + * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + * Able to run at compile time btw + */ +constexpr ui FNV32(std::string_view str) { + ui ret = 0x811C9DC5; + for (char it : str) { + ret ^= static_cast(it); + ret *= 16777619; + } + return ret; +} + namespace Image { void ReverseBuf(vec& buf, int w, int h, int c); void RemoveAlphaChannel(vec& buf, int w, int h); diff --git a/source/app.cpp b/source/app.cpp index 4e4e1d4..1eb89b9 100644 --- a/source/app.cpp +++ b/source/app.cpp @@ -7,7 +7,6 @@ namespace Amy { void App::Run() { pLast = Utils::GetTimeNano(); - consoleInit(GFX_BOTTOM, NULL); while (aptMainLoop()) { ull c = Utils::GetTimeNano(); pDelta = static_cast(static_cast(c) - diff --git a/source/assets.cpp b/source/assets.cpp new file mode 100644 index 0000000..454a0ad --- /dev/null +++ b/source/assets.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +namespace Amy { +void AssetMgr::AutoLoad(const ID& id, ksr path) { + if (path.ends_with(".png") || path.ends_with(".jpg") || + path.ends_with(".bmp")) { + Texture* tex = new Texture(); + tex->Load(path); + Add(id, tex); + } else if (path.ends_with(".shbin")) { + C3D::Shader* shader = new C3D::Shader(); + shader->Load(path); + Add(id, shader); + } else { + throw std::runtime_error("[amy]: assets: " + id.GetName() + " (" + path + + ") is unsupported for AssetMgr::AutoLoad!"); + } +} +} // namespace Amy \ No newline at end of file diff --git a/source/c3d.cpp b/source/c3d.cpp index be06db3..386a920 100755 --- a/source/c3d.cpp +++ b/source/c3d.cpp @@ -2,7 +2,7 @@ #include #include -#include +// #include namespace Amy { @@ -46,7 +46,7 @@ void C3D::Shader::Load(const std::string& path) { auto code = Utils::LoadFile2Mem(path); if (!code.size()) { throw std::runtime_error( - std::format("[amy] unsable to load shader ({})", path)); + std::format("[amy] unable to load shader ({})", path)); } Load(code); } @@ -60,8 +60,9 @@ void C3D::Shader::Load(const std::vector& data) { } void C3D::Shader::Compile(const std::string& code) { - auto ret = Pica::AssembleCode(code.c_str()); - Load(ret); + throw std::runtime_error("[amy]: unable to compile shader (not allowed)"); + /*auto ret = Pica::AssembleCode(code.c_str()); + Load(ret);*/ } void C3D::Shader::Use() { diff --git a/source/image.cpp b/source/image.cpp index 626387d..655b650 100755 --- a/source/image.cpp +++ b/source/image.cpp @@ -2,7 +2,7 @@ #include #include -// #define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_IMPLEMENTATION #include namespace Amy { @@ -75,6 +75,9 @@ int Image::GetBppOfFmt(const Image::Format& fmt) { case RGB565: return 2; break; + case A8: + return 1; + break; default: return 0; // Unknown diff --git a/source/iron/iron.cpp b/source/iron/iron.cpp index bef900a..cc068d7 100644 --- a/source/iron/iron.cpp +++ b/source/iron/iron.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -103,6 +104,12 @@ void Iron::Draw(const std::vector& data) { scissor == data[i]->ScissorRect && tex == data[i]->Tex) { auto c = data[i].get(); + if (!pCheckSize(c->IndexBuf.size(), c->VertexBuf.size())) { + throw Error("iron: too much draw data!!!" + + std::format("\nIdx: {}\nVtx: {}", c->IndexBuf.size(), + c->VertexBuf.size())); + } + for (int j = 0; j < c->IndexBuf.size(); j++) { m_ibuf[m_idx++] = m_vtx + c->IndexBuf[j]; } @@ -118,15 +125,34 @@ void Iron::Draw(const std::vector& data) { C3D::DrawElements(i - start, m_ibuf.data() + start); } C3D::DepthTest(true); + /*std::ofstream off("hello.txt"); + for (int i = 0; i < m_idx; i++) { + auto& v = m_vbuf[m_ibuf[i]]; + off << std::format("{} -> [{}] [{}] #{:08X}\n", m_ibuf[i], v.pos, v.uv, + v.color); + } + off.close(); + throw std::runtime_error("halt");*/ +} + +bool Iron::pCheckSize(size_t idx, size_t vtx) { + return idx < m_ibuf.size() && vtx < m_vbuf.size(); } void Iron::pSetupShader() { m_shader = new C3D::Shader(); - m_shader->Load("romfs:/shaders/lithium.shbin"); + m_shader->pCode = DVLB_ParseFile((u32*)li_shader, li_shader_size); + shaderProgramInit(&m_shader->pProgram); + shaderProgramSetVsh(&m_shader->pProgram, &m_shader->pCode->DVLE[0]); + AttrInfo_Init(&m_shader->pInfo); + AttrInfo_AddLoader(&m_shader->pInfo, 0, GPU_FLOAT, 2); + AttrInfo_AddLoader(&m_shader->pInfo, 1, GPU_FLOAT, 2); + AttrInfo_AddLoader(&m_shader->pInfo, 2, GPU_UNSIGNED_BYTE, 4); + // m_shader->Load("romfs:/shaders/lithium.shbin"); // m_shader->Compile(__ironshader__); - m_shader->Input(GPU_FLOAT, 2); // pos - m_shader->Input(GPU_FLOAT, 2); // uv - m_shader->Input(GPU_UNSIGNED_BYTE, 4); // color + // m_shader->Input(GPU_FLOAT, 2); // pos + // m_shader->Input(GPU_FLOAT, 2); // uv + // m_shader->Input(GPU_UNSIGNED_BYTE, 4); // color uLocProj = m_shader->loc("projection"); } @@ -141,6 +167,9 @@ void Iron::pInitSolidTex() { std::vector pixels(16 * 16 * 4, 0xff); m_solid = new Texture(); m_solid->Load(pixels, 16, 16); + if (!m_solid->Ptr()) { + throw Error("white tex failed to load!"); + } } bool Iron::InBox(const fvec2& pos, const fvec2& size, const fvec4& area) { diff --git a/source/texture.cpp b/source/texture.cpp index 6e3e3fd..b589413 100755 --- a/source/texture.cpp +++ b/source/texture.cpp @@ -24,9 +24,11 @@ GPU_TEXCOLOR image2TexFmt(const Image::Format& fmt) { case Image::RGB565: return GPU_RGB565; break; + case Image::A8: + return GPU_A8; + break; default: // Dummy - return GPU_A4; throw std::runtime_error( "[amy] texture: Unsupported texture Format used!"); break; @@ -38,55 +40,55 @@ Texture::~Texture() { Unload(); } void Texture::Unload() { if (pLoaded) { - C3D_TexDelete(&pTex); + C3D_TexDelete(pTex); + delete pTex; + pTex = nullptr; pLoaded = false; } } void Texture::Load(ksr path) { Image img(path); - if (img.Width() > 1024 || img.Height() > 1024) { - throw std::runtime_error("Max Texture Size is 1024x1024!"); - } Load(img.GetBuffer(), img.Width(), img.Height(), img.Bpp(), img.Fmt()); } void Texture::Load(kvr pixels, int w, int h, int bpp, Image::Format fmt) { if (w > 1024 || h > 1024) { - throw std::runtime_error("Max Texture Size is 1024x1024!"); + throw std::runtime_error("[amy] texture: Max Texture Size is 1024x1024!"); } Unload(); pSize.x = w; - if (Utils::IsSingleBitNum(pSize.x)) { + if (!Utils::IsSingleBitNum(pSize.x)) { pSize.x = Utils::NextPow2(pSize.x); } pSize.y = h; - if (Utils::IsSingleBitNum(pSize.y)) { + if (!Utils::IsSingleBitNum(pSize.y)) { pSize.y = Utils::NextPow2(pSize.y); } auto filter = GPU_NEAREST; auto Format = image2TexFmt(fmt); - C3D_TexInit(&pTex, (u16)pSize.x, (u16)pSize.y, Format); - C3D_TexSetFilter(&pTex, filter, filter); + pTex = new C3D_Tex; + C3D_TexInit(pTex, (u16)pSize.x, (u16)pSize.y, Format); + C3D_TexSetFilter(pTex, filter, filter); // Using std::fill_n instead cause i hate this error lines // under the memset func in my editor - std::fill_n((unsigned char*)pTex.data, pTex.size, 0); + std::fill_n((uc*)pTex->data, pTex->size, 0); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { int dst_pos = tile3dsTex(x, y, pSize.x) * bpp; int src_pos = (y * w + x) * bpp; /// Best idea i had for (int i = 0; i < bpp; i++) { - ((u8*)pTex.data)[dst_pos + bpp - 1 - i] = pixels[src_pos + i]; + ((u8*)pTex->data)[dst_pos + bpp - 1 - i] = pixels[src_pos + i]; } } } - C3D_TexFlush(&pTex); - pTex.border = 0x00000000; - C3D_TexSetWrap(&pTex, GPU_REPEAT, GPU_REPEAT); + C3D_TexFlush(pTex); + pTex->border = 0x00000000; + C3D_TexSetWrap(pTex, GPU_REPEAT, GPU_REPEAT); pLoaded = true; } -void Texture::Bind(int reg) { C3D_TexBind(reg, &pTex); } +void Texture::Bind(int reg) { C3D_TexBind(reg, pTex); } } // namespace Amy \ No newline at end of file diff --git a/source/utils.cpp b/source/utils.cpp index 3a11a3e..3dd88b3 100755 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -3,14 +3,6 @@ namespace Amy { namespace Utils { -ui fastHash(ksr s) { - ui hash = 5381; - for (auto& it : s) { - hash = (hash * 33) + static_cast(it); - } - return hash; -} - vec LoadFile2Mem(ksr path) { std::ifstream iff(path, std::ios::binary); if (!iff) {