diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 319f794c..38975456 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -48,4 +48,4 @@ jobs: - name: Benchmark working-directory: ${{env.BENCH_ROOT}}/build-bench - run: ctest -V \ No newline at end of file + run: ctest -V -C Release \ No newline at end of file diff --git a/3rdparty/SPIRV-Cross b/3rdparty/SPIRV-Cross new file mode 160000 index 00000000..0e2880ab --- /dev/null +++ b/3rdparty/SPIRV-Cross @@ -0,0 +1 @@ +Subproject commit 0e2880ab990e79ce6cc8c79c219feda42d98b1e8 diff --git a/3rdparty/glslang b/3rdparty/glslang new file mode 160000 index 00000000..8a85691a --- /dev/null +++ b/3rdparty/glslang @@ -0,0 +1 @@ +Subproject commit 8a85691a0740d390761a1008b4696f57facd02c4 diff --git a/MobileGL/MG_Benchmark/CMakeLists.txt b/MobileGL/MG_Benchmark/CMakeLists.txt index 8ae6353a..2e6df4d7 100644 --- a/MobileGL/MG_Benchmark/CMakeLists.txt +++ b/MobileGL/MG_Benchmark/CMakeLists.txt @@ -1,9 +1,13 @@ cmake_minimum_required(VERSION 3.14) -project(MobileGLTest) +project(MobileGLBenchmark) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Enable downloading dependencies +set(BENCHMARK_DOWNLOAD_DEPENDENCIES ON CACHE BOOL "Allow downloading benchmark dependencies") +set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "Disable GTest tests") +set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Disable testing") include(FetchContent) FetchContent_Declare( @@ -14,15 +18,18 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(benchmark) +enable_testing() + set(MGL_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..) add_executable(SanityBench SanityBench.cpp ) + target_link_libraries(SanityBench PRIVATE benchmark::benchmark ) -enable_testing() +add_test(NAME SanityBench COMMAND SanityBench --benchmark_counters_tabular=true) -add_test(NAME SanityBench COMMAND SanityBench) \ No newline at end of file +add_subdirectory(Program) \ No newline at end of file diff --git a/MobileGL/MG_Benchmark/Program/CMakeLists.txt b/MobileGL/MG_Benchmark/Program/CMakeLists.txt new file mode 100644 index 00000000..f2fc3012 --- /dev/null +++ b/MobileGL/MG_Benchmark/Program/CMakeLists.txt @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 3.24) + +# Add the directory containing our custom Find modules +list(APPEND CMAKE_MODULE_PATH "${MGL_ROOT}/buildsystem/cmake") + +# Find the required libraries using our custom Find modules +find_package(glslang REQUIRED) +find_package(SPIRVCross REQUIRED) +find_package(SPIRVTools) + +message(STATUS "glslang_LIBRARIES: ${glslang_LIBRARIES}") +message(STATUS "SPIRVTools_LIBRARIES: ${SPIRVTools_LIBRARIES}") +message(STATUS "SPIRVCross_LIBRARIES: ${SPIRVCross_LIBRARIES}") + +# Set the libraries to link against +set(LINK_LIBRARIES + ${glslang_LIBRARIES} + ${SPIRVCross_LIBRARIES} +) + +if (SPIRVTools_FOUND) + list(APPEND LINK_LIBRARIES ${SPIRVTools_LIBRARIES}) +endif() + +add_executable( + ProgramBench + ProgramBench.cpp + + ${MGL_ROOT}/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/ProgramState/ProgramState.cpp + ${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp + + ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToStr/GLEnumConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToGlslang/GLShaderLangConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp + ${MGL_ROOT}/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp + + ${MGL_ROOT}/MobileGL/MG_Util/ShaderTranspiler/glslang/UniformTraverser.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/SPIRVCrossToGL/SpvcTypeConverter.cpp + + ${MGL_ROOT}/MobileGL/MG_State/GLState/Core.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/BufferState/BufferState.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/TextureState/TextureUnit.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/TextureState/TextureState.cpp + ${MGL_ROOT}/MobileGL/MG_State/GLState/ErrorState/Error.cpp + ${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp + ${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp + ${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp + ${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.cpp + ${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToStr/GLEnumConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToStr/GLExtensionConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToStr/DataTypeConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToGlslang/GLShaderLangConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToGL/ErrorCodeConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Metrics/BufferMetrics.cpp + ${MGL_ROOT}/MobileGL/MG_Backend/Init.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Debug/Log.cpp +) + +target_include_directories(ProgramBench PRIVATE + ${MGL_ROOT}/include + ${MGL_ROOT}/MobileGL +) + +target_link_libraries( + ProgramBench + benchmark::benchmark + ${LINK_LIBRARIES} +) + +add_test(NAME ProgramBench COMMAND ProgramBench --benchmark_counters_tabular=true) \ No newline at end of file diff --git a/MobileGL/MG_Benchmark/Program/ProgramBench.cpp b/MobileGL/MG_Benchmark/Program/ProgramBench.cpp new file mode 100644 index 00000000..d047f43f --- /dev/null +++ b/MobileGL/MG_Benchmark/Program/ProgramBench.cpp @@ -0,0 +1,244 @@ +#include +#include + +#include "MG_Impl/GLImpl/Program/GL_Program.h" +#include "MG_State/GLState/Core.h" + +using namespace MobileGL; +using namespace MobileGL::MG_Impl::GLImpl; + +// Shader source code for benchmarking +const char* vsSrc = R"(#version 460 + +layout (location = 0) in vec4 Position; +in float fIn4; +in float fIn2; +in float fIn5; +in float fIn6; +in float fIn1; +in float fIn3; + +layout(location = 0) uniform mat4 ProjMat; +layout(location = 10) uniform mat3 TestMat3; +layout(location = 20) uniform mat2 TestMat2; +uniform vec2 InSize; +uniform vec2 OutSize; + +out vec2 texCoord; +out vec2 oneTexel; + +void main(){ + vec4 outPos = ProjMat * vec4(Position.xy, 0.0, 1.0); + gl_Position = vec4(outPos.xy, 0.2, 1.0); + + // Use TestMat2 and TestMat3 to prevent optimization + vec2 dummy2 = TestMat2[0]; + vec3 dummy3 = TestMat3[0]; + + oneTexel = (1.0 * (fIn1 * fIn2 * fIn3 * fIn4 * fIn5 * fIn6)) / InSize; + + texCoord = Position.xy / OutSize; +})"; + +const char* fsSrc = R"(#version 460 + +uniform sampler2D InSampler; + +in vec2 texCoord; +in vec2 oneTexel; + +uniform vec2 InSize; + +layout(location = 1) uniform vec3 Gray; +uniform vec3 RedMatrix; +uniform vec3 GreenMatrix0; +uniform vec3 BlueMatrix; +uniform vec3 Offset; +uniform vec3 ColorScale; +layout(location = 6) uniform float Saturation; +uniform int AQuickFoxJumpsOverALazyDog; +uniform int intVal; + +out vec4 fragColor; + +void main() { + vec4 InTexel = texture(InSampler, texCoord); + + // Color Matrix + float RedValue = dot(InTexel.rgb, RedMatrix); + float GreenValue = dot(InTexel.rgb, GreenMatrix0); + float BlueValue = dot(InTexel.rgb, BlueMatrix); + vec3 OutColor = vec3(RedValue, GreenValue, BlueValue); + + // Offset & Scale + OutColor = (OutColor * ColorScale) + Offset; + + // Saturation + float Luma = dot(OutColor, Gray); + vec3 Chroma = OutColor - Luma; + OutColor = (Chroma * Saturation) + Luma; + + fragColor = vec4(OutColor, float(intVal)); +})"; + +static void BM_CompileVertexShader(benchmark::State& state) { + // Initialize GL context + MG_State::pGLContext = new MG_State::GLState::GLContext(); + + SizeT vsLen = strlen(vsSrc); + + for (auto _ : state) { + // Create and compile vertex shader + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, NULL); + CompileShader(vs); + + // Cleanup + DeleteShader(vs); + } + + // Set the counter for Compiles/Second + state.counters["VS_Compiles/Second"] = state.iterations(); + state.counters["VS_Bytes/Second"] = state.iterations() * vsLen; + + // Cleanup GL context + delete MG_State::pGLContext; +} +BENCHMARK(BM_CompileVertexShader)->Unit(benchmark::kMillisecond); + +static void BM_CompileFragmentShader(benchmark::State& state) { + // Initialize GL context + MG_State::pGLContext = new MG_State::GLState::GLContext(); + + SizeT fsLen = strlen(fsSrc); + + for (auto _ : state) { + // Create and compile fragment shader + GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fsSrc, NULL); + CompileShader(fs); + + // Cleanup + DeleteShader(fs); + } + + // Set the counter for Compiles/Second + state.counters["FS_Compiles/Second"] = state.iterations(); + state.counters["FS_Bytes/Second"] = state.iterations() * fsLen; + + // Cleanup GL context + delete MG_State::pGLContext; +} +BENCHMARK(BM_CompileFragmentShader)->Unit(benchmark::kMillisecond); + +static void BM_CompileBothShaders(benchmark::State& state) { + // Initialize GL context + MG_State::pGLContext = new MG_State::GLState::GLContext(); + SizeT vsLen = strlen(vsSrc); + SizeT fsLen = strlen(fsSrc); + + for (auto _ : state) { + // Create and compile vertex shader + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, NULL); + CompileShader(vs); + + // Create and compile fragment shader + GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fsSrc, NULL); + CompileShader(fs); + + // Cleanup + DeleteShader(vs); + DeleteShader(fs); + } + + // Set the counter for Compiles/Second + state.counters["VS_Compiles/Second"] = state.iterations(); + state.counters["FS_Compiles/Second"] = state.iterations(); + state.counters["VS_Bytes/Second"] = state.iterations() * vsLen; + state.counters["FS_Bytes/Second"] = state.iterations() * fsLen; + + // Cleanup GL context + delete MG_State::pGLContext; +} +BENCHMARK(BM_CompileBothShaders)->Unit(benchmark::kMillisecond); + +static void BM_LinkProgram(benchmark::State& state) { + // Initialize GL context + MG_State::pGLContext = new MG_State::GLState::GLContext(); + + // Create and compile vertex shader + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, NULL); + CompileShader(vs); + + // Create and compile fragment shader + GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fsSrc, NULL); + CompileShader(fs); + + for (auto _ : state) { + GLuint program = CreateProgram(); + AttachShader(program, vs); + AttachShader(program, fs); + LinkProgram(program); + DeleteProgram(program); + } + + // Cleanup + DeleteShader(vs); + DeleteShader(fs); + + // Set the counter for Compiles/Second + state.counters["Links/Second"] = state.iterations(); + + // Cleanup GL context + delete MG_State::pGLContext; +} +BENCHMARK(BM_LinkProgram)->Unit(benchmark::kMillisecond); + +static void BM_CompileAndLink(benchmark::State& state) { + // Initialize GL context + MG_State::pGLContext = new MG_State::GLState::GLContext(); + + SizeT vsLen = strlen(vsSrc); + SizeT fsLen = strlen(fsSrc); + + for (auto _ : state) { + // Create and compile vertex shader + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, NULL); + CompileShader(vs); + + // Create and compile fragment shader + GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fsSrc, NULL); + CompileShader(fs); + + GLuint program = CreateProgram(); + AttachShader(program, vs); + AttachShader(program, fs); + LinkProgram(program); + DeleteProgram(program); + + // Cleanup + DeleteShader(vs); + DeleteShader(fs); + } + + + // Set the counter for Compiles/Second + state.counters["VS_Compiles/Second"] = state.iterations(); + state.counters["FS_Compiles/Second"] = state.iterations(); + state.counters["VS_Bytes/Second"] = state.iterations() * vsLen; + state.counters["FS_Bytes/Second"] = state.iterations() * fsLen; + state.counters["Links/Second"] = state.iterations(); + + // Cleanup GL context + delete MG_State::pGLContext; +} + +BENCHMARK(BM_CompileAndLink)->Unit(benchmark::kMillisecond); + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index 5e389b15..b38dffa1 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -396,13 +396,25 @@ namespace MobileGL { return; } - if (location >= programObject->GetUniformCount() || programObject->GetUniformName(location).empty()) { + // Check if location is valid + if (location < 0 || location > programObject->GetMaxUniformLocation()) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeShared("MG_Impl/GLImpl", __func__, "`location` does not correspond to a valid uniform variable location for the specified program object.")); return; } + + // Check if the location corresponds to an active uniform + const auto& uniformName = programObject->GetUniformName(location); + if (uniformName.empty()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeShared("MG_Impl/GLImpl", __func__, + "`location` does not correspond to a valid uniform variable location for the specified program object.")); + return; + } + auto isOpaque = programObject->IsUniformOpaqueAtLocation(location); if (!isOpaque) { // TODO: probably handle int/float differences @@ -513,6 +525,74 @@ namespace MobileGL { } } + // Helper function to transpose a 2x2 matrix + void TransposeMatrix2x2(const GLfloat* input, GLfloat* output) { + // Input matrix is in column-major order (OpenGL default) + // [0 2] + // [1 3] + // + // Output matrix should be in row-major order if transpose is true + // [0 1] + // [2 3] + output[0] = input[0]; // 0,0 element stays the same + output[1] = input[2]; // 0,1 element becomes 1,0 + output[2] = input[1]; // 1,0 element becomes 0,1 + output[3] = input[3]; // 1,1 element stays the same + } + + // Helper function to transpose a 3x3 matrix + void TransposeMatrix3x3(const GLfloat* input, GLfloat* output) { + // Input matrix is in column-major order (OpenGL default) + // [0 3 6] + // [1 4 7] + // [2 5 8] + // + // Output matrix should be in row-major order if transpose is true + // [0 1 2] + // [3 4 5] + // [6 7 8] + output[0] = input[0]; // 0,0 element stays the same + output[1] = input[3]; // 0,1 element becomes 1,0 + output[2] = input[6]; // 0,2 element becomes 2,0 + output[3] = input[1]; // 1,0 element becomes 0,1 + output[4] = input[4]; // 1,1 element stays the same + output[5] = input[7]; // 1,2 element becomes 2,1 + output[6] = input[2]; // 2,0 element becomes 0,2 + output[7] = input[5]; // 2,1 element becomes 1,2 + output[8] = input[8]; // 2,2 element stays the same + } + + // Helper function to transpose a 4x4 matrix + void TransposeMatrix4x4(const GLfloat* input, GLfloat* output) { + // Input matrix is in column-major order (OpenGL default) + // [0 4 8 12] + // [1 5 9 13] + // [2 6 10 14] + // [3 7 11 15] + // + // Output matrix should be in row-major order if transpose is true + // [0 1 2 3] + // [4 5 6 7] + // [8 9 10 11] + // [12 13 14 15] + output[0] = input[0]; // 0,0 element stays the same + output[1] = input[4]; // 0,1 element becomes 1,0 + output[2] = input[8]; // 0,2 element becomes 2,0 + output[3] = input[12]; // 0,3 element becomes 3,0 + output[4] = input[1]; // 1,0 element becomes 0,1 + output[5] = input[5]; // 1,1 element stays the same + output[6] = input[9]; // 1,2 element becomes 2,1 + output[7] = input[13]; // 1,3 element becomes 3,1 + output[8] = input[2]; // 2,0 element becomes 0,2 + output[9] = input[6]; // 2,1 element becomes 1,2 + output[10] = input[10]; // 2,2 element stays the same + output[11] = input[14]; // 2,3 element becomes 3,2 + output[12] = input[3]; // 3,0 element becomes 0,3 + output[13] = input[7]; // 3,1 element becomes 1,3 + output[14] = input[11]; // 3,2 element becomes 2,3 + output[15] = input[15]; // 3,3 element stays the same + } + void Uniform1fv_State(GLint location, GLsizei count, const GLfloat* value) { Uniformv_State<1>(location, count, value); } @@ -560,7 +640,7 @@ namespace MobileGL { return; } - if (location >= programObject->GetUniformCount() || location < -1) { + if (location > programObject->GetMaxUniformLocation() || location < -1) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeShared("MG_Impl/GLImpl", __func__, @@ -570,12 +650,15 @@ namespace MobileGL { // For matrix uniforms, we handle each matrix individually for (GLint i = 0; i < count; i++) { - // Note: In this implementation, we're not actually transposing the matrix data - // as we're directly copying to UBO. The transpose parameter is typically used - // in OpenGL to indicate whether the matrix should be transposed before being - // loaded into the uniform variable. In our case, we assume the shader compiler - // has handled the appropriate matrix layout. - Uniform_State<4>(*programObject, location + i, value + i * 4); + if (transpose == GL_TRUE) { + // Transpose the matrix before uploading + GLfloat transposedMatrix[4]; + TransposeMatrix2x2(value + i * 4, transposedMatrix); + Uniform_State<4>(*programObject, location + i, transposedMatrix); + } else { + // No transpose needed, directly copy the matrix data + Uniform_State<4>(*programObject, location + i, value + i * 4); + } } } @@ -594,7 +677,7 @@ namespace MobileGL { return; } - if (location >= programObject->GetUniformCount() || location < -1) { + if (location > programObject->GetMaxUniformLocation() || location < -1) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeShared("MG_Impl/GLImpl", __func__, @@ -604,12 +687,15 @@ namespace MobileGL { // For matrix uniforms, we handle each matrix individually for (GLint i = 0; i < count; i++) { - // Note: In this implementation, we're not actually transposing the matrix data - // as we're directly copying to UBO. The transpose parameter is typically used - // in OpenGL to indicate whether the matrix should be transposed before being - // loaded into the uniform variable. In our case, we assume the shader compiler - // has handled the appropriate matrix layout. - Uniform_State<9>(*programObject, location + i, value + i * 9); + if (transpose == GL_TRUE) { + // Transpose the matrix before uploading + GLfloat transposedMatrix[9]; + TransposeMatrix3x3(value + i * 9, transposedMatrix); + Uniform_State<9>(*programObject, location + i, transposedMatrix); + } else { + // No transpose needed, directly copy the matrix data + Uniform_State<9>(*programObject, location + i, value + i * 9); + } } } @@ -628,7 +714,7 @@ namespace MobileGL { return; } - if (location >= programObject->GetUniformCount() || location < -1) { + if (location > programObject->GetMaxUniformLocation() || location < -1) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeShared("MG_Impl/GLImpl", __func__, @@ -638,12 +724,15 @@ namespace MobileGL { // For matrix uniforms, we handle each matrix individually for (GLint i = 0; i < count; i++) { - // Note: In this implementation, we're not actually transposing the matrix data - // as we're directly copying to UBO. The transpose parameter is typically used - // in OpenGL to indicate whether the matrix should be transposed before being - // loaded into the uniform variable. In our case, we assume the shader compiler - // has handled the appropriate matrix layout. - Uniform_State<16>(*programObject, location + i, value + i * 16); + if (transpose == GL_TRUE) { + // Transpose the matrix before uploading + GLfloat transposedMatrix[16]; + TransposeMatrix4x4(value + i * 16, transposedMatrix); + Uniform_State<16>(*programObject, location + i, transposedMatrix); + } else { + // No transpose needed, directly copy the matrix data + Uniform_State<16>(*programObject, location + i, value + i * 16); + } } } diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index 9278ba28..656e9454 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -70,8 +70,8 @@ namespace MobileGL { return; } - auto uniformCount = m_program->getNumUniformVariables(); - for (int i = 0; i < uniformCount; i++) { + m_activeUniformCount = m_program->getNumUniformVariables(); + for (int i = 0; i < m_activeUniformCount; i++) { auto& uniform = m_program->getUniform(i); auto location = uniform.layoutLocation(); m_maxUniformLocation = std::max(m_maxUniformLocation, location); @@ -85,7 +85,7 @@ namespace MobileGL { m_uniformOffsets.resize(m_maxUniformLocation + 1); m_uniformArraySizes.resize(m_maxUniformLocation + 1); - for (int i = 0; i < uniformCount; i++) { + for (int i = 0; i < m_activeUniformCount; i++) { auto& uniform = m_program->getUniform(i); auto location = uniform.layoutLocation(); m_uniformNames[location] = uniform.name; @@ -167,17 +167,18 @@ namespace MobileGL { SpvcSession session(m_generatedSpirv[0]); auto srcResult = ShaderCompiler::DecompileShader(session); assert(srcResult); - auto src = srcResult.value(); + // auto src = srcResult.value(); + // printf("decompiled src: \n%s\n", src.c_str()); auto& meta = session.GetMetadata(); auto size = meta.uboSize; m_uboScratch.resize(size); - m_uniformOffsets.resize(meta.plainUniformOffsetsInUBO.size()); + m_uniformOffsets.resize(m_maxUniformLocation + 1); for (const auto& [name, offset] : meta.plainUniformOffsetsInUBO) { if (m_uniformLocations.find(name) != m_uniformLocations.end()) m_uniformOffsets[m_uniformLocations[name]] = offset; } - m_uniformSizesInBytes.resize(meta.plainUniformMemberSizesInBytes.size()); + m_uniformSizesInBytes.resize(m_maxUniformLocation + 1); for (const auto& [name, size] : meta.plainUniformMemberSizesInBytes) { if (m_uniformLocations.find(name) != m_uniformLocations.end()) m_uniformSizesInBytes[m_uniformLocations[name]] = size; diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h index dec3b366..afdcca0a 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -1,6 +1,7 @@ #pragma once #include #include "ShaderObject.h" +#include "MG_Util/Metrics/BufferMetrics.h" #include "MG_Util/ShaderTranspiler/SpvcSession.h" namespace MobileGL { @@ -20,7 +21,8 @@ namespace MobileGL { Vector>& GetAttachedShaders(); const String& GetInfoLog() const { return m_infoLog; } Int GetUniformMaxLength() const { return m_uniformNameMaxLength; } - Uint GetUniformCount() { return m_uniformNames.size(); } + Uint GetUniformCount() { return m_activeUniformCount; } + Uint GetMaxUniformLocation() const { return m_maxUniformLocation; } Int GetUniformLocation(const String& name) { const auto it = m_uniformLocations.find(name); return (it == m_uniformLocations.end()) ? -1 : (Int)it->second; @@ -40,7 +42,7 @@ namespace MobileGL { return m_uniformOffsets[location]; } Uint GetUniformSizesInBytes(Uint location) const { - return m_uniformSizesInBytes[location]; + return MG_Util::GetGLTypeSize(m_uniformTypes[location]); } Int GetAttributeLocation(const String& name) { @@ -101,6 +103,7 @@ namespace MobileGL { Vector m_uniformSizesInBytes; Vector m_uboScratch; + Uint m_activeUniformCount = 0; Uint m_maxUniformLocation = 0; Int m_uniformNameMaxLength = 0; Int m_attribInNameMaxLength = 0; diff --git a/MobileGL/MG_Test/Buffer/CMakeLists.txt b/MobileGL/MG_Test/Buffer/CMakeLists.txt index 9cf1dfda..16c18571 100644 --- a/MobileGL/MG_Test/Buffer/CMakeLists.txt +++ b/MobileGL/MG_Test/Buffer/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable( ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Metrics/BufferMetrics.cpp ${MGL_ROOT}/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp ${MGL_ROOT}/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp ${MGL_ROOT}/MobileGL/MG_Backend/Init.cpp diff --git a/MobileGL/MG_Test/CMakeLists.txt b/MobileGL/MG_Test/CMakeLists.txt index b4efe926..ff8235ef 100644 --- a/MobileGL/MG_Test/CMakeLists.txt +++ b/MobileGL/MG_Test/CMakeLists.txt @@ -4,6 +4,11 @@ project(MobileGLTest) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(MGL_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..) + +# Add the directory containing our custom Find modules +list(APPEND CMAKE_MODULE_PATH "${MGL_ROOT}/buildsystem/cmake") + include(FetchContent) FetchContent_Declare( googletest @@ -16,7 +21,10 @@ FetchContent_MakeAvailable(googletest) enable_testing() -set(MGL_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..) +# Find the required libraries using our custom Find modules +find_package(glslang REQUIRED) +find_package(SPIRVCross REQUIRED) +find_package(SPIRVTools) add_executable( SanityTest @@ -27,55 +35,18 @@ target_link_libraries( GTest::gtest_main ) -# Vulkan SDK's cmake config is fundamentally broken. Directly find libs instead -if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - find_package(Vulkan REQUIRED) - get_filename_component(VULKAN_LIB_DIR ${Vulkan_LIBRARY} PATH) +message(STATUS "glslang_LIBRARIES: ${glslang_LIBRARIES}") +message(STATUS "SPIRVTools_LIBRARIES: ${SPIRVTools_LIBRARIES}") +message(STATUS "SPIRVCross_LIBRARIES: ${SPIRVCross_LIBRARIES}") - set(LINK_LIBRARIES - ${VULKAN_LIB_DIR}/libglslang.a - ${VULKAN_LIB_DIR}/libspirv-cross-c-shared.dylib - ${VULKAN_LIB_DIR}/libGenericCodeGen.a - ${VULKAN_LIB_DIR}/libglslang-default-resource-limits.a - ${VULKAN_LIB_DIR}/libMachineIndependent.a - ${VULKAN_LIB_DIR}/libOSDependent.a - ${VULKAN_LIB_DIR}/libSPIRV-Tools-diff.a - ${VULKAN_LIB_DIR}/libSPIRV-Tools-link.a - ${VULKAN_LIB_DIR}/libSPIRV-Tools-lint.a - ${VULKAN_LIB_DIR}/libSPIRV-Tools-opt.a - ${VULKAN_LIB_DIR}/libSPIRV-Tools-reduce.a - ${VULKAN_LIB_DIR}/libSPIRV-Tools.a - ${VULKAN_LIB_DIR}/libSPIRV.a - ${VULKAN_LIB_DIR}/libSPVRemapper.a) -elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") - find_package(Vulkan REQUIRED) - get_filename_component(VULKAN_LIB_DIR ${Vulkan_LIBRARY} PATH) +# Set the libraries to link against +set(LINK_LIBRARIES + ${glslang_LIBRARIES} + ${SPIRVCross_LIBRARIES} +) - set(LINK_LIBRARIES - ${VULKAN_LIB_DIR}/glslang$<$:d>.lib - ${VULKAN_LIB_DIR}/spirv-cross-c-shared$<$:d>.lib - ${VULKAN_LIB_DIR}/GenericCodeGen$<$:d>.lib - ${VULKAN_LIB_DIR}/glslang-default-resource-limits$<$:d>.lib - ${VULKAN_LIB_DIR}/MachineIndependent$<$:d>.lib - ${VULKAN_LIB_DIR}/OSDependent$<$:d>.lib - ${VULKAN_LIB_DIR}/SPIRV-Tools-diff$<$:d>.lib - ${VULKAN_LIB_DIR}/SPIRV-Tools-link$<$:d>.lib - ${VULKAN_LIB_DIR}/SPIRV-Tools-lint$<$:d>.lib - ${VULKAN_LIB_DIR}/SPIRV-Tools-opt$<$:d>.lib - ${VULKAN_LIB_DIR}/SPIRV-Tools-reduce$<$:d>.lib - ${VULKAN_LIB_DIR}/SPIRV-Tools$<$:d>.lib - ${VULKAN_LIB_DIR}/SPIRV$<$:d>.lib - ${VULKAN_LIB_DIR}/SPVRemapper$<$:d>.lib) -else () - find_package(glslang CONFIG REQUIRED) - find_package(spirv_cross_core CONFIG REQUIRED) - find_package(spirv_cross_glsl CONFIG REQUIRED) - find_package(spirv_cross_hlsl CONFIG REQUIRED) - find_package(spirv_cross_msl CONFIG REQUIRED) - find_package(spirv_cross_cpp CONFIG REQUIRED) - find_package(spirv_cross_reflect CONFIG REQUIRED) - find_package(spirv_cross_c CONFIG REQUIRED) - set(LINK_LIBRARIES glslang::glslang spirv-cross-c) +if (SPIRVTools_FOUND) + list(APPEND LINK_LIBRARIES ${SPIRVTools_LIBRARIES}) endif() include(GoogleTest) diff --git a/MobileGL/MG_Test/Program/CMakeLists.txt b/MobileGL/MG_Test/Program/CMakeLists.txt index 3d828123..9b2aa5d2 100644 --- a/MobileGL/MG_Test/Program/CMakeLists.txt +++ b/MobileGL/MG_Test/Program/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable( ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Metrics/BufferMetrics.cpp ${MGL_ROOT}/MobileGL/MG_Backend/Init.cpp ${MGL_ROOT}/MobileGL/MG_Util/Debug/Log.cpp ) diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index 1a3d6b87..8d69bbc6 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -32,6 +32,8 @@ in float fIn1; in float fIn3; layout(location = 0) uniform mat4 ProjMat; +layout(location = 10) uniform mat3 TestMat3; +layout(location = 20) uniform mat2 TestMat2; uniform vec2 InSize; uniform vec2 OutSize; @@ -42,6 +44,10 @@ void main(){ vec4 outPos = ProjMat * vec4(Position.xy, 0.0, 1.0); gl_Position = vec4(outPos.xy, 0.2, 1.0); + // Use TestMat2 and TestMat3 to prevent optimization + vec2 dummy2 = TestMat2[0]; + vec3 dummy3 = TestMat3[0]; + oneTexel = (1.0 * (fIn1 * fIn2 * fIn3 * fIn4 * fIn5 * fIn6)) / InSize; texCoord = Position.xy / OutSize; @@ -140,7 +146,7 @@ TEST_F(ProgramTest, CompileAndLink) { ASSERT_EQ(GetUniformLocation(program, "Saturation"), 6); GLint uniformCount = 0; GetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformCount); - ASSERT_EQ(uniformCount, 12); + ASSERT_EQ(uniformCount, 14); GLint uniformNameMaxLength = 0; GetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformNameMaxLength); ASSERT_EQ(uniformNameMaxLength, 12); @@ -210,7 +216,7 @@ TEST_F(ProgramTest, UniformMatrixFunctions) { auto locProjMat = GetUniformLocation(program, "ProjMat"); ASSERT_NE(locProjMat, -1); - // 4x4 matrix (16 elements) + // 4x4 matrix (16 elements) - identity matrix GLfloat matrix4x4[16] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, @@ -218,9 +224,279 @@ TEST_F(ProgramTest, UniformMatrixFunctions) { 0.0f, 0.0f, 0.0f, 1.0f }; - // Test UniformMatrix4fv with count = 1 + // Test UniformMatrix4fv with count = 1 and transpose = GL_FALSE UniformMatrix4fv(locProjMat, 1, GL_FALSE, matrix4x4); - // Test with multiple matrices (count > 1) - // For this test, we would need uniforms that are arrays of matrices + // Test UniformMatrix4fv with count = 1 and transpose = GL_TRUE + UniformMatrix4fv(locProjMat, 1, GL_TRUE, matrix4x4); + + // Test with a non-identity matrix + GLfloat nonIdentityMatrix[16] = { + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f + }; + + // Test with transpose = GL_FALSE + UniformMatrix4fv(locProjMat, 1, GL_FALSE, nonIdentityMatrix); + + // Test with transpose = GL_TRUE + UniformMatrix4fv(locProjMat, 1, GL_TRUE, nonIdentityMatrix); +} + +TEST_F(ProgramTest, UniformMatrixTranspose) { + char infoLog[1024] = ""; + + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, NULL); + printf("Compiling vertex shader: %s\n", vsSrc); + CompileShader(vs); + GLint vsStatus = GL_FALSE; + GetShaderiv(vs, GL_COMPILE_STATUS, &vsStatus); + GetShaderInfoLog(vs, 1024, nullptr, infoLog); + ASSERT_EQ(vsStatus, GL_TRUE) << infoLog; + printf("Compiled vertex shader.\n"); + + GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fsSrc, NULL); + printf("Compiling fragment shader: %s\n", fsSrc); + CompileShader(fs); + GLint fsStatus = GL_FALSE; + GetShaderiv(fs, GL_COMPILE_STATUS, &fsStatus); + GetShaderInfoLog(fs, 1024, nullptr, infoLog); + ASSERT_EQ(fsStatus, GL_TRUE) << infoLog; + printf("Compiled fragment shader.\n"); + + GLuint program = CreateProgram(); + AttachShader(program, vs); + AttachShader(program, fs); + + BindAttribLocation(program, 1, "fIn1"); + BindAttribLocation(program, 3, "fIn3"); + BindAttribLocation(program, 5, "fIn5"); + printf("Linking program...\n"); + LinkProgram(program); + printf("Program linked.\n"); + + UseProgram(program); + + // Test 2x2 matrix transpose + auto locMat2 = GetUniformLocation(program, "TestMat2"); + ASSERT_NE(locMat2, -1); + // Test matrix (column-major as expected by OpenGL): + // [1 3] + // [2 4] + GLfloat matrix2x2[4] = { + 1.0f, 2.0f, // First column + 3.0f, 4.0f // Second column + }; + + // Expected values when transpose = GL_FALSE (no transpose): + // [1 3] + // [2 4] + GLfloat expected2x2_no_transpose[4] = {1.0f, 2.0f, 3.0f, 4.0f}; + + // Expected values when transpose = GL_TRUE (transposed): + // [1 2] + // [3 4] + // Stored in column-major order: [1, 3, 2, 4] + GLfloat expected2x2_transpose[4] = {1.0f, 3.0f, 2.0f, 4.0f}; + + // Test with transpose = GL_FALSE + UniformMatrix2fv(locMat2, 1, GL_FALSE, matrix2x2); + GLfloat result2x2_no_transpose[4]; + GetUniformfv(program, locMat2, result2x2_no_transpose); + for (int i = 0; i < 4; i++) { + EXPECT_FLOAT_EQ(result2x2_no_transpose[i], expected2x2_no_transpose[i]); + } + + // Test with transpose = GL_TRUE + UniformMatrix2fv(locMat2, 1, GL_TRUE, matrix2x2); + GLfloat result2x2_transpose[4]; + GetUniformfv(program, locMat2, result2x2_transpose); + for (int i = 0; i < 4; i++) { + EXPECT_FLOAT_EQ(result2x2_transpose[i], expected2x2_transpose[i]); + } + + // Test 3x3 matrix transpose + auto locMat3 = GetUniformLocation(program, "TestMat3"); + ASSERT_NE(locMat3, -1); + // Test matrix (column-major as expected by OpenGL): + // [1 4 7] + // [2 5 8] + // [3 6 9] + GLfloat matrix3x3[9] = { + 1.0f, 2.0f, 3.0f, // First column + 4.0f, 5.0f, 6.0f, // Second column + 7.0f, 8.0f, 9.0f // Third column + }; + + // Expected values when transpose = GL_FALSE (no transpose): + // [1 4 7] + // [2 5 8] + // [3 6 9] + GLfloat expected3x3_no_transpose[9] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}; + + // Expected values when transpose = GL_TRUE (transposed): + // [1 2 3] + // [4 5 6] + // [7 8 9] + // Stored in column-major order: [1, 4, 7, 2, 5, 8, 3, 6, 9] + GLfloat expected3x3_transpose[9] = {1.0f, 4.0f, 7.0f, 2.0f, 5.0f, 8.0f, 3.0f, 6.0f, 9.0f}; + + // Test with transpose = GL_FALSE + UniformMatrix3fv(locMat3, 1, GL_FALSE, matrix3x3); + GLfloat result3x3_no_transpose[9]; + GetUniformfv(program, locMat3, result3x3_no_transpose); + for (int i = 0; i < 9; i++) { + EXPECT_FLOAT_EQ(result3x3_no_transpose[i], expected3x3_no_transpose[i]); + } + + // Test with transpose = GL_TRUE + UniformMatrix3fv(locMat3, 1, GL_TRUE, matrix3x3); + GLfloat result3x3_transpose[9]; + GetUniformfv(program, locMat3, result3x3_transpose); + for (int i = 0; i < 9; i++) { + EXPECT_FLOAT_EQ(result3x3_transpose[i], expected3x3_transpose[i]); + } + + // Test 4x4 matrix transpose + auto locProjMat = GetUniformLocation(program, "ProjMat"); + ASSERT_NE(locProjMat, -1); + + // Test matrix (column-major as expected by OpenGL): + // [1 5 9 13] + // [2 6 10 14] + // [3 7 11 15] + // [4 8 12 16] + GLfloat matrix4x4[16] = { + 1.0f, 2.0f, 3.0f, 4.0f, // First column + 5.0f, 6.0f, 7.0f, 8.0f, // Second column + 9.0f, 10.0f, 11.0f, 12.0f, // Third column + 13.0f, 14.0f, 15.0f, 16.0f // Fourth column + }; + + // Expected values when transpose = GL_FALSE (no transpose): + // [1 5 9 13] + // [2 6 10 14] + // [3 7 11 15] + // [4 8 12 16] + GLfloat expected4x4_no_transpose[16] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f}; + + // Expected values when transpose = GL_TRUE (transposed): + // [1 2 3 4] + // [5 6 7 8] + // [9 10 11 12] + // [13 14 15 16] + // Stored in column-major order + GLfloat expected4x4_transpose[16] = {1.0f, 5.0f, 9.0f, 13.0f, 2.0f, 6.0f, 10.0f, 14.0f, + 3.0f, 7.0f, 11.0f, 15.0f, 4.0f, 8.0f, 12.0f, 16.0f}; + + // Test with transpose = GL_FALSE + UniformMatrix4fv(locProjMat, 1, GL_FALSE, matrix4x4); + GLfloat result4x4_no_transpose[16]; + GetUniformfv(program, locProjMat, result4x4_no_transpose); + for (int i = 0; i < 16; i++) { + EXPECT_FLOAT_EQ(result4x4_no_transpose[i], expected4x4_no_transpose[i]); + } + + // Test with transpose = GL_TRUE + UniformMatrix4fv(locProjMat, 1, GL_TRUE, matrix4x4); + GLfloat result4x4_transpose[16]; + GetUniformfv(program, locProjMat, result4x4_transpose); + for (int i = 0; i < 16; i++) { + EXPECT_FLOAT_EQ(result4x4_transpose[i], expected4x4_transpose[i]); + } +} + +TEST_F(ProgramTest, UniformLocationGaps) { + char infoLog[1024] = ""; + + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, NULL); + CompileShader(vs); + GLint vsStatus = GL_FALSE; + GetShaderiv(vs, GL_COMPILE_STATUS, &vsStatus); + GetShaderInfoLog(vs, 1024, nullptr, infoLog); + ASSERT_EQ(vsStatus, GL_TRUE) << infoLog; + + GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fsSrc, NULL); + CompileShader(fs); + GLint fsStatus = GL_FALSE; + GetShaderiv(fs, GL_COMPILE_STATUS, &fsStatus); + GetShaderInfoLog(fs, 1024, nullptr, infoLog); + ASSERT_EQ(fsStatus, GL_TRUE) << infoLog; + + GLuint program = CreateProgram(); + AttachShader(program, vs); + AttachShader(program, fs); + + BindAttribLocation(program, 1, "fIn1"); + BindAttribLocation(program, 3, "fIn3"); + BindAttribLocation(program, 5, "fIn5"); + LinkProgram(program); + + UseProgram(program); + + // Test that uniform locations are correctly assigned even with gaps + // ProjMat is at location 0 + ASSERT_EQ(GetUniformLocation(program, "ProjMat"), 0); + + // TestMat3 is at location 10 (gap from 1-9) + ASSERT_EQ(GetUniformLocation(program, "TestMat3"), 10); + + // TestMat2 is at location 20 (gap from 11-19) + ASSERT_EQ(GetUniformLocation(program, "TestMat2"), 20); + + // Gray is at location 1 (no gap) + ASSERT_EQ(GetUniformLocation(program, "Gray"), 1); + + // Saturation is at location 6 (gap from 2-5) + ASSERT_EQ(GetUniformLocation(program, "Saturation"), 6); + + // Verify that locations in gaps correctly return -1 + ASSERT_EQ(GetUniformLocation(program, "NonExistentUniform"), -1); + + // Test uniform operations on locations with gaps + GLfloat matrix3[9] = { + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f + }; + + // Test setting and getting uniform at location 10 (TestMat3) + UniformMatrix3fv(10, 1, GL_FALSE, matrix3); + GLfloat result[9]; + GetUniformfv(program, 10, result); + for (int i = 0; i < 9; i++) { + EXPECT_FLOAT_EQ(result[i], matrix3[i]); + } + + // Test setting and getting uniform at location 20 (TestMat2) + GLfloat matrix2[4] = { + 1.0f, 0.0f, + 0.0f, 1.0f + }; + UniformMatrix2fv(20, 1, GL_FALSE, matrix2); + GLfloat result2[4]; + GetUniformfv(program, 20, result2); + for (int i = 0; i < 4; i++) { + EXPECT_FLOAT_EQ(result2[i], matrix2[i]); + } + + // Test that accessing a gap location (e.g., 5) doesn't cause issues + // This should not crash or cause undefined behavior + Uniform1i(5, 114514); // Just to make sure we don't crash + + // Verify that we can still use uniforms with sequential locations + auto locRed = GetUniformLocation(program, "RedMatrix"); + Uniform3f(locRed, 1.0, 3.0, 5.0); + float redVal[3]; + GetUniformfv(program, locRed, redVal); + ASSERT_EQ(redVal[0], 1.0); + ASSERT_EQ(redVal[1], 3.0); + ASSERT_EQ(redVal[2], 5.0); } diff --git a/MobileGL/MG_Test/VertexArray/CMakeLists.txt b/MobileGL/MG_Test/VertexArray/CMakeLists.txt index 2dcac661..dcbdd056 100644 --- a/MobileGL/MG_Test/VertexArray/CMakeLists.txt +++ b/MobileGL/MG_Test/VertexArray/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable( ${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp ${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp + ${MGL_ROOT}/MobileGL/MG_Util/Metrics/BufferMetrics.cpp ${MGL_ROOT}/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp ${MGL_ROOT}/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp ${MGL_ROOT}/MobileGL/MG_Backend/Init.cpp diff --git a/MobileGL/MG_Util/Metrics/BufferMetrics.cpp b/MobileGL/MG_Util/Metrics/BufferMetrics.cpp new file mode 100644 index 00000000..5b275112 --- /dev/null +++ b/MobileGL/MG_Util/Metrics/BufferMetrics.cpp @@ -0,0 +1,117 @@ +// +// Created by Swung0x48 on 2025/8/22. +// + +#include "BufferMetrics.h" + +namespace MobileGL { +namespace MG_Util { + SizeT GetGLTypeSize(GLenum type) { + switch (type) { + // Scalars + case GL_BYTE: + case GL_UNSIGNED_BYTE: + case GL_UNSIGNED_BYTE_3_3_2: + return 1; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_HALF_FLOAT: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + return 2; + case GL_INT: + case GL_UNSIGNED_INT: + case GL_FLOAT: + case GL_FIXED: + case GL_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL_UNSIGNED_INT_5_9_9_9_REV: + return 4; + case GL_DOUBLE: + #ifdef GL_ARB_gpu_shader_int64 + case GL_UNSIGNED_INT64_ARB: + case GL_INT64_ARB: + #endif + return 8; + + // ---- Vector Types ---- + // 2-component vec + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + case GL_UNSIGNED_INT_VEC2: + case GL_BOOL_VEC2: + return 2 * sizeof(GLfloat); // GL_BOOL_VEC2 is also 2 * 4 bytes + case GL_DOUBLE_VEC2: + return 2 * sizeof(GLdouble); + + // 3-component vec + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + case GL_UNSIGNED_INT_VEC3: + case GL_BOOL_VEC3: + return 3 * sizeof(GLfloat); + case GL_DOUBLE_VEC3: + return 3 * sizeof(GLdouble); + + // 4-component vec + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + case GL_UNSIGNED_INT_VEC4: + case GL_BOOL_VEC4: + return 4 * sizeof(GLfloat); + case GL_DOUBLE_VEC4: + return 4 * sizeof(GLdouble); + + // ---- Matrix Types ---- + // Double non-square + case GL_FLOAT_MAT2: + return 2 * 2 * sizeof(GLfloat); + case GL_FLOAT_MAT3: + return 3 * 3 * sizeof(GLfloat); + case GL_FLOAT_MAT4: + return 4 * 4 * sizeof(GLfloat); + + // Float non-square + case GL_FLOAT_MAT2x3: + return 2 * 3 * sizeof(GLfloat); + case GL_FLOAT_MAT2x4: + return 2 * 4 * sizeof(GLfloat); + case GL_FLOAT_MAT3x2: + return 3 * 2 * sizeof(GLfloat); + case GL_FLOAT_MAT3x4: + return 3 * 4 * sizeof(GLfloat); + case GL_FLOAT_MAT4x2: + return 4 * 2 * sizeof(GLfloat); + case GL_FLOAT_MAT4x3: + return 4 * 3 * sizeof(GLfloat); + + // Double mat + case GL_DOUBLE_MAT2: + return 2 * 2 * sizeof(GLdouble); + case GL_DOUBLE_MAT3: + return 3 * 3 * sizeof(GLdouble); + case GL_DOUBLE_MAT4: + return 4 * 4 * sizeof(GLdouble); + + // Double non-square + case GL_DOUBLE_MAT2x3: + return 2 * 3 * sizeof(GLdouble); + case GL_DOUBLE_MAT2x4: + return 2 * 4 * sizeof(GLdouble); + case GL_DOUBLE_MAT3x2: + return 3 * 2 * sizeof(GLdouble); + case GL_DOUBLE_MAT3x4: + return 3 * 4 * sizeof(GLdouble); + case GL_DOUBLE_MAT4x2: + return 4 * 2 * sizeof(GLdouble); + case GL_DOUBLE_MAT4x3: + return 4 * 3 * sizeof(GLdouble); + + default: + return -1; + } + } +} +} diff --git a/MobileGL/MG_Util/Metrics/BufferMetrics.h b/MobileGL/MG_Util/Metrics/BufferMetrics.h new file mode 100644 index 00000000..8b218978 --- /dev/null +++ b/MobileGL/MG_Util/Metrics/BufferMetrics.h @@ -0,0 +1,16 @@ +// +// Created by Swung0x48 on 2025/8/22. +// + +#ifndef BUFFERMETRICS_H +#define BUFFERMETRICS_H + +#include + +namespace MobileGL { +namespace MG_Util { + SizeT GetGLTypeSize(GLenum type); +} +} + +#endif //BUFFERMETRICS_H diff --git a/buildsystem/cmake/FindSPIRVCross.cmake b/buildsystem/cmake/FindSPIRVCross.cmake new file mode 100644 index 00000000..1425e5de --- /dev/null +++ b/buildsystem/cmake/FindSPIRVCross.cmake @@ -0,0 +1,301 @@ +# FindSPIRVCross.cmake +# -------------------- +# +# Find SPIRV-Cross library +# +# This module defines the following variables: +# SPIRVCross_FOUND - True if SPIRV-Cross library is found +# SPIRVCross_INCLUDE_DIRS - Include directories for SPIRV-Cross +# SPIRVCross_LIBRARIES - Libraries for SPIRV-Cross +# +# This module reads hints about search locations from: +# SPIRVCross_ROOT - Preferred installation prefix + +# First try to find SPIRV-Cross using CONFIG mode (for vcpkg, conan, etc.) +if (NOT SPIRVCross_FOUND) + find_package(spirv_cross_core CONFIG QUIET) + if (spirv_cross_core_FOUND) + message(STATUS "Found SPIRV-Cross via CONFIG mode") + # Try to find all components + find_package(spirv_cross_glsl CONFIG QUIET) + find_package(spirv_cross_hlsl CONFIG QUIET) + find_package(spirv_cross_msl CONFIG QUIET) + find_package(spirv_cross_cpp CONFIG QUIET) + find_package(spirv_cross_reflect CONFIG QUIET) + find_package(spirv_cross_c CONFIG QUIET) + + # Set the libraries (using the CONFIG mode variables) + set(SPIRVCross_LIBRARIES + spirv-cross-core + spirv-cross-glsl + spirv-cross-hlsl + spirv-cross-msl + spirv-cross-cpp + spirv-cross-reflect + spirv-cross-c + ) + + # Mark as found + set(SPIRVCross_FOUND TRUE) + endif() +endif() + +# If CONFIG mode failed, try to find Vulkan SDK +if (NOT SPIRVCross_FOUND) + find_package(Vulkan QUIET) + if (Vulkan_FOUND) + get_filename_component(VULKAN_SDK_DIR ${Vulkan_INCLUDE_DIRS} DIRECTORY) + message(STATUS "Found Vulkan SDK: ${VULKAN_SDK_DIR}") + + # Try to find headers in Vulkan SDK + find_path(SPIRVCross_INCLUDE_DIR + NAMES spirv_cross/spirv_cross.hpp + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES include + NO_DEFAULT_PATH + ) + + # Try to find libraries in Vulkan SDK (separate debug/release searches) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + find_library(SPIRVCross_c_LIBRARY + NAMES spirv-cross-cd libspirv-cross-cd spirv-cross-c-sharedd libspirv-cross-c-sharedd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_cpp_LIBRARY + NAMES spirv-cross-cppd libspirv-cross-cppd spirv-cross-cpp-sharedd libspirv-cross-cpp-sharedd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_core_LIBRARY + NAMES spirv-cross-cored libspirv-cross-cored + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_reflect_LIBRARY + NAMES spirv-cross-reflectd libspirv-cross-reflectd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_glsl_LIBRARY + NAMES spirv-cross-glsld libspirv-cross-glsld + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_hlsl_LIBRARY + NAMES spirv-cross-hlsld libspirv-cross-hlsld + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_msl_LIBRARY + NAMES spirv-cross-msld libspirv-cross-msld + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + else() + find_library(SPIRVCross_c_LIBRARY + NAMES spirv-cross-c libspirv-cross-c spirv-cross-c-shared libspirv-cross-c-shared + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_cpp_LIBRARY + NAMES spirv-cross-cpp libspirv-cross-cpp spirv-cross-cpp-shared libspirv-cross-cpp-shared + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_core_LIBRARY + NAMES spirv-cross-core libspirv-cross-core + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_reflect_LIBRARY + NAMES spirv-cross-reflect libspirv-cross-reflect + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_glsl_LIBRARY + NAMES spirv-cross-glsl libspirv-cross-glsl + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_hlsl_LIBRARY + NAMES spirv-cross-hlsl libspirv-cross-hlsl + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVCross_msl_LIBRARY + NAMES spirv-cross-msl libspirv-cross-msl + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + endif() + endif() +endif() + +# If Vulkan SDK search failed, try standard search +if (NOT SPIRVCross_FOUND) + # Look for the header file + find_path(SPIRVCross_INCLUDE_DIR + NAMES spirv_cross/spirv_cross.hpp + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES include + ) + + # Look for the libraries (separate debug/release searches) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + find_library(SPIRVCross_c_LIBRARY + NAMES spirv-cross-cd libspirv-cross-cd spirv-cross-c-sharedd libspirv-cross-c-sharedd + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_cpp_LIBRARY + NAMES spirv-cross-cppd libspirv-cross-cppd spirv-cross-cpp-sharedd libspirv-cross-cpp-sharedd + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_core_LIBRARY + NAMES spirv-cross-cored libspirv-cross-cored + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_reflect_LIBRARY + NAMES spirv-cross-reflectd libspirv-cross-reflectd + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_glsl_LIBRARY + NAMES spirv-cross-glsld libspirv-cross-glsld + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_hlsl_LIBRARY + NAMES spirv-cross-hlsld libspirv-cross-hlsld + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_msl_LIBRARY + NAMES spirv-cross-msld libspirv-cross-msld + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + else() + find_library(SPIRVCross_c_LIBRARY + NAMES spirv-cross-c libspirv-cross-c spirv-cross-c-shared libspirv-cross-c-shared + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_core_LIBRARY + NAMES spirv-cross-core libspirv-cross-core + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_reflect_LIBRARY + NAMES spirv-cross-reflect libspirv-cross-reflect + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_glsl_LIBRARY + NAMES spirv-cross-glsl libspirv-cross-glsl + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_hlsl_LIBRARY + NAMES spirv-cross-hlsl libspirv-cross-hlsl + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVCross_msl_LIBRARY + NAMES spirv-cross-msl libspirv-cross-msl + HINTS ${SPIRVCross_ROOT} ENV SPIRVCross_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + endif() +endif() + +# If we found the main library, mark as found +if (SPIRVCross_c_LIBRARY OR SPIRVCross_cpp_LIBRARY AND SPIRVCross_INCLUDE_DIR) + set(SPIRVCross_FOUND TRUE) +endif() + +# Set the include directory and libraries +if (SPIRVCross_FOUND) + # If we already found the libraries through CONFIG mode, don't override them + if (NOT SPIRVCross_LIBRARIES) + set(SPIRVCross_INCLUDE_DIRS ${SPIRVCross_INCLUDE_DIR}) + set(SPIRVCross_LIBRARIES + ${SPIRVCross_c_LIBRARY} + ${SPIRVCross_cpp_LIBRARY} + ${SPIRVCross_core_LIBRARY} + ${SPIRVCross_reflect_LIBRARY} + ${SPIRVCross_glsl_LIBRARY} + ${SPIRVCross_hlsl_LIBRARY} + ${SPIRVCross_msl_LIBRARY} + ) + endif() +else() + # If we didn't find anything, set the variables from standard search + set(SPIRVCross_INCLUDE_DIRS ${SPIRVCross_INCLUDE_DIR}) + set(SPIRVCross_LIBRARIES + ${SPIRVCross_c_LIBRARY} + ${SPIRVCross_cpp_LIBRARY} + ${SPIRVCross_core_LIBRARY} + ${SPIRVCross_reflect_LIBRARY} + ${SPIRVCross_glsl_LIBRARY} + ${SPIRVCross_hlsl_LIBRARY} + ${SPIRVCross_msl_LIBRARY} + ) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SPIRVCross + REQUIRED_VARS SPIRVCross_LIBRARIES +) + +mark_as_advanced( + SPIRVCross_INCLUDE_DIR + SPIRVCross_LIBRARY + SPIRVCross_c_LIBRARY + SPIRVCross_cpp_LIBRARY + SPIRVCross_core_LIBRARY + SPIRVCross_reflect_LIBRARY + SPIRVCross_glsl_LIBRARY + SPIRVCross_hlsl_LIBRARY + SPIRVCross_msl_LIBRARY +) \ No newline at end of file diff --git a/buildsystem/cmake/FindSPIRVTools.cmake b/buildsystem/cmake/FindSPIRVTools.cmake new file mode 100644 index 00000000..1fae50c2 --- /dev/null +++ b/buildsystem/cmake/FindSPIRVTools.cmake @@ -0,0 +1,153 @@ +# FindSPIRVTools.cmake +# -------------------- +# +# Find SPIRV-Tools library +# +# This module defines the following variables: +# SPIRVTools_FOUND - True if SPIRV-Tools library is found +# SPIRVTools_INCLUDE_DIRS - Include directories for SPIRV-Tools +# SPIRVTools_LIBRARIES - Libraries for SPIRV-Tools +# +# This module reads hints about search locations from: +# SPIRVTools_ROOT - Preferred installation prefix + +# First try to find SPIRV-Tools using CONFIG mode (for vcpkg, conan, etc.) +if (NOT SPIRVTools_FOUND) + find_package(SPIRV-Tools CONFIG QUIET) + if (SPIRVTools_FOUND) + message(STATUS "Found SPIRV-Tools via CONFIG mode: ${SPIRV-Tools_DIR}") + set(SPIRVTools_INCLUDE_DIRS ${SPIRV-Tools_INCLUDE_DIRS}) + set(SPIRVTools_LIBRARIES ${SPIRV-Tools_LIBRARIES}) + endif() +endif() + +# If CONFIG mode failed, try to find Vulkan SDK +if (NOT SPIRVTools_FOUND) + find_package(Vulkan QUIET) + if (Vulkan_FOUND) + get_filename_component(VULKAN_SDK_DIR ${Vulkan_INCLUDE_DIRS} DIRECTORY) + message(STATUS "Found Vulkan SDK: ${VULKAN_SDK_DIR}") + + # Try to find headers in Vulkan SDK + find_path(SPIRVTools_INCLUDE_DIR + NAMES spirv-tools/libspirv.h + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES include + NO_DEFAULT_PATH + ) + + # Try to find libraries in Vulkan SDK (separate debug/release searches) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + find_library(SPIRVTools_LIBRARY + NAMES SPIRV-Toolsd libSPIRV-Toolsd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVTools_opt_LIBRARY + NAMES SPIRV-Tools-optd libSPIRV-Tools-optd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVTools_link_LIBRARY + NAMES SPIRV-Tools-linkd libSPIRV-Tools-linkd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + else() + find_library(SPIRVTools_LIBRARY + NAMES SPIRV-Tools libSPIRV-Tools + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVTools_opt_LIBRARY + NAMES SPIRV-Tools-opt libSPIRV-Tools-opt + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(SPIRVTools_link_LIBRARY + NAMES SPIRV-Tools-link libSPIRV-Tools-link + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + endif() + endif() +endif() + +# If Vulkan SDK search failed, try standard search +if (NOT SPIRVTools_FOUND) + # Look for the header file + find_path(SPIRVTools_INCLUDE_DIR + NAMES spirv-tools/libspirv.h + HINTS ${SPIRVTools_ROOT} ENV SPIRVTools_ROOT + PATH_SUFFIXES include + ) + + # Look for the libraries (separate debug/release searches) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + find_library(SPIRVTools_LIBRARY + NAMES SPIRV-Toolsd libSPIRV-Toolsd + HINTS ${SPIRVTools_ROOT} ENV SPIRVTools_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVTools_opt_LIBRARY + NAMES SPIRV-Tools-optd libSPIRV-Tools-optd + HINTS ${SPIRVTools_ROOT} ENV SPIRVTools_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVTools_link_LIBRARY + NAMES SPIRV-Tools-linkd libSPIRV-Tools-linkd + HINTS ${SPIRVTools_ROOT} ENV SPIRVTools_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + else() + find_library(SPIRVTools_LIBRARY + NAMES SPIRV-Tools libSPIRV-Tools + HINTS ${SPIRVTools_ROOT} ENV SPIRVTools_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVTools_opt_LIBRARY + NAMES SPIRV-Tools-opt libSPIRV-Tools-opt + HINTS ${SPIRVTools_ROOT} ENV SPIRVTools_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(SPIRVTools_link_LIBRARY + NAMES SPIRV-Tools-link libSPIRV-Tools-link + HINTS ${SPIRVTools_ROOT} ENV SPIRVTools_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + endif() +endif() + +# Set the include directory and libraries +set(SPIRVTools_INCLUDE_DIRS ${SPIRVTools_INCLUDE_DIR}) +set(SPIRVTools_LIBRARIES + ${SPIRVTools_LIBRARY} + ${SPIRVTools_opt_LIBRARY} + ${SPIRVTools_link_LIBRARY} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SPIRVTools + REQUIRED_VARS SPIRVTools_LIBRARY +) + +mark_as_advanced( + SPIRVTools_INCLUDE_DIR + SPIRVTools_LIBRARY + SPIRVTools_opt_LIBRARY + SPIRVTools_link_LIBRARY +) \ No newline at end of file diff --git a/buildsystem/cmake/Findglslang.cmake b/buildsystem/cmake/Findglslang.cmake new file mode 100644 index 00000000..7880b03b --- /dev/null +++ b/buildsystem/cmake/Findglslang.cmake @@ -0,0 +1,236 @@ +# Findglslang.cmake +# ------------------ +# +# Find glslang library +# +# This module defines the following variables: +# glslang_FOUND - True if glslang library is found +# glslang_INCLUDE_DIRS - Include directories for glslang +# glslang_LIBRARIES - Libraries for glslang +# +# This module reads hints about search locations from: +# glslang_ROOT - Preferred installation prefix + +# First try to find glslang using CONFIG mode (for vcpkg, conan, etc.) +if (NOT glslang_FOUND) + find_package(glslang CONFIG QUIET) + if (glslang_FOUND) + message(STATUS "Found glslang via CONFIG mode: ${glslang_DIR}") + set(glslang_LIBRARY glslang::glslang) + endif() +endif() + +# If CONFIG mode failed, try to find Vulkan SDK +if (NOT glslang_FOUND) + find_package(Vulkan QUIET) + if (Vulkan_FOUND) + get_filename_component(VULKAN_SDK_DIR ${Vulkan_INCLUDE_DIRS} DIRECTORY) + message(STATUS "Found Vulkan SDK: ${VULKAN_SDK_DIR}") + + # Try to find headers in Vulkan SDK + find_path(glslang_INCLUDE_DIR + NAMES glslang/Public/ShaderLang.h + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES include + NO_DEFAULT_PATH + ) + + # Try to find libraries in Vulkan SDK (separate debug/release searches) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + find_library(glslang_LIBRARY + NAMES glslangd libglslangd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_OSDependent_LIBRARY + NAMES OSDependentd libOSDependentd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_MachineIndependent_LIBRARY + NAMES MachineIndependentd libMachineIndependentd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_GenericCodeGen_LIBRARY + NAMES GenericCodeGend libGenericCodeGend + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_SPIRV_LIBRARY + NAMES SPIRVd libSPIRVd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_glslang_default_resource_limits_LIBRARY + NAMES glslang-default-resource-limitsd libglslang-default-resource-limitsd + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + else() + find_library(glslang_LIBRARY + NAMES glslang libglslang + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_OSDependent_LIBRARY + NAMES OSDependent libOSDependent + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_MachineIndependent_LIBRARY + NAMES MachineIndependent libMachineIndependent + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_GenericCodeGen_LIBRARY + NAMES GenericCodeGen libGenericCodeGen + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_SPIRV_LIBRARY + NAMES SPIRV libSPIRV + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + + find_library(glslang_glslang_default_resource_limits_LIBRARY + NAMES glslang-default-resource-limits libglslang-default-resource-limits + HINTS ${VULKAN_SDK_DIR} + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + NO_DEFAULT_PATH + ) + endif() + endif() +endif() + +# If Vulkan SDK search failed, try standard search +if (NOT glslang_FOUND) + # Look for the header file + find_path(glslang_INCLUDE_DIR + NAMES glslang/Public/ShaderLang.h + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES include + ) + + # Look for the libraries (separate debug/release searches) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + find_library(glslang_LIBRARY + NAMES glslangd libglslangd + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_OSDependent_LIBRARY + NAMES OSDependentd libOSDependentd + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_MachineIndependent_LIBRARY + NAMES MachineIndependentd libMachineIndependentd + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_GenericCodeGen_LIBRARY + NAMES GenericCodeGend libGenericCodeGend + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_SPIRV_LIBRARY + NAMES SPIRVd libSPIRVd + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_glslang_default_resource_limits_LIBRARY + NAMES glslang-default-resource-limitsd libglslang-default-resource-limitsd + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + else() + find_library(glslang_LIBRARY + NAMES glslang libglslang + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_OSDependent_LIBRARY + NAMES OSDependent libOSDependent + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_MachineIndependent_LIBRARY + NAMES MachineIndependent libMachineIndependent + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_GenericCodeGen_LIBRARY + NAMES GenericCodeGen libGenericCodeGen + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_SPIRV_LIBRARY + NAMES SPIRV libSPIRV + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + + find_library(glslang_glslang_default_resource_limits_LIBRARY + NAMES glslang-default-resource-limits libglslang-default-resource-limits + HINTS ${glslang_ROOT} ENV glslang_ROOT + PATH_SUFFIXES lib lib64 lib/x86_64 lib/x64 + ) + endif() +endif() + +# Set the include directory and libraries +set(glslang_INCLUDE_DIRS ${glslang_INCLUDE_DIR}) +set(glslang_LIBRARIES + ${glslang_LIBRARY} + ${glslang_OSDependent_LIBRARY} + ${glslang_MachineIndependent_LIBRARY} + ${glslang_GenericCodeGen_LIBRARY} + ${glslang_SPIRV_LIBRARY} + ${glslang_glslang_default_resource_limits_LIBRARY} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(glslang + REQUIRED_VARS glslang_LIBRARIES +) + +mark_as_advanced( + glslang_INCLUDE_DIR + glslang_LIBRARY + glslang_OSDependent_LIBRARY + glslang_MachineIndependent_LIBRARY + glslang_GenericCodeGen_LIBRARY + glslang_SPIRV_LIBRARY + glslang_glslang_default_resource_limits_LIBRARY +) \ No newline at end of file