Relax storage restrictions on vertex/pixel shaders.

This commit is contained in:
Erin Maus
2026-07-22 13:54:59 -04:00
parent 8c18d3ce8e
commit 7d09352e94
7 changed files with 106 additions and 17 deletions
+1 -1
View File
@@ -566,7 +566,7 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagess
} }
} }
return Shader::validate(stages, err); return Shader::validate(stages, err, options);
} }
Texture *Graphics::getDefaultTexture(TextureType type, DataBaseType dataType, bool depthSample) Texture *Graphics::getDefaultTexture(TextureType type, DataBaseType dataType, bool depthSample)
+14 -8
View File
@@ -650,7 +650,7 @@ Shader::Shader(StrongRef<ShaderStage> _stages[], const CompileOptions &options)
, debugName(options.debugName) , debugName(options.debugName)
{ {
std::string err; std::string err;
if (!validateInternal(_stages, err, reflection)) if (!validateInternal(_stages, err, reflection, options))
throw love::Exception("%s", err.c_str()); throw love::Exception("%s", err.c_str());
std::vector<std::string> unsetVertexInputLocations; std::vector<std::string> unsetVertexInputLocations;
@@ -1029,10 +1029,10 @@ bool Shader::isUsingDeprecatedTextureUniform() const
return it != reflection.allUniforms.end() && it->second->stageMask != 0; return it != reflection.allUniforms.end() && it->second->stageMask != 0;
} }
bool Shader::validate(StrongRef<ShaderStage> stages[], std::string& err) bool Shader::validate(StrongRef<ShaderStage> stages[], std::string& err, const CompileOptions &options)
{ {
Reflection reflection; Reflection reflection;
return validateInternal(stages, err, reflection); return validateInternal(stages, err, reflection, options);
} }
static DataBaseType getBaseType(glslang::TBasicType basictype) static DataBaseType getBaseType(glslang::TBasicType basictype)
@@ -1246,7 +1246,7 @@ static bool AddFieldsToFormat(std::vector<Buffer::DataDeclaration> &format, int
return true; return true;
} }
bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err, Reflection &reflection) bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err, Reflection &reflection, const CompileOptions &options)
{ {
glslang::TProgram program; glslang::TProgram program;
@@ -1349,9 +1349,9 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
} }
else if (type->isImage()) else if (type->isImage())
{ {
if ((info.stages & (~EShLangComputeMask)) != 0) if ((info.stages & (~EShLangComputeMask)) != 0 && !options.features[FEATURE_STORAGE_TEXTURES])
{ {
err = "Shader validation error:\nStorage Texture uniform variables (image2D, etc) are only allowed in compute shaders."; err = "Shader validation error:\nStorage Texture uniform variables (image2D, etc) are only allowed in compute shaders unless explicitly enabled.";
return false; return false;
} }
@@ -1467,9 +1467,9 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
{ {
const glslang::TQualifier &qualifiers = type->getQualifier(); const glslang::TQualifier &qualifiers = type->getQualifier();
if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && ((info.stages & (~EShLangComputeMask)) != 0)) if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && ((info.stages & (~EShLangComputeMask)) != 0) && !options.features[FEATURE_WRITABLE_BUFFERS])
{ {
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must be marked as readonly in vertex and pixel shaders."; err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must be marked as readonly in vertex and pixel shaders unless explicitly enabled.";
return false; return false;
} }
@@ -1872,5 +1872,11 @@ bool Shader::getConstant(BuiltinUniform in, const char *&out)
return builtinNames.find(in, out); return builtinNames.find(in, out);
} }
STRINGMAP_CLASS_BEGIN(Shader, Shader::Feature, Shader::FEATURE_MAX_ENUM, feature)
{
{ "write", Shader::FEATURE_WRITE },
}
STRINGMAP_CLASS_END(Shader, Shader::Feature, Shader::FEATURE_MAX_ENUM, feature)
} // graphics } // graphics
} // love } // love
+11 -2
View File
@@ -56,6 +56,12 @@ public:
LANGUAGE_MAX_ENUM LANGUAGE_MAX_ENUM
}; };
enum Feature
{
FEATURE_WRITE,
FEATURE_MAX_ENUM
};
// Built-in uniform variables. // Built-in uniform variables.
enum BuiltinUniform enum BuiltinUniform
{ {
@@ -119,6 +125,7 @@ public:
{ {
std::map<std::string, std::string> defines; std::map<std::string, std::string> defines;
std::string debugName; std::string debugName;
bool features[FEATURE_MAX_ENUM] = {};
}; };
struct SourceInfo struct SourceInfo
@@ -275,7 +282,7 @@ public:
static SourceInfo getSourceInfo(const std::string &src); static SourceInfo getSourceInfo(const std::string &src);
static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const CompileOptions &options, const SourceInfo &info, bool gles, bool checksystemfeatures); static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const CompileOptions &options, const SourceInfo &info, bool gles, bool checksystemfeatures);
static bool validate(StrongRef<ShaderStage> stages[], std::string &err); static bool validate(StrongRef<ShaderStage> stages[], std::string &err, const CompileOptions &options);
static bool initialize(); static bool initialize();
static void deinitialize(); static void deinitialize();
@@ -288,6 +295,8 @@ public:
static bool getConstant(const char *in, BuiltinUniform &out); static bool getConstant(const char *in, BuiltinUniform &out);
static bool getConstant(BuiltinUniform in, const char *&out); static bool getConstant(BuiltinUniform in, const char *&out);
STRINGMAP_CLASS_DECLARE(Feature);
protected: protected:
struct Reflection struct Reflection
@@ -330,7 +339,7 @@ protected:
static std::string canonicaliizeUniformName(const std::string &name); static std::string canonicaliizeUniformName(const std::string &name);
static size_t getUniformDataSizePacked(const UniformInfo &u); static size_t getUniformDataSizePacked(const UniformInfo &u);
static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, Reflection &reflection); static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, Reflection &reflection, const CompileOptions &options);
static DataBaseType getDataBaseType(PixelFormat format); static DataBaseType getDataBaseType(PixelFormat format);
static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b); static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b);
+42 -3
View File
@@ -444,7 +444,7 @@ void Graphics::setActive(bool enable)
active = enable; active = enable;
} }
static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarriers, GLbitfield &postDispatchBarriers) static bool shaderBarriers(Shader *shader, GLbitfield &preDispatchBarriers, GLbitfield &postDispatchBarriers)
{ {
for (auto buffer : shader->getActiveWritableStorageBuffers()) for (auto buffer : shader->getActiveWritableStorageBuffers())
{ {
@@ -505,7 +505,7 @@ bool Graphics::dispatch(love::graphics::Shader *s, int x, int y, int z)
GLbitfield preDispatchBarriers = 0; GLbitfield preDispatchBarriers = 0;
GLbitfield postDispatchBarriers = 0; GLbitfield postDispatchBarriers = 0;
if (!computeDispatchBarriers(shader, preDispatchBarriers, postDispatchBarriers)) if (!shaderBarriers(shader, preDispatchBarriers, postDispatchBarriers))
return false; return false;
// glMemoryBarrier before dispatch to make sure non-compute-read -> // glMemoryBarrier before dispatch to make sure non-compute-read ->
@@ -534,7 +534,7 @@ bool Graphics::dispatch(love::graphics::Shader *s, love::graphics::Buffer *indir
GLbitfield preDispatchBarriers = 0; GLbitfield preDispatchBarriers = 0;
GLbitfield postDispatchBarriers = 0; GLbitfield postDispatchBarriers = 0;
if (!computeDispatchBarriers(shader, preDispatchBarriers, postDispatchBarriers)) if (!shaderBarriers(shader, preDispatchBarriers, postDispatchBarriers))
return false; return false;
if (preDispatchBarriers != 0) if (preDispatchBarriers != 0)
@@ -559,6 +559,13 @@ void Graphics::draw(const DrawCommand &cmd)
VertexAttributes attributes; VertexAttributes attributes;
findVertexAttributes(cmd.attributesID, attributes); findVertexAttributes(cmd.attributesID, attributes);
GLbitfield preDrawBarriers = 0;
GLbitfield postDrawBarriers = 0;
shaderBarriers((Shader *)Shader::current, preDrawBarriers, postDrawBarriers);
if (preDrawBarriers != 0)
glMemoryBarrier(preDrawBarriers);
gl.prepareDraw(this); gl.prepareDraw(this);
gl.setVertexAttributes(attributes, *cmd.buffers); gl.setVertexAttributes(attributes, *cmd.buffers);
gl.bindTextureToUnit(cmd.texture, 0, false); gl.bindTextureToUnit(cmd.texture, 0, false);
@@ -576,6 +583,9 @@ void Graphics::draw(const DrawCommand &cmd)
else else
glDrawArrays(glprimitivetype, cmd.vertexStart, cmd.vertexCount); glDrawArrays(glprimitivetype, cmd.vertexStart, cmd.vertexCount);
if (postDrawBarriers != 0)
glMemoryBarrier(postDrawBarriers);
++drawCalls; ++drawCalls;
} }
@@ -584,6 +594,13 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
VertexAttributes attributes; VertexAttributes attributes;
findVertexAttributes(cmd.attributesID, attributes); findVertexAttributes(cmd.attributesID, attributes);
GLbitfield preDrawBarriers = 0;
GLbitfield postDrawBarriers = 0;
shaderBarriers((Shader *)Shader::current, preDrawBarriers, postDrawBarriers);
if (preDrawBarriers != 0)
glMemoryBarrier(preDrawBarriers);
gl.prepareDraw(this); gl.prepareDraw(this);
gl.setVertexAttributes(attributes, *cmd.buffers); gl.setVertexAttributes(attributes, *cmd.buffers);
gl.bindTextureToUnit(cmd.texture, 0, false); gl.bindTextureToUnit(cmd.texture, 0, false);
@@ -606,6 +623,9 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount); glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount);
else else
glDrawElements(glprimitivetype, cmd.indexCount, gldatatype, gloffset); glDrawElements(glprimitivetype, cmd.indexCount, gldatatype, gloffset);
if (postDrawBarriers != 0)
glMemoryBarrier(postDrawBarriers);
++drawCalls; ++drawCalls;
} }
@@ -638,6 +658,11 @@ void Graphics::drawQuads(int start, int count, VertexAttributesID attributesID,
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX; const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4; const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
GLbitfield preDrawBarriers = 0;
GLbitfield postDrawBarriers = 0;
shaderBarriers((Shader *)Shader::current, preDrawBarriers, postDrawBarriers);
VertexAttributes attributes; VertexAttributes attributes;
findVertexAttributes(attributesID, attributes); findVertexAttributes(attributesID, attributes);
@@ -655,9 +680,16 @@ void Graphics::drawQuads(int start, int count, VertexAttributesID attributesID,
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
{ {
if (preDrawBarriers != 0)
glMemoryBarrier(preDrawBarriers);
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex); int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
glDrawElementsBaseVertex(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0), basevertex); glDrawElementsBaseVertex(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0), basevertex);
if (postDrawBarriers != 0)
glMemoryBarrier(postDrawBarriers);
++drawCalls; ++drawCalls;
basevertex += quadcount * 4; basevertex += quadcount * 4;
@@ -671,11 +703,18 @@ void Graphics::drawQuads(int start, int count, VertexAttributesID attributesID,
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
{ {
if (preDrawBarriers != 0)
glMemoryBarrier(preDrawBarriers);
gl.setVertexAttributes(attributes, bufferscopy); gl.setVertexAttributes(attributes, bufferscopy);
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex); int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
glDrawElements(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0)); glDrawElements(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
if (postDrawBarriers != 0)
glMemoryBarrier(postDrawBarriers);
++drawCalls; ++drawCalls;
if (count > MAX_QUADS_PER_DRAW) if (count > MAX_QUADS_PER_DRAW)
+23 -3
View File
@@ -972,6 +972,7 @@ void Graphics::draw(const DrawCommand &cmd)
0); 0);
} }
endDraw();
drawCalls++; drawCalls++;
} }
@@ -1005,6 +1006,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
0); 0);
} }
endDraw();
drawCalls++; drawCalls++;
} }
@@ -1036,6 +1038,7 @@ void Graphics::drawQuads(int start, int count, VertexAttributesID attributesID,
0); 0);
baseVertex += quadcount * 4; baseVertex += quadcount * 4;
endDraw();
drawCalls++; drawCalls++;
} }
} }
@@ -1281,7 +1284,7 @@ graphics::StreamBuffer *Graphics::newStreamBuffer(BufferUsage type, size_t size)
return new StreamBuffer(this, type, size); return new StreamBuffer(this, type, size);
} }
static bool computeDispatchBarrierFlags(Shader *shader, VkAccessFlags &dstAccessFlags, VkPipelineStageFlags &dstStageFlags) static bool shaderBarrierFlags(Shader *shader, VkAccessFlags &dstAccessFlags, VkPipelineStageFlags &dstStageFlags)
{ {
for (const auto &info : shader->getActiveTextureInfo()) for (const auto &info : shader->getActiveTextureInfo())
{ {
@@ -1323,7 +1326,7 @@ bool Graphics::dispatch(love::graphics::Shader *shader, int x, int y, int z)
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
VkPipelineStageFlags dstStageMask = 0; VkPipelineStageFlags dstStageMask = 0;
if (!computeDispatchBarrierFlags(computeShader, barrier.dstAccessMask, dstStageMask)) if (!shaderBarrierFlags(computeShader, barrier.dstAccessMask, dstStageMask))
return false; return false;
usedShadersInFrame.insert(computeShader); usedShadersInFrame.insert(computeShader);
@@ -1352,7 +1355,7 @@ bool Graphics::dispatch(love::graphics::Shader *shader, love::graphics::Buffer *
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
VkPipelineStageFlags dstStageMask = 0; VkPipelineStageFlags dstStageMask = 0;
if (!computeDispatchBarrierFlags(computeShader, barrier.dstAccessMask, dstStageMask)) if (!shaderBarrierFlags(computeShader, barrier.dstAccessMask, dstStageMask))
return false; return false;
usedShadersInFrame.insert(computeShader); usedShadersInFrame.insert(computeShader);
@@ -2778,6 +2781,23 @@ void Graphics::prepareDraw(VertexAttributesID attributesID, const BufferBindings
vkCmdBindVertexBuffers(commandBuffers.at(currentFrame), VERTEX_BUFFER_BINDING_START, buffercount, vkbuffers, vkoffsets); vkCmdBindVertexBuffers(commandBuffers.at(currentFrame), VERTEX_BUFFER_BINDING_START, buffercount, vkbuffers, vkoffsets);
} }
void Graphics::endDraw()
{
auto shader = dynamic_cast<Shader *>(Shader::current);
if (!shader)
return;
VkMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
VkPipelineStageFlags dstStageMask = 0;
if (!shaderBarrierFlags(shader, barrier.dstAccessMask, dstStageMask))
return;
if (barrier.dstAccessMask != 0 || dstStageMask != 0)
vkCmdPipelineBarrier(commandBuffers.at(currentFrame), VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dstStageMask, 0, 1, &barrier, 0, nullptr, 0, nullptr);
}
void Graphics::setDefaultRenderPass() void Graphics::setDefaultRenderPass()
{ {
uint32_t numClearValues = 2; uint32_t numClearValues = 2;
+1
View File
@@ -379,6 +379,7 @@ private:
void applyScissor(); void applyScissor();
VkSampler createSampler(const SamplerState &sampler); VkSampler createSampler(const SamplerState &sampler);
void requestSwapchainRecreation(); void requestSwapchainRecreation();
void endDraw();
VkInstance instance = VK_NULL_HANDLE; VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
+14
View File
@@ -1586,6 +1586,20 @@ static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string
if (!lua_isnoneornil(L, -1)) if (!lua_isnoneornil(L, -1))
options.debugName = luax_checkstring(L, -1); options.debugName = luax_checkstring(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
for (int feature = 0; feature < Shader::FEATURE_MAX_ENUM; ++feature)
{
const char *featureKey;
if (Shader::getConstant((Shader::Feature)feature, featureKey))
{
lua_getfield(L, optionsidx, featureKey);
if (!lua_isnoneornil(L, -1))
{
options.features[feature] = lua_toboolean(L, -1);
}
lua_pop(L, 1);
}
}
} }
return 0; return 0;