From e9572b3a4494970854b08fb3a055024b736dd16d Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Tue, 4 Nov 2025 12:40:37 +0100 Subject: [PATCH 01/21] Add missing include in Playlist.cpp --- src/playlist/Playlist.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/playlist/Playlist.cpp b/src/playlist/Playlist.cpp index c9e97c164..8cb115284 100644 --- a/src/playlist/Playlist.cpp +++ b/src/playlist/Playlist.cpp @@ -1,6 +1,7 @@ #include "Playlist.hpp" #include +#include // Fall back to boost if compiler doesn't support C++17 #include PROJECTM_FILESYSTEM_INCLUDE From cc8a3f612a1d22fa2e75d6065cd71bd03dc8fdbb Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Tue, 4 Nov 2025 13:42:44 +0100 Subject: [PATCH 02/21] Use Boost's own CMake configuration file instead of CMake's module Otherwise, breaks builds with newer Boost versions. Also set policy CMP0167 to NEW and forcing CMake to use the Config package and not the built-in module. Will now require Boost 1.70, but this should be available everywhere. --- cmake/FilesystemSupport.cmake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/FilesystemSupport.cmake b/cmake/FilesystemSupport.cmake index d3914f75e..0bba31a78 100644 --- a/cmake/FilesystemSupport.cmake +++ b/cmake/FilesystemSupport.cmake @@ -22,7 +22,12 @@ if(NOT ENABLE_BOOST_FILESYSTEM AND STD_FILESYSTEM_EXISTS) set(PROJECTM_FILESYSTEM_INCLUDE ) set(PROJECTM_FILESYSTEM_USE_BOOST FALSE) else() - find_package(Boost REQUIRED COMPONENTS Filesystem) + # Requires Boost 1.70 or higher (which was released back in 2019) + if(POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) + endif() + + find_package(Boost CONFIG NO_MODULE REQUIRED COMPONENTS Filesystem) set(PROJECTM_FILESYSTEM_NAMESPACE boost) set(PROJECTM_FILESYSTEM_INCLUDE ) From ddf8f98cef60021c8b9ae621cc6801455d60fccd Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Mon, 10 Nov 2025 12:24:45 +0100 Subject: [PATCH 03/21] Force use of Boost's config package in projectM's installed CMake config --- src/libprojectM/projectM4Config.cmake.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libprojectM/projectM4Config.cmake.in b/src/libprojectM/projectM4Config.cmake.in index 00244c4df..883a6738b 100644 --- a/src/libprojectM/projectM4Config.cmake.in +++ b/src/libprojectM/projectM4Config.cmake.in @@ -13,7 +13,11 @@ if(NOT "@ENABLE_EMSCRIPTEN@") # ENABLE_EMSCRIPTEN endif() endif() if("@ENABLE_BOOST_FILESYSTEM@") # ENABLE_BOOST_FILESYSTEM - find_dependency(Boost COMPONENTS Filesystem) + if(POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) + endif() + + find_dependency(Boost CONFIG NO_MODULE COMPONENTS Filesystem) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Windows") find_dependency(GLEW) From e3a4ddf04d1b28029909088c44747ebb7a22bdac Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Tue, 4 Nov 2025 13:51:29 +0100 Subject: [PATCH 04/21] Silence CMake warning if projectM-Eval is not found externally. Also output additional information in both cases (found/not found). --- CMakeLists.txt | 7 ++++++- vendor/CMakeLists.txt | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e9c01fb2d..52c2ed173 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,7 +105,12 @@ if(ENABLE_SYSTEM_GLM) endif() if(ENABLE_SYSTEM_PROJECTM_EVAL) - find_package(projectM-Eval) + find_package(projectM-Eval QUIET) + if(NOT TARGET projectM::Eval) + message(STATUS "projectM-Eval could not be found externally. Using sources from vendor dir (if present).") + else() + message(STATUS "Found external projectM-Eval: Version ${projectM-Eval_VERSION}") + endif() endif() if(NOT BUILD_SHARED_LIBS AND CMAKE_SYSTEM_NAME STREQUAL "Windows") diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 6b3ac3e3e..74d7a6f75 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -3,6 +3,6 @@ if(NOT ENABLE_SYSTEM_GLM) endif() add_subdirectory(hlslparser) if(NOT TARGET projectM::Eval) -add_subdirectory(projectm-eval) + add_subdirectory(projectm-eval) endif() add_subdirectory(SOIL2) From e3f19db74507e40a6eac665de3ccb2a2aa2a4b6b Mon Sep 17 00:00:00 2001 From: yoyofr Date: Mon, 10 Nov 2025 12:27:17 +0100 Subject: [PATCH 05/21] Use previous frame's image (plus motion vectors) in blur textures Signed-off-by: Kai Blaschke --- src/libprojectM/MilkdropPreset/MilkdropPreset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libprojectM/MilkdropPreset/MilkdropPreset.cpp b/src/libprojectM/MilkdropPreset/MilkdropPreset.cpp index d2c3e5473..e311cf0bb 100755 --- a/src/libprojectM/MilkdropPreset/MilkdropPreset.cpp +++ b/src/libprojectM/MilkdropPreset/MilkdropPreset.cpp @@ -120,7 +120,7 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio // Update blur textures { - const auto warpedImage = m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0); + const auto warpedImage = m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0); assert(warpedImage.get()); m_state.blurTexture.Update(*warpedImage, m_perFrameContext); } From 7e5f84bda6ae6d45f2c400b67cdafb45ab04ff8b Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 12 Nov 2025 09:31:01 +0100 Subject: [PATCH 06/21] Set vertex_point_size uniform for all uses of the untextured vertex shader Only custom waves set the point size to 2, default waveforms still render the 2x2 pattern when using dots. Other draw calls aren't affected as they only draw lines/triangles, but it's good practice to not leave the uniform in an undefined state. Signed-off-by: Kai Blaschke --- src/libprojectM/MilkdropPreset/Border.cpp | 1 + src/libprojectM/MilkdropPreset/CustomShape.cpp | 2 ++ src/libprojectM/MilkdropPreset/CustomWaveform.cpp | 1 + src/libprojectM/MilkdropPreset/DarkenCenter.cpp | 1 + src/libprojectM/MilkdropPreset/Filters.cpp | 1 + src/libprojectM/MilkdropPreset/Waveform.cpp | 1 + 6 files changed, 7 insertions(+) diff --git a/src/libprojectM/MilkdropPreset/Border.cpp b/src/libprojectM/MilkdropPreset/Border.cpp index 57b34c7cf..e8e20750c 100644 --- a/src/libprojectM/MilkdropPreset/Border.cpp +++ b/src/libprojectM/MilkdropPreset/Border.cpp @@ -35,6 +35,7 @@ void Border::Draw(const PerFrameContext& presetPerFrameContext) m_presetState.untexturedShader.Bind(); m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); + m_presetState.untexturedShader.SetUniformFloat("vertex_point_size", 1.0f); std::array vertices{}; for (int border = 0; border < 2; border++) diff --git a/src/libprojectM/MilkdropPreset/CustomShape.cpp b/src/libprojectM/MilkdropPreset/CustomShape.cpp index d88a1e946..755d85529 100644 --- a/src/libprojectM/MilkdropPreset/CustomShape.cpp +++ b/src/libprojectM/MilkdropPreset/CustomShape.cpp @@ -253,6 +253,7 @@ void CustomShape::Draw() m_presetState.untexturedShader.Bind(); m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); + m_presetState.untexturedShader.SetUniformFloat("vertex_point_size", 1.0f); glBindVertexArray(m_vaoIdUntextured); glDrawArrays(GL_TRIANGLE_FAN, 0, sides + 2); @@ -271,6 +272,7 @@ void CustomShape::Draw() m_presetState.untexturedShader.Bind(); m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); + m_presetState.untexturedShader.SetUniformFloat("vertex_point_size", 1.0f); glVertexAttrib4f(1, static_cast(*m_perFrameContext.border_r), diff --git a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp index d9d0937f6..d676d3bc9 100644 --- a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp +++ b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp @@ -184,6 +184,7 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext) m_presetState.untexturedShader.Bind(); m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); + m_presetState.untexturedShader.SetUniformFloat("vertex_point_size", m_drawThick ? 2.0f : 1.0f); auto iterations = (m_drawThick && !m_useDots) ? 4 : 1; diff --git a/src/libprojectM/MilkdropPreset/DarkenCenter.cpp b/src/libprojectM/MilkdropPreset/DarkenCenter.cpp index 5e5efac74..0038d40a6 100644 --- a/src/libprojectM/MilkdropPreset/DarkenCenter.cpp +++ b/src/libprojectM/MilkdropPreset/DarkenCenter.cpp @@ -46,6 +46,7 @@ void DarkenCenter::Draw() m_presetState.untexturedShader.Bind(); m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); + m_presetState.untexturedShader.SetUniformFloat("vertex_point_size", 1.0f); glDrawArrays(GL_TRIANGLE_FAN, 0, 6); diff --git a/src/libprojectM/MilkdropPreset/Filters.cpp b/src/libprojectM/MilkdropPreset/Filters.cpp index 9c70904a1..d3acd56e9 100644 --- a/src/libprojectM/MilkdropPreset/Filters.cpp +++ b/src/libprojectM/MilkdropPreset/Filters.cpp @@ -31,6 +31,7 @@ void Filters::Draw() m_presetState.untexturedShader.Bind(); m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); + m_presetState.untexturedShader.SetUniformFloat("vertex_point_size", 1.0f); glBindVertexArray(m_vaoID); glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0); diff --git a/src/libprojectM/MilkdropPreset/Waveform.cpp b/src/libprojectM/MilkdropPreset/Waveform.cpp index 80e09e406..eefcd5752 100644 --- a/src/libprojectM/MilkdropPreset/Waveform.cpp +++ b/src/libprojectM/MilkdropPreset/Waveform.cpp @@ -56,6 +56,7 @@ void Waveform::Draw(const PerFrameContext& presetPerFrameContext) m_presetState.untexturedShader.Bind(); m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); + m_presetState.untexturedShader.SetUniformFloat("vertex_point_size", 1.0f); glBindVertexArray(m_vaoID); glBindBuffer(GL_ARRAY_BUFFER, m_vboID); From 82b71e2161dc94404c78a23fb05138836fb564e7 Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 12 Nov 2025 09:54:47 +0100 Subject: [PATCH 07/21] Fix some default waveform rendering bugs All waves were rendered upside-down, breaking presets requiring the wave to be at the top or bottom due to warp movement. Using the flipped transformation matrix for drawing will take care of this globally. Also fixed two small mistakes regarding operators. Signed-off-by: Kai Blaschke --- src/libprojectM/MilkdropPreset/Waveform.cpp | 2 +- src/libprojectM/MilkdropPreset/Waveforms/Line.cpp | 2 +- src/libprojectM/MilkdropPreset/Waveforms/WaveformMath.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libprojectM/MilkdropPreset/Waveform.cpp b/src/libprojectM/MilkdropPreset/Waveform.cpp index eefcd5752..bd9a662f3 100644 --- a/src/libprojectM/MilkdropPreset/Waveform.cpp +++ b/src/libprojectM/MilkdropPreset/Waveform.cpp @@ -55,7 +55,7 @@ void Waveform::Draw(const PerFrameContext& presetPerFrameContext) glLineWidth(1); m_presetState.untexturedShader.Bind(); - m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); + m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjectionFlipped); m_presetState.untexturedShader.SetUniformFloat("vertex_point_size", 1.0f); glBindVertexArray(m_vaoID); diff --git a/src/libprojectM/MilkdropPreset/Waveforms/Line.cpp b/src/libprojectM/MilkdropPreset/Waveforms/Line.cpp index 97cd904f2..6f2aa780d 100644 --- a/src/libprojectM/MilkdropPreset/Waveforms/Line.cpp +++ b/src/libprojectM/MilkdropPreset/Waveforms/Line.cpp @@ -17,7 +17,7 @@ void Line::GenerateVertices(const PresetState& presetState, const PerFrameContex m_wave1Vertices.resize(m_samples); - ClipWaveformEdges(1.57f + m_mysteryWaveParam); + ClipWaveformEdges(1.57f * m_mysteryWaveParam); for (int i = 0; i < m_samples; i++) { diff --git a/src/libprojectM/MilkdropPreset/Waveforms/WaveformMath.cpp b/src/libprojectM/MilkdropPreset/Waveforms/WaveformMath.cpp index fc5cdf5bc..27bdcaf48 100644 --- a/src/libprojectM/MilkdropPreset/Waveforms/WaveformMath.cpp +++ b/src/libprojectM/MilkdropPreset/Waveforms/WaveformMath.cpp @@ -71,7 +71,7 @@ auto WaveformMath::GetVertices(const PresetState& presetState, m_mysteryWaveParam = static_cast(*presetPerFrameContext.wave_mystery); - if (UsesNormalizedMysteryParam() && (m_mysteryWaveParam < 1.0f || m_mysteryWaveParam > 1.0f)) + if (UsesNormalizedMysteryParam() && (m_mysteryWaveParam < -1.0f || m_mysteryWaveParam > 1.0f)) { m_mysteryWaveParam = m_mysteryWaveParam * 0.5f + 0.5f; m_mysteryWaveParam -= std::floor(m_mysteryWaveParam); From 406e5c9c20db71b283b88ad3011635b5eb92ba0a Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Tue, 11 Nov 2025 09:57:54 +0100 Subject: [PATCH 08/21] Remove decay value fix for 16-bit GPUs, as it capped decay to 0.9375. --- src/libprojectM/MilkdropPreset/PerPixelMesh.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp b/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp index 8f0f49a2f..929077632 100644 --- a/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp +++ b/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp @@ -308,13 +308,7 @@ void PerPixelMesh::WarpedBlit(const PresetState& presetState, 0.5f / static_cast(presetState.renderContext.viewportSizeY)}; // Decay - float decay = static_cast(*perFrameContext.decay); - if (decay < 0.9999f) - { - // Milkdrop uses a GPU-specific value, either 0.0 or 2.0. - // We'll simply assume 2, as it's the default value. - decay = std::min(decay, (32.0f - 2.0f) / 32.0f); - } + float decay = std::min(static_cast(*perFrameContext.decay), 1.0f); // No blending between presets here, so we make sure blending is disabled. glDisable(GL_BLEND); From f8fe003416f0f25e88665fae85c5ac60ce5b7f22 Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 10:36:27 +0100 Subject: [PATCH 09/21] fixed an issue with sep attribute mngt / per pixel Signed-off-by: Kai Blaschke --- src/libprojectM/MilkdropPreset/CustomWaveform.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp index d676d3bc9..2185afda7 100644 --- a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp +++ b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp @@ -84,6 +84,9 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext) int const maxSampleCount{m_spectrum ? libprojectM::Audio::SpectrumSamples : libprojectM::Audio::WaveformSamples}; + int sampleCount = std::min(maxSampleCount, static_cast(*m_perFrameContext.samples)); + sampleCount -= m_sep; + // Initialize and execute per-frame code LoadPerFrameEvaluationVariables(presetPerFrameContext); m_perFrameContext.ExecutePerFrameCode(); @@ -91,8 +94,7 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext) // Copy Q and T vars to per-point context InitPerPointEvaluationVariables(); - int sampleCount = std::min(maxSampleCount, static_cast(*m_perFrameContext.samples)); - sampleCount -= m_sep; + sampleCount = std::min(maxSampleCount, static_cast(*m_perFrameContext.samples)); // If there aren't enough samples to draw a single line or dot, skip drawing the waveform. if ((m_useDots && sampleCount < 1) || sampleCount < 2) From 961ed473b63e4644565ccc876b3ce5c6ac09106d Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 10:40:42 +0100 Subject: [PATCH 10/21] Add additional row-access functions for matrices in HLSL transpiler Fixes "martin - mayday (shifters escape retouched).milk" and probably some others. Signed-off-by: Kai Blaschke --- vendor/hlslparser/src/GLSLGenerator.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vendor/hlslparser/src/GLSLGenerator.cpp b/vendor/hlslparser/src/GLSLGenerator.cpp index c8afbd797..a68ab5876 100644 --- a/vendor/hlslparser/src/GLSLGenerator.cpp +++ b/vendor/hlslparser/src/GLSLGenerator.cpp @@ -257,10 +257,25 @@ bool GLSLGenerator::Generate(HLSLTree* tree, Target target, Version version, con // Output the special function used to access rows in a matrix. m_writer.WriteLine(0, "vec2 %s(mat2 m, int i) { return vec2( m[0][i], m[1][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec2 %s(mat2 m, float i_float) { int i=int(i_float); return vec2( m[0][i], m[1][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec2 %s(mat2x3 m, int i) { return vec2( m[0][i], m[1][i]); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec2 %s(mat2x3 m, float i_float) { int i=int(i_float); return vec2( m[0][i], m[1][i]); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec2 %s(mat2x4 m, int i) { return vec2( m[0][i], m[1][i]); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec2 %s(mat2x4 m, float i_float) { int i=int(i_float); return vec2( m[0][i], m[1][i]); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec3 %s(mat3 m, int i) { return vec3( m[0][i], m[1][i], m[2][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec3 %s(mat3 m, float i_float) { int i=int(i_float); return vec3( m[0][i], m[1][i], m[2][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec3 %s(mat3x2 m, int i) { return vec3( m[0][i], m[1][i], m[2][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec3 %s(mat3x2 m, float i_float) { int i=int(i_float); return vec3( m[0][i], m[1][i], m[2][i] ); }", m_matrixRowFunction); m_writer.WriteLine(0, "vec3 %s(mat3x4 m, int i) { return vec3( m[0][i], m[1][i], m[2][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec3 %s(mat3x4 m, float i_float) { int i=int(i_float); return vec3( m[0][i], m[1][i], m[2][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec4 %s(mat4 m, int i) { return vec4( m[0][i], m[1][i], m[2][i], m[3][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec4 %s(mat4 m, float i_float) { int i=int(i_float); return vec4( m[0][i], m[1][i], m[2][i], m[3][i] ); }", m_matrixRowFunction); m_writer.WriteLine(0, "vec4 %s(mat4x3 m, int i) { return vec4( m[0][i], m[1][i], m[2][i], m[3][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec4 %s(mat4x3 m, float i_float) { int i=int(i_float); return vec4( m[0][i], m[1][i], m[2][i], m[3][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec4 %s(mat4x2 m, int i) { return vec4( m[0][i], m[1][i], m[2][i], m[3][i] ); }", m_matrixRowFunction); + m_writer.WriteLine(0, "vec4 %s(mat4x2 m, float i_float) { int i=int(i_float); return vec4( m[0][i], m[1][i], m[2][i], m[3][i] ); }", m_matrixRowFunction); // Output the special function used to do matrix cast for OpenGL 2.0 if (m_versionLegacy) From 082f3b7a4556ab77e3007344a29aadccafb84152 Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 11:53:30 +0100 Subject: [PATCH 11/21] Remove warp texel offset, introduced drift in warp shader. Signed-off-by: Kai Blaschke --- src/libprojectM/MilkdropPreset/PerPixelMesh.cpp | 6 ------ .../Shaders/PresetWarpVertexShaderGlsl330.vert | 9 ++------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp b/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp index 929077632..e88136142 100644 --- a/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp +++ b/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp @@ -303,10 +303,6 @@ void PerPixelMesh::WarpedBlit(const PresetState& presetState, 11.49f + 4.0f * cosf(warpTime * 0.933f + 5), }; - // Texel alignment - glm::vec2 const texelOffsets{0.5f / static_cast(presetState.renderContext.viewportSizeX), - 0.5f / static_cast(presetState.renderContext.viewportSizeY)}; - // Decay float decay = std::min(static_cast(*perFrameContext.decay), 1.0f); @@ -325,7 +321,6 @@ void PerPixelMesh::WarpedBlit(const PresetState& presetState, m_perPixelMeshShader.SetUniformFloat("warpTime", warpTime); m_perPixelMeshShader.SetUniformFloat("warpScaleInverse", warpScaleInverse); m_perPixelMeshShader.SetUniformFloat4("warpFactors", warpFactors); - m_perPixelMeshShader.SetUniformFloat2("texelOffset", texelOffsets); m_perPixelMeshShader.SetUniformFloat("decay", decay); } else @@ -339,7 +334,6 @@ void PerPixelMesh::WarpedBlit(const PresetState& presetState, shader.SetUniformFloat("warpTime", warpTime); shader.SetUniformFloat("warpScaleInverse", warpScaleInverse); shader.SetUniformFloat4("warpFactors", warpFactors); - shader.SetUniformFloat2("texelOffset", texelOffsets); shader.SetUniformFloat("decay", decay); } diff --git a/src/libprojectM/MilkdropPreset/Shaders/PresetWarpVertexShaderGlsl330.vert b/src/libprojectM/MilkdropPreset/Shaders/PresetWarpVertexShaderGlsl330.vert index c52ff6089..c631fcb50 100644 --- a/src/libprojectM/MilkdropPreset/Shaders/PresetWarpVertexShaderGlsl330.vert +++ b/src/libprojectM/MilkdropPreset/Shaders/PresetWarpVertexShaderGlsl330.vert @@ -25,7 +25,6 @@ uniform vec4 aspect; uniform float warpTime; uniform float warpScaleInverse; uniform vec4 warpFactors; -uniform vec2 texelOffset; uniform float decay; out vec4 frag_COLOR; @@ -43,8 +42,8 @@ void main() { float v = pos.y * aspectY * 0.5 * zoom2Inverse + 0.5; // original UV coordinates - vec2 uv_original = vec2(pos.x * 0.5 + 0.5 + texelOffset.x, - pos.y * 0.5 + 0.5 + texelOffset.y); + vec2 uv_original = vec2(pos.x * 0.5 + 0.5, + pos.y * 0.5 + 0.5); // Stretch on X, Y u = (u - warp_center.x) / stretch.x + warp_center.x; @@ -73,10 +72,6 @@ void main() { u = (u - 0.5) * invAspectX + 0.5; v = (v - 0.5) * invAspectY + 0.5; - // Final half-texel translation - u += texelOffset.x; - v += texelOffset.y; - frag_COLOR = vec4(decay, decay, decay, 1.0); frag_TEXCOORD0.xy = vec2(u, v); frag_TEXCOORD0.zw = uv_original; From ae4246c8b7c74cab4d5281f097182e593600db00 Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 12:01:48 +0100 Subject: [PATCH 12/21] Add missing "fwidth" intrinsic in HLSL transpiler Signed-off-by: Kai Blaschke --- vendor/hlslparser/src/HLSLParser.cpp | 147 ++++++++++++++------------- 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/vendor/hlslparser/src/HLSLParser.cpp b/vendor/hlslparser/src/HLSLParser.cpp index 358df2770..aaf73007c 100644 --- a/vendor/hlslparser/src/HLSLParser.cpp +++ b/vendor/hlslparser/src/HLSLParser.cpp @@ -36,7 +36,7 @@ enum CompareFunctionsResult Function2Better }; - + /** This structure stores a HLSLFunction-like declaration for an intrinsic function */ struct Intrinsic { @@ -103,7 +103,7 @@ struct Intrinsic HLSLFunction function; HLSLArgument argument[4]; }; - + Intrinsic SamplerIntrinsic(const char* name, HLSLBaseType returnType, HLSLBaseType arg1, HLSLBaseType samplerType, HLSLBaseType arg2) { Intrinsic i(name, returnType, arg1, arg2); @@ -113,7 +113,7 @@ Intrinsic SamplerIntrinsic(const char* name, HLSLBaseType returnType, HLSLBaseTy -static const int _numberTypeRank[NumericType_Count][NumericType_Count] = +static const int _numberTypeRank[NumericType_Count][NumericType_Count] = { //F B I U { 0, 4, 4, 4 }, // NumericType_Float @@ -261,7 +261,7 @@ static const EffectState samplerStates[] = { {"MipMapLodBias", 8, floatValues}, {"MaxMipLevel", 9, integerValues}, {"MaxAnisotropy", 10, integerValues}, - {"sRGBTexture", 11, booleanValues}, + {"sRGBTexture", 11, booleanValues}, }; static const EffectState effectStates[] = { @@ -415,7 +415,7 @@ static const EffectState pipelineStates[] = { #define SAMPLER_INTRINSIC_FUNCTION(name, sampler, arg1) \ Intrinsic( name, HLSLBaseType_Float4, sampler, arg1) #endif - + const Intrinsic _intrinsic[] = { INTRINSIC_FLOAT1_FUNCTION( "abs" ), @@ -478,6 +478,11 @@ const Intrinsic _intrinsic[] = Intrinsic( "length", HLSLBaseType_Float, HLSLBaseType_Float3 ), Intrinsic( "length", HLSLBaseType_Float, HLSLBaseType_Float4 ), + Intrinsic( "fwidth", HLSLBaseType_Float, HLSLBaseType_Float ), + Intrinsic( "fwidth", HLSLBaseType_Float, HLSLBaseType_Float2 ), + Intrinsic( "fwidth", HLSLBaseType_Float, HLSLBaseType_Float3 ), + Intrinsic( "fwidth", HLSLBaseType_Float, HLSLBaseType_Float4 ), + Intrinsic( "distance", HLSLBaseType_Float, HLSLBaseType_Float , HLSLBaseType_Float ), Intrinsic( "distance", HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float2 ), Intrinsic( "distance", HLSLBaseType_Float, HLSLBaseType_Float3, HLSLBaseType_Float3 ), @@ -617,10 +622,10 @@ const Intrinsic _intrinsic[] = INTRINSIC_FLOAT1_FUNCTION( "log" ), INTRINSIC_FLOAT1_FUNCTION( "log2" ), INTRINSIC_FLOAT1_FUNCTION( "log10" ), - + INTRINSIC_FLOAT1_FUNCTION( "ddx" ), INTRINSIC_FLOAT1_FUNCTION( "ddy" ), - + INTRINSIC_FLOAT1_FUNCTION( "sign" ), INTRINSIC_FLOAT2_FUNCTION( "step" ), INTRINSIC_FLOAT2_FUNCTION( "reflect" ), @@ -657,15 +662,15 @@ const Intrinsic _intrinsic[] = Intrinsic("asuint", HLSLBaseType_Uint, HLSLBaseType_Float), SAMPLER_INTRINSIC_FUNCTION("tex2D", HLSLBaseType_Sampler2D, HLSLBaseType_Float2), - + Intrinsic("tex2Dproj", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float4), SAMPLER_INTRINSIC_FUNCTION("tex2Dlod", HLSLBaseType_Sampler2D, HLSLBaseType_Float4), - + Intrinsic("tex2Dlod", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float4, HLSLBaseType_Int2), // With offset. SAMPLER_INTRINSIC_FUNCTION("tex2Dbias", HLSLBaseType_Sampler2D, HLSLBaseType_Float4), - + Intrinsic("tex2Dgrad", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2), Intrinsic("tex2Dgather", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Int), Intrinsic("tex2Dgather", HLSLBaseType_Float4, HLSLBaseType_Sampler2D, HLSLBaseType_Float2, HLSLBaseType_Int2, HLSLBaseType_Int), // With offset. @@ -718,7 +723,7 @@ const int _binaryOpPriority[] = // IC: I'm not sure this table is right, but any errors should be caught by the backend compiler. // Also, this is operator dependent. The type resulting from (float4 * float4x4) is not the same as (float4 + float4x4). // We should probably distinguish between component-wise operator and only allow same dimensions -HLSLBaseType _binaryOpTypeLookup[HLSLBaseType_NumericCount][HLSLBaseType_NumericCount] = +HLSLBaseType _binaryOpTypeLookup[HLSLBaseType_NumericCount][HLSLBaseType_NumericCount] = { { // float HLSLBaseType_Float, HLSLBaseType_Float2, HLSLBaseType_Float3, HLSLBaseType_Float4, @@ -1001,7 +1006,7 @@ static const char* GetBinaryOpName(HLSLBinaryOp binaryOp) * 4.) Conversion + scalar dimension promotion * 5.) Truncation (vector -> scalar or lower component vector, matrix -> scalar or lower component matrix) * 6.) Conversion + truncation - */ + */ static int GetTypeCastRank(HLSLTree * tree, const HLSLType& srcType, const HLSLType& dstType) { /*if (srcType.array != dstType.array || srcType.arraySize != dstType.arraySize) @@ -1039,7 +1044,7 @@ static int GetTypeCastRank(HLSLTree * tree, const HLSLType& srcType, const HLSLT { return srcType.samplerType == dstType.samplerType ? 0 : -1; } - + return 0; } @@ -1081,7 +1086,7 @@ static int GetTypeCastRank(HLSLTree * tree, const HLSLType& srcType, const HLSLT } return result; - + } static bool GetFunctionCallCastRanks(HLSLTree* tree, const HLSLFunctionCall* call, const HLSLFunction* function, int* rankBuffer) @@ -1095,7 +1100,7 @@ static bool GetFunctionCallCastRanks(HLSLTree* tree, const HLSLFunctionCall* cal const HLSLExpression* expression = call->argument; const HLSLArgument* argument = function->argument; - + for (int i = 0; i < call->numArguments; ++i) { int rank = GetTypeCastRank(tree, expression->expressionType, argument->type); @@ -1105,7 +1110,7 @@ static bool GetFunctionCallCastRanks(HLSLTree* tree, const HLSLFunctionCall* cal } rankBuffer[i] = rank; - + argument = argument->nextArgument; expression = expression->nextExpression; } @@ -1129,7 +1134,7 @@ struct CompareRanks }; static CompareFunctionsResult CompareFunctions(HLSLTree* tree, const HLSLFunctionCall* call, const HLSLFunction* function1, const HLSLFunction* function2) -{ +{ #if defined _WIN32 && !defined alloca int* function1Ranks = static_cast(_alloca(sizeof(int) * call->numArguments)); @@ -1161,7 +1166,7 @@ static CompareFunctionsResult CompareFunctions(HLSLTree* tree, const HLSLFunctio std::sort(function1Ranks, function1Ranks + call->numArguments, CompareRanks()); std::sort(function2Ranks, function2Ranks + call->numArguments, CompareRanks()); - + for (int i = 0; i < call->numArguments; ++i) { if (function1Ranks[i] < function2Ranks[i]) @@ -1242,7 +1247,7 @@ static bool GetBinaryOpResultType(HLSLBinaryOp binaryOp, const HLSLType& type1, result.array = false; result.arraySize = NULL; result.flags = (type1.flags & type2.flags) & HLSLTypeFlag_Const; // Propagate constness. - + return result.baseType != HLSLBaseType_Unknown; } @@ -1358,7 +1363,7 @@ bool HLSLParser::ParseTopLevel(HLSLStatement*& statement) int line = GetLineNumber(); const char* fileName = GetFileName(); - + HLSLType type; //HLSLBaseType type; //const char* typeName = NULL; @@ -1390,7 +1395,7 @@ bool HLSLParser::ParseTopLevel(HLSLStatement*& statement) structure->name = structName; m_userTypes.PushBack(structure); - + HLSLStructField* lastField = NULL; // Add the struct to our list of user defined types. @@ -1544,7 +1549,7 @@ bool HLSLParser::ParseTopLevel(HLSLStatement*& statement) // Note, no semi-colon at the end of a function declaration. statement = function; - + return true; } else @@ -1693,7 +1698,7 @@ bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& retur } HLSLAttribute * attributes = NULL; - ParseAttributeBlock(attributes); // @@ Leak if not assigned to node? + ParseAttributeBlock(attributes); // @@ Leak if not assigned to node? #if 0 // @@ Work in progress. // Static statements: @if only for now. @@ -1704,9 +1709,9 @@ bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& retur //HLSLIfStatement* ifStatement = m_tree->AddNode(fileName, line); //ifStatement->isStatic = true; //ifStatement->attributes = attributes; - + HLSLExpression * condition = NULL; - + m_allowUndeclaredIdentifiers = true; // Not really correct... better to push to stack? if (!Expect('(') || !ParseExpression(condition) || !Expect(')')) { @@ -1714,25 +1719,25 @@ bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& retur return false; } m_allowUndeclaredIdentifiers = false; - + if ((condition->expressionType.flags & HLSLTypeFlag_Const) == 0) { m_tokenizer.Error("Syntax error: @if condition is not constant"); return false; } - + int conditionValue; if (!m_tree->GetExpressionValue(condition, conditionValue)) { m_tokenizer.Error("Syntax error: Cannot evaluate @if condition"); return false; } - + if (!conditionValue) m_disableSemanticValidation = true; - + HLSLStatement * ifStatements = NULL; HLSLStatement * elseStatements = NULL; - + if (!ParseStatementOrBlock(ifStatements, returnType, /*scoped=*/false)) { m_disableSemanticValidation = false; @@ -1741,7 +1746,7 @@ bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& retur if (Accept(HLSLToken_Else)) { if (conditionValue) m_disableSemanticValidation = true; - + if (!ParseStatementOrBlock(elseStatements, returnType, /*scoped=*/false)) { m_disableSemanticValidation = false; @@ -1749,12 +1754,12 @@ bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& retur } } m_disableSemanticValidation = false; - + if (conditionValue) statement = ifStatements; else statement = elseStatements; - + // @@ Free the pruned statements? - + return true; } else { @@ -1762,7 +1767,7 @@ bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& retur } } #endif - + // If statement. if (Accept(HLSLToken_If)) { @@ -1783,7 +1788,7 @@ bool HLSLParser::ParseStatement(HLSLStatement*& statement, const HLSLType& retur } return true; } - + // For statement. if (Accept(HLSLToken_For)) { @@ -2184,15 +2189,15 @@ bool HLSLParser::AcceptAssign(HLSLBinaryOp& binaryOp) else if (Accept(HLSLToken_MinusEqual)) { binaryOp = HLSLBinaryOp_SubAssign; - } + } else if (Accept(HLSLToken_TimesEqual)) { binaryOp = HLSLBinaryOp_MulAssign; - } + } else if (Accept(HLSLToken_DivideEqual)) { binaryOp = HLSLBinaryOp_DivAssign; - } + } else { return false; @@ -2242,10 +2247,10 @@ bool HLSLParser::ParseBinaryExpression(int priority, HLSLExpression*& expression return false; } - + // Propagate constness. binaryExpression->expressionType.flags = (expression->expressionType.flags | expression2->expressionType.flags) & HLSLTypeFlag_Const; - + expression = binaryExpression; } else if (_conditionalOpPriority > priority && Accept('?')) @@ -2253,7 +2258,7 @@ bool HLSLParser::ParseBinaryExpression(int priority, HLSLExpression*& expression HLSLConditionalExpression* conditionalExpression = m_tree->AddNode(fileName, line); conditionalExpression->condition = expression; - + HLSLExpression* expression1 = NULL; HLSLExpression* expression2 = NULL; if (!ParseBinaryExpression(_conditionalOpPriority, expression1) || !Expect(':') || !ParseBinaryExpression(_conditionalOpPriority, expression2)) @@ -2311,7 +2316,7 @@ bool HLSLParser::ParsePartialConstructor(HLSLExpression*& expression, HLSLBaseTy if (!ParseExpressionList(')', false, constructorExpression->argument, numArguments)) { return false; - } + } constructorExpression->expressionType = constructorExpression->type; constructorExpression->expressionType.flags = HLSLTypeFlag_Const; expression = constructorExpression; @@ -2336,7 +2341,7 @@ bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, char& need } if (unaryOp == HLSLUnaryOp_BitNot) { - if (unaryExpression->expression->expressionType.baseType < HLSLBaseType_FirstInteger || + if (unaryExpression->expression->expressionType.baseType < HLSLBaseType_FirstInteger || unaryExpression->expression->expressionType.baseType > HLSLBaseType_LastInteger) { const char * typeName = GetTypeName(unaryExpression->expression->expressionType); @@ -2359,7 +2364,7 @@ bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, char& need } unaryExpression->expressionType = HLSLType(HLSLBaseType_Bool); - + // Propagate constness. unaryExpression->expressionType.flags = unaryExpression->expression->expressionType.flags & HLSLTypeFlag_Const; } @@ -2395,7 +2400,7 @@ bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, char& need expression = unaryExpression; return true; } - + // Expressions inside parenthesis or casts. char expressionEndChar = 0; if (Accept('(')) @@ -2426,7 +2431,7 @@ bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, char& need castingExpression->expressionType = type; return Expect(')') && ParseExpression(castingExpression->expression); } - + int numArguments = 0; if (!ParseExpressionList(expressionEndChar, false, expression, numArguments)) { @@ -2447,7 +2452,7 @@ bool HLSLParser::ParseTerminalExpression(HLSLExpression*& expression, char& need // Terminal values. float fValue = 0.0f; int iValue = 0; - + if (AcceptFloat(fValue)) { HLSLLiteralExpression* literalExpression = m_tree->AddNode(fileName, line); @@ -2728,7 +2733,7 @@ bool HLSLParser::ParseArgumentList(HLSLArgument*& firstArgument, int& numArgumen { const char* fileName = GetFileName(); int line = GetLineNumber(); - + HLSLArgument* lastArgument = NULL; numArguments = 0; @@ -3000,7 +3005,7 @@ const EffectState* GetEffectState(const char* name, bool isSamplerState, bool is { const EffectState* validStates = effectStates; int count = sizeof(effectStates)/sizeof(effectStates[0]); - + if (isPipeline) { validStates = pipelineStates; @@ -3016,7 +3021,7 @@ const EffectState* GetEffectState(const char* name, bool isSamplerState, bool is // Case insensitive comparison. for (int i = 0; i < count; i++) { - if (String_EqualNoCase(name, validStates[i].name)) + if (String_EqualNoCase(name, validStates[i].name)) { return &validStates[i]; } @@ -3028,12 +3033,12 @@ const EffectState* GetEffectState(const char* name, bool isSamplerState, bool is static const EffectStateValue* GetStateValue(const char* name, const EffectState* state) { // Case insensitive comparison. - for (int i = 0; ; i++) + for (int i = 0; ; i++) { const EffectStateValue & value = state->values[i]; if (value.name == NULL) break; - if (String_EqualNoCase(name, value.name)) + if (String_EqualNoCase(name, value.name)) { return &value; } @@ -3099,7 +3104,7 @@ bool HLSLParser::ParseStateValue(const EffectState * state, HLSLStateAssignment* const bool expectsFloat = state->values == floatValues; const bool expectsBoolean = state->values == booleanValues; - if (!expectsExpression && !expectsInteger && !expectsFloat && !expectsBoolean) + if (!expectsExpression && !expectsInteger && !expectsFloat && !expectsBoolean) { if (m_tokenizer.GetToken() != HLSLToken_Identifier) { @@ -3176,7 +3181,7 @@ bool HLSLParser::ParseStateValue(const EffectState * state, HLSLStateAssignment* return false; } } - else + else { // Expect one of the allowed values. const EffectStateValue * stateValue = GetStateValue(m_tokenizer.GetIdentifier(), state); @@ -3233,7 +3238,7 @@ bool HLSLParser::ParseAttributeList(HLSLAttribute*& firstAttribute) { const char* fileName = GetFileName(); int line = GetLineNumber(); - + HLSLAttribute * lastAttribute = firstAttribute; do { const char * identifier = NULL; @@ -3242,12 +3247,12 @@ bool HLSLParser::ParseAttributeList(HLSLAttribute*& firstAttribute) } HLSLAttribute * attribute = m_tree->AddNode(fileName, line); - + if (strcmp(identifier, "unroll") == 0) attribute->attributeType = HLSLAttributeType_Unroll; else if (strcmp(identifier, "flatten") == 0) attribute->attributeType = HLSLAttributeType_Flatten; else if (strcmp(identifier, "branch") == 0) attribute->attributeType = HLSLAttributeType_Branch; else if (strcmp(identifier, "nofastmath") == 0) attribute->attributeType = HLSLAttributeType_NoFastMath; - + // @@ parse arguments, () not required if attribute constructor has no arguments. if (firstAttribute == NULL) @@ -3259,7 +3264,7 @@ bool HLSLParser::ParseAttributeList(HLSLAttribute*& firstAttribute) lastAttribute->nextAttribute = attribute; } lastAttribute = attribute; - + } while(Accept(',')); return true; @@ -3339,7 +3344,7 @@ bool HLSLParser::ParseStage(HLSLStatement*& statement) bool HLSLParser::Parse(const char* fileName, const char* buffer, size_t length) -{ +{ HLSLRoot* root = m_tree->GetRoot(); HLSLStatement* lastStatement = NULL; @@ -3352,7 +3357,7 @@ bool HLSLParser::Parse(const char* fileName, const char* buffer, size_t length) return false; } if (statement != NULL) - { + { if (lastStatement == NULL) { root->statement = statement; @@ -3556,7 +3561,7 @@ bool HLSLParser::ApplyPreprocessor(const char* fileName, const char* buffer, siz m_tokenizer.Next(); } - if (valueProcessed == "main") + if (valueProcessed == "main") { valueProcessed = "sampler_fw_main"; } @@ -3854,12 +3859,12 @@ bool HLSLParser::AcceptTypeModifier(int& flags) bool HLSLParser::AcceptInterpolationModifier(int& flags) { if (Accept("linear")) - { - flags |= HLSLTypeFlag_Linear; + { + flags |= HLSLTypeFlag_Linear; return true; } else if (Accept("centroid")) - { + { flags |= HLSLTypeFlag_Centroid; return true; } @@ -3908,7 +3913,7 @@ bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/ case HLSLToken_Double1x1: type.baseType = HLSLBaseType_Float; break; - case HLSLToken_Float2: + case HLSLToken_Float2: case HLSLToken_Float2x1: case HLSLToken_Half2: case HLSLToken_Half2x1: @@ -4041,7 +4046,7 @@ bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/ if (type.baseType != HLSLBaseType_Void) { m_tokenizer.Next(); - + if (IsSamplerType(type.baseType)) { // Parse optional sampler type. @@ -4058,7 +4063,7 @@ bool HLSLParser::AcceptType(bool allowVoid, HLSLType& type/*, bool acceptFlags*/ return false; } m_tokenizer.Next(); - + if (!Expect('>')) { m_tokenizer.Error("Syntax error: '>' expected for sampler type"); @@ -4303,7 +4308,7 @@ const HLSLFunction* HLSLParser::MatchFunctionCall(const HLSLFunctionCall* functi if (function->name == name) { nameMatches = true; - + CompareFunctionsResult result = CompareFunctions( m_tree, functionCall, function, matchedFunction ); if (result == Function1Better) { @@ -4454,7 +4459,7 @@ bool HLSLParser::GetMemberType(const HLSLType& objectType, HLSLMemberAccess * me static const HLSLBaseType intType[] = { HLSLBaseType_Int, HLSLBaseType_Int2, HLSLBaseType_Int3, HLSLBaseType_Int4 }; static const HLSLBaseType uintType[] = { HLSLBaseType_Uint, HLSLBaseType_Uint2, HLSLBaseType_Uint3, HLSLBaseType_Uint4 }; static const HLSLBaseType boolType[] = { HLSLBaseType_Bool, HLSLBaseType_Bool2, HLSLBaseType_Bool3, HLSLBaseType_Bool4 }; - + switch (baseTypeDescriptions[objectType.baseType].numericType) { case NumericType_Float: @@ -4474,7 +4479,7 @@ bool HLSLParser::GetMemberType(const HLSLType& objectType, HLSLMemberAccess * me } memberAccess->swizzle = true; - + return true; } From fdd94289276886666b7be806d7ea883043ee8a4d Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 12:08:00 +0100 Subject: [PATCH 13/21] Fix a constant's type in float acos(float) overload in HLSL transpiler Signed-off-by: Kai Blaschke --- vendor/hlslparser/src/GLSLGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/hlslparser/src/GLSLGenerator.cpp b/vendor/hlslparser/src/GLSLGenerator.cpp index a68ab5876..f5cda9bb2 100644 --- a/vendor/hlslparser/src/GLSLGenerator.cpp +++ b/vendor/hlslparser/src/GLSLGenerator.cpp @@ -426,7 +426,7 @@ bool GLSLGenerator::Generate(HLSLTree* tree, Target target, Version version, con * (such as post-increment or -decrement) the result would not be what we expect * if we evaluated this equation as a macro in OutputExpression. */ - m_writer.WriteLine(0, "float %s(float x) { if (abs(x) > 1.0) { return 0.39269908169872415*(4-sign(x)*(abs(x) - 3.0)*(abs(x) - 3.0)); } else { return acos(x); } }", m_acosFunction); + m_writer.WriteLine(0, "float %s(float x) { if (abs(x) > 1.0) { return 0.39269908169872415*(4.0-sign(x)*(abs(x) - 3.0)*(abs(x) - 3.0)); } else { return acos(x); } }", m_acosFunction); m_writer.WriteLine(0, "vec2 %s(vec2 x) { vec2 ret; ret.x = %s(x.x); ret.y = %s(x.y); return ret; }", m_acosFunction, m_acosFunction, m_acosFunction); m_writer.WriteLine(0, "vec3 %s(vec3 x) { vec3 ret; ret.x = %s(x.x); ret.y = %s(x.y); ret.z = %s(x.z); return ret; }", m_acosFunction, m_acosFunction, m_acosFunction, m_acosFunction); m_writer.WriteLine(0, "vec4 %s(vec4 x) { vec4 ret; ret.x = %s(x.x); ret.y = %s(x.y); ret.z = %s(x.z); ret.w = %s(x.w); return ret; }", m_acosFunction, m_acosFunction, m_acosFunction, m_acosFunction, m_acosFunction); From 0ac5c2479294e71b1026605707f6406e4760abdd Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 12:15:16 +0100 Subject: [PATCH 14/21] Place shader "PS" function into its own line and wrap arguments for readability. Signed-off-by: Kai Blaschke --- src/libprojectM/MilkdropPreset/MilkdropShader.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libprojectM/MilkdropPreset/MilkdropShader.cpp b/src/libprojectM/MilkdropPreset/MilkdropShader.cpp index 345ef69ff..f2b203fe3 100644 --- a/src/libprojectM/MilkdropPreset/MilkdropShader.cpp +++ b/src/libprojectM/MilkdropPreset/MilkdropShader.cpp @@ -371,11 +371,22 @@ void MilkdropShader::PreprocessPresetShader(std::string& program) { if (m_type == ShaderType::WarpShader) { - program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float4 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR0, out float4 _mv_tex_coords : COLOR1)\n"); + program.replace(int(found), 11, R"( +void PS(float4 _vDiffuse : COLOR, + float4 _uv : TEXCOORD0, + float2 _rad_ang : TEXCOORD1, + out float4 _return_value : COLOR0, + out float4 _mv_tex_coords : COLOR1) +)"); } else { - program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float2 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR)\n"); + program.replace(int(found), 11, R"( +void PS(float4 _vDiffuse : COLOR, + float2 _uv : TEXCOORD0, + float2 _rad_ang : TEXCOORD1, + out float4 _return_value : COLOR) +)"); } } else From f55d4a97f47ea5f758deddade2ac8fdc3806eaab Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 12:26:28 +0100 Subject: [PATCH 15/21] Fix wrong type used in PCM smoothing in custom waves. Signed-off-by: Kai Blaschke --- src/libprojectM/MilkdropPreset/CustomWaveform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp index 2185afda7..e3f94056b 100644 --- a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp +++ b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp @@ -115,7 +115,7 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext) // PCM data smoothing const int offset1 = m_spectrum ? 0 : (maxSampleCount - sampleCount) / 2 - m_sep / 2; const int offset2 = m_spectrum ? 0 : (maxSampleCount - sampleCount) / 2 + m_sep / 2; - const int t = m_spectrum ? static_cast(static_cast(maxSampleCount - m_sep) / static_cast(sampleCount)) : 1; + const float t = m_spectrum ? static_cast(maxSampleCount - m_sep) / static_cast(sampleCount) : 1.0f; const float mix1 = std::pow(m_smoothing * 0.98f, 0.5f); const float mix2 = 1.0f - mix1; From 19e945c7339cdb329a0298f14c075f95fabbb350 Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 13:03:32 +0100 Subject: [PATCH 16/21] Apply possible HLSL compiler optimization for pow() function with literal 1 as exponent Signed-off-by: Kai Blaschke --- vendor/hlslparser/src/GLSLGenerator.cpp | 68 ++++++++++++++++++++----- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/vendor/hlslparser/src/GLSLGenerator.cpp b/vendor/hlslparser/src/GLSLGenerator.cpp index f5cda9bb2..c913e8268 100644 --- a/vendor/hlslparser/src/GLSLGenerator.cpp +++ b/vendor/hlslparser/src/GLSLGenerator.cpp @@ -119,7 +119,7 @@ GLSLGenerator::GLSLGenerator() : #else m_version = Version_330; #endif - + m_versionLegacy = false; m_inAttribPrefix = NULL; m_outAttribPrefix = NULL; @@ -244,7 +244,7 @@ bool GLSLGenerator::Generate(HLSLTree* tree, Target target, Version version, con m_writer.WriteLine(0, "precision highp float;"); } else if (m_version == Version_300_ES) - { + { m_writer.WriteLine(0, "#version 300 es"); m_writer.WriteLine(0, "precision highp float;"); m_writer.WriteLine(0, "precision highp sampler3D;"); @@ -420,7 +420,7 @@ bool GLSLGenerator::Generate(HLSLTree* tree, Target target, Version version, con * domain. To do this, we bail out to a separate function that piecewise-defines * a polynomial that qualitatively matches what we see under DX9. The DX9 * implementation itself is unknown, so this is only a rough match. - * + * * We also implement this as a separate function because we need to evaluate the * argument multiple times. If the argument expression involves side-effects * (such as post-increment or -decrement) the result would not be what we expect @@ -547,7 +547,7 @@ void GLSLGenerator::OutputExpressionList(HLSLExpression* expression, HLSLArgumen { m_writer.Write(", "); } - + HLSLType* expectedType = NULL; if (argument != NULL) { @@ -1028,16 +1028,60 @@ void GLSLGenerator::OutputExpression(HLSLExpression* expression, const HLSLType* Error("%s expects 2 arguments", functionName); return; } + /* See rsqrt above regarding abs(). Note that this behaves * as expected on some drivers but not others, so we add * the abs() call for compatibility across drivers. + * + * There's one special case though: if the exponent is a literal "1" (or "1.0"), + * don't use pow() at all, just return the base (arg 1) unchanged, even if negative. + * This is probably due to an HLSL compiler optimization which does the same thing. + * When not optimized, pow(x, 1) with a negative value of x would return NaN instead. */ - m_writer.Write("pow(abs("); - OutputExpression(argument[0], &functionCall->function->returnType); - m_writer.Write("),"); - OutputExpression(argument[1], &functionCall->function->returnType); - m_writer.Write(")"); - handled = true; + if (argument[1]->nodeType == HLSLNodeType_LiteralExpression) + { + HLSLLiteralExpression* literalExpression = static_cast(argument[1]); + float value = 0.0; + bool found = false; + switch (literalExpression->type) + { + case HLSLBaseType_Float: + value = literalExpression->fValue; + found = true; + break; + case HLSLBaseType_Int: + case HLSLBaseType_Uint: + value = literalExpression->iValue; + found = true; + break; + case HLSLBaseType_Bool: + value = literalExpression->bValue; + found = true; + break; + default: + break; + } + + // Replace the function call with just arg 1. + if (found && value == 1.0) + { + m_writer.Write("("); + OutputExpression(argument[0], &functionCall->function->returnType); + m_writer.Write(")"); + handled = true; + } + } + + // Other cases, including variable exponent arguments, will still call pow(). + if (!handled) + { + m_writer.Write("pow(abs("); + OutputExpression(argument[0], &functionCall->function->returnType); + m_writer.Write("),"); + OutputExpression(argument[1], &functionCall->function->returnType); + m_writer.Write(")"); + handled = true; + } } else if (String_Equal(functionName, "ldexp")) { @@ -1173,7 +1217,7 @@ void GLSLGenerator::OutputIdentifier(const char* name) { name = m_asinFunction; } - else + else { // The identifier could be a GLSL reserved word (if it's not also a HLSL reserved word). name = GetSafeIdentifierName(name); @@ -2155,7 +2199,7 @@ void GLSLGenerator::Error(const char* format, ...) va_start(arg, format); Log_ErrorArgList(format, arg); va_end(arg); -} +} const char* GLSLGenerator::GetSafeIdentifierName(const char* name) const { From 7b299dec57c2b422f77250a5867b150de80e79bb Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Mon, 24 Nov 2025 10:58:52 +0100 Subject: [PATCH 17/21] Keep references to textures and samplers in descriptors Previously, if the TextureManager was recreated, e.g. when changing the texture paths, all loaded textures were deleted, even if currently in use by a preset. Storing textures and samplers as shared pointers instead of weak pointers will make sure the objects are kept alive until the preset is unloaded. Signed-off-by: Kai Blaschke --- .../Renderer/TextureSamplerDescriptor.cpp | 50 +++++++++---------- .../Renderer/TextureSamplerDescriptor.hpp | 16 ++++-- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/libprojectM/Renderer/TextureSamplerDescriptor.cpp b/src/libprojectM/Renderer/TextureSamplerDescriptor.cpp index b9a7870f9..25efe8e39 100644 --- a/src/libprojectM/Renderer/TextureSamplerDescriptor.cpp +++ b/src/libprojectM/Renderer/TextureSamplerDescriptor.cpp @@ -20,71 +20,71 @@ TextureSamplerDescriptor::TextureSamplerDescriptor(const std::shared_ptr bool { - return m_texture.expired() || m_sampler.expired(); + return m_texture->Empty(); } void TextureSamplerDescriptor::Bind(GLint unit, const Shader& shader) const { - auto texture = m_texture.lock(); - auto sampler = m_sampler.lock(); - if (texture && sampler) + if (m_texture && m_sampler) { - texture->Bind(unit, sampler); + m_texture->Bind(unit, m_sampler); shader.SetUniformInt(std::string("sampler_" + m_samplerName).c_str(), unit); // Might be setting this more than once if the texture is used with different wrap/filter modes, but this rarely happens. - shader.SetUniformFloat4(std::string("texsize_" + m_sizeName).c_str(), {texture->Width(), - texture->Height(), - 1.0f / static_cast(texture->Width()), - 1.0f / static_cast(texture->Height())}); + shader.SetUniformFloat4(std::string("texsize_" + m_sizeName).c_str(), {m_texture->Width(), + m_texture->Height(), + 1.0f / static_cast(m_texture->Width()), + 1.0f / static_cast(m_texture->Height())}); // Bind shorthand random texture size uniform if (m_sizeName.substr(0, 4) == "rand" && m_sizeName.length() > 7 && m_sizeName.at(6) == '_') { - shader.SetUniformFloat4(std::string("texsize_" + m_sizeName.substr(0, 6)).c_str(), {texture->Width(), - texture->Height(), - 1.0f / static_cast(texture->Width()), - 1.0f / static_cast(texture->Height())}); + shader.SetUniformFloat4(std::string("texsize_" + m_sizeName.substr(0, 6)).c_str(), {m_texture->Width(), + m_texture->Height(), + 1.0f / static_cast(m_texture->Width()), + 1.0f / static_cast(m_texture->Height())}); } } } void TextureSamplerDescriptor::Unbind(GLint unit) { - auto texture = m_texture.lock(); - if (texture) + if (m_texture) { - texture->Unbind(unit); + m_texture->Unbind(unit); } Sampler::Unbind(unit); } auto TextureSamplerDescriptor::Texture() const -> std::shared_ptr { - return m_texture.lock(); + return m_texture; } -void TextureSamplerDescriptor::Texture(std::weak_ptr texture) +void TextureSamplerDescriptor::Texture(const std::shared_ptr& texture) { m_texture = texture; } +void TextureSamplerDescriptor::Texture(const std::weak_ptr& texture) +{ + m_texture = texture.lock(); +} + auto TextureSamplerDescriptor::Sampler() const -> std::shared_ptr { - return m_sampler.lock(); + return m_sampler; } auto TextureSamplerDescriptor::SamplerDeclaration() const -> std::string { - auto texture = m_texture.lock(); - auto sampler = m_sampler.lock(); - if (!texture || !sampler) + if (!m_texture || !m_sampler) { return {}; } std::string declaration = "uniform "; - if (texture->Type() == GL_TEXTURE_3D) + if (m_texture->Type() == GL_TEXTURE_3D) { declaration.append("sampler3D sampler_"); } @@ -109,9 +109,7 @@ auto TextureSamplerDescriptor::SamplerDeclaration() const -> std::string auto TextureSamplerDescriptor::TexSizeDeclaration() const -> std::string { - auto texture = m_texture.lock(); - auto sampler = m_sampler.lock(); - if (!texture || !sampler) + if (!m_texture || !m_sampler) { return {}; } diff --git a/src/libprojectM/Renderer/TextureSamplerDescriptor.hpp b/src/libprojectM/Renderer/TextureSamplerDescriptor.hpp index 40deba8ec..0e4d7997c 100644 --- a/src/libprojectM/Renderer/TextureSamplerDescriptor.hpp +++ b/src/libprojectM/Renderer/TextureSamplerDescriptor.hpp @@ -1,6 +1,10 @@ #pragma once +#include "Renderer/Sampler.hpp" #include "Renderer/Shader.hpp" +#include "Renderer/Texture.hpp" + +#include namespace libprojectM { namespace Renderer { @@ -65,11 +69,17 @@ class TextureSamplerDescriptor */ auto Texture() const -> std::shared_ptr; + /** + * @brief Updates the internal texture with a new one. + * @param texture A shared pointer to the new texture. + */ + void Texture(const std::shared_ptr& texture); + /** * @brief Updates the internal texture with a new one. * @param texture A weak pointer to the new texture. */ - void Texture(std::weak_ptr texture); + void Texture(const std::weak_ptr& texture); /** * @brief Returns a pointer to the stored sampler. @@ -96,8 +106,8 @@ class TextureSamplerDescriptor void TryUpdate(TextureManager& textureManager); private: - std::weak_ptr m_texture; //!< A weak reference to the texture. - std::weak_ptr m_sampler; //!< A weak reference to the sampler. + std::shared_ptr m_texture; //!< A reference to the texture. + std::shared_ptr m_sampler; //!< A reference to the sampler. std::string m_samplerName; //!< The name of the texture sampler as referenced in the shader. std::string m_sizeName; //!< The name of the "texsize_" uniform as referenced in the shader. bool m_updateFailed{false}; //!< Set to true if the update try failed, e.g. texture could not be loaded. From c700dba6162747dc4e8c1343f2b02b030893fbbe Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Tue, 4 Nov 2025 12:02:57 +0100 Subject: [PATCH 18/21] Add workflow configurations to test boost-filesystem and more platforms/archs --- .github/workflows/build_android.yml | 73 +++++++-------- .github/workflows/build_emscripten.yml | 31 +++++-- .github/workflows/build_linux.yml | 92 +++++++++---------- .github/workflows/build_osx.yml | 89 ++++++++++--------- .github/workflows/build_windows.yml | 118 +++++++++++++++++-------- 5 files changed, 226 insertions(+), 177 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 9092d0825..cf828730e 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -1,20 +1,27 @@ -name: Android (arm64-v8a, API level 33) +name: Android on: push: - branches: + branches: - "*" tags: - "*" - + pull_request: - branches: + branches: - "*" jobs: build-shared: - name: Shared Library - runs-on: ubuntu-latest + name: "Libs: ${{ matrix.libs }}, ABI: ${{ matrix.abi }}, Platform: ${{ matrix.platform-version }}, Build OS: ${{ matrix.runs-on }}" + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + libs: ['shared', 'static'] + abi: ['arm64-v8a', 'armeabi-v7a', 'x86_64'] + platform-version: ['33'] + runs-on: ['ubuntu-24.04'] steps: - uses: actions/checkout@v4 @@ -27,41 +34,23 @@ jobs: sudo apt-get install -y ninja-build - name: Configure Build - run: cmake -G "Ninja Multi-Config" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DANDROID_PLATFORM=33 -DANDROID_ABI=arm64-v8a -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=NO -DENABLE_SDL_UI=OFF - - - name: Build Debug - run: cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --parallel - - - name: Build Release - run: cmake --build "${{ github.workspace }}/cmake-build" --config "Release" --parallel - - - name: Install run: | - cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --target install - cmake --build "${{ github.workspace }}/cmake-build" --config "Release" --target install - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: projectm-android-shared-latest - path: install/* - - build-static: - name: Static Library - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - - name: Install Packages - run: | - sudo apt-get update - sudo apt-get install -y libgl1-mesa-dev mesa-common-dev libsdl2-dev libglm-dev libgtest-dev libgmock-dev ninja-build - - - name: Configure Build - run: cmake -G "Ninja Multi-Config" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DANDROID_PLATFORM=33 -DANDROID_ABI=arm64-v8a -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=NO -DENABLE_SDL_UI=OFF + if [ "${{ matrix.libs }}" == "shared" ]; then + shared_libs=ON + else + shared_libs=OFF + fi + cmake -G "Ninja Multi-Config" \ + -S "${{ github.workspace }}" \ + -B "${{ github.workspace }}/cmake-build" \ + -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" \ + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ + -DANDROID_PLATFORM="${{ matrix.platform-version }}" \ + -DANDROID_ABI="${{ matrix.abi }}" \ + -DCMAKE_VERBOSE_MAKEFILE=YES \ + -DBUILD_SHARED_LIBS="${shared_libs}" \ + -DBUILD_TESTING=NO \ + -DENABLE_SDL_UI=OFF - name: Build Debug run: cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --parallel @@ -77,5 +66,5 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: projectm-android-static-latest - path: install/* + name: projectm-android-${{ matrix.libs }}-${{ matrix.abi }}-api${{ matrix.platform-version }} + path: install/* diff --git a/.github/workflows/build_emscripten.yml b/.github/workflows/build_emscripten.yml index 505aa1bfc..41d76356d 100644 --- a/.github/workflows/build_emscripten.yml +++ b/.github/workflows/build_emscripten.yml @@ -2,19 +2,27 @@ name: Emscripten on: push: - branches: + branches: - "*" tags: - "*" - + pull_request: - branches: + branches: - "*" jobs: build: - name: Static Library - runs-on: ubuntu-latest + name: "Libs: ${{ matrix.libs }}, FS Lib: ${{ matrix.fslib }}, SDK: ${{ matrix.emsdk-version }}, Arch: WASM, Build OS: ${{ matrix.runs-on }}" + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + include: + - libs: static + fslib: stl + emsdk-version: 3.1.53 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -25,7 +33,7 @@ jobs: uses: mymindstorm/setup-emsdk@v14 with: # Make sure to set a version number! - version: 3.1.53 + version: ${{ matrix.emsdk-version }} # This is the name of the cache folder. # The cache folder will be placed in the build directory, # so make sure it doesn't conflict with anything! @@ -45,7 +53,14 @@ jobs: cd ${{ github.workspace }}/build-gtest && ./setup.sh && ./build-emscripten.sh - name: Configure Build - run: emcmake cmake -G "Unix Makefiles" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_TESTING=YES -DGTest_DIR="${{ github.workspace }}/build-gtest/dist/emscripten/lib/lib/cmake/GTest" + run: | + emcmake cmake -G "Unix Makefiles" \ + -S "${{ github.workspace }}" \ + -B "${{ github.workspace }}/cmake-build" \ + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ + -DCMAKE_VERBOSE_MAKEFILE=YES \ + -DBUILD_TESTING=YES \ + -DGTest_DIR="${{ github.workspace }}/build-gtest/dist/emscripten/lib/lib/cmake/GTest" - name: Build Debug run: emmake cmake --build "${{ github.workspace }}/cmake-build" --parallel @@ -65,5 +80,5 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: projectm-emscripten-static-latest + name: projectm-emscripten-${{ matrix.libs }}-emsdk-${{ matrix.emsdk-version }} path: install/* \ No newline at end of file diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 4dac1296b..ec6fcbdf3 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -1,20 +1,26 @@ -name: Ubuntu Linux (x86_64) +name: Linux on: push: - branches: + branches: - "*" tags: - "*" - + pull_request: - branches: + branches: - "*" jobs: build-shared: - name: Shared Library - runs-on: ubuntu-latest + name: "Libs: ${{ matrix.libs }}, FS Lib: ${{ matrix.fslib }}, Build OS: ${{ matrix.runs-on }}" + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + libs: ['shared', 'static'] + fslib: ['stl', 'boost'] + runs-on: ['ubuntu-24.04', 'ubuntu-22.04'] steps: - uses: actions/checkout@v4 @@ -24,47 +30,41 @@ jobs: - name: Install Packages run: | sudo apt-get update - sudo apt-get install -y libgl1-mesa-dev mesa-common-dev libsdl2-dev libglm-dev libgtest-dev libgmock-dev ninja-build - - - name: Configure Build - run: cmake -G "Ninja Multi-Config" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=YES - - - name: Build Debug - run: cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --parallel - - - name: Run Unit Tests - run: ctest --test-dir "${{ github.workspace }}/cmake-build" --verbose --build-config "Debug" - - - name: Build Release - run: cmake --build "${{ github.workspace }}/cmake-build" --config "Release" --parallel - - - name: Install + sudo apt-get install --yes --no-install-recommends \ + libgl1-mesa-dev \ + mesa-common-dev \ + libsdl2-dev \ + libglm-dev \ + libgtest-dev \ + libgmock-dev \ + ninja-build + + - name: Install boost-filesystem-dev + if: "${{ contains(matrix.fslib, 'boost') }}" run: | - cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --target install - cmake --build "${{ github.workspace }}/cmake-build" --config "Release" --target install - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: projectm-linux-shared-latest - path: install/* - - build-static: - name: Static Library - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - - name: Install Packages - run: | - sudo apt-get update - sudo apt-get install -y libgl1-mesa-dev mesa-common-dev libsdl2-dev libglm-dev libgtest-dev libgmock-dev ninja-build + sudo apt-get install --yes --no-install-recommends \ + libboost-filesystem-dev - name: Configure Build - run: cmake -G "Ninja Multi-Config" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=YES + run: | + if [ "${{ matrix.libs }}" == "shared" ]; then + shared_libs=ON + else + shared_libs=OFF + fi + if [ "${{ matrix.fslib }}" == "boost" ]; then + use_boost=ON + else + use_boost=OFF + fi + cmake -G "Ninja Multi-Config" \ + -S "${{ github.workspace }}" \ + -B "${{ github.workspace }}/cmake-build" \ + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ + -DCMAKE_VERBOSE_MAKEFILE=YES \ + -DBUILD_SHARED_LIBS="${shared_libs}" \ + -DENABLE_BOOST_FILESYSTEM="${use_boost}" \ + -DBUILD_TESTING=YES - name: Build Debug run: cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --parallel @@ -83,5 +83,5 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: projectm-linux-static-latest - path: install/* + name: projectm-linux-${{ matrix.libs }}-${{ matrix.fslib }}-${{ matrix.runs-on }} + path: install/* diff --git a/.github/workflows/build_osx.yml b/.github/workflows/build_osx.yml index f6abe3e8f..4247426f4 100644 --- a/.github/workflows/build_osx.yml +++ b/.github/workflows/build_osx.yml @@ -1,66 +1,71 @@ -name: macOS (x86_64) +name: macOS on: push: - branches: + branches: - "*" tags: - "*" - + pull_request: - branches: + branches: - "*" jobs: build-shared: - name: Shared Library - runs-on: macos-latest + name: "Libs: ${{ matrix.libs }}, FS Lib: ${{ matrix.fslib }}, Arch: ${{ matrix.arch }}, Build OS: ${{ matrix.runs-on }}" + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + libs: ['shared', 'static'] + fslib: ['stl', 'boost'] + arch: ['arm64', 'x86_64'] + runs-on: ['macos-15', 'macos-15-intel'] + exclude: + - arch: arm64 + runs-on: macos-15-intel + - arch: x86_64 + runs-on: macos-15 + ### BROKEN: Homebrew's Boost build for arm64 does not properly populate the include dir of the Boost CMake targets. + - arch: arm64 + runs-on: macos-15 + fslib: boost steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Install Packages run: brew install sdl2 ninja googletest - - name: Configure Build - run: cmake -G "Ninja Multi-Config" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=YES - - - name: Build Debug - run: cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --parallel - - - name: Run Unit Tests - run: ctest --test-dir "${{ github.workspace }}/cmake-build" --verbose --build-config "Debug" - - - name: Build Release - run: cmake --build "${{ github.workspace }}/cmake-build" --config "Release" --parallel - - - name: Install + - name: Install boost-filesystem-dev + if: "${{ contains(matrix.fslib, 'boost') }}" run: | - cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --target install - cmake --build "${{ github.workspace }}/cmake-build" --config "Release" --target install - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: projectm-osx-shared-latest - path: install/* + brew install boost - build-static: - name: Static Library - runs-on: macos-latest - - steps: - uses: actions/checkout@v4 with: submodules: 'recursive' - - name: Install Packages - run: brew install sdl2 ninja googletest - - name: Configure Build - run: cmake -G "Ninja Multi-Config" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=YES + run: | + if [ "${{ matrix.libs }}" == "shared" ]; then + shared_libs=ON + else + shared_libs=OFF + fi + if [ "${{ matrix.fslib }}" == "boost" ]; then + use_boost=ON + else + use_boost=OFF + fi + cmake -G "Ninja Multi-Config" \ + -S "${{ github.workspace }}" \ + -B "${{ github.workspace }}/cmake-build" \ + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ + -DCMAKE_VERBOSE_MAKEFILE=YES \ + -DBUILD_SHARED_LIBS="${shared_libs}" \ + -DENABLE_BOOST_FILESYSTEM="${use_boost}" \ + -DCMAKE_OSX_ARCHITECTURES="${{ matrix.arch }}" \ + -DBUILD_TESTING=YES - name: Build Debug run: cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --parallel @@ -79,5 +84,5 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: projectm-osx-static-latest - path: install/* + name: projectm-osx-${{ matrix.libs }}-${{ matrix.fslib }}-${{ matrix.arch }}-${{ matrix.runs-on }} + path: install/* diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 9bdc4301e..c7cad7e05 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -1,60 +1,100 @@ -name: Windows (x64) +name: Windows on: push: - branches: + branches: - "*" tags: - "*" - + pull_request: - branches: + branches: - "*" jobs: build-shared: - name: Shared Library - runs-on: windows-latest + name: "Libs: ${{ matrix.libs }}, FS Lib: ${{ matrix.fslib }}, Arch: ${{ matrix.arch }}, Build OS: ${{ matrix.runs-on }}" + runs-on: ${{ matrix.runs-on }} + env: + USERNAME: projectM-visualizer + VCPKG_EXE: ${{ github.workspace }}/vcpkg/vcpkg + FEED_URL: https://nuget.pkg.github.com/projectM-visualizer/index.json + VCPKG_BINARY_SOURCES: "clear;nuget,https://nuget.pkg.github.com/projectM-visualizer/index.json,readwrite" + strategy: + fail-fast: false + matrix: + libs: ['shared', 'static'] + fslib: ['stl', 'boost'] + arch: ['X64', 'Win32'] + runs-on: ['windows-2025', 'windows-2022'] steps: - - uses: actions/checkout@v4 + - name: Checkout vcpkg + uses: actions/checkout@v4 with: - submodules: 'recursive' - - - name: Configure Build (MD) - run: cmake -G "Visual Studio 17 2022" -A "X64" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=YES - - - name: Build Debug - run: cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --parallel + repository: microsoft/vcpkg + path: vcpkg + submodules: recursive - - name: Run Unit Tests - run: ctest --test-dir "${{ github.workspace }}/cmake-build" --verbose --build-config "Debug" - - - name: Build Release - run: cmake --build "${{ github.workspace }}/cmake-build" --config "Release" --parallel + - name: Bootstrap vcpkg + shell: pwsh + run: ${{ github.workspace }}/vcpkg/bootstrap-vcpkg.bat - - name: Install + - name: Add NuGet sources + shell: pwsh run: | - cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --target INSTALL - cmake --build "${{ github.workspace }}/cmake-build" --config "Release" --target INSTALL - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: projectm-windows-shared-latest - path: install/* - - build-static: - name: Static Library - runs-on: windows-latest - - steps: - - uses: actions/checkout@v4 + .$(${{ env.VCPKG_EXE }} fetch nuget) ` + sources add ` + -Source "${{ env.FEED_URL }}" ` + -StorePasswordInClearText ` + -Name GitHubPackages ` + -UserName "${{ env.USERNAME }}" ` + -Password "${{ secrets.VCPKG_PACKAGES_TOKEN }}" + .$(${{ env.VCPKG_EXE }} fetch nuget) ` + setapikey "${{ secrets.VCPKG_PACKAGES_TOKEN }}" ` + -Source "${{ env.FEED_URL }}" + + - name: Checkout libprojectM + uses: actions/checkout@v4 with: submodules: 'recursive' - - name: Configure Build (MD) - run: cmake -G "Visual Studio 17 2022" -A "X64" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=YES + - name: Configure Build + run: | + if("${{ matrix.libs }}" -eq "shared") { + $shared_libs = "ON" + } else { + $shared_libs = "OFF" + } + if("${{ matrix.fslib }}" -eq "boost") { + $use_boost = "ON" + } else { + $use_boost = "OFF" + } + if("${{ matrix.arch }}" -eq "X64") { + $triplet = "x64-windows" + } else { + $triplet = "x86-windows" + } + if("${{ matrix.libs }}" -eq "shared") { + $runtime = "DLL" + } else { + $runtime = "" + $triplet = $triplet + "-static" + } + + cmake -G "Visual Studio 17 2022" ` + -A "${{ matrix.arch }}" ` + -S "${{ github.workspace }}" ` + -B "${{ github.workspace }}/cmake-build" ` + -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" ` + -DVCPKG_TARGET_TRIPLET="$($triplet)" ` + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" ` + -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>$($runtime)" ` + -DCMAKE_VERBOSE_MAKEFILE=YES ` + -DBUILD_SHARED_LIBS="$($shared_libs)" ` + -DENABLE_BOOST_FILESYSTEM="$($use_boost)" ` + -DBUILD_TESTING=YES - name: Build Debug run: cmake --build "${{ github.workspace }}/cmake-build" --config "Debug" --parallel @@ -73,5 +113,5 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: projectm-windows-static-latest - path: install/* + name: projectm-windows-${{ matrix.libs }}-${{ matrix.fslib }}-${{ matrix.arch }}-${{ matrix.runs-on }} + path: install/* From 66b0b47f2f8a6598fdacef027ff2792f96f16808 Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Mon, 10 Nov 2025 12:17:46 +0100 Subject: [PATCH 19/21] Bump projectm-eval submodule version to 1.0.5 --- vendor/projectm-eval | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/projectm-eval b/vendor/projectm-eval index 612afc49a..811eea559 160000 --- a/vendor/projectm-eval +++ b/vendor/projectm-eval @@ -1 +1 @@ -Subproject commit 612afc49a4ee05dcfd68548a5c9bf1aaa031d17d +Subproject commit 811eea5594cc4092d0985fea9ccf0e52dec8a20a From 3371d864c8e97075185bd7f3fc7c68d15d9c9012 Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Tue, 11 Nov 2025 09:40:10 +0100 Subject: [PATCH 20/21] Update vcpkg repo baseline commit to include projectm-eval 1.0.5 --- vcpkg-configuration.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json index feb661216..3c0661aa9 100644 --- a/vcpkg-configuration.json +++ b/vcpkg-configuration.json @@ -1,7 +1,7 @@ { "default-registry": { "kind": "git", - "baseline": "a0f7f5379aa39d638efb1b89ac88a39c1011e4aa", + "baseline": "b6070378242bbf0a69b33c38fb5c1d7713330293", "repository": "https://github.com/microsoft/vcpkg" } } From 3158ee615eaafd93a8912b5f6dd84a9c47b2e00a Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Mon, 10 Nov 2025 10:57:17 +0100 Subject: [PATCH 21/21] Bump libprojectM version to 4.1.6 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52c2ed173..402d0a619 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,7 @@ endif() project(libprojectM LANGUAGES C CXX - VERSION 4.1.5 + VERSION 4.1.6 ) # The API (SO) version for the shared library. Should be incremented whenever the binary interface changes