From 2c651f2359e743dc7b47a5154846e94b851b4859 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 6 Aug 2025 10:32:16 +0800 Subject: [PATCH 1/7] [Optimization] (MG_State/BufferState, VertexArrayState): optimize performance on vao deletion --- .../GLState/BufferState/BufferState.cpp | 1 + MobileGL/MG_State/GLState/Core.cpp | 8 +++- .../VertexArrayState/VertexArrayState.cpp | 47 +++++++++---------- .../VertexArrayState/VertexArrayState.h | 4 +- 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/MobileGL/MG_State/GLState/BufferState/BufferState.cpp b/MobileGL/MG_State/GLState/BufferState/BufferState.cpp index 0dec2446..438e9c92 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferState.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferState.cpp @@ -36,6 +36,7 @@ namespace MobileGL { return m_bindingSlots[i]; } } + assert(false); } void BufferState::MarkBufferObjectForDeletion(Uint index) { diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 54f75164..89d36fac 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -60,8 +60,12 @@ namespace MobileGL { void GLContext::MarkBufferObjectForDeletion(Uint index) { if (ValidateBufferObject(index)) { auto bufferObject = m_bufferState.GetBufferObject(index); - for (SizeT i = 0; i < m_vertexArrayState.GetAllVertexArrays().size(); ++i) { - auto vao = m_vertexArrayState.GetAllVertexArrays()[i]; + auto& vaos = m_vertexArrayState.GetAllVertexArrays(); + for (SizeT i = 0; i < vaos.size(); ++i) { + auto vao = vaos[i]; + if (vao == nullptr) + continue; + if (vao->GetIndexBufferBindingSlot().GetBoundObject() == bufferObject) { vao->GetIndexBufferBindingSlot().Bind(nullptr); } diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp index 2ff2500f..630f510e 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp @@ -8,11 +8,11 @@ namespace MobileGL { } SharedPtr VertexArrayState::GetVertexArrayObject(Uint index) { - auto it = m_vertexArrays.find(index); - if (it != m_vertexArrays.end()) { - return it->second; - } - return nullptr; + if (index >= m_vertexArrays.size()) + // FIXME: report a GL error here + return nullptr; + + return m_vertexArrays[index]; } Vector VertexArrayState::GenerateNames(Uint number) { @@ -23,35 +23,37 @@ namespace MobileGL { void VertexArrayState::Bind(Uint index) { if (index == 0) { + // FIXME: should we make a dummy VAO at index 0? m_boundVertexArray = nullptr; return; } - auto it = m_vertexArrays.find(index); - if (it != m_vertexArrays.end()) { - m_boundVertexArray = it->second; - } - else { - m_boundVertexArray = nullptr; - } + m_boundVertexArray = GetVertexArrayObject(index); } SharedPtr VertexArrayState::CreateVertexArrayObject(Uint index) { - auto vao = MakeShared(); - m_vertexArrays[index] = vao; + if (index >= m_vertexArrays.size()) { + // power-of-2 reallocation + m_vertexArrays.reserve(std::bit_ceil(index + 1)); + m_vertexArrays.resize(index + 1, nullptr); + } + auto vao = m_vertexArrays[index] = MakeShared(); return vao; } void VertexArrayState::MarkVertexArrayForDeletion(Uint index) { if (m_indexGenerator.IsValid(index)) { - if (m_boundVertexArray && m_vertexArrays.find(index) != m_vertexArrays.end() && - m_vertexArrays[index] == m_boundVertexArray) { + if (m_boundVertexArray) { m_boundVertexArray = nullptr; } - m_vertexArrays.erase(index); + if (ValidateVertexArrayObject(index)) { + m_vertexArrays[index] = nullptr; + } + m_indexGenerator.Delete(index); } + // FIXME: report GL error here? } bool VertexArrayState::ValidateName(Uint index) const { @@ -59,20 +61,15 @@ namespace MobileGL { } bool VertexArrayState::ValidateVertexArrayObject(Uint index) const { - return m_vertexArrays.find(index) != m_vertexArrays.end(); + return index < m_vertexArrays.size() && m_vertexArrays[index] != nullptr; } SharedPtr VertexArrayState::GetBoundVertexArray() { return m_boundVertexArray; } - Vector> VertexArrayState::GetAllVertexArrays() { - Vector> arrays; - arrays.reserve(m_vertexArrays.size()); - for (const auto& pair : m_vertexArrays) { - arrays.push_back(pair.second); - } - return arrays; + Vector>& VertexArrayState::GetAllVertexArrays() { + return m_vertexArrays; } } } diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.h b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.h index d6b8707a..8c6cef59 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.h +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.h @@ -15,10 +15,10 @@ namespace MobileGL { bool ValidateName(Uint index) const; bool ValidateVertexArrayObject(Uint index) const; SharedPtr GetBoundVertexArray(); - Vector> GetAllVertexArrays(); + Vector>& GetAllVertexArrays(); private: - UnorderedMap> m_vertexArrays; + Vector> m_vertexArrays; IndexGenerator m_indexGenerator; SharedPtr m_boundVertexArray; }; From 3606b8001698154eddf9582ac7de50f92a036071 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 6 Aug 2025 13:25:24 +0800 Subject: [PATCH 2/7] [Chore] (MG_Benchmark): add benchmark --- MobileGL/MG_Benchmark/CMakeLists.txt | 25 +++++++++++++++++++++ MobileGL/MG_Benchmark/SanityBench.cpp | 31 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 MobileGL/MG_Benchmark/CMakeLists.txt create mode 100644 MobileGL/MG_Benchmark/SanityBench.cpp diff --git a/MobileGL/MG_Benchmark/CMakeLists.txt b/MobileGL/MG_Benchmark/CMakeLists.txt new file mode 100644 index 00000000..7eb3e71e --- /dev/null +++ b/MobileGL/MG_Benchmark/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.14) +project(MobileGLTest) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +include(FetchContent) +FetchContent_Declare( + benchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG v1.9.4 +) +FetchContent_MakeAvailable(benchmark) + +set(MGL_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..) + +add_executable( + SanityBench + SanityBench.cpp +) +target_link_libraries( + SanityBench + benchmark::benchmark +) diff --git a/MobileGL/MG_Benchmark/SanityBench.cpp b/MobileGL/MG_Benchmark/SanityBench.cpp new file mode 100644 index 00000000..cb0a49f3 --- /dev/null +++ b/MobileGL/MG_Benchmark/SanityBench.cpp @@ -0,0 +1,31 @@ +#include +#include + +static void Sanity_VectorResize(benchmark::State& state) { + for (auto _ : state) { + std::vector v; + v.resize(state.range(0), 0); + for (int i = 0; i < state.range(0); i += 16) { + v[i] = i; + } + } + state.SetBytesProcessed(int64_t(state.iterations()) * + int64_t(state.range(0))); +} +BENCHMARK(Sanity_VectorResize)->Arg(2<<5)->Arg(2<<10)->Arg(2<<20); + + +static void Sanity_memcpy(benchmark::State& state) { + char* src = new char[state.range(0)]; + char* dst = new char[state.range(0)]; + memset(src, 'x', state.range(0)); + for (auto _ : state) + memcpy(dst, src, state.range(0)); + state.SetBytesProcessed(int64_t(state.iterations()) * + int64_t(state.range(0))); + delete[] src; + delete[] dst; +} +BENCHMARK(Sanity_memcpy)->Range(8, 8<<10); + +BENCHMARK_MAIN(); \ No newline at end of file From 510c2fa01ec9a7b5442fd674dd288d0eb956db01 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 6 Aug 2025 13:41:10 +0800 Subject: [PATCH 3/7] [Chore] (workflow): setup CI for benchmarks --- .github/workflows/benchmark.yml | 46 ++++++++++++++++++++++++++++ MobileGL/MG_Benchmark/CMakeLists.txt | 12 +++++--- 2 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/benchmark.yml diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..278db3f2 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,46 @@ +name: Benchmark + +on: + push: + branches: [ dev ] + +jobs: + benchmark: + runs-on: ubuntu-latest + env: + BENCH_ROOT: ${{github.workspace}}/MobileGL/MG_Benchmark + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + submodules: true + + - name: Get CMake + uses: lukka/get-cmake@latest + + - name: Prepare Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.1 + with: + vulkan-query-version: 1.4.304.1 + vulkan-components: Glslang, SPIRV-Cross, SPIRV-Tools, SPIRV-Reflect, SPIRV-Headers + vulkan-use-cache: true + + - name: Test Vulkan SDK Install + run: | + echo "Vulkan SDK Version=='$VULKAN_SDK_VERSION'" + echo "VULKAN_SDK=='$VULKAN_SDK'" + test -n "$VULKAN_SDK_VERSION" || exit 4 + ls $VULKAN_SDK/lib + + - name: Configure CMake + working-directory: ${{env.BENCH_ROOT}} + run: cmake -S . -B build-bench + + - name: Build + working-directory: ${{env.BENCH_ROOT}}/build-bench + run: cmake --build . -j${nproc} + + - name: Test + working-directory: ${{env.BENCH_ROOT}}/build-bench + run: ctest -V \ No newline at end of file diff --git a/MobileGL/MG_Benchmark/CMakeLists.txt b/MobileGL/MG_Benchmark/CMakeLists.txt index 7eb3e71e..8ffe3bb7 100644 --- a/MobileGL/MG_Benchmark/CMakeLists.txt +++ b/MobileGL/MG_Benchmark/CMakeLists.txt @@ -15,11 +15,13 @@ FetchContent_MakeAvailable(benchmark) set(MGL_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..) -add_executable( - SanityBench +add_executable(SanityBench SanityBench.cpp ) -target_link_libraries( - SanityBench - benchmark::benchmark +target_link_libraries(SanityBench + PRIVATE benchmark::benchmark ) + +enable_testing() + +add_test(NAME SanityBench COMMAND SanityBench) \ No newline at end of file From 8501745c43bd7906bd0d837b2b032924c281400b Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 6 Aug 2025 13:45:45 +0800 Subject: [PATCH 4/7] [Fix] (workflow): fix CI for benchmark --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 278db3f2..a45fc121 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -35,7 +35,7 @@ jobs: - name: Configure CMake working-directory: ${{env.BENCH_ROOT}} - run: cmake -S . -B build-bench + run: cmake -S . -B build-bench -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_ENABLE_TESTING=OFF - name: Build working-directory: ${{env.BENCH_ROOT}}/build-bench From 70614b769a17ebd4afbb3005a2d89f6f12a4dbdf Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 6 Aug 2025 13:49:32 +0800 Subject: [PATCH 5/7] [Fix] (MG_Benchmark/Sanity): add missing include --- MobileGL/MG_Benchmark/SanityBench.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/MobileGL/MG_Benchmark/SanityBench.cpp b/MobileGL/MG_Benchmark/SanityBench.cpp index cb0a49f3..ace37cb8 100644 --- a/MobileGL/MG_Benchmark/SanityBench.cpp +++ b/MobileGL/MG_Benchmark/SanityBench.cpp @@ -1,5 +1,6 @@ #include #include +#include static void Sanity_VectorResize(benchmark::State& state) { for (auto _ : state) { From cbf6019d92c993661315c9a3c33dd101169f6e01 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 6 Aug 2025 13:53:51 +0800 Subject: [PATCH 6/7] [Chore] (workflow): fix some wording --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index a45fc121..7a2ee199 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -41,6 +41,6 @@ jobs: working-directory: ${{env.BENCH_ROOT}}/build-bench run: cmake --build . -j${nproc} - - name: Test + - name: Benchmark working-directory: ${{env.BENCH_ROOT}}/build-bench run: ctest -V \ No newline at end of file From 548263279e9c98f73f906f0dd23635c0717c954e Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 6 Aug 2025 17:14:01 +0800 Subject: [PATCH 7/7] [Feat] (MG_Util/ShaderTranspiler): POC to query member offsets from UBO --- MobileGL/MG_Test/Program/ProgramTest.cpp | 3 +++ MobileGL/MG_Util/ShaderTranspiler/Types.h | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index 21d87417..cf663ec3 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -223,4 +223,7 @@ TEST_F(ProgramTest, DecompProgram) { EXPECT_EQ(it->second, uniform.location); } } + + auto ubo0 = sessions[0].GetShaderInterface(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER); + auto ubo1 = sessions[1].GetShaderInterface(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER); } diff --git a/MobileGL/MG_Util/ShaderTranspiler/Types.h b/MobileGL/MG_Util/ShaderTranspiler/Types.h index a16019bf..066c82cf 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/Types.h +++ b/MobileGL/MG_Util/ShaderTranspiler/Types.h @@ -1,4 +1,5 @@ #pragma once +#include "MG_Util/Debug/Log.h" namespace MobileGL { namespace MG_Util { @@ -95,6 +96,22 @@ namespace MobileGL { var.name = list[i].name; var.location = spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationLocation); variables.push_back(var); + + if (resource_type == SPVC_RESOURCE_TYPE_UNIFORM_BUFFER) { + spvc_type type = spvc_compiler_get_type_handle(compiler, list[i].base_type_id); + size_t num_members = spvc_type_get_num_member_types(type); + printf("uniform %s {\n", var.name.c_str()); + for (size_t j = 0; j < num_members; ++j) { + const char *memberName = spvc_compiler_get_member_name(compiler, list[i].base_type_id, j); + // auto memberTypeId = spvc_type_get_member_type(type, j); + // auto memberType = spvc_compiler_get_type_handle(compiler, memberTypeId); + + unsigned memberOffset = 0; + spvc_compiler_type_struct_member_offset(compiler, type, j, &memberOffset); + printf("\b%s; // %u\n", memberName, memberOffset); + } + printf("}\n"); + } } std::sort(variables.begin(), variables.end()); return variables;