Kaynağa Gözat

fix(shaders): resolve #require directives with generated module code (#529)

Wallpaper Engine dynamically generates shader modules when it encounters
#require directives (e.g. #require LightingV1). The existing code commented
out #require lines but never injected the module code, leaving functions
like PerformLighting_V1 undefined and causing shader compilation failures.

This adds a module resolution system: preprocessRequires() now extracts
the module name from each #require directive and dispatches to
resolveRequireModule(), which generates the appropriate GLSL code.

The generated code is inserted directly into m_preprocessed at the
#require location (not appended to m_includes, which was already consumed
by preprocessIncludes at this point in the pipeline).

For LightingV1, a stub PerformLighting_V1() is generated that returns
vec3(0.0). This is consistent with the engine's current state — light
objects are not yet supported (ObjectParser already logs this), so no
dynamic lighting contribution is available. Ambient lighting computed
by the calling shader code is unaffected.

Also adds defensive checks for malformed #require directives (missing
or empty module names).
Alexander Stoyanov 4 ay önce
ebeveyn
işleme
0ee94393dc

+ 57 - 6
src/WallpaperEngine/Render/Shaders/ShaderUnit.cpp

@@ -307,19 +307,70 @@ void ShaderUnit::preprocessIncludes () {
 
 void ShaderUnit::preprocessRequires () {
     size_t start = 0, end = 0;
-    // comment out requires
+
     while ((start = this->m_preprocessed.find ("#require", end)) != std::string::npos) {
-	// TODO: CHECK FOR ERRORS HERE
 	const size_t lineEnd = this->m_preprocessed.find_first_of ('\n', start);
-	sLog.out ("Shader has a require block ", this->m_preprocessed.substr (start, lineEnd - start));
-	// replace the first two letters with a comment so the filelength doesn't change
+
+	const size_t nameStart = start + std::string ("#require ").length ();
+
+	if (nameStart >= lineEnd) {
+	    sLog.error ("Malformed #require directive (no module name) in shader ", this->m_file);
+	    end = lineEnd;
+	    continue;
+	}
+
+	std::string moduleName = this->m_preprocessed.substr (nameStart, lineEnd - nameStart);
+
+	while (!moduleName.empty () && (moduleName.back () == ' ' || moduleName.back () == '\r')) {
+	    moduleName.pop_back ();
+	}
+
+	if (moduleName.empty ()) {
+	    sLog.error ("Malformed #require directive (empty module name) in shader ", this->m_file);
+	    end = lineEnd;
+	    continue;
+	}
+
+	sLog.out ("Resolving require module: ", moduleName, " in shader ", this->m_file);
+
+	std::string moduleCode = this->resolveRequireModule (moduleName);
+
+	// comment out the #require directive
 	this->m_preprocessed = this->m_preprocessed.replace (start, 2, "//");
 
-	// go to the end of the line
-	end = lineEnd;
+	if (!moduleCode.empty ()) {
+	    // insert the generated code directly into m_preprocessed at the #require location
+	    // (m_includes was already consumed by preprocessIncludes, so appending there would be lost)
+	    this->m_preprocessed.insert (start, moduleCode);
+	    end = start + moduleCode.length ();
+	} else {
+	    end = lineEnd;
+	}
     }
 }
 
+std::string ShaderUnit::resolveRequireModule (const std::string& moduleName) const {
+    if (moduleName == "LightingV1") {
+	return this->generateLightingV1 ();
+    }
+
+    sLog.error ("Unknown #require module: ", moduleName, " in shader ", this->m_file);
+    return "";
+}
+
+std::string ShaderUnit::generateLightingV1 () const {
+    // PerformLighting_V1 is dynamically generated by Wallpaper Engine based on the scene's
+    // light sources. Since linux-wallpaperengine does not yet support light objects, we
+    // generate a stub that returns no dynamic light contribution.
+    return "// begin of generated module LightingV1\n"
+	   "vec3 PerformLighting_V1(vec3 worldPos, vec3 albedo, vec3 normal, vec3 viewDir,\n"
+	   "    vec3 specularTint, vec3 baseReflectance, float roughness, float metallic)\n"
+	   "{\n"
+	   "    return vec3(0.0);\n"
+	   "}\n"
+	   "// end of generated module LightingV1\n";
+}
+
 void ShaderUnit::parseComboConfiguration (const std::string& content, const int defaultValue) {
     // TODO: SUPPORT REQUIRES SO WE PROPERLY FOLLOW THE REQUIRED CHAIN
     const auto data = JSON::parse (content);

+ 14 - 1
src/WallpaperEngine/Render/Shaders/ShaderUnit.h

@@ -80,9 +80,22 @@ private:
      */
     void preprocessIncludes ();
     /**
-     * Parses the input shader lookin for require directives to comment them out for now
+     * Parses the input shader looking for require directives and resolves them into generated code
      */
     void preprocessRequires ();
+    /**
+     * Resolves a #require module name to generated GLSL code
+     *
+     * @param moduleName The module to resolve (e.g. "LightingV1")
+     * @return Generated GLSL code for the module, or empty string if unknown
+     */
+    [[nodiscard]] std::string resolveRequireModule (const std::string& moduleName) const;
+    /**
+     * Generates the LightingV1 module stub (PerformLighting_V1 function)
+     *
+     * @return GLSL code defining PerformLighting_V1
+     */
+    [[nodiscard]] std::string generateLightingV1 () const;
 
     /**
      * Parses a COMBO value to add the proper define to the code