From 4ad657c161df9a12c2d3da9da20b29670f017ffa Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Wed, 11 Dec 2024 16:37:52 +0100 Subject: [PATCH 1/4] hlslparser: Add missing "ldexp" HLSL intrinsic. Had to simulate it with the "x * exp2(exp)" expression in GLSL, as GLSL only supports integer types as exponents in its native ldexp implementation, while HLSL solely uses floats. --- vendor/hlslparser/src/GLSLGenerator.cpp | 17 +++++++++++++++++ vendor/hlslparser/src/HLSLParser.cpp | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/vendor/hlslparser/src/GLSLGenerator.cpp b/vendor/hlslparser/src/GLSLGenerator.cpp index b34f7d191..f836f381a 100644 --- a/vendor/hlslparser/src/GLSLGenerator.cpp +++ b/vendor/hlslparser/src/GLSLGenerator.cpp @@ -1019,6 +1019,23 @@ void GLSLGenerator::OutputExpression(HLSLExpression* expression, const HLSLType* m_writer.Write(")"); handled = true; } + else if (String_Equal(functionName, "ldexp")) + { + /* HLSL has the second argument as float, while GLSL only supports ints, so we simulate the HLSL behaviour + * by using the equivalent "x * exp2(exp)" expression. */ + HLSLExpression* argument[2]; + if (GetFunctionArguments(functionCall, argument, 2) != 2) + { + Error("%s expects 2 arguments", functionName); + return; + } + m_writer.Write("("); + OutputExpression(argument[0], &functionCall->function->returnType); + m_writer.Write("*exp2("); + OutputExpression(argument[1], &functionCall->function->returnType); + m_writer.Write("))"); + handled = true; + } if (!handled) { diff --git a/vendor/hlslparser/src/HLSLParser.cpp b/vendor/hlslparser/src/HLSLParser.cpp index eb5cd4b92..5c907de45 100644 --- a/vendor/hlslparser/src/HLSLParser.cpp +++ b/vendor/hlslparser/src/HLSLParser.cpp @@ -620,6 +620,27 @@ const Intrinsic _intrinsic[] = INTRINSIC_FLOAT2_FUNCTION( "step" ), INTRINSIC_FLOAT2_FUNCTION( "reflect" ), + Intrinsic("ldexp", HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float), + Intrinsic("ldexp", HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2), + Intrinsic("ldexp", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3), + Intrinsic("ldexp", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4), + + Intrinsic("ldexp", HLSLBaseType_Float2x2, HLSLBaseType_Float2x2, HLSLBaseType_Float2x2), + Intrinsic("ldexp", HLSLBaseType_Float2x3, HLSLBaseType_Float2x3, HLSLBaseType_Float2x3), + Intrinsic("ldexp", HLSLBaseType_Float2x4, HLSLBaseType_Float2x4, HLSLBaseType_Float2x4), + + Intrinsic("ldexp", HLSLBaseType_Float3x2, HLSLBaseType_Float3x2, HLSLBaseType_Float3x2), + Intrinsic("ldexp", HLSLBaseType_Float3x3, HLSLBaseType_Float3x3, HLSLBaseType_Float3x3), + Intrinsic("ldexp", HLSLBaseType_Float3x4, HLSLBaseType_Float3x4, HLSLBaseType_Float3x4), + + Intrinsic("ldexp", HLSLBaseType_Float4x2, HLSLBaseType_Float4x2, HLSLBaseType_Float4x2), + Intrinsic("ldexp", HLSLBaseType_Float4x3, HLSLBaseType_Float4x3, HLSLBaseType_Float4x3), + Intrinsic("ldexp", HLSLBaseType_Float4x4, HLSLBaseType_Float4x4, HLSLBaseType_Float4x4), + + Intrinsic("refract", HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float), + Intrinsic("refract", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float), + Intrinsic("refract", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float), + Intrinsic("isnan", HLSLBaseType_Bool, HLSLBaseType_Float), Intrinsic("isinf", HLSLBaseType_Bool, HLSLBaseType_Float), From 18c3ad764c3369dd1d69f87607316214747d9795 Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Wed, 11 Dec 2024 17:06:00 +0100 Subject: [PATCH 2/4] hlslparser: Add missing "faceforward" HLSL intrinsic. --- vendor/hlslparser/src/HLSLParser.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vendor/hlslparser/src/HLSLParser.cpp b/vendor/hlslparser/src/HLSLParser.cpp index 5c907de45..b769b4802 100644 --- a/vendor/hlslparser/src/HLSLParser.cpp +++ b/vendor/hlslparser/src/HLSLParser.cpp @@ -641,6 +641,11 @@ const Intrinsic _intrinsic[] = Intrinsic("refract", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float), Intrinsic("refract", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float), + Intrinsic("faceforward", HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float, HLSLBaseType_Float), + Intrinsic("faceforward", HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2, HLSLBaseType_Float2), + Intrinsic("faceforward", HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3, HLSLBaseType_Float3), + Intrinsic("faceforward", HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4, HLSLBaseType_Float4), + Intrinsic("isnan", HLSLBaseType_Bool, HLSLBaseType_Float), Intrinsic("isinf", HLSLBaseType_Bool, HLSLBaseType_Float), From ca9af047046c70dcc9207ce023d23dfe1f5ad3eb Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Mon, 7 Oct 2024 12:08:44 +0200 Subject: [PATCH 3/4] Fix texture loading if casing mismatches filename Always use lower-case unqualified names to find and cache the actual textures. --- src/libprojectM/CMakeLists.txt | 2 + .../MilkdropPreset/MilkdropShader.cpp | 4 +- src/libprojectM/PresetFactoryManager.cpp | 7 ++-- src/libprojectM/Renderer/FileScanner.cpp | 7 ++-- src/libprojectM/Renderer/TextureManager.cpp | 41 ++++++++----------- src/libprojectM/Renderer/TextureManager.hpp | 2 +- src/libprojectM/Utils.cpp | 33 +++++++++++++++ src/libprojectM/Utils.hpp | 15 +++++++ 8 files changed, 77 insertions(+), 34 deletions(-) create mode 100644 src/libprojectM/Utils.cpp create mode 100644 src/libprojectM/Utils.hpp diff --git a/src/libprojectM/CMakeLists.txt b/src/libprojectM/CMakeLists.txt index 0e431c967..134850496 100644 --- a/src/libprojectM/CMakeLists.txt +++ b/src/libprojectM/CMakeLists.txt @@ -23,6 +23,8 @@ add_library(projectM_main OBJECT ProjectMCWrapper.hpp TimeKeeper.cpp TimeKeeper.hpp + Utils.cpp + Utils.hpp projectM-opengl.h ) diff --git a/src/libprojectM/MilkdropPreset/MilkdropShader.cpp b/src/libprojectM/MilkdropPreset/MilkdropShader.cpp index 64f656152..030729dc1 100644 --- a/src/libprojectM/MilkdropPreset/MilkdropShader.cpp +++ b/src/libprojectM/MilkdropPreset/MilkdropShader.cpp @@ -2,6 +2,7 @@ #include "PerFrameContext.hpp" #include "PresetState.hpp" +#include "Utils.hpp" #include @@ -69,8 +70,7 @@ void MilkdropShader::LoadTexturesAndCompile(PresetState& presetState) baseName = name.substr(3); } - std::string lowerCaseName(baseName); - std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(), tolower); + std::string lowerCaseName = Utils::ToLower(baseName); // The "main" and "blurX" textures are preset-specific and are not managed by TextureManager. if (lowerCaseName == "main") diff --git a/src/libprojectM/PresetFactoryManager.cpp b/src/libprojectM/PresetFactoryManager.cpp index d4598af99..a5f823547 100644 --- a/src/libprojectM/PresetFactoryManager.cpp +++ b/src/libprojectM/PresetFactoryManager.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include #include @@ -135,9 +137,8 @@ auto PresetFactoryManager::ParseExtension(const std::string& filename) -> std::s if (start == std::string::npos || start >= (filename.length() - 1)) { return ""; } - std::string ext = filename.substr(start + 1, filename.length()); - std::transform(ext.begin(), ext.end(), ext.begin(), tolower); - return ext; + + return Utils::ToLower(filename.substr(start + 1, filename.length())); } } // namespace libprojectM diff --git a/src/libprojectM/Renderer/FileScanner.cpp b/src/libprojectM/Renderer/FileScanner.cpp index ee409b713..26ad774ff 100644 --- a/src/libprojectM/Renderer/FileScanner.cpp +++ b/src/libprojectM/Renderer/FileScanner.cpp @@ -1,5 +1,7 @@ #include "FileScanner.hpp" +#include "Utils.hpp" + #include #include @@ -17,7 +19,7 @@ FileScanner::FileScanner(const std::vector& rootDirs, std::vector @@ -165,7 +166,6 @@ void TextureManager::PurgeTextures() auto TextureManager::TryLoadingTexture(const std::string& name) -> TextureSamplerDescriptor { - TextureSamplerDescriptor texDesc; GLint wrapMode{0}; GLint filterMode{0}; std::string unqualifiedName; @@ -174,24 +174,22 @@ auto TextureManager::TryLoadingTexture(const std::string& name) -> TextureSample ScanTextures(); - std::string lowerCaseFileName(name); - std::transform(lowerCaseFileName.begin(), lowerCaseFileName.end(), lowerCaseFileName.begin(), tolower); - + std::string lowerCaseUnqualifiedName = Utils::ToLower(unqualifiedName); for (const auto& file : m_scannedTextureFiles) { - if (file.lowerCaseBaseName != unqualifiedName) + if (file.lowerCaseBaseName != lowerCaseUnqualifiedName) { continue; } - texDesc = LoadTexture(file.filePath, name); + auto texture = LoadTexture(file); - if (!texDesc.Empty()) + if (texture) { #ifdef DEBUG std::cerr << "Loaded texture " << unqualifiedName << std::endl; #endif - return texDesc; + return {texture, m_samplers.at({wrapMode, filterMode}), name, unqualifiedName};; } } @@ -203,24 +201,20 @@ auto TextureManager::TryLoadingTexture(const std::string& name) -> TextureSample return {m_placeholderTexture, m_samplers.at({wrapMode, filterMode}), name, unqualifiedName}; } -auto TextureManager::LoadTexture(const std::string& fileName, const std::string& name) -> TextureSamplerDescriptor +auto TextureManager::LoadTexture(const ScannedFile& file) -> std::shared_ptr { - GLint wrapMode; - GLint filterMode; std::string unqualifiedName; - ExtractTextureSettings(name, wrapMode, filterMode, unqualifiedName); - auto sampler = m_samplers.at({wrapMode, filterMode}); - if (m_textures.find(name) != m_textures.end()) + if (m_textures.find(file.lowerCaseBaseName) != m_textures.end()) { - return {m_textures.at(name), sampler, name, unqualifiedName}; + return m_textures.at(file.lowerCaseBaseName); } int width{}; int height{}; unsigned int const tex = SOIL_load_OGL_texture( - fileName.c_str(), + file.filePath.c_str(), SOIL_LOAD_RGBA, SOIL_CREATE_NEW_ID, SOIL_FLAG_MULTIPLY_ALPHA, &width, &height); @@ -232,10 +226,10 @@ auto TextureManager::LoadTexture(const std::string& fileName, const std::string& uint32_t memoryBytes = width * height * 4; // RGBA, unsigned byte color channels. auto newTexture = std::make_shared(unqualifiedName, tex, GL_TEXTURE_2D, width, height, true); - m_textures[name] = newTexture; - m_textureStats.insert({name, {memoryBytes}}); + m_textures[file.lowerCaseBaseName] = newTexture; + m_textureStats.insert({file.lowerCaseBaseName, {memoryBytes}}); - return {newTexture, sampler, name, unqualifiedName}; + return newTexture; } auto TextureManager::GetRandomTexture(const std::string& randomName) -> TextureSamplerDescriptor @@ -247,8 +241,7 @@ auto TextureManager::GetRandomTexture(const std::string& randomName) -> TextureS ScanTextures(); - std::string lowerCaseName(randomName); - std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(), tolower); + std::string lowerCaseName = Utils::ToLower(randomName); if (m_scannedTextureFiles.empty()) { @@ -300,8 +293,7 @@ auto TextureManager::GetRandomTexture(const std::string& randomName) -> TextureS void TextureManager::AddTextureFile(const std::string& fileName, const std::string& baseName) { - std::string lowerCaseBaseName(baseName); - std::transform(lowerCaseBaseName.begin(), lowerCaseBaseName.end(), lowerCaseBaseName.begin(), tolower); + std::string lowerCaseBaseName = Utils::ToLower(baseName); ScannedFile file; file.filePath = fileName; @@ -320,8 +312,7 @@ void TextureManager::ExtractTextureSettings(const std::string& qualifiedName, GL return; } - std::string lowerQualifiedName(qualifiedName); - std::transform(lowerQualifiedName.begin(), lowerQualifiedName.end(), lowerQualifiedName.begin(), tolower); + std::string lowerQualifiedName = Utils::ToLower(qualifiedName); // Default mode for user textures is "fw" (bilinear filtering + wrap). wrapMode = GL_REPEAT; diff --git a/src/libprojectM/Renderer/TextureManager.hpp b/src/libprojectM/Renderer/TextureManager.hpp index 87883196e..388f722ac 100644 --- a/src/libprojectM/Renderer/TextureManager.hpp +++ b/src/libprojectM/Renderer/TextureManager.hpp @@ -82,7 +82,7 @@ class TextureManager void Preload(); - TextureSamplerDescriptor LoadTexture(const std::string& fileName, const std::string& name); + auto LoadTexture(const ScannedFile& file) -> std::shared_ptr; void AddTextureFile(const std::string& fileName, const std::string& baseName); diff --git a/src/libprojectM/Utils.cpp b/src/libprojectM/Utils.cpp new file mode 100644 index 000000000..f6a8ff3df --- /dev/null +++ b/src/libprojectM/Utils.cpp @@ -0,0 +1,33 @@ +#include "Utils.hpp" + +#include + +namespace libprojectM { +namespace Utils { + +auto ToLower(const std::string& str) -> std::string +{ + std::string lowerStr(str); + ToLowerInPlace(lowerStr); + return lowerStr; +} + +auto ToUpper(const std::string& str) -> std::string +{ + std::string upperStr(str); + ToUpperInPlace(upperStr); + return upperStr; +} + +void ToLowerInPlace(std::string& str) +{ + std::transform(str.begin(), str.end(), str.begin(), ::tolower); +} + +void ToUpperInPlace(std::string& str) +{ + std::transform(str.begin(), str.end(), str.begin(), ::toupper); +} + +} // namespace Utils +} // namespace libprojectM diff --git a/src/libprojectM/Utils.hpp b/src/libprojectM/Utils.hpp new file mode 100644 index 000000000..122fb2f55 --- /dev/null +++ b/src/libprojectM/Utils.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace libprojectM { +namespace Utils { + +auto ToLower(const std::string& str) -> std::string; +auto ToUpper(const std::string& str) -> std::string; + +void ToLowerInPlace(std::string& str); +void ToUpperInPlace(std::string& str); + +} // namespace Utils +} // namespace libprojectM From 327fad55e4b482217f6cba876a7d0700c2972b34 Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Wed, 11 Dec 2024 18:16:41 +0100 Subject: [PATCH 4/4] Add both long and short sampler names for random textures. If a preset uses different long-form samplers for a single random slot, this will result in a failure, e.g. using both "sampler_rand00_something" and "sampler_rand00_else". As this also isn't supported by Milkdrop and would create a conflict situation, we don't care. --- .../MilkdropPreset/MilkdropShader.cpp | 27 +++++++++++++++++++ .../Renderer/TextureSamplerDescriptor.cpp | 9 +++++++ 2 files changed, 36 insertions(+) diff --git a/src/libprojectM/MilkdropPreset/MilkdropShader.cpp b/src/libprojectM/MilkdropPreset/MilkdropShader.cpp index 030729dc1..1dadec340 100644 --- a/src/libprojectM/MilkdropPreset/MilkdropShader.cpp +++ b/src/libprojectM/MilkdropPreset/MilkdropShader.cpp @@ -517,6 +517,33 @@ void MilkdropShader::GetReferencedSamplers(const std::string& program) found = program.find("texsize_", found); } + { + // Remove duplicate mentions or "randXX" names, keeping the long forms only (first one will determine the actual texture loaded). + auto samplerName = m_samplerNames.begin(); + std::locale loc; + while (samplerName != m_samplerNames.end()) + { + std::string lowerCaseName = Utils::ToLower(*samplerName); + if (lowerCaseName.length() == 6 && + lowerCaseName.substr(0, 4) == "rand" && std::isdigit(lowerCaseName.at(4), loc) && std::isdigit(lowerCaseName.at(5), loc)) + { + auto additionalName = samplerName; + additionalName++; + if (additionalName != m_samplerNames.end()) + { + std::string addLowerCaseName = Utils::ToLower(*additionalName); + if (addLowerCaseName.length() > 7 && + addLowerCaseName.substr(0, 6) == lowerCaseName && + addLowerCaseName[6] == '_') + { + samplerName = m_samplerNames.erase(samplerName); + } + } + } + samplerName++; + } + } + if (program.find("GetBlur3") != std::string::npos) { UpdateMaxBlurLevel(BlurTexture::BlurLevel::Blur3); diff --git a/src/libprojectM/Renderer/TextureSamplerDescriptor.cpp b/src/libprojectM/Renderer/TextureSamplerDescriptor.cpp index 75592c6fd..b9a7870f9 100644 --- a/src/libprojectM/Renderer/TextureSamplerDescriptor.cpp +++ b/src/libprojectM/Renderer/TextureSamplerDescriptor.cpp @@ -95,6 +95,15 @@ auto TextureSamplerDescriptor::SamplerDeclaration() const -> std::string declaration.append(m_samplerName); declaration.append(";\n"); + // Add short sampler name for prefixed random textures. + // E.g. "sampler_rand00" if a sampler "sampler_rand00_smalltiled" was declared + if (m_samplerName.substr(0, 4) == "rand" && m_samplerName.length() > 7 && m_samplerName.at(6) == '_') + { + declaration.append("uniform sampler2D sampler_"); + declaration.append(m_samplerName.substr(0, 6)); + declaration.append(";\n"); + } + return declaration; }