Skip to content

Commit

Permalink
move common shader:send texture/buffer code to platform-agnostic layer.
Browse files Browse the repository at this point in the history
Fixes issues in the Metal backend when a shader using a readonly buffer is created.
  • Loading branch information
slime73 committed Sep 20, 2024
1 parent 0a552ef commit 876625d
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 413 deletions.
129 changes: 123 additions & 6 deletions src/modules/graphics/Shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,125 @@ bool Shader::hasUniform(const std::string &name) const
return it != reflection.allUniforms.end() && it->second->active;
}

void Shader::setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture)
{
const BuiltinUniform builtins[3] = {
BUILTIN_TEXTURE_VIDEO_Y,
BUILTIN_TEXTURE_VIDEO_CB,
BUILTIN_TEXTURE_VIDEO_CR,
};

love::graphics::Texture *textures[3] = {ytexture, cbtexture, crtexture};

for (int i = 0; i < 3; i++)
{
const UniformInfo *info = getUniformInfo(builtins[i]);
if (info != nullptr)
sendTextures(info, &textures[i], 1, true);
}
}

void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count)
{
Shader::sendTextures(info, textures, count, false);
}

void Shader::sendBuffers(const UniformInfo *info, Buffer **buffers, int count)
{
Shader::sendBuffers(info, buffers, count, false);
}

void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate)
{
UniformType basetype = info->baseType;

if (basetype != UNIFORM_SAMPLER && basetype != UNIFORM_STORAGETEXTURE)
return;

if (!internalUpdate && current == this)
flushBatchedDraws();

count = std::min(count, info->count);

for (int i = 0; i < count; i++)
{
love::graphics::Texture *tex = textures[i];
bool isdefault = tex == nullptr;

if (tex != nullptr)
{
if (!validateTexture(info, tex, internalUpdate))
continue;
}
else
{
auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
tex = gfx->getDefaultTexture(info->textureType, info->dataBaseType, info->isDepthSampler);
}

tex->retain();

int resourceindex = info->resourceIndex + i;

if (activeTextures[resourceindex] != nullptr)
activeTextures[resourceindex]->release();

activeTextures[resourceindex] = tex;

applyTexture(info, i, tex, basetype, isdefault);
}
}

void Shader::sendBuffers(const UniformInfo *info, Buffer **buffers, int count, bool internalUpdate)
{
UniformType basetype = info->baseType;

if (basetype != UNIFORM_TEXELBUFFER && basetype != UNIFORM_STORAGEBUFFER)
return;

if (!internalUpdate && current == this)
flushBatchedDraws();

count = std::min(count, info->count);

for (int i = 0; i < count; i++)
{
love::graphics::Buffer *buffer = buffers[i];
bool isdefault = buffer == nullptr;

if (buffer != nullptr)
{
if (!validateBuffer(info, buffer, internalUpdate))
continue;
}
else
{
auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
if (basetype == UNIFORM_TEXELBUFFER)
buffer = gfx->getDefaultTexelBuffer(info->dataBaseType);
else
buffer = gfx->getDefaultStorageBuffer();
}

buffer->retain();

int resourceindex = info->resourceIndex + i;

if (activeBuffers[resourceindex] != nullptr)
activeBuffers[resourceindex]->release();

activeBuffers[resourceindex] = buffer;

applyBuffer(info, i, buffer, basetype, isdefault);
}
}

void Shader::flushBatchedDraws() const
{
if (current == this)
Graphics::flushBatchedDrawsGlobal();
}

const Shader::UniformInfo *Shader::getMainTextureInfo() const
{
return getUniformInfo(BUILTIN_TEXTURE_MAIN);
Expand Down Expand Up @@ -1532,17 +1651,15 @@ bool Shader::validateBuffer(const UniformInfo *info, Buffer *buffer, bool intern
{
if (info->bufferStride != buffer->getArrayStride())
{
if (internalUpdate)
return false;
else
// Don't prevent this from working for internally bound default resources.
if (!internalUpdate)
throw love::Exception("Shader storage block '%s' has an array stride of %d bytes, but the given Buffer has an array stride of %d bytes.",
info->name.c_str(), info->bufferStride, buffer->getArrayStride());
}
else if (info->bufferMemberCount != buffer->getDataMembers().size())
{
if (internalUpdate)
return false;
else
// Don't prevent this from working for internally bound default resources.
if (!internalUpdate)
throw love::Exception("Shader storage block '%s' has a struct with %d fields, but the given Buffer has a format with %d members.",
info->name.c_str(), info->bufferMemberCount, buffer->getDataMembers().size());
}
Expand Down
14 changes: 11 additions & 3 deletions src/modules/graphics/Shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,8 @@ class Shader : public Object, public Resource

virtual void updateUniform(const UniformInfo *info, int count) = 0;

virtual void sendTextures(const UniformInfo *info, Texture **textures, int count) = 0;
virtual void sendBuffers(const UniformInfo *info, Buffer **buffers, int count) = 0;
void sendTextures(const UniformInfo *info, Texture **textures, int count);
void sendBuffers(const UniformInfo *info, Buffer **buffers, int count);

/**
* Gets whether a uniform with the specified name exists and is actively
Expand All @@ -257,7 +257,7 @@ class Shader : public Object, public Resource
/**
* Sets the textures used when rendering a video. For internal use only.
**/
virtual void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) = 0;
void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture);

const UniformInfo *getMainTextureInfo() const;
void validateDrawState(PrimitiveType primtype, Texture *maintexture) const;
Expand Down Expand Up @@ -319,6 +319,14 @@ class Shader : public Object, public Resource
// std140 uniform buffer alignment-aware copy.
void copyToUniformBuffer(const UniformInfo *info, const void *src, void *dst, int count) const;

void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalupdate);
void sendBuffers(const UniformInfo *info, Buffer **buffers, int count, bool internalupdate);

virtual void applyTexture(const UniformInfo *info, int i, Texture *texture, UniformType basetype, bool isdefault) = 0;
virtual void applyBuffer(const UniformInfo *info, int i, Buffer *buffer, UniformType basetype, bool isdefault) = 0;

void flushBatchedDraws() const;

static std::string canonicaliizeUniformName(const std::string &name);
static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, Reflection &reflection);
static DataBaseType getDataBaseType(PixelFormat format);
Expand Down
6 changes: 3 additions & 3 deletions src/modules/graphics/metal/Shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,7 @@ class Shader final : public love::graphics::Shader
int getVertexAttributeIndex(const std::string &name) override;
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
void updateUniform(const UniformInfo *info, int count) override;
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override;
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
ptrdiff_t getHandle() const override { return 0; }
void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;

id<MTLRenderPipelineState> getCachedRenderPipeline(Graphics *gfx, const RenderPipelineKey &key);
id<MTLComputePipelineState> getComputePipeline() const { return computePipeline; }
Expand Down Expand Up @@ -140,6 +137,9 @@ class Shader final : public love::graphics::Shader
void buildLocalUniforms(const spirv_cross::CompilerMSL &msl, const spirv_cross::SPIRType &type, size_t baseoffset, const std::string &basename);
void compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program);

void applyTexture(const UniformInfo *info, int i, love::graphics::Texture *texture, UniformType basetype, bool isdefault) override;
void applyBuffer(const UniformInfo *info, int i, love::graphics::Buffer *buffer, UniformType basetype, bool isdefault) override;

id<MTLFunction> functions[SHADERSTAGE_MAX_ENUM];

UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
Expand Down
150 changes: 29 additions & 121 deletions src/modules/graphics/metal/Shader.mm
Original file line number Diff line number Diff line change
Expand Up @@ -675,11 +675,11 @@ static EShLanguage getGLSLangStage(ShaderStageType stage)
{
case UNIFORM_SAMPLER:
case UNIFORM_STORAGETEXTURE:
sendTextures(info, &activeTextures[info->resourceIndex], info->count);
sendTextures(info, &activeTextures[info->resourceIndex], info->count, true);
break;
case UNIFORM_TEXELBUFFER:
case UNIFORM_STORAGEBUFFER:
sendBuffers(info, &activeBuffers[info->resourceIndex], info->count);
sendBuffers(info, &activeBuffers[info->resourceIndex], info->count, true);
break;
default:
break;
Expand Down Expand Up @@ -741,139 +741,47 @@ static EShLanguage getGLSLangStage(ShaderStageType stage)
copyToUniformBuffer(info, info->data, dst, count);
}

void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count)
void Shader::applyTexture(const UniformInfo *info, int i, love::graphics::Texture *texture, UniformType /*basetype*/, bool isdefault)
{ @autoreleasepool {
if (info->baseType != UNIFORM_SAMPLER && info->baseType != UNIFORM_STORAGETEXTURE)
if (info->location < 0)
return;

if (current == this)
Graphics::flushBatchedDrawsGlobal();

count = std::min(count, info->count);
int bindingindex = info->location + i;
if (bindingindex < 0)
return;

for (int i = 0; i < count; i++)
auto &binding = textureBindings[bindingindex];
if (isdefault && (binding.access & ACCESS_WRITE) != 0)
{
love::graphics::Texture *tex = textures[i];
bool isdefault = tex == nullptr;

if (tex != nullptr)
{
if (!validateTexture(info, tex, false))
continue;
}
else
{
auto gfx = Graphics::getInstance();
tex = gfx->getDefaultTexture(info->textureType, info->dataBaseType, info->isDepthSampler);
}

tex->retain();

int resourceindex = info->resourceIndex + i;

if (activeTextures[resourceindex] != nullptr)
activeTextures[resourceindex]->release();

activeTextures[resourceindex] = tex;

if (info->location < 0)
continue;

int bindingindex = info->location + i;
if (bindingindex < 0)
continue;

auto &binding = textureBindings[bindingindex];
if (isdefault && (binding.access & ACCESS_WRITE) != 0)
{
binding.texture = nil;
binding.samplerTexture = nullptr;
}
else
{
binding.texture = getMTLTexture(tex);
binding.samplerTexture = tex;
}
binding.texture = nil;
binding.samplerTexture = nullptr;
}
else
{
binding.texture = getMTLTexture(texture);
binding.samplerTexture = texture;
}
}}

void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
{
bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;

if (!texelbinding && !storagebinding)
void Shader::applyBuffer(const UniformInfo *info, int i, love::graphics::Buffer *buffer, UniformType basetype, bool isdefault)
{ @autoreleasepool {
if (info->location < 0)
return;

if (current == this)
Graphics::flushBatchedDrawsGlobal();

count = std::min(count, info->count);

for (int i = 0; i < count; i++)
int bindingindex = info->location + i;
if (basetype == UNIFORM_TEXELBUFFER && bindingindex >= 0)
{
love::graphics::Buffer *buffer = buffers[i];
bool isdefault = buffer == nullptr;

if (buffer != nullptr)
{
if (!validateBuffer(info, buffer, false))
continue;
}
else
{
auto gfx = Graphics::getInstance();
if (texelbinding)
buffer = gfx->getDefaultTexelBuffer(info->dataBaseType);
else
buffer = gfx->getDefaultStorageBuffer();
}

buffer->retain();

int resourceindex = info->resourceIndex + i;

if (activeBuffers[resourceindex] != nullptr)
activeBuffers[resourceindex]->release();

activeBuffers[resourceindex] = buffer;

if (info->location < 0)
continue;

int bindingindex = info->location + i;
if (texelbinding && bindingindex >= 0)
{
textureBindings[bindingindex].texture = getMTLTexture(buffer);
}
else if (storagebinding && bindingindex >= 0)
{
auto &binding = bufferBindings[bindingindex];
if (isdefault && (binding.access & ACCESS_WRITE) != 0)
binding.buffer = nil;
else
binding.buffer = getMTLBuffer(buffer);
}
textureBindings[bindingindex].texture = getMTLTexture(buffer);
}
}

void Shader::setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture)
{
const BuiltinUniform builtins[3] = {
BUILTIN_TEXTURE_VIDEO_Y,
BUILTIN_TEXTURE_VIDEO_CB,
BUILTIN_TEXTURE_VIDEO_CR,
};

love::graphics::Texture *textures[3] = {ytexture, cbtexture, crtexture};

for (int i = 0; i < 3; i++)
else if (basetype == UNIFORM_STORAGEBUFFER && bindingindex >= 0)
{
const UniformInfo *info = builtinUniformInfo[builtins[i]];
if (info != nullptr)
sendTextures(info, &textures[i], 1);
auto &binding = bufferBindings[bindingindex];
if (isdefault && (binding.access & ACCESS_WRITE) != 0)
binding.buffer = nil;
else
binding.buffer = getMTLBuffer(buffer);
}
}
}}

id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(graphics::Graphics *gfx, const RenderPipelineKey &key)
{
Expand Down
Loading

0 comments on commit 876625d

Please sign in to comment.