diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 25154fa45..2bb03affe 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -291,7 +291,7 @@ Shader *Graphics::newShader(const std::vector &stagessource) } - return newShaderInternal(stages[SHADERSTAGE_VERTEX], stages[SHADERSTAGE_PIXEL]); + return newShaderInternal(stages); } Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength) @@ -332,6 +332,7 @@ bool Graphics::validateShader(bool gles, const std::vector &stagess bool validstages[SHADERSTAGE_MAX_ENUM] = {}; validstages[SHADERSTAGE_VERTEX] = true; validstages[SHADERSTAGE_PIXEL] = true; + validstages[SHADERSTAGE_COMPUTE] = true; // Don't use cached shader stages, since the gles flag may not match the // current renderer. @@ -362,7 +363,7 @@ bool Graphics::validateShader(bool gles, const std::vector &stagess } } - return Shader::validate(stages[SHADERSTAGE_VERTEX], stages[SHADERSTAGE_PIXEL], err); + return Shader::validate(stages, err); } int Graphics::getWidth() const diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index f202d69d3..e7f3d5cdb 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -932,7 +932,7 @@ protected: ShaderStage *newShaderStage(ShaderStageType stage, const std::string &source, const Shader::SourceInfo &info); virtual ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0; - virtual Shader *newShaderInternal(ShaderStage *vertex, ShaderStage *pixel) = 0; + virtual Shader *newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) = 0; virtual StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) = 0; virtual void setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBtexture) = 0; diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 7bc8fae58..2a89fd8c8 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -389,6 +389,24 @@ void main() { } )"; +static const char compute_header[] = R"( +#define love_NumWorkGroups gl_NumWorkGroups +#define love_WorkGroupID gl_WorkGroupID +#define love_LocalInvocationID gl_LocalInvocationID +#define love_GlobalInvocationID gl_GlobalInvocationID +#define love_LocalInvocationIndex gl_LocalInvocationIndex +)"; + +static const char compute_functions[] = R"()"; + +static const char compute_main[] = R"( +void computemain(); + +void main() { + computemain(); +} +)"; + struct StageInfo { const char *name; @@ -403,6 +421,7 @@ static const StageInfo stageInfo[] = { { "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main, vertex_main_raw }, { "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom, pixel_main_raw }, + { "COMPUTE", compute_header, compute_functions, compute_main, compute_main, compute_main }, }; static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == SHADERSTAGE_MAX_ENUM, "Stages array size must match ShaderStage enum."); @@ -465,6 +484,15 @@ static Shader::EntryPoint getPixelEntryPoint(const std::string &src, bool &mrt) return Shader::ENTRYPOINT_NONE; } +static Shader::EntryPoint getComputeEntryPoint(const std::string &src) { + std::smatch m; + + if (std::regex_search(src, m, std::regex("void\\s+computemain\\s*\\("))) + return Shader::ENTRYPOINT_RAW; + + return Shader::ENTRYPOINT_NONE; +} + } // glsl static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes."); @@ -480,6 +508,9 @@ Shader::SourceInfo Shader::getSourceInfo(const std::string &src) info.language = glsl::getTargetLanguage(src); info.stages[SHADERSTAGE_VERTEX] = glsl::getVertexEntryPoint(src); info.stages[SHADERSTAGE_PIXEL] = glsl::getPixelEntryPoint(src, info.usesMRT); + info.stages[SHADERSTAGE_COMPUTE] = glsl::getComputeEntryPoint(src); + if (info.stages[SHADERSTAGE_COMPUTE]) + info.language = LANGUAGE_GLSL4; return info; } @@ -494,6 +525,9 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, if (info.stages[stage] == ENTRYPOINT_RAW && info.language == LANGUAGE_GLSL1) throw love::Exception("Shaders using a raw entry point (vertexmain or pixelmain) must use GLSL 3 or greater."); + if (stage == SHADERSTAGE_COMPUTE && info.language != LANGUAGE_GLSL4) + throw love::Exception("Compute shaders must use GLSL 4."); + const auto &features = gfx->getCapabilities().features; if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3]) @@ -541,15 +575,15 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, return ss.str(); } -Shader::Shader(ShaderStage *vertex, ShaderStage *pixel) +Shader::Shader(StrongRef _stages[]) : stages() { std::string err; - if (!validateInternal(vertex, pixel, err, validationReflection)) + if (!validateInternal(_stages, err, validationReflection)) throw love::Exception("%s", err.c_str()); - stages[SHADERSTAGE_VERTEX] = vertex; - stages[SHADERSTAGE_PIXEL] = pixel; + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) + stages[i] = _stages[i]; } Shader::~Shader() @@ -641,21 +675,21 @@ void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const } } -bool Shader::validate(ShaderStage* vertex, ShaderStage* pixel, std::string& err) +bool Shader::validate(StrongRef stages[], std::string& err) { ValidationReflection reflection; - return validateInternal(vertex, pixel, err, reflection); + return validateInternal(stages, err, reflection); } -bool Shader::validateInternal(ShaderStage *vertex, ShaderStage *pixel, std::string &err, ValidationReflection &reflection) +bool Shader::validateInternal(StrongRef stages[], std::string &err, ValidationReflection &reflection) { glslang::TProgram program; - if (vertex != nullptr) - program.addShader(vertex->getGLSLangShader()); - - if (pixel != nullptr) - program.addShader(pixel->getGLSLangShader()); + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) + { + if (stages[i] != nullptr) + program.addShader(stages[i]->getGLSLangShader()); + } if (!program.link(EShMsgDefault)) { diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index abc24a832..85137a57d 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -164,7 +164,7 @@ public: // Pointer to the default Shader. static Shader *standardShaders[STANDARD_MAX_ENUM]; - Shader(ShaderStage *vertex, ShaderStage *pixel); + Shader(StrongRef stages[]); virtual ~Shader(); /** @@ -219,7 +219,7 @@ public: static SourceInfo getSourceInfo(const std::string &src); static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const SourceInfo &info); - static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err); + static bool validate(StrongRef stages[], std::string &err); static bool initialize(); static void deinitialize(); @@ -246,7 +246,7 @@ protected: bool usesPointSize; }; - static bool validateInternal(ShaderStage* vertex, ShaderStage* pixel, std::string& err, ValidationReflection &reflection); + static bool validateInternal(StrongRef stages[], std::string& err, ValidationReflection &reflection); StrongRef stages[SHADERSTAGE_MAX_ENUM]; diff --git a/src/modules/graphics/ShaderStage.cpp b/src/modules/graphics/ShaderStage.cpp index a539860b5..d3efb747a 100644 --- a/src/modules/graphics/ShaderStage.cpp +++ b/src/modules/graphics/ShaderStage.cpp @@ -148,6 +148,8 @@ ShaderStage::ShaderStage(Graphics *gfx, ShaderStageType stage, const std::string glslangStage = EShLangVertex; else if (stage == SHADERSTAGE_PIXEL) glslangStage = EShLangFragment; + else if (stage == SHADERSTAGE_COMPUTE) + glslangStage = EShLangCompute; else throw love::Exception("Cannot compile shader stage: unknown stage type."); @@ -212,8 +214,9 @@ const char *ShaderStage::getConstant(ShaderStageType in) StringMap::Entry ShaderStage::stageNameEntries[] = { - { "vertex", SHADERSTAGE_VERTEX }, - { "pixel", SHADERSTAGE_PIXEL }, + { "vertex", SHADERSTAGE_VERTEX }, + { "pixel", SHADERSTAGE_PIXEL }, + { "compute", SHADERSTAGE_COMPUTE }, }; StringMap ShaderStage::stageNames(ShaderStage::stageNameEntries, sizeof(ShaderStage::stageNameEntries)); diff --git a/src/modules/graphics/ShaderStage.h b/src/modules/graphics/ShaderStage.h index 11fd79765..1193c71e0 100644 --- a/src/modules/graphics/ShaderStage.h +++ b/src/modules/graphics/ShaderStage.h @@ -45,6 +45,7 @@ enum ShaderStageType { SHADERSTAGE_VERTEX, SHADERSTAGE_PIXEL, + SHADERSTAGE_COMPUTE, SHADERSTAGE_MAX_ENUM }; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 1779d13e3..2cd180626 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -155,9 +155,9 @@ love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStageType st return new ShaderStage(this, stage, source, gles, cachekey); } -love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) +love::graphics::Shader *Graphics::newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) { - return new Shader(vertex, pixel); + return new Shader(stages); } love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 44d2a1c1d..9ec95d3ac 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -137,7 +137,7 @@ private: }; love::graphics::ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) override; - love::graphics::Shader *newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) override; + love::graphics::Shader *newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override; love::graphics::StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) override; void setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBtexture) override; void initCapabilities() override; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 045e6254f..e16d30431 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -42,8 +42,8 @@ static bool isBuffer(Shader::UniformType utype) return utype == Shader::UNIFORM_TEXELBUFFER || utype == Shader::UNIFORM_STORAGEBUFFER; } -Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) - : love::graphics::Shader(vertex, pixel) +Shader::Shader(StrongRef stages[SHADERSTAGE_MAX_ENUM]) + : love::graphics::Shader(stages) , program(0) , builtinUniforms() , builtinUniformInfo() diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index 8c0ee538c..dbce1ddd4 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -43,7 +43,7 @@ class Shader final : public love::graphics::Shader, public Volatile { public: - Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel); + Shader(StrongRef stages[SHADERSTAGE_MAX_ENUM]); virtual ~Shader(); // Implements Volatile diff --git a/src/modules/graphics/opengl/ShaderStage.cpp b/src/modules/graphics/opengl/ShaderStage.cpp index ca3439229..9a280207f 100644 --- a/src/modules/graphics/opengl/ShaderStage.cpp +++ b/src/modules/graphics/opengl/ShaderStage.cpp @@ -53,6 +53,8 @@ bool ShaderStage::loadVolatile() glstage = GL_VERTEX_SHADER; else if (stage == SHADERSTAGE_PIXEL) glstage = GL_FRAGMENT_SHADER; + else if (stage == SHADERSTAGE_COMPUTE) + glstage = GL_COMPUTE_SHADER; else throw love::Exception("%s shader stage is not handled in OpenGL backend code.", typestr);