mirror of
https://github.com/Almamu/linux-wallpaperengine.git
synced 2025-09-14 13:56:48 +08:00
feat: added glslang and spirv-core for handling shaders, should provide better results than current systems
This commit is contained in:
parent
4a063d0b84
commit
85eecc3ea1
2
.github/workflows/arch.yml
vendored
2
.github/workflows/arch.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Publish parallel-disk-usage to the AUR
|
- name: Publish linux-wallpaperengine-git to the AUR
|
||||||
uses: KSXGitHub/github-actions-deploy-aur@v2.7.2
|
uses: KSXGitHub/github-actions-deploy-aur@v2.7.2
|
||||||
with:
|
with:
|
||||||
pkgname: linux-wallpaperengine-git
|
pkgname: linux-wallpaperengine-git
|
||||||
|
@ -34,6 +34,10 @@ find_package(MPV REQUIRED)
|
|||||||
find_package(LZ4 REQUIRED)
|
find_package(LZ4 REQUIRED)
|
||||||
find_package(FFMPEG REQUIRED)
|
find_package(FFMPEG REQUIRED)
|
||||||
find_package(PulseAudio REQUIRED)
|
find_package(PulseAudio REQUIRED)
|
||||||
|
find_package(glslang REQUIRED)
|
||||||
|
find_package(spirv_cross_core CONFIG REQUIRED)
|
||||||
|
find_package(spirv_cross_glsl CONFIG REQUIRED)
|
||||||
|
|
||||||
|
|
||||||
# Download CEF of specified version for current platform
|
# Download CEF of specified version for current platform
|
||||||
# Specify the CEF distribution version.
|
# Specify the CEF distribution version.
|
||||||
@ -118,6 +122,9 @@ include_directories(
|
|||||||
src
|
src
|
||||||
${CEF_INCLUDE_PATH}
|
${CEF_INCLUDE_PATH}
|
||||||
${CMAKE_SOURCE_DIR}
|
${CMAKE_SOURCE_DIR}
|
||||||
|
${glslang_INCLUDE_DIRS}
|
||||||
|
${spirv_cross_core_INCLUDE_DIRS}
|
||||||
|
${spirv_cross_glsl_INCLUDE_DIRS}
|
||||||
include)
|
include)
|
||||||
|
|
||||||
|
|
||||||
@ -281,8 +288,10 @@ add_executable(
|
|||||||
src/WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.h
|
src/WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.h
|
||||||
src/WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.cpp
|
src/WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.cpp
|
||||||
|
|
||||||
src/WallpaperEngine/Render/Shaders/Compiler.h
|
src/WallpaperEngine/Render/Shaders/CCompiler.h
|
||||||
src/WallpaperEngine/Render/Shaders/Compiler.cpp
|
src/WallpaperEngine/Render/Shaders/CCompiler.cpp
|
||||||
|
src/WallpaperEngine/Render/Shaders/CGLSLContext.cpp
|
||||||
|
src/WallpaperEngine/Render/Shaders/CGLSLContext.h
|
||||||
|
|
||||||
src/WallpaperEngine/Render/Helpers/CContextAware.cpp
|
src/WallpaperEngine/Render/Helpers/CContextAware.cpp
|
||||||
src/WallpaperEngine/Render/Helpers/CContextAware.h
|
src/WallpaperEngine/Render/Helpers/CContextAware.h
|
||||||
@ -463,6 +472,9 @@ target_link_libraries (linux-wallpaperengine PUBLIC
|
|||||||
${FFMPEG_LIBRARIES}
|
${FFMPEG_LIBRARIES}
|
||||||
${MPV_LIBRARY}
|
${MPV_LIBRARY}
|
||||||
${PULSEAUDIO_LIBRARY}
|
${PULSEAUDIO_LIBRARY}
|
||||||
|
glslang
|
||||||
|
spirv-cross-core
|
||||||
|
spirv-cross-glsl
|
||||||
glfw
|
glfw
|
||||||
libcef_lib libcef_dll_wrapper)
|
libcef_lib libcef_dll_wrapper)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "CLog.h"
|
#include "CLog.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
using namespace WallpaperEngine::Logging;
|
using namespace WallpaperEngine::Logging;
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ CLog::CLog () {
|
|||||||
|
|
||||||
CLog& CLog::get () {
|
CLog& CLog::get () {
|
||||||
if (sInstance == nullptr)
|
if (sInstance == nullptr)
|
||||||
sInstance.reset (new CLog ());
|
sInstance = std::make_shared<CLog> ();
|
||||||
|
|
||||||
return *sInstance;
|
return *sInstance;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include "WallpaperEngine/Render/Objects/Effects/CPass.h"
|
#include "WallpaperEngine/Render/Objects/Effects/CPass.h"
|
||||||
#include "WallpaperEngine/Render/Wallpapers/CScene.h"
|
#include "WallpaperEngine/Render/Wallpapers/CScene.h"
|
||||||
|
|
||||||
#include "WallpaperEngine/Render/Shaders/Compiler.h"
|
#include "WallpaperEngine/Render/Shaders/CCompiler.h"
|
||||||
|
|
||||||
#include "WallpaperEngine/Assets/ITexture.h"
|
#include "WallpaperEngine/Assets/ITexture.h"
|
||||||
|
|
||||||
@ -34,17 +34,17 @@ class CImage final : public CObject {
|
|||||||
void setup ();
|
void setup ();
|
||||||
void render () override;
|
void render () override;
|
||||||
|
|
||||||
const Core::Objects::CImage* getImage () const;
|
[[nodiscard]] const Core::Objects::CImage* getImage () const;
|
||||||
const std::vector<CEffect*>& getEffects () const;
|
[[nodiscard]] const std::vector<CEffect*>& getEffects () const;
|
||||||
glm::vec2 getSize () const;
|
[[nodiscard]] glm::vec2 getSize () const;
|
||||||
|
|
||||||
GLuint getSceneSpacePosition () const;
|
[[nodiscard]] GLuint getSceneSpacePosition () const;
|
||||||
GLuint getCopySpacePosition () const;
|
[[nodiscard]] GLuint getCopySpacePosition () const;
|
||||||
GLuint getPassSpacePosition () const;
|
[[nodiscard]] GLuint getPassSpacePosition () const;
|
||||||
GLuint getTexCoordCopy () const;
|
[[nodiscard]] GLuint getTexCoordCopy () const;
|
||||||
GLuint getTexCoordPass () const;
|
[[nodiscard]] GLuint getTexCoordPass () const;
|
||||||
const ITexture* getTexture () const;
|
[[nodiscard]] const ITexture* getTexture () const;
|
||||||
double getAnimationTime () const;
|
[[nodiscard]] double getAnimationTime () const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a ping-pong on the available framebuffers to be able to continue rendering things to them
|
* Performs a ping-pong on the available framebuffers to be able to continue rendering things to them
|
||||||
|
@ -282,7 +282,7 @@ Core::Objects::Images::Materials::CPass* CPass::getPass () {
|
|||||||
return this->m_pass;
|
return this->m_pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint CPass::compileShader (Render::Shaders::Compiler* shader, GLuint type) {
|
GLuint CPass::compileShader (Render::Shaders::CCompiler* shader, GLuint type) {
|
||||||
// reserve shaders in OpenGL
|
// reserve shaders in OpenGL
|
||||||
const GLuint shaderID = glCreateShader (type);
|
const GLuint shaderID = glCreateShader (type);
|
||||||
|
|
||||||
@ -332,16 +332,18 @@ void CPass::setupShaders () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prepare the shaders
|
// prepare the shaders
|
||||||
this->m_fragShader = new Render::Shaders::Compiler (
|
this->m_fragShader = new Render::Shaders::CCompiler (
|
||||||
this->m_material->getImage ()->getContainer (), this->m_pass->getShader (), Shaders::Compiler::Type_Pixel,
|
this->m_material->getImage ()->getContainer (), this->m_pass->getShader (), Shaders::CGLSLContext::ShaderType_Pixel,
|
||||||
this->m_pass->getCombos (), &m_foundCombos, this->m_pass->getTextures (), this->m_pass->getConstants ());
|
this->m_pass->getCombos (), &m_foundCombos, this->m_pass->getTextures (), this->m_pass->getConstants ());
|
||||||
this->m_fragShader->precompile ();
|
this->m_fragShader->compile ();
|
||||||
this->m_vertShader = new Render::Shaders::Compiler (
|
this->m_vertShader = new Render::Shaders::CCompiler (
|
||||||
this->m_material->getImage ()->getContainer (), this->m_pass->getShader (), Shaders::Compiler::Type_Vertex,
|
this->m_material->getImage ()->getContainer (), this->m_pass->getShader (), Shaders::CGLSLContext::ShaderType_Vertex,
|
||||||
this->m_pass->getCombos (), &m_foundCombos, this->m_pass->getTextures (), this->m_pass->getConstants ());
|
this->m_pass->getCombos (), &m_foundCombos, this->m_pass->getTextures (), this->m_pass->getConstants ());
|
||||||
this->m_vertShader->precompile ();
|
this->m_vertShader->compile ();
|
||||||
this->m_fragShader->precompile ();
|
|
||||||
this->m_vertShader->precompile ();
|
// they're re-compiled to ensure they are using the latest constants available
|
||||||
|
this->m_fragShader->compile ();
|
||||||
|
this->m_vertShader->compile ();
|
||||||
|
|
||||||
// compile the shaders
|
// compile the shaders
|
||||||
const GLuint vertexShaderID = compileShader (this->m_vertShader, GL_VERTEX_SHADER);
|
const GLuint vertexShaderID = compileShader (this->m_vertShader, GL_VERTEX_SHADER);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstant.h"
|
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstant.h"
|
||||||
#include "WallpaperEngine/Render/CFBO.h"
|
#include "WallpaperEngine/Render/CFBO.h"
|
||||||
#include "WallpaperEngine/Render/Objects/Effects/CMaterial.h"
|
#include "WallpaperEngine/Render/Objects/Effects/CMaterial.h"
|
||||||
#include "WallpaperEngine/Render/Shaders/Compiler.h"
|
#include "WallpaperEngine/Render/Shaders/CCompiler.h"
|
||||||
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
|
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
|
||||||
|
|
||||||
#include "WallpaperEngine/Render/Helpers/CContextAware.h"
|
#include "WallpaperEngine/Render/Helpers/CContextAware.h"
|
||||||
@ -34,7 +34,7 @@ class CPass final : public Helpers::CContextAware {
|
|||||||
void setModelMatrix (const glm::mat4* model);
|
void setModelMatrix (const glm::mat4* model);
|
||||||
void setViewProjectionMatrix (const glm::mat4* viewProjection);
|
void setViewProjectionMatrix (const glm::mat4* viewProjection);
|
||||||
|
|
||||||
const CMaterial* getMaterial () const;
|
[[nodiscard]] const CMaterial* getMaterial () const;
|
||||||
Core::Objects::Images::Materials::CPass* getPass ();
|
Core::Objects::Images::Materials::CPass* getPass ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -95,7 +95,7 @@ class CPass final : public Helpers::CContextAware {
|
|||||||
const GLuint* value;
|
const GLuint* value;
|
||||||
};
|
};
|
||||||
|
|
||||||
static GLuint compileShader (Render::Shaders::Compiler* shader, GLuint type);
|
static GLuint compileShader (Render::Shaders::CCompiler* shader, GLuint type);
|
||||||
void setupTextures ();
|
void setupTextures ();
|
||||||
void setupShaders ();
|
void setupShaders ();
|
||||||
void setupShaderVariables ();
|
void setupShaderVariables ();
|
||||||
@ -152,8 +152,8 @@ class CPass final : public Helpers::CContextAware {
|
|||||||
*/
|
*/
|
||||||
std::map<int, const ITexture*> m_finalTextures;
|
std::map<int, const ITexture*> m_finalTextures;
|
||||||
|
|
||||||
Render::Shaders::Compiler* m_fragShader;
|
Render::Shaders::CCompiler* m_fragShader;
|
||||||
Render::Shaders::Compiler* m_vertShader;
|
Render::Shaders::CCompiler* m_vertShader;
|
||||||
|
|
||||||
const CFBO* m_drawTo;
|
const CFBO* m_drawTo;
|
||||||
const ITexture* m_input;
|
const ITexture* m_input;
|
||||||
|
340
src/WallpaperEngine/Render/Shaders/CCompiler.cpp
Normal file
340
src/WallpaperEngine/Render/Shaders/CCompiler.cpp
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
#include "common.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
// filesystem
|
||||||
|
#include <WallpaperEngine/FileSystem/FileSystem.h>
|
||||||
|
|
||||||
|
// shader compiler
|
||||||
|
#include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantFloat.h>
|
||||||
|
#include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantInteger.h>
|
||||||
|
#include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector4.h>
|
||||||
|
#include <WallpaperEngine/Render/Shaders/CCompiler.h>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
|
||||||
|
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableFloat.h"
|
||||||
|
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableInteger.h"
|
||||||
|
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector2.h"
|
||||||
|
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector3.h"
|
||||||
|
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.h"
|
||||||
|
|
||||||
|
#include "CGLSLContext.h"
|
||||||
|
#include "WallpaperEngine/Assets/CAssetLoadException.h"
|
||||||
|
|
||||||
|
using namespace WallpaperEngine::Core;
|
||||||
|
using namespace WallpaperEngine::Assets;
|
||||||
|
|
||||||
|
namespace WallpaperEngine::Render::Shaders {
|
||||||
|
CCompiler::CCompiler (CContainer* container, std::string filename, CGLSLContext::ShaderType type, std::map<std::string, int>* combos,
|
||||||
|
std::map<std::string, bool>* foundCombos, const std::vector<std::string>& textures,
|
||||||
|
const std::map<std::string, CShaderConstant*>& constants) :
|
||||||
|
m_combos (combos),
|
||||||
|
m_foundCombos (foundCombos),
|
||||||
|
m_passTextures (textures),
|
||||||
|
m_type (type),
|
||||||
|
m_file (std::move (filename)),
|
||||||
|
m_constants (constants),
|
||||||
|
m_container (container) {
|
||||||
|
if (type == CGLSLContext::ShaderType_Vertex)
|
||||||
|
this->m_content = this->m_container->readVertexShader (this->m_file);
|
||||||
|
else if (type == CGLSLContext::ShaderType_Pixel)
|
||||||
|
this->m_content = this->m_container->readFragmentShader (this->m_file);
|
||||||
|
else if (type == CGLSLContext::ShaderType_Include)
|
||||||
|
sLog.exception ("Include shaders should never be compiled, they're part of a bigger shader: ", this->m_file);
|
||||||
|
|
||||||
|
// clone the combos into the baseCombos to keep track of values that must be embedded no matter what
|
||||||
|
for (const auto& [name, value] : *this->m_combos)
|
||||||
|
this->m_baseCombos.insert (std::make_pair (name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CCompiler::lookupShaderFile (const std::string& filename) {
|
||||||
|
// TODO:
|
||||||
|
// includes are not compiled, lookup the file and return the contents
|
||||||
|
// there might be situations where an #include is part of a comment
|
||||||
|
// instead of trying to figure out if an #include is part of that
|
||||||
|
// just try to load it
|
||||||
|
// if nothing is found, nothing is really lost
|
||||||
|
// but if something is found, depending on the type of comment and the contents
|
||||||
|
// the included file could break the shader, we'll need to perform some more
|
||||||
|
// checks here at some point, but for now should be enough
|
||||||
|
try {
|
||||||
|
return this->m_container->readIncludeShader (filename);
|
||||||
|
} catch (CAssetLoadException&) {
|
||||||
|
sLog.error ("Cannot find include file ", filename, " in shader ", this->m_file, ". Using empty content.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& CCompiler::getCompiled () {
|
||||||
|
return this->m_compiledContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCompiler::compile () {
|
||||||
|
std::string precompile = "#version 330\n"
|
||||||
|
"// ======================================================\n"
|
||||||
|
"// Processed shader " +
|
||||||
|
this->m_file +
|
||||||
|
"\n"
|
||||||
|
"// ======================================================\n"
|
||||||
|
"precision highp float;\n"
|
||||||
|
"#define mul(x, y) ((y) * (x))\n"
|
||||||
|
"#define max(x, y) max (y, x)\n"
|
||||||
|
"#define lerp mix\n"
|
||||||
|
"#define frac fract\n"
|
||||||
|
"#define CAST2(x) (vec2(x))\n"
|
||||||
|
"#define CAST3(x) (vec3(x))\n"
|
||||||
|
"#define CAST4(x) (vec4(x))\n"
|
||||||
|
"#define CAST3X3(x) (mat3(x))\n"
|
||||||
|
"#define saturate(x) (clamp(x, 0.0, 1.0))\n"
|
||||||
|
"#define texSample2D texture\n"
|
||||||
|
"#define texSample2DLod textureLod\n"
|
||||||
|
"#define atan2 atan\n"
|
||||||
|
"#define fmod(x, y) ((x)-(y)*trunc((x)/(y)))\n"
|
||||||
|
"#define ddx dFdx\n"
|
||||||
|
"#define ddy(x) dFdy(-(x))\n"
|
||||||
|
"#define GLSL 1\n\n";
|
||||||
|
|
||||||
|
if (this->m_type == CGLSLContext::ShaderType_Vertex) {
|
||||||
|
precompile += "#define attribute in\n"
|
||||||
|
"#define varying out\n";
|
||||||
|
} else {
|
||||||
|
precompile += "out vec4 out_FragColor;\n"
|
||||||
|
"#define varying in\n";
|
||||||
|
}
|
||||||
|
// searches for the combos available and adds the defines required
|
||||||
|
|
||||||
|
// go line by line in the shader content
|
||||||
|
size_t start = 0, end = 0;
|
||||||
|
while ((end = this->m_content.find ('\n', start)) != std::string::npos) {
|
||||||
|
// Extract a line from the string
|
||||||
|
std::string line = this->m_content.substr (start, end - start);
|
||||||
|
size_t combo = line.find("// [COMBO] ");
|
||||||
|
size_t uniform = line.find("uniform ");
|
||||||
|
size_t comment = line.find("// ");
|
||||||
|
size_t semicolon = line.find(';');
|
||||||
|
|
||||||
|
if (combo != std::string::npos) {
|
||||||
|
this->parseComboConfiguration (line.substr(combo + strlen("// [COMBO] ")), 0);
|
||||||
|
} else if (uniform != std::string::npos && comment != std::string::npos && semicolon != std::string::npos) {
|
||||||
|
// uniforms with comments should never have a value assigned, use this fact to detect the required parts
|
||||||
|
size_t last_space = line.find_last_of (' ', semicolon);
|
||||||
|
|
||||||
|
if (last_space != std::string::npos) {
|
||||||
|
size_t previous_space = line.find_last_of (' ', last_space - 1);
|
||||||
|
|
||||||
|
if (previous_space != std::string::npos) {
|
||||||
|
// extract type and name
|
||||||
|
std::string type = line.substr (previous_space + 1, last_space - previous_space - 1);
|
||||||
|
std::string name = line.substr (last_space + 1, semicolon - last_space - 1);
|
||||||
|
std::string json = line.substr (comment + 2);
|
||||||
|
|
||||||
|
this->parseParameterConfiguration (type, name, json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next line
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all the defines we have for now
|
||||||
|
for (const auto& [name, value] : *this->m_foundCombos) {
|
||||||
|
// find the right value for the combo in the combos map
|
||||||
|
auto combo = this->m_combos->find (name);
|
||||||
|
|
||||||
|
if (combo == this->m_combos->end ())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
precompile += "#define " + name + " " + std::to_string (combo->second) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// add base combos that come from the pass change that MUST be added
|
||||||
|
for (const auto& [name, value] : this->m_baseCombos) {
|
||||||
|
auto alreadyFound = this->m_foundCombos->find (name);
|
||||||
|
|
||||||
|
if (alreadyFound != this->m_foundCombos->end ())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
precompile += "#define " + name + " " + std::to_string (value) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
precompile += this->m_content;
|
||||||
|
|
||||||
|
// reset end so we start from the beginning
|
||||||
|
end = 0;
|
||||||
|
|
||||||
|
// then apply includes in-place
|
||||||
|
while((start = precompile.find("#include", end)) != std::string::npos) {
|
||||||
|
size_t lineEnd = precompile.find_first_of ('\n', start);
|
||||||
|
// TODO: CHECK FOR ERRORS HERE, MALFORMED INCLUDES WILL NOT BE PROPERLY HANDLED
|
||||||
|
size_t quoteStart = precompile.find_first_of ('"', start) + 1;
|
||||||
|
size_t quoteEnd = precompile.find_first_of('"', quoteStart);
|
||||||
|
std::string filename = precompile.substr(quoteStart, quoteEnd - quoteStart);
|
||||||
|
std::string content = this->lookupShaderFile (filename);
|
||||||
|
|
||||||
|
// file contents ready, replace things
|
||||||
|
precompile = precompile.replace (start, lineEnd - start,
|
||||||
|
"// begin of include from file " + filename + "\n" +
|
||||||
|
content +
|
||||||
|
"\n// end of included from file " + filename + "\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
// go back to the beginning of the line to properly continue detecting things
|
||||||
|
end = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// content should be ready, finally ask glslang to compile the shader
|
||||||
|
this->m_compiledContent = CGLSLContext::get().toGlsl (precompile, this->m_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCompiler::parseComboConfiguration (const std::string& content, int defaultValue) {
|
||||||
|
json data = json::parse (content);
|
||||||
|
const auto combo = jsonFindRequired (data, "combo", "cannot parse combo information");
|
||||||
|
const auto type = data.find ("type");
|
||||||
|
const auto defvalue = data.find ("default");
|
||||||
|
|
||||||
|
// check the combos
|
||||||
|
const auto entry = this->m_combos->find (combo->get<std::string> ());
|
||||||
|
|
||||||
|
// add the combo to the found list
|
||||||
|
this->m_foundCombos->insert (std::make_pair<std::string, int> (*combo, true));
|
||||||
|
|
||||||
|
// if the combo was not found in the predefined values this means that the default value in the JSON data can be
|
||||||
|
// used so only define the ones that are not already defined
|
||||||
|
if (entry == this->m_combos->end ()) {
|
||||||
|
if (type != data.end ())
|
||||||
|
sLog.error ("Resorting to default value as type ", *type, " is unknown");
|
||||||
|
|
||||||
|
// if no combo is defined just load the default settings
|
||||||
|
if (defvalue == data.end ()) {
|
||||||
|
// TODO: PROPERLY SUPPORT EMPTY COMBOS
|
||||||
|
this->m_combos->insert (std::make_pair<std::string, int> (*combo, (int) defaultValue));
|
||||||
|
} else if (defvalue->is_number_float ()) {
|
||||||
|
sLog.exception ("float combos are not supported in shader ", this->m_file, ". ", *combo);
|
||||||
|
} else if (defvalue->is_number_integer ()) {
|
||||||
|
this->m_combos->insert (std::make_pair<std::string, int> (*combo, defvalue->get<int> ()));
|
||||||
|
} else if (defvalue->is_string ()) {
|
||||||
|
sLog.exception ("string combos are not supported in shader ", this->m_file, ". ", *combo);
|
||||||
|
} else {
|
||||||
|
sLog.exception ("cannot parse combo information ", *combo, ". unknown type for ", defvalue->dump ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCompiler::parseParameterConfiguration (const std::string& type, const std::string& name,
|
||||||
|
const std::string& content) {
|
||||||
|
json data = json::parse (content);
|
||||||
|
const auto material = data.find ("material");
|
||||||
|
const auto defvalue = data.find ("default");
|
||||||
|
// auto range = data.find ("range");
|
||||||
|
const auto combo = data.find ("combo");
|
||||||
|
|
||||||
|
// this is not a real parameter
|
||||||
|
auto constant = this->m_constants.end ();
|
||||||
|
|
||||||
|
if (material != data.end ())
|
||||||
|
constant = this->m_constants.find (*material);
|
||||||
|
|
||||||
|
if (constant == this->m_constants.end () && defvalue == data.end ()) {
|
||||||
|
if (type != "sampler2D")
|
||||||
|
sLog.exception ("Cannot parse parameter data for ", name, " in shader ", this->m_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Variables::CShaderVariable* parameter = nullptr;
|
||||||
|
|
||||||
|
// TODO: SUPPORT VALUES FOR ALL THESE TYPES
|
||||||
|
if (type == "vec4") {
|
||||||
|
parameter = new Variables::CShaderVariableVector4 (
|
||||||
|
constant == this->m_constants.end () ? WallpaperEngine::Core::aToVector4 (*defvalue)
|
||||||
|
: *constant->second->as<CShaderConstantVector4> ()->getValue ());
|
||||||
|
} else if (type == "vec3") {
|
||||||
|
parameter = new Variables::CShaderVariableVector3 (
|
||||||
|
constant == this->m_constants.end () ? WallpaperEngine::Core::aToVector3 (*defvalue)
|
||||||
|
: *constant->second->as<CShaderConstantVector4> ()->getValue ());
|
||||||
|
} else if (type == "vec2") {
|
||||||
|
parameter = new Variables::CShaderVariableVector2 (WallpaperEngine::Core::aToVector2 (*defvalue));
|
||||||
|
} else if (type == "float") {
|
||||||
|
float value = 0;
|
||||||
|
|
||||||
|
if (constant == this->m_constants.end ())
|
||||||
|
value = defvalue->get<float> ();
|
||||||
|
else if (constant->second->is<CShaderConstantFloat> ())
|
||||||
|
value = *constant->second->as<CShaderConstantFloat> ()->getValue ();
|
||||||
|
else if (constant->second->is<CShaderConstantInteger> ())
|
||||||
|
value = *constant->second->as<CShaderConstantInteger> ()->getValue ();
|
||||||
|
|
||||||
|
parameter = new Variables::CShaderVariableFloat (value);
|
||||||
|
} else if (type == "int") {
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
if (constant == this->m_constants.end ())
|
||||||
|
value = defvalue->get<int> ();
|
||||||
|
else if (constant->second->is<CShaderConstantFloat> ())
|
||||||
|
value = *constant->second->as<CShaderConstantFloat> ()->getValue ();
|
||||||
|
else if (constant->second->is<CShaderConstantInteger> ())
|
||||||
|
value = *constant->second->as<CShaderConstantInteger> ()->getValue ();
|
||||||
|
|
||||||
|
parameter = new Variables::CShaderVariableInteger (value);
|
||||||
|
} else if (type == "sampler2D" || type == "sampler2DComparison") {
|
||||||
|
// samplers can have special requirements, check what sampler we're working with and create definitions
|
||||||
|
// if needed
|
||||||
|
const auto textureName = data.find ("default");
|
||||||
|
// extract the texture number from the name
|
||||||
|
const char value = name.at (std::string ("g_Texture").length ());
|
||||||
|
// now convert it to integer
|
||||||
|
size_t index = value - '0';
|
||||||
|
|
||||||
|
if (combo != data.end ()) {
|
||||||
|
// if the texture exists (and is not null), add to the combo
|
||||||
|
if (this->m_passTextures.size () > index &&
|
||||||
|
(!this->m_passTextures.at (index).empty () || textureName != data.end ())) {
|
||||||
|
// add the new combo to the list
|
||||||
|
this->m_combos->insert (std::make_pair<std::string, int> (*combo, 1));
|
||||||
|
|
||||||
|
// textures linked to combos need to be tracked too
|
||||||
|
if (this->m_foundCombos->find (*combo) == this->m_foundCombos->end ())
|
||||||
|
this->m_foundCombos->insert (std::make_pair<std::string, bool> (*combo, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textureName != data.end ())
|
||||||
|
this->m_textures.insert (std::make_pair (index, *textureName));
|
||||||
|
|
||||||
|
// samplers are not saved, we can ignore them for now
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
sLog.error ("Unknown parameter type: ", type, " for ", name, " in shader ", this->m_file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material != data.end ()) {
|
||||||
|
parameter->setIdentifierName (*material);
|
||||||
|
parameter->setName (name);
|
||||||
|
|
||||||
|
this->m_parameters.push_back (parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variables::CShaderVariable* CCompiler::findParameter (const std::string& identifier) {
|
||||||
|
for (const auto& cur : this->m_parameters)
|
||||||
|
if (cur->getIdentifierName () == identifier)
|
||||||
|
return cur;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Variables::CShaderVariable*>& CCompiler::getParameters () const {
|
||||||
|
return this->m_parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, int>* CCompiler::getCombos () const {
|
||||||
|
return this->m_combos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::map<int, std::string>& CCompiler::getTextures () const {
|
||||||
|
return this->m_textures;
|
||||||
|
}
|
||||||
|
} // namespace WallpaperEngine::Render::Shaders
|
@ -11,6 +11,8 @@
|
|||||||
#include "WallpaperEngine/FileSystem/FileSystem.h"
|
#include "WallpaperEngine/FileSystem/FileSystem.h"
|
||||||
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
|
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
|
||||||
|
|
||||||
|
#include "CGLSLContext.h"
|
||||||
|
|
||||||
namespace WallpaperEngine::Render::Shaders {
|
namespace WallpaperEngine::Render::Shaders {
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
using namespace WallpaperEngine::Assets;
|
using namespace WallpaperEngine::Assets;
|
||||||
@ -19,22 +21,8 @@ using namespace WallpaperEngine::Core::Objects::Effects::Constants;
|
|||||||
/**
|
/**
|
||||||
* A basic shader loader that adds basic function definitions to every loaded shader
|
* A basic shader loader that adds basic function definitions to every loaded shader
|
||||||
*/
|
*/
|
||||||
class Compiler {
|
class CCompiler {
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Types of shaders
|
|
||||||
*/
|
|
||||||
enum Type {
|
|
||||||
Type_Vertex = 0,
|
|
||||||
Type_Pixel = 1,
|
|
||||||
Type_Include = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Types of variables the pre-processor understands
|
|
||||||
*/
|
|
||||||
static std::vector<std::string> sTypes;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiler constructor, loads the given shader file and prepares
|
* Compiler constructor, loads the given shader file and prepares
|
||||||
* the pre-processing and compilation of the shader, adding
|
* the pre-processing and compilation of the shader, adding
|
||||||
@ -49,16 +37,16 @@ class Compiler {
|
|||||||
* @param constants Default values for shader variables
|
* @param constants Default values for shader variables
|
||||||
* @param recursive Whether the compiler should add base definitions or not
|
* @param recursive Whether the compiler should add base definitions or not
|
||||||
*/
|
*/
|
||||||
Compiler (CContainer* container, std::string filename, Type type, std::map<std::string, int>* combos,
|
CCompiler (CContainer* container, std::string filename, CGLSLContext::ShaderType type, std::map<std::string, int>* combos,
|
||||||
std::map<std::string, bool>* foundCombos, const std::vector<std::string>& textures,
|
std::map<std::string, bool>* foundCombos, const std::vector<std::string>& textures,
|
||||||
const std::map<std::string, CShaderConstant*>& constants, bool recursive = false);
|
const std::map<std::string, CShaderConstant*>& constants);
|
||||||
/**
|
/**
|
||||||
* Performs the actual pre-compilation/pre-processing over the shader files
|
* Performs the actual pre-compilation/pre-processing over the shader files
|
||||||
* This step is kinda big, replaces variables names on sVariableReplacement,
|
* This step is kinda big, replaces variables names on sVariableReplacement,
|
||||||
* ensures #include directives are correctly handled
|
* ensures #include directives are correctly handled
|
||||||
* and takes care of attribute comments for the wallpaper engine specifics
|
* and takes care of attribute comments for the wallpaper engine specifics
|
||||||
*/
|
*/
|
||||||
void precompile ();
|
void compile ();
|
||||||
/**
|
/**
|
||||||
* @return The compiled shader's text (if available)
|
* @return The compiled shader's text (if available)
|
||||||
*/
|
*/
|
||||||
@ -86,97 +74,6 @@ class Compiler {
|
|||||||
[[nodiscard]] const std::map<int, std::string>& getTextures () const;
|
[[nodiscard]] const std::map<int, std::string>& getTextures () const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* Checks if there is "str" in the current position without advancing the
|
|
||||||
* iterator in use
|
|
||||||
*
|
|
||||||
* @param str The string to check for
|
|
||||||
* @param it The position to start checking at
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
bool peekString (std::string str, std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* Checks for a semicolon as current character, advancing the iterator
|
|
||||||
* after finding it, otherwise returns an error
|
|
||||||
*
|
|
||||||
* @param it The position where to expect the semicolon
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
bool expectSemicolon (std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* Ignores contiguous space characters in the string advancing the iterator
|
|
||||||
* until the first non-space character
|
|
||||||
*
|
|
||||||
* @param it The iterator to increase
|
|
||||||
*/
|
|
||||||
void ignoreSpaces (std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* Ignores all characters until next line-fee (\n) advancing the interator
|
|
||||||
*
|
|
||||||
* @param it The iterator to increase
|
|
||||||
*/
|
|
||||||
void ignoreUpToNextLineFeed (std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* Ignores all characters until a block comment end is found, advancing the iterator
|
|
||||||
*
|
|
||||||
* @param it The iterator to increase
|
|
||||||
*/
|
|
||||||
void ignoreUpToBlockCommentEnd (std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* Parses the current position as a variable type, extracts it and compares it
|
|
||||||
* to the registered types in the pre-processor, returning it's name if valid
|
|
||||||
* increasing the iterator at the same time
|
|
||||||
*
|
|
||||||
* @param it The position to extract it from
|
|
||||||
*
|
|
||||||
* @return The type name
|
|
||||||
*/
|
|
||||||
std::string extractType (std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* Parses the current position as a variable name, extractig it's name and
|
|
||||||
* increasing the iterator as the name is extracted
|
|
||||||
*
|
|
||||||
* @param it The position to start extracting the variable name from
|
|
||||||
*
|
|
||||||
* @return The variable name
|
|
||||||
*/
|
|
||||||
std::string extractName (std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* Parses the current position as an array indicator
|
|
||||||
*
|
|
||||||
* @param it The position to start extracting the array from
|
|
||||||
* @param mustExists Whether the array indicator must exists or not
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
std::string extractArray (std::string::const_iterator& it, bool mustExists = false);
|
|
||||||
/**
|
|
||||||
* Parses the current position as a quoted value, extracting it's value
|
|
||||||
* and increasing the iterator at the same time
|
|
||||||
*
|
|
||||||
* @param it The position to start extracting the value from
|
|
||||||
*
|
|
||||||
* @return The value
|
|
||||||
*/
|
|
||||||
std::string extractQuotedValue (std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* Tries to find the given shader file and compile it
|
|
||||||
*
|
|
||||||
* @param filename The shader's filename
|
|
||||||
*
|
|
||||||
* @return The compiled contents
|
|
||||||
*/
|
|
||||||
std::string lookupShaderFile (std::string filename);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Whether the character in the current position is a character or not
|
|
||||||
*/
|
|
||||||
static bool isChar (const std::string::const_iterator& it);
|
|
||||||
/**
|
|
||||||
* @return Whether the character in the current position is a number or not
|
|
||||||
*/
|
|
||||||
static bool isNumeric (const std::string::const_iterator& it);
|
|
||||||
/**
|
/**
|
||||||
* Parses a COMBO value to add the proper define to the code
|
* Parses a COMBO value to add the proper define to the code
|
||||||
*
|
*
|
||||||
@ -193,9 +90,13 @@ class Compiler {
|
|||||||
*/
|
*/
|
||||||
void parseParameterConfiguration (const std::string& type, const std::string& name, const std::string& content);
|
void parseParameterConfiguration (const std::string& type, const std::string& name, const std::string& content);
|
||||||
/**
|
/**
|
||||||
* Applies any available patches for this shader
|
* Tries to find the given shader file and compile it
|
||||||
|
*
|
||||||
|
* @param filename The shader's filename
|
||||||
|
*
|
||||||
|
* @return The compiled contents
|
||||||
*/
|
*/
|
||||||
void applyPatches ();
|
std::string lookupShaderFile (const std::string& filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The shader file this instance is loading
|
* The shader file this instance is loading
|
||||||
@ -205,24 +106,14 @@ class Compiler {
|
|||||||
* The original file content
|
* The original file content
|
||||||
*/
|
*/
|
||||||
std::string m_content;
|
std::string m_content;
|
||||||
/** The content of all the included files */
|
|
||||||
std::string m_includesContent;
|
|
||||||
/**
|
/**
|
||||||
* The final, compiled content ready to be used by OpenGL
|
* The final, compiled content ready to be used by OpenGL
|
||||||
*/
|
*/
|
||||||
std::string m_compiledContent;
|
std::string m_compiledContent;
|
||||||
/**
|
|
||||||
* Whether there was any kind of error in the compilation or not
|
|
||||||
*/
|
|
||||||
bool m_error;
|
|
||||||
/**
|
|
||||||
* Extra information about the error (if any)
|
|
||||||
*/
|
|
||||||
std::string m_errorInfo;
|
|
||||||
/**
|
/**
|
||||||
* The type of shader
|
* The type of shader
|
||||||
*/
|
*/
|
||||||
Type m_type;
|
CGLSLContext::ShaderType m_type;
|
||||||
/**
|
/**
|
||||||
* The parameters the shader needs
|
* The parameters the shader needs
|
||||||
*/
|
*/
|
||||||
@ -250,10 +141,6 @@ class Compiler {
|
|||||||
* The shader constants with values for variables inside the shader
|
* The shader constants with values for variables inside the shader
|
||||||
*/
|
*/
|
||||||
const std::map<std::string, CShaderConstant*>& m_constants;
|
const std::map<std::string, CShaderConstant*>& m_constants;
|
||||||
/**
|
|
||||||
* Whether this compilation is a recursive one or not
|
|
||||||
*/
|
|
||||||
bool m_recursive;
|
|
||||||
/**
|
/**
|
||||||
* The container to load files from
|
* The container to load files from
|
||||||
*/
|
*/
|
||||||
@ -262,6 +149,5 @@ class Compiler {
|
|||||||
* List of textures that the shader expects (inferred from sampler2D and it's JSON data)
|
* List of textures that the shader expects (inferred from sampler2D and it's JSON data)
|
||||||
*/
|
*/
|
||||||
std::map<int, std::string> m_textures;
|
std::map<int, std::string> m_textures;
|
||||||
bool m_includesProcessed = false;
|
|
||||||
};
|
};
|
||||||
} // namespace WallpaperEngine::Render::Shaders
|
} // namespace WallpaperEngine::Render::Shaders
|
181
src/WallpaperEngine/Render/Shaders/CGLSLContext.cpp
Normal file
181
src/WallpaperEngine/Render/Shaders/CGLSLContext.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
#include "CGLSLContext.h"
|
||||||
|
#include "WallpaperEngine/Logging/CLog.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <glslang/Include/ResourceLimits.h>
|
||||||
|
#include <glslang/Public/ShaderLang.h>
|
||||||
|
#include <glslang/SPIRV/GlslangToSpv.h>
|
||||||
|
#include <spirv_cross/spirv_glsl.hpp>
|
||||||
|
|
||||||
|
using namespace WallpaperEngine::Render::Shaders;
|
||||||
|
|
||||||
|
TBuiltInResource BuiltInResource = {
|
||||||
|
.maxLights = 32,
|
||||||
|
.maxClipPlanes = 6,
|
||||||
|
.maxTextureUnits = 32,
|
||||||
|
.maxTextureCoords = 32,
|
||||||
|
.maxVertexAttribs = 64,
|
||||||
|
.maxVertexUniformComponents = 4096,
|
||||||
|
.maxVaryingFloats = 64,
|
||||||
|
.maxVertexTextureImageUnits = 32,
|
||||||
|
.maxCombinedTextureImageUnits = 80,
|
||||||
|
.maxTextureImageUnits = 32,
|
||||||
|
.maxFragmentUniformComponents = 4096,
|
||||||
|
.maxDrawBuffers = 32,
|
||||||
|
.maxVertexUniformVectors = 128,
|
||||||
|
.maxVaryingVectors = 8,
|
||||||
|
.maxFragmentUniformVectors = 16,
|
||||||
|
.maxVertexOutputVectors = 16,
|
||||||
|
.maxFragmentInputVectors = 15,
|
||||||
|
.minProgramTexelOffset = -8,
|
||||||
|
.maxProgramTexelOffset = 7,
|
||||||
|
.maxClipDistances = 8,
|
||||||
|
.maxComputeWorkGroupCountX = 65535,
|
||||||
|
.maxComputeWorkGroupCountY = 65535,
|
||||||
|
.maxComputeWorkGroupCountZ = 65535,
|
||||||
|
.maxComputeWorkGroupSizeX = 1024,
|
||||||
|
.maxComputeWorkGroupSizeY = 1024,
|
||||||
|
.maxComputeWorkGroupSizeZ = 64,
|
||||||
|
.maxComputeUniformComponents = 1024,
|
||||||
|
.maxComputeTextureImageUnits = 16,
|
||||||
|
.maxComputeImageUniforms = 8,
|
||||||
|
.maxComputeAtomicCounters = 8,
|
||||||
|
.maxComputeAtomicCounterBuffers = 1,
|
||||||
|
.maxVaryingComponents = 60,
|
||||||
|
.maxVertexOutputComponents = 64,
|
||||||
|
.maxGeometryInputComponents = 64,
|
||||||
|
.maxGeometryOutputComponents = 128,
|
||||||
|
.maxFragmentInputComponents = 128,
|
||||||
|
.maxImageUnits = 8,
|
||||||
|
.maxCombinedImageUnitsAndFragmentOutputs = 8,
|
||||||
|
.maxCombinedShaderOutputResources = 8,
|
||||||
|
.maxImageSamples = 0,
|
||||||
|
.maxVertexImageUniforms = 0,
|
||||||
|
.maxTessControlImageUniforms = 0,
|
||||||
|
.maxTessEvaluationImageUniforms = 0,
|
||||||
|
.maxGeometryImageUniforms = 0,
|
||||||
|
.maxFragmentImageUniforms = 8,
|
||||||
|
.maxCombinedImageUniforms = 8,
|
||||||
|
.maxGeometryTextureImageUnits = 16,
|
||||||
|
.maxGeometryOutputVertices = 256,
|
||||||
|
.maxGeometryTotalOutputComponents = 1024,
|
||||||
|
.maxGeometryUniformComponents = 1024,
|
||||||
|
.maxGeometryVaryingComponents = 64,
|
||||||
|
.maxTessControlInputComponents = 128,
|
||||||
|
.maxTessControlOutputComponents = 128,
|
||||||
|
.maxTessControlTextureImageUnits = 16,
|
||||||
|
.maxTessControlUniformComponents = 1024,
|
||||||
|
.maxTessControlTotalOutputComponents = 4096,
|
||||||
|
.maxTessEvaluationInputComponents = 128,
|
||||||
|
.maxTessEvaluationOutputComponents = 128,
|
||||||
|
.maxTessEvaluationTextureImageUnits = 16,
|
||||||
|
.maxTessEvaluationUniformComponents = 1024,
|
||||||
|
.maxTessPatchComponents = 120,
|
||||||
|
.maxPatchVertices = 32,
|
||||||
|
.maxTessGenLevel = 64,
|
||||||
|
.maxViewports = 16,
|
||||||
|
.maxVertexAtomicCounters = 0,
|
||||||
|
.maxTessControlAtomicCounters = 0,
|
||||||
|
.maxTessEvaluationAtomicCounters = 0,
|
||||||
|
.maxGeometryAtomicCounters = 0,
|
||||||
|
.maxFragmentAtomicCounters = 8,
|
||||||
|
.maxCombinedAtomicCounters = 8,
|
||||||
|
.maxAtomicCounterBindings = 1,
|
||||||
|
.maxVertexAtomicCounterBuffers = 0,
|
||||||
|
.maxTessControlAtomicCounterBuffers = 0,
|
||||||
|
.maxTessEvaluationAtomicCounterBuffers = 0,
|
||||||
|
.maxGeometryAtomicCounterBuffers = 0,
|
||||||
|
.maxFragmentAtomicCounterBuffers = 1,
|
||||||
|
.maxCombinedAtomicCounterBuffers = 1,
|
||||||
|
.maxAtomicCounterBufferSize = 16384,
|
||||||
|
.maxTransformFeedbackBuffers = 4,
|
||||||
|
.maxTransformFeedbackInterleavedComponents = 64,
|
||||||
|
.maxCullDistances = 8,
|
||||||
|
.maxCombinedClipAndCullDistances = 8,
|
||||||
|
.maxSamples = 4,
|
||||||
|
.maxMeshOutputVerticesNV = 256,
|
||||||
|
.maxMeshOutputPrimitivesNV = 512,
|
||||||
|
.maxMeshWorkGroupSizeX_NV = 32,
|
||||||
|
.maxMeshWorkGroupSizeY_NV = 1,
|
||||||
|
.maxMeshWorkGroupSizeZ_NV = 1,
|
||||||
|
.maxTaskWorkGroupSizeX_NV = 32,
|
||||||
|
.maxTaskWorkGroupSizeY_NV = 1,
|
||||||
|
.maxTaskWorkGroupSizeZ_NV = 1,
|
||||||
|
.maxMeshViewCountNV = 4,
|
||||||
|
.limits = {
|
||||||
|
.nonInductiveForLoops = 1,
|
||||||
|
.whileLoops = 1,
|
||||||
|
.doWhileLoops = 1,
|
||||||
|
.generalUniformIndexing = 1,
|
||||||
|
.generalAttributeMatrixVectorIndexing = 1,
|
||||||
|
.generalVaryingIndexing = 1,
|
||||||
|
.generalSamplerIndexing = 1,
|
||||||
|
.generalVariableIndexing = 1,
|
||||||
|
.generalConstantMatrixVectorIndexing = 1,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CGLSLContext::CGLSLContext () {
|
||||||
|
assert (this->sInstance == nullptr);
|
||||||
|
|
||||||
|
glslang::InitializeProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
CGLSLContext::~CGLSLContext () {
|
||||||
|
glslang::FinalizeProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
CGLSLContext& CGLSLContext::get () {
|
||||||
|
if (sInstance == nullptr)
|
||||||
|
sInstance = std::make_shared<CGLSLContext> ();
|
||||||
|
|
||||||
|
return *sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CGLSLContext::toGlsl (const std::string& content, ShaderType type) {
|
||||||
|
if (type == ShaderType_Include) {
|
||||||
|
sLog.error ("Include shaders cannot be converted as they should be part of a bigger shader");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
EShLanguage shaderType = type == ShaderType_Vertex ? EShLangVertex : EShLangFragment;
|
||||||
|
glslang::TShader shader (shaderType);
|
||||||
|
|
||||||
|
const char* shaderSource = content.c_str();
|
||||||
|
shader.setStrings(&shaderSource, 1);
|
||||||
|
shader.setEntryPoint("main");
|
||||||
|
// TODO: USE HLSL IF GLSL-TO-GLSL DOESN'T WORK
|
||||||
|
shader.setEnvInput(glslang::EShSourceGlsl, shaderType, glslang::EShClientOpenGL, 330);
|
||||||
|
shader.setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
|
||||||
|
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_5);
|
||||||
|
shader.setAutoMapLocations (true);
|
||||||
|
shader.setAutoMapBindings (true);
|
||||||
|
|
||||||
|
if (!shader.parse (&BuiltInResource, 100, false, EShMsgDefault)) {
|
||||||
|
sLog.error ("GLSL Parsing Failed: %s", shader.getInfoLog());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
glslang::TProgram program;
|
||||||
|
program.addShader (&shader);
|
||||||
|
|
||||||
|
if (!program.link (EShMsgDefault)) {
|
||||||
|
sLog.error ("Program Linking Failed: %s", program.getInfoLog());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> spirv;
|
||||||
|
glslang::GlslangToSpv (*program.getIntermediate (shaderType), spirv);
|
||||||
|
|
||||||
|
spirv_cross::CompilerGLSL compiler(spirv);
|
||||||
|
spirv_cross::CompilerGLSL::Options options;
|
||||||
|
options.version = 450; // OpenGL 4.5 / Vulkan GLSL TODO: MAYBE THIS CAN BE CHANGED?
|
||||||
|
options.es = false;
|
||||||
|
compiler.set_common_options(options);
|
||||||
|
|
||||||
|
return compiler.compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CGLSLContext> CGLSLContext::sInstance = nullptr;
|
31
src/WallpaperEngine/Render/Shaders/CGLSLContext.h
Normal file
31
src/WallpaperEngine/Render/Shaders/CGLSLContext.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace WallpaperEngine::Render::Shaders {
|
||||||
|
class CGLSLContext {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Types of shaders
|
||||||
|
*/
|
||||||
|
enum ShaderType {
|
||||||
|
ShaderType_Vertex = 0,
|
||||||
|
ShaderType_Pixel = 1,
|
||||||
|
ShaderType_Include = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
CGLSLContext ();
|
||||||
|
~CGLSLContext ();
|
||||||
|
|
||||||
|
[[nodiscard]] std::string toGlsl (const std::string& content, ShaderType type);
|
||||||
|
|
||||||
|
[[nodiscard]] static CGLSLContext& get ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::shared_ptr<CGLSLContext> sInstance;
|
||||||
|
};
|
||||||
|
} // namespace WallpaperEngine::Render::Shaders
|
@ -1,771 +0,0 @@
|
|||||||
#include "common.h"
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
// filesystem
|
|
||||||
#include <WallpaperEngine/FileSystem/FileSystem.h>
|
|
||||||
|
|
||||||
// shader compiler
|
|
||||||
#include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantFloat.h>
|
|
||||||
#include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantInteger.h>
|
|
||||||
#include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector4.h>
|
|
||||||
#include <WallpaperEngine/Render/Shaders/Compiler.h>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
#include "WallpaperEngine/Assets/CAssetLoadException.h"
|
|
||||||
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
|
|
||||||
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableFloat.h"
|
|
||||||
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableInteger.h"
|
|
||||||
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector2.h"
|
|
||||||
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector3.h"
|
|
||||||
#include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.h"
|
|
||||||
|
|
||||||
using namespace WallpaperEngine::Core;
|
|
||||||
using namespace WallpaperEngine::Assets;
|
|
||||||
|
|
||||||
namespace WallpaperEngine::Render::Shaders {
|
|
||||||
Compiler::Compiler (CContainer* container, std::string filename, Type type, std::map<std::string, int>* combos,
|
|
||||||
std::map<std::string, bool>* foundCombos, const std::vector<std::string>& textures,
|
|
||||||
const std::map<std::string, CShaderConstant*>& constants, bool recursive) :
|
|
||||||
m_combos (combos),
|
|
||||||
m_foundCombos (foundCombos),
|
|
||||||
m_passTextures (textures),
|
|
||||||
m_recursive (recursive),
|
|
||||||
m_type (type),
|
|
||||||
m_file (std::move (filename)),
|
|
||||||
m_error (),
|
|
||||||
m_constants (constants),
|
|
||||||
m_container (container) {
|
|
||||||
if (type == Type_Vertex)
|
|
||||||
this->m_content = this->m_container->readVertexShader (this->m_file);
|
|
||||||
else if (type == Type_Pixel)
|
|
||||||
this->m_content = this->m_container->readFragmentShader (this->m_file);
|
|
||||||
else if (type == Type_Include)
|
|
||||||
this->m_content = this->m_container->readIncludeShader (this->m_file);
|
|
||||||
|
|
||||||
// clone the combos into the baseCombos to keep track of values that must be embedded no matter what
|
|
||||||
for (const auto& [name, value] : *this->m_combos)
|
|
||||||
this->m_baseCombos.insert (std::make_pair (name, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::peekString (std::string str, std::string::const_iterator& it) {
|
|
||||||
std::string::const_iterator check = str.begin ();
|
|
||||||
std::string::const_iterator cur = it;
|
|
||||||
|
|
||||||
while (cur != this->m_content.end () && check != str.end ()) {
|
|
||||||
if (*cur != *check)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
++cur;
|
|
||||||
++check;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur == this->m_content.end ()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check != str.end ()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
it = cur;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::expectSemicolon (std::string::const_iterator& it) {
|
|
||||||
if (*it != ';') {
|
|
||||||
this->m_error = true;
|
|
||||||
this->m_errorInfo = std::string ("Expected semicolon but got ") + *it;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
++it;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::ignoreSpaces (std::string::const_iterator& it) {
|
|
||||||
while (it != this->m_content.end () && (*it == ' ' || *it == '\t'))
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::ignoreUpToNextLineFeed (std::string::const_iterator& it) {
|
|
||||||
while (it != this->m_content.end () && *it != '\n')
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::ignoreUpToBlockCommentEnd (std::string::const_iterator& it) {
|
|
||||||
while (it != this->m_content.end () && !this->peekString ("*/", it))
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Compiler::extractType (std::string::const_iterator& it) {
|
|
||||||
// first of all check for highp/mediump/lowp as these operators have to be ignored
|
|
||||||
this->peekString ("highp", it);
|
|
||||||
this->peekString ("mediump", it);
|
|
||||||
this->peekString ("lowp", it);
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
|
|
||||||
auto cur = sTypes.begin ();
|
|
||||||
const auto end = sTypes.end ();
|
|
||||||
|
|
||||||
while (cur != end) {
|
|
||||||
if (this->peekString (*cur + " ", it))
|
|
||||||
return *cur;
|
|
||||||
|
|
||||||
++cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->m_error = true;
|
|
||||||
this->m_errorInfo = "Expected type";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Compiler::extractName (std::string::const_iterator& it) {
|
|
||||||
std::string::const_iterator cur = it;
|
|
||||||
std::string::const_iterator begin = cur;
|
|
||||||
|
|
||||||
// first character has to be a valid alphabetic characer
|
|
||||||
if (!this->isChar (cur) && *cur != '_') {
|
|
||||||
this->m_error = true;
|
|
||||||
this->m_errorInfo = "Expected name doesn't start with a valid character";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
++cur;
|
|
||||||
|
|
||||||
while (cur != this->m_content.end () && (this->isChar (cur) || *cur == '_' || this->isNumeric (cur)))
|
|
||||||
++cur;
|
|
||||||
|
|
||||||
it = cur;
|
|
||||||
|
|
||||||
return {begin, cur};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Compiler::extractArray (std::string::const_iterator& it, bool mustExists) {
|
|
||||||
auto cur = it;
|
|
||||||
auto begin = cur;
|
|
||||||
|
|
||||||
if (*cur != '[') {
|
|
||||||
if (!mustExists) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
this->m_error = true;
|
|
||||||
this->m_errorInfo = "Expected an array but found nothing";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
++cur;
|
|
||||||
|
|
||||||
while (cur != this->m_content.end () && *cur != ']')
|
|
||||||
++cur;
|
|
||||||
|
|
||||||
it = ++cur;
|
|
||||||
|
|
||||||
return {begin, cur};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::isChar (const std::string::const_iterator& it) {
|
|
||||||
return ((*it) >= 'A' && (*it) <= 'Z') || ((*it) >= 'a' && (*it) <= 'z');
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::isNumeric (const std::string::const_iterator& it) {
|
|
||||||
return (*it) >= '0' && (*it) <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Compiler::extractQuotedValue (std::string::const_iterator& it) {
|
|
||||||
std::string::const_iterator cur = it;
|
|
||||||
|
|
||||||
if (*cur != '"') {
|
|
||||||
m_error = true;
|
|
||||||
m_errorInfo = std::string ("Expected opening \" but got ") + (*cur);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
++cur;
|
|
||||||
|
|
||||||
while (cur != this->m_content.end () && *cur != '\n' && *cur != '"')
|
|
||||||
++cur;
|
|
||||||
|
|
||||||
if (cur == this->m_content.end ()) {
|
|
||||||
m_error = true;
|
|
||||||
m_errorInfo = "Expected closing \" not found";
|
|
||||||
it = cur;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string filename = std::string (++it, cur);
|
|
||||||
|
|
||||||
it = ++cur;
|
|
||||||
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Compiler::lookupShaderFile (std::string filename) {
|
|
||||||
// now compile the new shader
|
|
||||||
// do not include the default header (as it's already included in the parent)
|
|
||||||
Compiler loader (this->m_container, std::move (filename), Type_Include, this->m_combos, this->m_foundCombos,
|
|
||||||
this->m_passTextures, this->m_constants, true);
|
|
||||||
|
|
||||||
loader.precompile ();
|
|
||||||
|
|
||||||
return loader.getCompiled ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string& Compiler::getCompiled () {
|
|
||||||
return this->m_compiledContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::precompile () {
|
|
||||||
// TODO: SEPARATE THIS IN TWO SO THE COMBOS ARE DETECTED FIRST AND WE DO NOT REQUIRE DOUBLE COMPILATION OF THE
|
|
||||||
// SHADER'S SOURCE
|
|
||||||
#define BREAK_IF_ERROR \
|
|
||||||
if (this->m_error) { \
|
|
||||||
sLog.exception ("ERROR PRE-COMPILING SHADER.", this->m_errorInfo); \
|
|
||||||
}
|
|
||||||
// parse the shader and find #includes and such things and translate them to the correct name
|
|
||||||
// also remove any #version definition to prevent errors
|
|
||||||
std::string::const_iterator it = this->m_content.begin ();
|
|
||||||
|
|
||||||
// reset error indicator
|
|
||||||
this->m_error = false;
|
|
||||||
this->m_errorInfo = "";
|
|
||||||
this->m_compiledContent = "";
|
|
||||||
this->m_includesContent = "";
|
|
||||||
this->m_includesProcessed = false;
|
|
||||||
|
|
||||||
// search preprocessor macros and parse them
|
|
||||||
while (it != this->m_content.end () && !this->m_error) {
|
|
||||||
if (*it == ' ' || *it == '\t' || *it == '\n' || *it == '\r' || *it == '\0' || *it == '{' || *it == '}' ||
|
|
||||||
*it == '[' || *it == ']' || *it == '.') {
|
|
||||||
this->m_compiledContent += *it;
|
|
||||||
++it;
|
|
||||||
} else if (*it == '#') {
|
|
||||||
if (this->peekString ("#include ", it)) {
|
|
||||||
// ignore whitespaces
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
// extract value between quotes
|
|
||||||
std::string filename = this->extractQuotedValue (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
|
|
||||||
if (this->m_recursive) {
|
|
||||||
this->m_compiledContent += "// begin of include from file " + filename + "\r\n";
|
|
||||||
this->m_compiledContent += this->lookupShaderFile (filename);
|
|
||||||
this->m_compiledContent += "\r\n// end of included from file " + filename + "\r\n";
|
|
||||||
} else {
|
|
||||||
// load the content to the includes contents and continue with the next one
|
|
||||||
// try to find the file first
|
|
||||||
this->m_includesContent += "// begin of included from file " + filename + "\r\n";
|
|
||||||
this->m_includesContent += this->lookupShaderFile (filename);
|
|
||||||
this->m_includesContent += "\r\n// end of included from file " + filename + "\r\n";
|
|
||||||
}
|
|
||||||
} else if (this->peekString ("#require ", it)) {
|
|
||||||
// TODO: PROPERLY IMPLEMENT #require
|
|
||||||
this->m_compiledContent += "// #require ";
|
|
||||||
// ignore whitespaces
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
std::string name = this->extractName (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->m_compiledContent += name;
|
|
||||||
} else {
|
|
||||||
this->m_compiledContent += '#';
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
} else if (*it == 'u') {
|
|
||||||
// uniforms might have extra information for their values
|
|
||||||
if (this->peekString ("uniform ", it)) {
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
std::string type = this->extractType (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
std::string name = this->extractName (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
std::string array = this->extractArray (it, false);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
this->expectSemicolon (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
|
|
||||||
// check if there is any actual extra information and parse it
|
|
||||||
if (this->peekString ("//", it)) {
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
std::string::const_iterator begin = it;
|
|
||||||
this->ignoreUpToNextLineFeed (it);
|
|
||||||
|
|
||||||
std::string configuration;
|
|
||||||
configuration.append (begin, it);
|
|
||||||
|
|
||||||
// parse the parameter information
|
|
||||||
this->parseParameterConfiguration (type, name, configuration);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->m_compiledContent += "uniform ";
|
|
||||||
this->m_compiledContent += type;
|
|
||||||
this->m_compiledContent += " ";
|
|
||||||
this->m_compiledContent += name;
|
|
||||||
this->m_compiledContent += array;
|
|
||||||
this->m_compiledContent += "; // ";
|
|
||||||
this->m_compiledContent += configuration;
|
|
||||||
} else {
|
|
||||||
this->m_compiledContent += "uniform ";
|
|
||||||
this->m_compiledContent += type;
|
|
||||||
this->m_compiledContent += " ";
|
|
||||||
this->m_compiledContent += name;
|
|
||||||
this->m_compiledContent += array;
|
|
||||||
this->m_compiledContent += ";";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// continue reading the original shader as this most likely is a variable name or something
|
|
||||||
// that is not really interesting for the compiler
|
|
||||||
this->m_compiledContent += *it;
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
} else if (*it == 'a') {
|
|
||||||
// find attribute definitions
|
|
||||||
if (this->peekString ("attribute ", it)) {
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
std::string type = this->extractType (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
std::string name = this->extractName (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
std::string array = this->extractArray (it, false);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
this->expectSemicolon (it);
|
|
||||||
BREAK_IF_ERROR
|
|
||||||
|
|
||||||
this->m_compiledContent += "attribute " + type + " " + name + array + ";";
|
|
||||||
} else {
|
|
||||||
// check for types first
|
|
||||||
std::string type = this->extractType (it);
|
|
||||||
|
|
||||||
// types not found, try names
|
|
||||||
if (!this->m_error) {
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
this->m_compiledContent += type;
|
|
||||||
} else {
|
|
||||||
this->m_error = false;
|
|
||||||
std::string name = this->extractName (it);
|
|
||||||
|
|
||||||
if (!this->m_error) {
|
|
||||||
// check if the name is a translated one or not
|
|
||||||
this->m_compiledContent += name;
|
|
||||||
} else {
|
|
||||||
this->m_error = false;
|
|
||||||
this->m_compiledContent += *it;
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (*it == '/') {
|
|
||||||
if (this->peekString ("//", it)) {
|
|
||||||
std::string::const_iterator begin = it - 2;
|
|
||||||
// is there a COMBO mark to take care of?
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
|
|
||||||
if (this->peekString ("[COMBO]", it)) {
|
|
||||||
// parse combo json data to define the proper variables
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
begin = it;
|
|
||||||
this->ignoreUpToNextLineFeed (it);
|
|
||||||
|
|
||||||
std::string configuration;
|
|
||||||
configuration.append (begin, it);
|
|
||||||
|
|
||||||
this->m_compiledContent += "// [COMBO] " + configuration;
|
|
||||||
|
|
||||||
this->parseComboConfiguration (configuration, 0);
|
|
||||||
BREAK_IF_ERROR;
|
|
||||||
} else if (this->peekString ("[COMBO_OFF]", it)) {
|
|
||||||
// parse combo json data to define the proper variables
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
begin = it;
|
|
||||||
this->ignoreUpToNextLineFeed (it);
|
|
||||||
|
|
||||||
std::string configuration;
|
|
||||||
configuration.append (begin, it);
|
|
||||||
|
|
||||||
this->m_compiledContent += "// [COMBO_OFF] " + configuration;
|
|
||||||
|
|
||||||
this->parseComboConfiguration (configuration, 0);
|
|
||||||
BREAK_IF_ERROR;
|
|
||||||
} else {
|
|
||||||
// the comment can be ignored and put back as is
|
|
||||||
this->ignoreUpToNextLineFeed (it);
|
|
||||||
this->m_compiledContent.append (begin, it);
|
|
||||||
}
|
|
||||||
} else if (this->peekString ("/*", it)) {
|
|
||||||
std::string::const_iterator begin = it - 2;
|
|
||||||
this->ignoreUpToBlockCommentEnd (it);
|
|
||||||
this->m_compiledContent.append (begin, it);
|
|
||||||
} else {
|
|
||||||
this->m_compiledContent += *it;
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// check for types first
|
|
||||||
std::string type = this->extractType (it);
|
|
||||||
|
|
||||||
// type found
|
|
||||||
if (!this->m_error) {
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
// check for main, and take it into account, this also helps adding the includes
|
|
||||||
std::string name = this->extractName (it);
|
|
||||||
|
|
||||||
this->ignoreSpaces (it);
|
|
||||||
|
|
||||||
if (this->peekString ("(", it)) {
|
|
||||||
if (!this->m_includesProcessed) {
|
|
||||||
this->m_compiledContent += "\n\n" + this->m_includesContent + "\n\n";
|
|
||||||
this->m_includesProcessed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->m_compiledContent += type + " " + name + "(";
|
|
||||||
} else {
|
|
||||||
this->m_compiledContent += type + " " + name;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this->m_error = false;
|
|
||||||
std::string name = this->extractName (it);
|
|
||||||
|
|
||||||
if (!this->m_error) {
|
|
||||||
// check if the name is a translated one or not
|
|
||||||
this->m_compiledContent += name;
|
|
||||||
} else {
|
|
||||||
this->m_error = false;
|
|
||||||
this->m_compiledContent += *it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string finalCode;
|
|
||||||
|
|
||||||
if (!this->m_recursive) {
|
|
||||||
// add the opengl compatibility at the top
|
|
||||||
finalCode = "#version 330\n"
|
|
||||||
"// ======================================================\n"
|
|
||||||
"// Processed shader " +
|
|
||||||
this->m_file +
|
|
||||||
"\n"
|
|
||||||
"// ======================================================\n"
|
|
||||||
"precision highp float;\n"
|
|
||||||
"#define mul(x, y) ((y) * (x))\n"
|
|
||||||
"#define max(x, y) max (y, x)\n"
|
|
||||||
"#define lerp mix\n"
|
|
||||||
"#define frac fract\n"
|
|
||||||
"#define CAST2(x) (vec2(x))\n"
|
|
||||||
"#define CAST3(x) (vec3(x))\n"
|
|
||||||
"#define CAST4(x) (vec4(x))\n"
|
|
||||||
"#define CAST3X3(x) (mat3(x))\n"
|
|
||||||
"#define saturate(x) (clamp(x, 0.0, 1.0))\n"
|
|
||||||
"#define texSample2D texture\n"
|
|
||||||
"#define texSample2DLod textureLod\n"
|
|
||||||
"#define atan2 atan\n"
|
|
||||||
"#define fmod(x, y) ((x)-(y)*trunc((x)/(y)))\n"
|
|
||||||
"#define ddx dFdx\n"
|
|
||||||
"#define ddy(x) dFdy(-(x))\n"
|
|
||||||
"#define GLSL 1\n\n";
|
|
||||||
|
|
||||||
if (this->m_type == Type_Vertex) {
|
|
||||||
finalCode += "#define attribute in\n"
|
|
||||||
"#define varying out\n";
|
|
||||||
} else {
|
|
||||||
finalCode += "out vec4 out_FragColor;\n"
|
|
||||||
"#define varying in\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
finalCode += "// ======================================================\n"
|
|
||||||
"// Shader combo parameter definitions\n"
|
|
||||||
"// ======================================================\n";
|
|
||||||
|
|
||||||
finalCode += "// found combos from current shader\n";
|
|
||||||
|
|
||||||
// add combo values
|
|
||||||
for (const auto& [name, value] : *this->m_foundCombos) {
|
|
||||||
// find the right value for the combo in the combos map
|
|
||||||
auto combo = this->m_combos->find (name);
|
|
||||||
|
|
||||||
if (combo == this->m_combos->end ())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
finalCode += "#define " + name + " " + std::to_string (combo->second) + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
finalCode += "// combos from pass\n";
|
|
||||||
|
|
||||||
// add base combos that come from the pass change that MUST be added
|
|
||||||
for (const auto& [name, value] : this->m_baseCombos) {
|
|
||||||
auto alreadyFound = this->m_foundCombos->find (name);
|
|
||||||
|
|
||||||
if (alreadyFound != this->m_foundCombos->end ())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
finalCode += "#define " + name + " " + std::to_string (value) + "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace gl_FragColor with the equivalent
|
|
||||||
std::string from = "gl_FragColor";
|
|
||||||
std::string to = "out_FragColor";
|
|
||||||
|
|
||||||
size_t start_pos = 0;
|
|
||||||
while ((start_pos = this->m_compiledContent.find (from, start_pos)) != std::string::npos) {
|
|
||||||
this->m_compiledContent.replace (start_pos, from.length (), to);
|
|
||||||
start_pos += to.length (); // Handles case where 'to' is a substring of 'from'
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace sample occurrences
|
|
||||||
from = "sample";
|
|
||||||
to = "_sample";
|
|
||||||
|
|
||||||
start_pos = 0;
|
|
||||||
while ((start_pos = this->m_compiledContent.find (from, start_pos)) != std::string::npos) {
|
|
||||||
// ensure that after it comes something like a space or a ; or a tab
|
|
||||||
std::string after = this->m_compiledContent.substr (start_pos + from.length (), 1);
|
|
||||||
|
|
||||||
if (after != " " && after != ";" && after != "\t" && after != "=" && after != "+" && after != "-" &&
|
|
||||||
after != "/" && after != "*" && after != "." && after != "," && after != ")") {
|
|
||||||
start_pos += to.length (); // Handles case where 'to' is a substring of 'from'
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->m_compiledContent.replace (start_pos, from.length (), to);
|
|
||||||
start_pos += to.length (); // Handles case where 'to' is a substring of 'from'
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this->applyPatches ();
|
|
||||||
} catch (CAssetLoadException&) {
|
|
||||||
// nothing important, no patch was found
|
|
||||||
}
|
|
||||||
|
|
||||||
finalCode += this->m_compiledContent;
|
|
||||||
|
|
||||||
if (!this->m_recursive) {
|
|
||||||
sLog.debug ("======================== COMPILED ", (this->m_type == Type_Vertex ? "VERTEX" : "FRAGMENT"),
|
|
||||||
" SHADER ", this->m_file, " ========================");
|
|
||||||
sLog.debug (finalCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the final final code here
|
|
||||||
this->m_compiledContent = finalCode;
|
|
||||||
#undef BREAK_IF_ERROR
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::applyPatches () {
|
|
||||||
// small patches for things, looks like the official wpengine does the same thing
|
|
||||||
std::filesystem::path file = this->m_file;
|
|
||||||
file = "patches" / file.filename ();
|
|
||||||
|
|
||||||
if (this->m_type == Type_Vertex)
|
|
||||||
file += ".vert";
|
|
||||||
else if (this->m_type == Type_Pixel)
|
|
||||||
file += ".frag";
|
|
||||||
|
|
||||||
file += ".json";
|
|
||||||
|
|
||||||
std::string tmp = file;
|
|
||||||
const std::string patchContents = this->m_container->readFileAsString (file);
|
|
||||||
|
|
||||||
json data = json::parse (patchContents);
|
|
||||||
const auto patches = data.find ("patches");
|
|
||||||
|
|
||||||
for (auto patch : *patches) {
|
|
||||||
auto matches = patch.find ("matches");
|
|
||||||
bool canApply = true;
|
|
||||||
|
|
||||||
// check for matches first, as these signal whether the patch can be applied or not
|
|
||||||
for (const auto& match : *matches) {
|
|
||||||
if (this->m_compiledContent.find (match) == std::string::npos) {
|
|
||||||
canApply = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canApply == false)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto replacements = patch.find ("replacements");
|
|
||||||
|
|
||||||
for (const auto& replacement : replacements->items ()) {
|
|
||||||
// replace gl_FragColor with the equivalent
|
|
||||||
std::string from = replacement.key ();
|
|
||||||
std::string to = replacement.value ();
|
|
||||||
|
|
||||||
size_t start_pos = 0;
|
|
||||||
while ((start_pos = this->m_compiledContent.find (from, start_pos)) != std::string::npos) {
|
|
||||||
this->m_compiledContent.replace (start_pos, from.length (), to);
|
|
||||||
start_pos += to.length (); // Handles case where 'to' is a substring of 'from'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::parseComboConfiguration (const std::string& content, int defaultValue) {
|
|
||||||
json data = json::parse (content);
|
|
||||||
const auto combo = jsonFindRequired (data, "combo", "cannot parse combo information");
|
|
||||||
const auto type = data.find ("type");
|
|
||||||
const auto defvalue = data.find ("default");
|
|
||||||
|
|
||||||
// add line feed just in case
|
|
||||||
this->m_compiledContent += "\n";
|
|
||||||
|
|
||||||
// check the combos
|
|
||||||
const auto entry = this->m_combos->find (combo->get<std::string> ());
|
|
||||||
|
|
||||||
// add the combo to the found list
|
|
||||||
this->m_foundCombos->insert (std::make_pair<std::string, int> (*combo, true));
|
|
||||||
|
|
||||||
// if the combo was not found in the predefined values this means that the default value in the JSON data can be
|
|
||||||
// used so only define the ones that are not already defined
|
|
||||||
if (entry == this->m_combos->end ()) {
|
|
||||||
if (type != data.end ())
|
|
||||||
sLog.error ("Resorting to default value as type ", *type, " is unknown");
|
|
||||||
|
|
||||||
// if no combo is defined just load the default settings
|
|
||||||
if (defvalue == data.end ()) {
|
|
||||||
// TODO: PROPERLY SUPPORT EMPTY COMBOS
|
|
||||||
this->m_combos->insert (std::make_pair<std::string, int> (*combo, (int) defaultValue));
|
|
||||||
} else if (defvalue->is_number_float ()) {
|
|
||||||
sLog.exception ("float combos are not supported in shader ", this->m_file, ". ", *combo);
|
|
||||||
} else if (defvalue->is_number_integer ()) {
|
|
||||||
this->m_combos->insert (std::make_pair<std::string, int> (*combo, defvalue->get<int> ()));
|
|
||||||
} else if (defvalue->is_string ()) {
|
|
||||||
sLog.exception ("string combos are not supported in shader ", this->m_file, ". ", *combo);
|
|
||||||
} else {
|
|
||||||
sLog.exception ("cannot parse combo information ", *combo, ". unknown type for ", defvalue->dump ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::parseParameterConfiguration (const std::string& type, const std::string& name,
|
|
||||||
const std::string& content) {
|
|
||||||
json data = json::parse (content);
|
|
||||||
const auto material = data.find ("material");
|
|
||||||
const auto defvalue = data.find ("default");
|
|
||||||
// auto range = data.find ("range");
|
|
||||||
const auto combo = data.find ("combo");
|
|
||||||
|
|
||||||
// this is not a real parameter
|
|
||||||
auto constant = this->m_constants.end ();
|
|
||||||
|
|
||||||
if (material != data.end ())
|
|
||||||
constant = this->m_constants.find (*material);
|
|
||||||
|
|
||||||
if (constant == this->m_constants.end () && defvalue == data.end ()) {
|
|
||||||
if (type != "sampler2D")
|
|
||||||
sLog.exception ("Cannot parse parameter data for ", name, " in shader ", this->m_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
Variables::CShaderVariable* parameter;
|
|
||||||
|
|
||||||
// TODO: SUPPORT VALUES FOR ALL THESE TYPES
|
|
||||||
if (type == "vec4") {
|
|
||||||
parameter = new Variables::CShaderVariableVector4 (
|
|
||||||
constant == this->m_constants.end () ? WallpaperEngine::Core::aToVector4 (*defvalue)
|
|
||||||
: *constant->second->as<CShaderConstantVector4> ()->getValue ());
|
|
||||||
} else if (type == "vec3") {
|
|
||||||
parameter = new Variables::CShaderVariableVector3 (
|
|
||||||
constant == this->m_constants.end () ? WallpaperEngine::Core::aToVector3 (*defvalue)
|
|
||||||
: *constant->second->as<CShaderConstantVector4> ()->getValue ());
|
|
||||||
} else if (type == "vec2") {
|
|
||||||
parameter = new Variables::CShaderVariableVector2 (WallpaperEngine::Core::aToVector2 (*defvalue));
|
|
||||||
} else if (type == "float") {
|
|
||||||
float value = 0;
|
|
||||||
|
|
||||||
if (constant == this->m_constants.end ())
|
|
||||||
value = defvalue->get<float> ();
|
|
||||||
else if (constant->second->is<CShaderConstantFloat> ())
|
|
||||||
value = *constant->second->as<CShaderConstantFloat> ()->getValue ();
|
|
||||||
else if (constant->second->is<CShaderConstantInteger> ())
|
|
||||||
value = *constant->second->as<CShaderConstantInteger> ()->getValue ();
|
|
||||||
|
|
||||||
parameter = new Variables::CShaderVariableFloat (value);
|
|
||||||
} else if (type == "int") {
|
|
||||||
int value = 0;
|
|
||||||
|
|
||||||
if (constant == this->m_constants.end ())
|
|
||||||
value = defvalue->get<int> ();
|
|
||||||
else if (constant->second->is<CShaderConstantFloat> ())
|
|
||||||
value = *constant->second->as<CShaderConstantFloat> ()->getValue ();
|
|
||||||
else if (constant->second->is<CShaderConstantInteger> ())
|
|
||||||
value = *constant->second->as<CShaderConstantInteger> ()->getValue ();
|
|
||||||
|
|
||||||
parameter = new Variables::CShaderVariableInteger (value);
|
|
||||||
} else if (type == "sampler2D" || type == "sampler2DComparison") {
|
|
||||||
// samplers can have special requirements, check what sampler we're working with and create definitions
|
|
||||||
// if needed
|
|
||||||
const auto textureName = data.find ("default");
|
|
||||||
// extract the texture number from the name
|
|
||||||
const char value = name.at (std::string ("g_Texture").length ());
|
|
||||||
// now convert it to integer
|
|
||||||
size_t index = value - '0';
|
|
||||||
|
|
||||||
if (combo != data.end ()) {
|
|
||||||
// if the texture exists (and is not null), add to the combo
|
|
||||||
if (this->m_passTextures.size () > index &&
|
|
||||||
(!this->m_passTextures.at (index).empty () || textureName != data.end ())) {
|
|
||||||
// add the new combo to the list
|
|
||||||
this->m_combos->insert (std::make_pair<std::string, int> (*combo, 1));
|
|
||||||
|
|
||||||
// textures linked to combos need to be tracked too
|
|
||||||
if (this->m_foundCombos->find (*combo) == this->m_foundCombos->end ())
|
|
||||||
this->m_foundCombos->insert (std::make_pair<std::string, bool> (*combo, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textureName != data.end ())
|
|
||||||
this->m_textures.insert (std::make_pair (index, *textureName));
|
|
||||||
|
|
||||||
// samplers are not saved, we can ignore them for now
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this->m_error = true;
|
|
||||||
this->m_errorInfo = "Unknown parameter type: " + type + " for " + name;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (material != data.end ()) {
|
|
||||||
parameter->setIdentifierName (*material);
|
|
||||||
parameter->setName (name);
|
|
||||||
|
|
||||||
this->m_parameters.push_back (parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Variables::CShaderVariable* Compiler::findParameter (const std::string& identifier) {
|
|
||||||
for (const auto& cur : this->m_parameters)
|
|
||||||
if (cur->getIdentifierName () == identifier)
|
|
||||||
return cur;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Variables::CShaderVariable*>& Compiler::getParameters () const {
|
|
||||||
return this->m_parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, int>* Compiler::getCombos () const {
|
|
||||||
return this->m_combos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::map<int, std::string>& Compiler::getTextures () const {
|
|
||||||
return this->m_textures;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> Compiler::sTypes = {"vec4", "uvec4", "ivec4", "dvec4", "bvec4", "vec3",
|
|
||||||
"uvec3", "ivec3", "dvec3", "bvec3", "vec2", "uvec2",
|
|
||||||
"ivec2", "dvec2", "bvec2", "float", "sampler2D", "sampler2DComparison",
|
|
||||||
"mat4x3", "mat4", "mat3", "uint", "uint4", "void"};
|
|
||||||
} // namespace WallpaperEngine::Render::Shaders
|
|
Loading…
Reference in New Issue
Block a user