[Feat] (MG_Util/Compiler): shader compiler WIP

This commit is contained in:
2025-07-13 21:14:18 +08:00
parent d6bdd6854f
commit ea30e0ee92
6 changed files with 333 additions and 74 deletions
+2 -1
View File
@@ -32,7 +32,8 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp
MobileGL/MG_Backend/Init.cpp
MobileGL/MG_Util/Pipelines/Shader/GLSLtoSPIRVPipeline.cpp
)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
+8
View File
@@ -39,11 +39,15 @@
#include <queue>
#include <format>
#include <memory>
#include <functional>
#include <glslang/Public/ShaderLang.h>
#include <glslang/Include/Types.h>
#include <glslang/Public/ShaderLang.h>
#include <spirv_cross/spirv_cross_c.h>
#include <glslang/SPIRV/GlslangToSpv.h>
#include <glslang/Include/intermediate.h>
#include <glslang/MachineIndependent/localintermediate.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/glm.hpp>
#include <ankerl/unordered_dense.h>
@@ -82,3 +86,7 @@
#include "MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.h"
#include "MG_Impl/GLImpl/Getter/GL_Getter.h"
#include "MG_Util/Pipelines/Pipeline.hpp"
#include "MG_Util/Pipelines/Pipelines/Shader/GLSLtoSPIRVPipeline.h"
+70 -63
View File
@@ -1,70 +1,77 @@
#ifndef AMMOPROTOCOL_MIDDLEWARE_HPP
#define AMMOPROTOCOL_MIDDLEWARE_HPP
#pragma once
#include <vector>
#include <functional>
#include <memory>
#ifndef MG_PIPELINE_HPP
#define MG_PIPELINE_HPP
using next_t = std::function<void()>;
using next_ptr_t = std::shared_ptr<next_t>;
namespace MobileGL {
namespace MG_Util {
namespace Pipeline {
using next_t = std::function<void()>;
using next_ptr_t = std::shared_ptr<next_t>;
template <typename T>
class middleware {
protected:
using T_ptr = std::shared_ptr<T>;
using callback_t = std::function<void(T_ptr, next_ptr_t)>;
public:
virtual callback_t handler() = 0;
};
template<typename T>
class Stage {
protected:
using T_ptr = std::shared_ptr<T>;
using callback_t = std::function<void(T_ptr, next_ptr_t)>;
public:
virtual callback_t handler() = 0;
};
template <typename T>
class middleware_manager {
protected:
using T_ptr = std::shared_ptr<T>;
using callback_t = std::function<void(T_ptr, next_ptr_t)>;
using middleware_ptr_t = std::shared_ptr<middleware<T>>;
public:
explicit middleware_manager(std::function<void(T&)> callback):
callback_(std::move(callback)) {}
template<typename T>
class Pipeline {
protected:
using T_ptr = std::shared_ptr<T>;
using callback_t = std::function<void(T_ptr, next_ptr_t)>;
using middleware_ptr_t = std::shared_ptr<Stage<T>>;
public:
explicit Pipeline(std::function<void(T &)> callback) :
callback_(Move(callback)) {}
void use(callback_t middleware) {
middlewares_.emplace_back(std::move(middleware));
Pipeline &Register(callback_t middleware) {
middlewares_.emplace_back(Move(middleware));
return *this;
}
Pipeline &Register(middleware_ptr_t middleware) {
middleware_storage_.emplace_back(middleware);
Register(middleware->handler());
return *this;
}
void Process(T_ptr payload) {
auto it_ptr = std::make_shared<typename std::vector<callback_t>::iterator>(
middlewares_.begin());
auto next = std::make_shared<next_t>();
auto weak_next = std::weak_ptr<next_t>(next);
// setup next callback
*next = [this, it_ptr, weak_next, payload] {
auto &it = *it_ptr;
// if there's something to do
if (it != middlewares_.end()) {
auto &func = *it;
// advance the iterator to the next Stage
++it;
auto next = weak_next.lock();
func(payload, next);
// proceed with post-Stage logic
} else {
callback_(*payload);
}
};
// bootstrap the whole thing
(*next)();
}
private:
std::vector<callback_t> middlewares_;
std::vector<middleware_ptr_t> middleware_storage_;
std::function<void(T &)> callback_;
};
}
}
}
void use(middleware_ptr_t middleware) {
middleware_storage_.emplace_back(middleware);
use(middleware->handler());
}
void process(T_ptr payload) {
auto it_ptr = std::make_shared<typename std::vector<callback_t>::iterator>(middlewares_.begin());
auto next = std::make_shared<next_t>();
auto weak_next = std::weak_ptr<next_t>(next);
// setup next callback
*next = [this, it_ptr, weak_next, payload] {
auto& it = *it_ptr;
// if there's something to do
if (it != middlewares_.end()) {
auto& func = *it;
// advance the iterator to the next middleware
++it;
auto next = weak_next.lock();
func(payload, next);
// proceed with post-middleware logic
} else {
callback_(*payload);
}
};
// bootstrap the whole thing
(*next)();
}
private:
std::vector<callback_t> middlewares_;
std::vector<middleware_ptr_t> middleware_storage_;
std::function<void(T&)> callback_;
};
#endif //AMMOPROTOCOL_MIDDLEWARE_HPP
#endif //MG_PIPELINE_HPP
@@ -3,3 +3,58 @@
//
#include "GLSLtoSPIRVPipeline.h"
MobileGL::MG_Util::Pipeline::GLSLtoSPIRVPipeline::GLSLtoSPIRVPipeline(std::function<void(GLSLtoSPIRVPayload &)> callback):
pipeline_(Move(callback)) {
pipeline_
.Register([this](SharedPtr<GLSLtoSPIRVPayload> payload, auto next) {
// source -> glslang TShader
auto& shaderType = payload->shaderType;
auto& sourceStr = payload->sourceStr;
glslang::InitializeProcess();
auto lang = GetEShLanguageByShaderType(shaderType);
if (lang == EShLanguage::EShLangCount) {
payload->log += "Error: [Preprocess] Unsupported shader type: " +
ConvertGLEnumToString(shaderType);
payload->errc = -1;
return;
}
auto& tshader = payload->TShader;
tshader = MakeUnique<glslang::TShader>(lang);
const char* src[] = { sourceStr.c_str() };
tshader->setStrings(src, 1);
tshader->setInvertY(true);
tshader->setEnvInput(glslang::EShSourceGlsl, lang, glslang::EShClientOpenGL, 150);
tshader->setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
tshader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_6);
tshader->setAutoMapLocations(true);
tshader->setAutoMapBindings(true);
tshader->setEnvInputVulkanRulesRelaxed(); // using EXT_vulkan_glsl_relaxed for gl_VertexID and gl_InstanceID?
if (!tshader->parse(&GetTBuiltInResourceInstance(), 450, ECoreProfile,
/*forceDefaultVersionAndProfile: */false,
/*forwardCompatible: */true, EShMsgDefault)) {
payload->log += "Error: [glslang] Cannot compile " + ConvertGLEnumToString(shaderType) + ":\n"
+ std::to_string(tshader->getInfoLog());
payload->errc = -2;
return;
}
(*next)();
glslang::FinalizeProcess();
})
.Register([this](auto payload, auto next) {
UniformTraverser uniformTraverser(payload->uniforms);
auto root = payload->TShader->getIntermediate()->getTreeRoot();
root->traverse(&uniformTraverser);
(*next)();
})
;
}
void MobileGL::MG_Util::Pipeline::GLSLtoSPIRVPipeline::Invoke(SharedPtr<GLSLtoSPIRVPayload> payload) {
pipeline_.Process(Move(payload));
}
@@ -2,15 +2,201 @@
// Created by Swung 0x48 on 2025-07-12.
//
#ifndef FOLD_CRAFT_LAUNCHER_GLSLTOSPIRVPIPELINE_H
#define FOLD_CRAFT_LAUNCHER_GLSLTOSPIRVPIPELINE_H
#ifndef MG_GLSLTOSPIRVPIPELINE_H
#define MG_GLSLTOSPIRVPIPELINE_H
#include "../../../Includes.h"
namespace MobileGL {
namespace MG_Util {
namespace Pipeline {
struct TUniform {
String name;
bool isSampler = false;
// For isSampler == false
glslang::TStorageQualifier storageQualifier;
Uint layoutLocation = 0;
Uint layoutBinding = 0;
glslang::TLayoutPacking layoutPacking;
// For isSampler == true
glslang::TSampler sampler;
};
struct GLSLtoSPIRVPayload {
GLenum shaderType;
String sourceStr;
UniquePtr<glslang::TShader> TShader;
Vector<TUniform> uniforms;
Int errc = 0;
String log;
};
class UniformTraverser : public glslang::TIntermTraverser {
public:
UniformTraverser(Vector<TUniform>& u): uniforms(u) {}
void visitSymbol(glslang::TIntermSymbol* symbol) override {
const auto& type = symbol->getType();
if (symbol->getQualifier().isUniform()) {
auto name = symbol->getName();
auto qualifier = symbol->getQualifier();
auto& uniform = uniforms.emplace_back();
uniform.isSampler = false;
uniform.name = name;
uniform.storageQualifier = qualifier.storage;
uniform.layoutLocation = qualifier.layoutLocation;
uniform.layoutBinding = qualifier.layoutBinding;
uniform.layoutPacking = qualifier.layoutPacking;
} else if (type.getBasicType() == glslang::EbtSampler) {
auto& uniform = uniforms.emplace_back();
uniform.name = symbol->getName();
uniform.isSampler = true;
uniform.sampler = type.getSampler();
}
}
Vector<TUniform>& uniforms;
};
class GLSLtoSPIRVPipeline {
explicit GLSLtoSPIRVPipeline(std::function<void(GLSLtoSPIRVPayload &)> callback);
void Invoke(SharedPtr<GLSLtoSPIRVPayload> payload);
inline static EShLanguage GetEShLanguageByShaderType(GLenum shaderType) {
switch (shaderType) {
case GL_VERTEX_SHADER:
return EShLanguage::EShLangVertex;
case GL_FRAGMENT_SHADER:
return EShLanguage::EShLangFragment;
case GL_COMPUTE_SHADER:
return EShLanguage::EShLangCompute;
case GL_TESS_CONTROL_SHADER:
return EShLanguage::EShLangTessControl;
case GL_TESS_EVALUATION_SHADER:
return EShLanguage::EShLangTessEvaluation;
case GL_GEOMETRY_SHADER:
return EShLanguage::EShLangGeometry;
default:
return EShLanguage::EShLangCount;
}
}
static inline TBuiltInResource& GetTBuiltInResourceInstance() {
static TBuiltInResource Resources{};
Resources.maxLights = 32;
Resources.maxClipPlanes = 6;
Resources.maxTextureUnits = 32;
Resources.maxTextureCoords = 32;
Resources.maxVertexAttribs = 64;
Resources.maxVertexUniformComponents = 4096;
Resources.maxVaryingFloats = 64;
Resources.maxVertexTextureImageUnits = 32;
Resources.maxCombinedTextureImageUnits = 80;
Resources.maxTextureImageUnits = 32;
Resources.maxFragmentUniformComponents = 4096;
Resources.maxDrawBuffers = 32;
Resources.maxVertexUniformVectors = 128;
Resources.maxVaryingVectors = 8;
Resources.maxFragmentUniformVectors = 16;
Resources.maxVertexOutputVectors = 16;
Resources.maxFragmentInputVectors = 15;
Resources.minProgramTexelOffset = -8;
Resources.maxProgramTexelOffset = 7;
Resources.maxClipDistances = 8;
Resources.maxComputeWorkGroupCountX = 65535;
Resources.maxComputeWorkGroupCountY = 65535;
Resources.maxComputeWorkGroupCountZ = 65535;
Resources.maxComputeWorkGroupSizeX = 1024;
Resources.maxComputeWorkGroupSizeY = 1024;
Resources.maxComputeWorkGroupSizeZ = 64;
Resources.maxComputeUniformComponents = 1024;
Resources.maxComputeTextureImageUnits = 16;
Resources.maxComputeImageUniforms = 8;
Resources.maxComputeAtomicCounters = 8;
Resources.maxComputeAtomicCounterBuffers = 1;
Resources.maxVaryingComponents = 60;
Resources.maxVertexOutputComponents = 64;
Resources.maxGeometryInputComponents = 64;
Resources.maxGeometryOutputComponents = 128;
Resources.maxFragmentInputComponents = 128;
Resources.maxImageUnits = 8;
Resources.maxCombinedImageUnitsAndFragmentOutputs = 8;
Resources.maxCombinedShaderOutputResources = 8;
Resources.maxImageSamples = 0;
Resources.maxVertexImageUniforms = 0;
Resources.maxTessControlImageUniforms = 0;
Resources.maxTessEvaluationImageUniforms = 0;
Resources.maxGeometryImageUniforms = 0;
Resources.maxFragmentImageUniforms = 8;
Resources.maxCombinedImageUniforms = 8;
Resources.maxGeometryTextureImageUnits = 16;
Resources.maxGeometryOutputVertices = 256;
Resources.maxGeometryTotalOutputComponents = 1024;
Resources.maxGeometryUniformComponents = 1024;
Resources.maxGeometryVaryingComponents = 64;
Resources.maxTessControlInputComponents = 128;
Resources.maxTessControlOutputComponents = 128;
Resources.maxTessControlTextureImageUnits = 16;
Resources.maxTessControlUniformComponents = 1024;
Resources.maxTessControlTotalOutputComponents = 4096;
Resources.maxTessEvaluationInputComponents = 128;
Resources.maxTessEvaluationOutputComponents = 128;
Resources.maxTessEvaluationTextureImageUnits = 16;
Resources.maxTessEvaluationUniformComponents = 1024;
Resources.maxTessPatchComponents = 120;
Resources.maxPatchVertices = 32;
Resources.maxTessGenLevel = 64;
Resources.maxViewports = 16;
Resources.maxVertexAtomicCounters = 0;
Resources.maxTessControlAtomicCounters = 0;
Resources.maxTessEvaluationAtomicCounters = 0;
Resources.maxGeometryAtomicCounters = 0;
Resources.maxFragmentAtomicCounters = 8;
Resources.maxCombinedAtomicCounters = 8;
Resources.maxAtomicCounterBindings = 1;
Resources.maxVertexAtomicCounterBuffers = 0;
Resources.maxTessControlAtomicCounterBuffers = 0;
Resources.maxTessEvaluationAtomicCounterBuffers = 0;
Resources.maxGeometryAtomicCounterBuffers = 0;
Resources.maxFragmentAtomicCounterBuffers = 1;
Resources.maxCombinedAtomicCounterBuffers = 1;
Resources.maxAtomicCounterBufferSize = 16384;
Resources.maxTransformFeedbackBuffers = 4;
Resources.maxTransformFeedbackInterleavedComponents = 64;
Resources.maxCullDistances = 8;
Resources.maxCombinedClipAndCullDistances = 8;
Resources.maxSamples = 4;
Resources.maxMeshOutputVerticesNV = 256;
Resources.maxMeshOutputPrimitivesNV = 512;
Resources.maxMeshWorkGroupSizeX_NV = 32;
Resources.maxMeshWorkGroupSizeY_NV = 1;
Resources.maxMeshWorkGroupSizeZ_NV = 1;
Resources.maxTaskWorkGroupSizeX_NV = 32;
Resources.maxTaskWorkGroupSizeY_NV = 1;
Resources.maxTaskWorkGroupSizeZ_NV = 1;
Resources.maxMeshViewCountNV = 4;
Resources.limits.nonInductiveForLoops = true;
Resources.limits.whileLoops = true;
Resources.limits.doWhileLoops = true;
Resources.limits.generalUniformIndexing = true;
Resources.limits.generalAttributeMatrixVectorIndexing = true;
Resources.limits.generalVaryingIndexing = true;
Resources.limits.generalSamplerIndexing = true;
Resources.limits.generalVariableIndexing = true;
Resources.limits.generalConstantMatrixVectorIndexing = true;
return Resources;
}
private:
Pipeline<GLSLtoSPIRVPayload> pipeline_;
};
}
}
}
class GLSLtoSPIRVPipeline {
};
#endif //FOLD_CRAFT_LAUNCHER_GLSLTOSPIRVPIPELINE_H
#endif //MG_GLSLTOSPIRVPIPELINE_H
+2
View File
@@ -47,6 +47,8 @@ namespace MobileGL {
using NulloptT = std::nullopt_t;
const NulloptT Nullopt = std::nullopt;
using std::format;
using Data = Vector<Uint8>;
struct DataPtr {
void* data;