linux-wallpaperengine/src/WallpaperEngine/Core/Objects/CEffect.cpp
Alexis Maiquez 7bffcded09
feat: make custom version of glslang to improve shader compatibility + random improvements (#291)
* feat: added glslang and spirv-core for handling shaders, should provide better results than current systems

* fix: brought back proper include placement, should fix some shaders not working

* chore: dial down required version to 330

* fix: crash when taking screenshot

* fix: use glReadnPixels for X11 copying to be more memory safe

* chore: reverted part of last commit

* chore: delay initialization of browser until it's used once

* chore: do not initialize web browser unless explicitly needed

* chore: cleanup filesystem functions to use containers directly

* chore: memory cleanup fixes

* chore: fix glReadPixels for older opengl versions

* chore: remove shader patches as they shouldn't be needed anymore

* chore: initialise variables

* chore: update deps, actions and readme

* chore: make use of custon glslang and SPIRV-Cross

* Revert "chore: update deps, actions and readme"

This reverts commit c3fbc9340b.

* chore: update actions to include submodules

* chore: do not depend on SPIRV-Tools

* fix: added log10 macro

* feat: update to latest glslang (swizzle and vec2/vec3/vec4 implicit casting to float)

* revert: delayed initialization of cef was causing issues, reverted

* chore: re-organized web wallpaper support to use custom scheme and better handle multiprocessing

* chore: make use of external repos for all deps instead of copying things manually and more cleanup work

* chore: wrong include file used in CGLSLContext.cpp

* chore: fix wayland generation folder not being present

* feat: somewhat support TEXB0004

* chore: improve function call matching and fallback to more lax method if no function is found

* chore: changed shader compilation slightly so they're passed onto glsl just once

* feat: swap android's fft implementation (which wasn't right) with kissfft's and fix update frequency issues

* chore: added missing dependency

* chore: added missing dep to PKGBUILD

* feat: add testing tools to run over all backgrounds and getting output data

* chore: jail CDirectory to the basepath and prevent accessing data outside of the main directory

* chore: process script now scales the previews so the html file is not too big

* chore: add showcase gallery to the README.md

* chore: update README

* chore: some readability improvements on code

* chore: fix segfault after code cleanup

* chore: make use of std::filesystem::canonical instead of basepath and slight typing changes on texture objects

* chore: fix path detection being wrong, make use of std::filesystem::path on CContainers

* chore: cleanup of the core part of the project

* chore: bring back std::move and make some methods const where it makes sense

* feat: added a pretty printer for easier debug and comparison between different versions of linux-wallpaperengine

* chore: refactored shader compilation code once more to be easier to follow and fixed the longstanding bug of #include not being added in the right place

* chore: more debug info for the pretty printer

* fix: some textures applied were not the right ones

* chore: properly set combos based on textures

* feat: take into account project properties for shader values
feat: proper parsing of combo values in shaders
fix: shader units weren't linked as they should
chore: more support for detecting shader things automatically

* fix: blending mode for passes using the wrong value
fix: shader uniforms from project properties should now be taken into account

* chore: use ubuntu 22 and ubuntu 24 as builders, ubuntu 20 is retired

* chore: use ubuntu 22 and ubuntu 24 as builders, ubuntu 20 is retired

* chore: hopefully fix github actions build

* refactor: simplified working with properties, constants and shader variables

* chore: remove a couple of todos that aren't needed anymore

* chore: simplify the texture detection a little bit, still work left to do

* fix: regression on texture setup not working properly

* fix: filenames with dots were not being handled properly

* chore: remove some uselesss messages

* chore: fixed std::string json values not casting anything to it as it was assumed

* fix: null user value for constants means it cannot be modified by the user

* chore: remove exception when a shader constant uses a non-existant property

* fix: angles can be an user setting too, also added detection for animation frames to show a warning

* fix: ensure variable information is not commented out by a line comment

* fix: shader includes weren't being processed properly

* chore: update to latest glslang and SPIRV-Cross to support non-integer array indices

* chore: make use of auto where it made sense

* feat: make use of in/out promotion on glslang-WallpaperEngine
feat: use glslang-WallpaperEngine linkin process as an extra validation

* chore: improve scripts for running the app

* chore: hide background structure dump behind a command-line switch

* chore: rewritten bloom effect as a json object inside C++ so it's easier to follow

* chore: removed deprecated parameters and switched to argparse instead of getopt
fix: clamping mode wasn't applied by background properly, only globally

* chore: removed help prompt from the output unless actually required

* fix: web subprocesses weren't launching due to parameter's parsing, temporal fix

* feat: added material command copy support

* feat: do not initialize some subsystems if they're disabled or not used by backgrounds

* chore: ignore type in combos as only seems to be used in the editor

* chore: update to latest glslang-WallpaperEngine changes

* chore: delete uniforms if they already exist before setting

* chore: more cleanup and fixes

* chore: more cleanup and fixes

* chore: more cleanup and fixes

* chore: update file functions to make use of shared_ptr/unique_ptr instead of copying things around

* chore: more changes to unique_ptr and shared_ptr

* chore: more changes to unique_ptr and shared_ptr

* chore: more changes to unique_ptr and shared_ptr
feat: improved render initialization process to make it easier and simpler to add new drivers (no more #ifdef in CWallpaperApplication.cpp)

* chore: change all is/as castings to use typeid so no string comparison takes place

* chore: more cleanup, default initialization of values wherever possible

* chore: moved more things to std::unique_ptr and std::shared_ptr

* chore: moved more things to std::unique_ptr and std::shared_ptr

* fix: browser context usage crashed the app

* chore: the setting controls fullscreen detection creation the same way audio works

* fix: ensure that at least one frame is rendered before detecting fullscreen windows

* chore: slight changes to output and documentation to properly reflect current build configuration

* chore: fix mipmap texture creation

* chore: fix pass uniforms not taking into account fragment shader's uniforms
chore: keep processed code in the shader sent to opengl so it appears on RenderDoc

* chore: formating issues by codefactor

* chore: do not use new to allocate the pretty printer

* fix: strchr wasn't properly done for window geometry

* chore: add recording mode for status page generation

* chore: update .gitignore

* chore: update script to make use of video generation instead of the old python scripts

* chore: also copy project.json so it can be used on the site too

* fix: regression on invisible images not being rendered

* feat: add option to disable camera parallax

* chore: add the reversing tools I have locally

* chore: mention some of the common issues in the README.md

* chore: take submodules into account for archlinux

* chore: missed cd "$pkgname" in arch's prepare step
2025-05-10 19:34:59 +02:00

320 lines
12 KiB
C++

#include "CEffect.h"
#include <utility>
#include "WallpaperEngine/Core/CProject.h"
#include "WallpaperEngine/Core/Objects/CImage.h"
#include "WallpaperEngine/Core/Objects/Images/Materials/CPass.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstant.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantFloat.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantInteger.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector2.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector3.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector4.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantProperty.h"
#include "WallpaperEngine/Core/UserSettings/CUserSettingBoolean.h"
#include "WallpaperEngine/Logging/CLog.h"
using namespace WallpaperEngine;
using namespace WallpaperEngine::Core::Objects;
using namespace WallpaperEngine::Core::UserSettings;
CEffect::CEffect (
std::string name, std::string description, std::string group, std::string preview,
std::shared_ptr <const Core::CProject> project, const CUserSettingBoolean* visible,
std::vector<std::string> dependencies, std::vector<const Effects::CFBO*> fbos,
std::vector<const Images::CMaterial*> materials
) :
m_name (std::move(name)),
m_description (std::move(description)),
m_group (std::move(group)),
m_preview (std::move(preview)),
m_visible (visible),
m_dependencies (std::move(dependencies)),
m_fbos (std::move(fbos)),
m_project (project),
m_materials (std::move(materials)) {}
const CEffect* CEffect::fromJSON (
const json& data, const CUserSettingBoolean* visible, std::shared_ptr <const Core::CProject> project,
const Images::CMaterial* material, const std::shared_ptr<const CContainer>& container
) {
const auto file = jsonFindRequired <std::string> (data, "file", "Object effect must have a file");
const auto effectpasses_it = data.find ("passes");
json content = json::parse (container->readFileAsString(file));
const auto effectName = jsonFindRequired <std::string> (content, "name", "Effect must have a name");
const auto passes_it = jsonFindRequired (content, "passes", "Effect must have a pass list");
const auto fbos_it = content.find ("fbos");
// info to override in the pass information, used by material generation
std::map<int, Images::CMaterial::OverrideInfo> overrides;
std::vector<const Effects::CFBO*> fbos;
if (fbos_it != content.end ())
fbos = CEffect::fbosFromJSON (fbos_it);
if (effectpasses_it != data.end ()) {
overrides = overridesFromJSON (effectpasses_it, material, project);
}
return new CEffect (
effectName,
jsonFindDefault<std::string> (content, "description", ""),
jsonFindRequired <std::string> (content, "group", "Effect must have a group"),
jsonFindDefault<std::string> (content, "preview", ""),
project,
visible,
dependenciesFromJSON (jsonFindRequired (content, "dependencies", "")),
fbos,
materialsFromJSON (passes_it, effectName, container, overrides)
);
}
std::map<std::string, int> CEffect::combosFromJSON (const json::const_iterator& combos_it) {
std::map<std::string, int> combos;
for (const auto& cur : combos_it->items ()) {
auto uppercase = std::string (cur.key ());
std::transform (uppercase.begin (), uppercase.end (), uppercase.begin (), ::toupper);
combos.emplace (uppercase, cur.value ());
}
return combos;
}
std::map<std::string, const Core::Objects::Effects::Constants::CShaderConstant*> CEffect::constantsFromJSON (
const json::const_iterator& constants_it, std::shared_ptr <const Core::CProject> project
) {
std::map<std::string, const Core::Objects::Effects::Constants::CShaderConstant*> constants;
for (auto& cur : constants_it->items ()) {
auto val = cur.value ();
Effects::Constants::CShaderConstant* constant = nullptr;
// if the constant is an object, that means the constant has some extra information
// for the UI, take the value, which is what we need
if (cur.value ().is_object ()) {
auto user = cur.value ().find ("user");
auto it = cur.value ().find ("value");
if (user == cur.value ().end () && it == cur.value ().end ()) {
sLog.error (R"(Found object for shader constant without "value" and "user" setting)");
continue;
}
if (user != cur.value ().end () && user->is_string ()) {
// look for a property with the correct name
const auto& properties = project->getProperties ();
const auto property = properties.find (*user);
if (property != properties.end ()) {
constant = new Effects::Constants::CShaderConstantProperty (property->second);
} else {
sLog.error ("Shader constant pointing to non-existant project property: ", user->get <std::string> ());
val = it.value ();
}
} else {
val = it.value ();
}
}
// TODO: REFACTOR THIS SO IT'S NOT SO DEEP INTO THE FUNCTION
if (constant == nullptr) {
if (val.is_number_float ()) {
constant = new Effects::Constants::CShaderConstantFloat (val.get<float> ());
} else if (val.is_number_integer ()) {
constant = new Effects::Constants::CShaderConstantInteger (val.get<int> ());
} else if (val.is_string ()) {
// count the amount of spaces to determine which type of vector we have
std::string value = val;
size_t spaces =
std::count_if (value.begin (), value.end (), [&] (const auto& item) { return item == ' '; });
if (spaces == 1) {
constant =
new Effects::Constants::CShaderConstantVector2 (WallpaperEngine::Core::aToVector2 (value));
} else if (spaces == 2) {
constant =
new Effects::Constants::CShaderConstantVector3 (WallpaperEngine::Core::aToVector3 (value));
} else if (spaces == 3) {
constant =
new Effects::Constants::CShaderConstantVector4 (WallpaperEngine::Core::aToVector4 (value));
} else {
sLog.exception ("unknown shader constant type ", value);
}
} else {
sLog.exception ("unknown shader constant type ", val);
}
}
constants.emplace (cur.key (), constant);
}
return constants;
}
std::vector<const Effects::CFBO*> CEffect::fbosFromJSON (const json::const_iterator& fbos_it) {
std::vector<const Effects::CFBO*> fbos;
for (const auto& cur : (*fbos_it))
fbos.push_back (Effects::CFBO::fromJSON (cur));
return fbos;
}
std::vector<std::string> CEffect::dependenciesFromJSON (const json::const_iterator& dependencies_it) {
std::vector<std::string> dependencies;
for (const auto& cur : (*dependencies_it))
dependencies.push_back (cur);
return dependencies;
}
std::vector<const Images::CMaterial*> CEffect::materialsFromJSON (
const json::const_iterator& passes_it, const std::string& name, const std::shared_ptr<const CContainer>& container,
std::map<int, Images::CMaterial::OverrideInfo> overrides
) {
std::vector<const Images::CMaterial*> materials;
int materialNumber = -1;
for (const auto& cur : (*passes_it)) {
++materialNumber;
const auto materialfile = cur.find ("material");
const auto target_it = cur.find ("target");
const auto bind_it = cur.find ("bind");
const auto command_it = cur.find ("command");
const auto compose_it = cur.find ("compose");
const Images::CMaterial* material = nullptr;
if (compose_it != cur.end ()) {
sLog.error ("Composing materials is not supported yet...");
}
if (materialfile != cur.end ()) {
std::map<int, const Effects::CBind*> textureBindings;
if (bind_it != cur.end ()) {
for (const auto& bindCur : (*bind_it)) {
const auto* bind = Effects::CBind::fromJSON (bindCur);
textureBindings.emplace (bind->getIndex (), bind);
}
}
const Images::CMaterial::OverrideInfo* overrideInfo = nullptr;
const auto overrideIt = overrides.find (materialNumber);
if (overrideIt != overrides.end ()) {
overrideInfo = &overrideIt->second;
}
if (target_it == cur.end ()) {
material = Images::CMaterial::fromFile (
materialfile->get<std::string> (), container, false, textureBindings, overrideInfo);
} else {
material = Images::CMaterial::fromFile (
materialfile->get<std::string> (), *target_it, container, false, textureBindings, overrideInfo);
}
} else if (command_it != cur.end ()) {
material = Images::CMaterial::fromCommand (cur);
} else {
sLog.exception ("Material without command nor material file: ", name, " (", materialNumber,")");
}
materials.push_back (material);
}
return materials;
}
std::map<int, Images::CMaterial::OverrideInfo> CEffect::overridesFromJSON (
const json::const_iterator& passes_it, const Images::CMaterial* material,
std::shared_ptr <const Core::CProject> project
) {
std::map<int, Images::CMaterial::OverrideInfo> result;
int materialNumber = -1;
for (const auto& cur : (*passes_it)) {
++materialNumber;
auto constants_it = cur.find ("constantshadervalues");
auto combos_it = cur.find ("combos");
auto textures_it = cur.find ("textures");
Images::CMaterial::OverrideInfo override;
int textureNumber = -1;
if (combos_it != cur.end ()) {
override.combos = CEffect::combosFromJSON (combos_it);
}
if (constants_it != cur.end ()) {
override.constants = CEffect::constantsFromJSON (constants_it, project);
}
if (textures_it != cur.end ()) {
// TODO: MAYBE CHANGE THIS TO BE SOMEWHERE ELSE? THIS IS REALLY MODIFYING THE DATA
// BUT IT'S USEFUL TO HAVE TO SIMPLIFY RENDERING CODE
for (const auto& texture : (*textures_it)) {
++textureNumber;
std::string name;
if (texture.is_null () && textureNumber > 0) {
continue;
}
if (textureNumber == 0) {
auto passTextures = (*material->getPasses ().begin ())->getTextures ();
if (passTextures.empty()) {
continue;
} else {
name = passTextures.begin ()->second;
}
} else {
name = texture;
}
override.textures.emplace (textureNumber, name);
}
}
result.emplace (materialNumber, override);
}
return result;
}
const std::vector<std::string>& CEffect::getDependencies () const {
return this->m_dependencies;
}
const std::vector<const Images::CMaterial*>& CEffect::getMaterials () const {
return this->m_materials;
}
const std::vector<const Effects::CFBO*>& CEffect::getFbos () const {
return this->m_fbos;
}
const Core::CProject& CEffect::getProject () const {
return *this->m_project;
}
bool CEffect::isVisible () const {
return this->m_visible->getBool ();
}
const Effects::CFBO* CEffect::findFBO (const std::string& name) {
for (const auto& cur : this->m_fbos)
if (cur->getName () == name)
return cur;
sLog.exception ("cannot find fbo ", name);
}