Browse Source

move common shader:send texture/buffer code to platform-agnostic layer.

Fixes issues in the Metal backend when a shader using a readonly buffer is created.
Sasha Szpakowski 11 months ago
parent
commit
876625d7e3

+ 123 - 6
src/modules/graphics/Shader.cpp

@@ -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);
@@ -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());
 		}

+ 11 - 3
src/modules/graphics/Shader.h

@@ -245,8 +245,8 @@ public:
 
 	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
@@ -257,7 +257,7 @@ public:
 	/**
 	 * 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;
@@ -319,6 +319,14 @@ protected:
 	// 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);

+ 3 - 3
src/modules/graphics/metal/Shader.h

@@ -109,10 +109,7 @@ public:
 	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; }
@@ -140,6 +137,9 @@ private:
 	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];

+ 29 - 121
src/modules/graphics/metal/Shader.mm

@@ -675,11 +675,11 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
 		{
 		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;
@@ -741,139 +741,47 @@ void Shader::updateUniform(const UniformInfo *info, int count)
 	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)
 {

+ 42 - 155
src/modules/graphics/opengl/Shader.cpp

@@ -553,172 +553,78 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
 	}
 }
 
-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)
 {
-	Shader::sendTextures(info, textures, count, false);
-}
-
-void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalUpdate)
-{
-	bool issampler = info->baseType == UNIFORM_SAMPLER;
-	bool isstoragetex = info->baseType == UNIFORM_STORAGETEXTURE;
-
-	if (!issampler && !isstoragetex)
-		return;
-
 	bool shaderactive = current == this;
 
-	if (!internalUpdate && shaderactive)
-		flushBatchedDraws();
-
-	count = std::min(count, info->count);
-
-	// Bind the textures to the texture units.
-	for (int i = 0; i < count; i++)
+	if (basetype == UNIFORM_STORAGETEXTURE)
 	{
-		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();
+		GLuint gltex = (GLuint) texture->getHandle();
 
-		activeTextures[resourceindex] = tex;
+		int bindingindex = info->ints[i];
+		auto &binding = storageTextureBindings[bindingindex];
 
-		if (isstoragetex)
+		if (isdefault && (info->access & ACCESS_WRITE) != 0)
 		{
-			GLuint gltex = (GLuint) tex->getHandle();
-
-			int bindingindex = info->ints[i];
-			auto &binding = storageTextureBindings[bindingindex];
-
-			if (isdefault && (info->access & ACCESS_WRITE) != 0)
-			{
-				binding.texture = nullptr;
-				binding.gltexture = 0;
-			}
-			else
-			{
-				binding.texture = tex;
-				binding.gltexture = gltex;
-
-				if (shaderactive)
-					glBindImageTexture(bindingindex, binding.gltexture, 0, GL_TRUE, 0, binding.access, binding.internalFormat);
-			}
+			binding.texture = nullptr;
+			binding.gltexture = 0;
 		}
 		else
 		{
-			GLuint gltex = (GLuint) tex->getHandle();
-
-			int texunit = info->ints[i];
+			binding.texture = texture;
+			binding.gltexture = gltex;
 
 			if (shaderactive)
-				gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
-
-			// Store texture id so it can be re-bound to the texture unit later.
-			textureUnits[texunit].texture = gltex;
+				glBindImageTexture(bindingindex, binding.gltexture, 0, GL_TRUE, 0, binding.access, binding.internalFormat);
 		}
 	}
-}
+	else
+	{
+		GLuint gltex = (GLuint) texture->getHandle();
 
-void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
-{
-	Shader::sendBuffers(info, buffers, count, false);
-}
+		int texunit = info->ints[i];
 
-void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
-{
-	bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
-	bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;
+		if (shaderactive)
+			gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
 
-	if (!texelbinding && !storagebinding)
-		return;
+		// Store texture id so it can be re-bound to the texture unit later.
+		textureUnits[texunit].texture = gltex;
+	}
+}
 
+void Shader::applyBuffer(const UniformInfo *info, int i, love::graphics::Buffer *buffer, UniformType basetype, bool isdefault)
+{
 	bool shaderactive = current == this;
 
-	if (!internalUpdate && shaderactive)
-		flushBatchedDraws();
-
-	count = std::min(count, info->count);
-
-	for (int i = 0; i < count; i++)
+	if (basetype == UNIFORM_TEXELBUFFER)
 	{
-		love::graphics::Buffer *buffer = buffers[i];
-		bool isdefault = buffer == nullptr;
+		GLuint gltex = (GLuint) buffer->getTexelBufferHandle();
+		int texunit = info->ints[i];
 
-		if (buffer != nullptr)
-		{
-			if (!validateBuffer(info, buffer, internalUpdate))
-				continue;
-		}
-		else
-		{
-			auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
-			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 (texelbinding)
-		{
-			GLuint gltex = (GLuint) buffer->getTexelBufferHandle();
-			int texunit = info->ints[i];
-
-			if (shaderactive)
-				gl.bindBufferTextureToUnit(gltex, texunit, false, false);
+		if (shaderactive)
+			gl.bindBufferTextureToUnit(gltex, texunit, false, false);
 
-			// Store texture id so it can be re-bound to the texture unit later.
-			textureUnits[texunit].texture = gltex;
-		}
-		else if (storagebinding)
-		{
-			GLuint glbuffer = (GLuint)buffer->getHandle();
-			int bindingindex = info->ints[i];
+		// Store texture id so it can be re-bound to the texture unit later.
+		textureUnits[texunit].texture = gltex;
+	}
+	else if (basetype == UNIFORM_STORAGEBUFFER)
+	{
+		GLuint glbuffer = (GLuint)buffer->getHandle();
+		int bindingindex = info->ints[i];
 
-			if (shaderactive)
-				gl.bindIndexedBuffer(glbuffer, BUFFERUSAGE_SHADER_STORAGE, bindingindex);
+		if (shaderactive)
+			gl.bindIndexedBuffer(glbuffer, BUFFERUSAGE_SHADER_STORAGE, bindingindex);
 
-			auto activeindex = storageBufferBindingIndexToActiveBinding[bindingindex];
+		auto activeindex = storageBufferBindingIndexToActiveBinding[bindingindex];
 
-			if (activeindex.first >= 0)
-				activeStorageBufferBindings[activeindex.first].buffer = glbuffer;
+		if (activeindex.first >= 0)
+			activeStorageBufferBindings[activeindex.first].buffer = glbuffer;
 
-			if (activeindex.second >= 0)
-				activeWritableStorageBuffers[activeindex.second] = isdefault ? nullptr : buffer;
-		}
+		if (activeindex.second >= 0)
+			activeWritableStorageBuffers[activeindex.second] = isdefault ? nullptr : buffer;
 	}
 }
 
-void Shader::flushBatchedDraws() const
-{
-	if (current == this)
-		Graphics::flushBatchedDrawsGlobal();
-}
-
 ptrdiff_t Shader::getHandle() const
 {
 	return program;
@@ -736,25 +642,6 @@ int Shader::getVertexAttributeIndex(const std::string &name)
 	return location;
 }
 
-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 = builtinUniformInfo[builtins[i]];
-
-		if (info != nullptr)
-			sendTextures(info, &textures[i], 1, true);
-	}
-}
-
 void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW, int viewportH)
 {
 	if (current != this)

+ 2 - 6
src/modules/graphics/opengl/Shader.h

@@ -65,10 +65,7 @@ public:
 	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;
-	void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
 
 	void updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW, int viewportH);
 
@@ -95,10 +92,9 @@ private:
 	void mapActiveUniforms();
 
 	void updateUniform(const UniformInfo *info, int count, bool internalupdate);
-	void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalupdate);
-	void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalupdate);
 
-	void flushBatchedDraws() const;
+	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;
 
 	// Get any warnings or errors generated only by the shader program object.
 	std::string getProgramWarnings() const;

+ 26 - 114
src/modules/graphics/vulkan/Shader.cpp

@@ -367,83 +367,14 @@ void Shader::updateUniform(const UniformInfo *info, int count)
 	}
 }
 
-void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures, int count)
+void Shader::applyTexture(const UniformInfo *info, int i, love::graphics::Texture *texture, UniformType /*basetype*/, bool isdefault)
 {
-	bool issampler = info->baseType == UNIFORM_SAMPLER;
-	bool isstoragetex = info->baseType == UNIFORM_STORAGETEXTURE;
-
-	count = std::min(count, info->count);
-
-	if (current == this)
-		Graphics::flushBatchedDrawsGlobal();
-
-	for (int i = 0; i < count; i++)
-	{
-		love::graphics::Texture *tex = textures[i];
-		bool isdefault = tex == nullptr;
-
-		if (tex != nullptr)
-		{
-			if (!validateTexture(info, tex, false))
-				continue;
-		}
-		else
-		{
-			auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
-			tex = gfx->getDefaultTexture(info->textureType, info->dataBaseType, info->isDepthSampler);
-		}
-
-		int resourceindex = info->resourceIndex + i;
-		auto prevtexture = activeTextures[resourceindex];
-		activeTextures[resourceindex] = tex;
-		activeTextures[resourceindex]->retain();
-		if (prevtexture)
-			prevtexture->release();
-
-		if (tex != prevtexture)
-			setTextureDescriptor(info, (isdefault && (info->access & ACCESS_WRITE) != 0) ? nullptr : tex, i);
-	}
+	setTextureDescriptor(info, (isdefault && (info->access & ACCESS_WRITE) != 0) ? nullptr : texture, i);
 }
 
-void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
+void Shader::applyBuffer(const UniformInfo *info, int i, love::graphics::Buffer *buffer, UniformType /*basetype*/, bool isdefault)
 {
-	bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
-	bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;
-
-	count = std::min(count, info->count);
-
-	if (current == this)
-		Graphics::flushBatchedDrawsGlobal();
-
-	for (int i = 0; i < count; i++)
-	{
-		love::graphics::Buffer *buffer = buffers[i];
-		bool isdefault = buffer == nullptr;
-
-		if (buffer != nullptr)
-		{
-			if (!validateBuffer(info, buffer, false))
-				continue;
-		}
-		else
-		{
-			auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
-			if (texelbinding)
-				buffer = gfx->getDefaultTexelBuffer(info->dataBaseType);
-			else
-				buffer = gfx->getDefaultStorageBuffer();
-		}
-
-		int resourceindex = info->resourceIndex + i;
-		auto prevbuffer = activeBuffers[resourceindex];
-		activeBuffers[resourceindex] = buffer;
-		activeBuffers[resourceindex]->retain();
-		if (prevbuffer)
-			prevbuffer->release();
-
-		if (buffer != prevbuffer)
-			setBufferDescriptor(info, (isdefault && (info->access & ACCESS_WRITE) != 0) ? nullptr : buffer, i);
-	}
+	setBufferDescriptor(info, (isdefault && (info->access & ACCESS_WRITE) != 0) ? nullptr : buffer, i);
 }
 
 void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
@@ -1078,37 +1009,6 @@ void Shader::createDescriptorPoolSizes()
 	}
 }
 
-void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture)
-{
-	std::array<graphics::Texture*, 3> textures = {
-		ytexture, cbtexture, crtexture
-	};
-
-	std::array<BuiltinUniform, 3> builtIns = {
-		BUILTIN_TEXTURE_VIDEO_Y,
-		BUILTIN_TEXTURE_VIDEO_CB,
-		BUILTIN_TEXTURE_VIDEO_CR,
-	};
-
-	static_assert(textures.size() == builtIns.size(), "expected number of textures to be the same");
-
-	for (size_t i = 0; i < textures.size(); i++)
-	{
-		const UniformInfo *u = builtinUniformInfo[builtIns[i]];
-		if (u != nullptr)
-		{
-			auto prevtexture = activeTextures[u->resourceIndex];
-			textures[i]->retain();
-			if (prevtexture)
-				prevtexture->release();
-			activeTextures[u->resourceIndex] = textures[i];
-
-			if (textures[i] != prevtexture)
-				setTextureDescriptor(u, textures[i], 0);
-		}
-	}
-}
-
 void Shader::setMainTex(graphics::Texture *texture)
 {
 	const UniformInfo *u = builtinUniformInfo[BUILTIN_TEXTURE_MAIN];
@@ -1134,10 +1034,13 @@ void Shader::setTextureDescriptor(const UniformInfo *info, love::graphics::Textu
 
 	// Samplers may change after this call, so they're set just before the
 	// descriptor set is used instead of here.
-	imageInfo.imageLayout = vkTexture != nullptr ? vkTexture->getImageLayout() : VK_IMAGE_LAYOUT_UNDEFINED;
-	imageInfo.imageView = vkTexture != nullptr ? (VkImageView)vkTexture->getRenderTargetHandle() : VK_NULL_HANDLE;
-
-	resourceDescriptorsDirty = true;
+	VkImageView view = vkTexture != nullptr ? (VkImageView)vkTexture->getRenderTargetHandle() : VK_NULL_HANDLE;
+	if (view != imageInfo.imageView)
+	{
+		imageInfo.imageLayout = vkTexture != nullptr ? vkTexture->getImageLayout() : VK_IMAGE_LAYOUT_UNDEFINED;
+		imageInfo.imageView = view;
+		resourceDescriptorsDirty = true;
+	}
 }
 
 void Shader::setBufferDescriptor(const UniformInfo *info, love::graphics::Buffer *buffer, int index)
@@ -1145,16 +1048,25 @@ void Shader::setBufferDescriptor(const UniformInfo *info, love::graphics::Buffer
 	if (info->baseType == UNIFORM_STORAGEBUFFER)
 	{
 		VkDescriptorBufferInfo &bufferInfo = descriptorBuffers[info->bindingStartIndex + index];
-		bufferInfo.buffer = buffer != nullptr ? (VkBuffer)buffer->getHandle() : VK_NULL_HANDLE;
-		bufferInfo.offset = 0;
-		bufferInfo.range = buffer != nullptr ? buffer->getSize() : 0;
+		VkBuffer vkbuffer = buffer != nullptr ? (VkBuffer)buffer->getHandle() : VK_NULL_HANDLE;
+		VkDeviceSize range = buffer != nullptr ? buffer->getSize() : 0;
+		if (vkbuffer != bufferInfo.buffer || bufferInfo.offset != 0 || range != bufferInfo.range)
+		{
+			bufferInfo.buffer = vkbuffer;
+			bufferInfo.offset = 0;
+			bufferInfo.range = range;
+			resourceDescriptorsDirty = true;
+		}
 	}
 	else if (info->baseType == UNIFORM_TEXELBUFFER)
 	{
-		descriptorBufferViews[info->bindingStartIndex + index] = buffer != nullptr ? (VkBufferView)buffer->getTexelBufferHandle() : VK_NULL_HANDLE;
+		VkBufferView view = buffer != nullptr ? (VkBufferView)buffer->getTexelBufferHandle() : VK_NULL_HANDLE;
+		if (view != descriptorBufferViews[info->bindingStartIndex + index])
+		{
+			descriptorBufferViews[info->bindingStartIndex + index] = view;
+			resourceDescriptorsDirty = true;
+		}
 	}
-
-	resourceDescriptorsDirty = true;
 }
 
 void Shader::createDescriptorPool()

+ 3 - 5
src/modules/graphics/vulkan/Shader.h

@@ -153,11 +153,6 @@ public:
 
 	void updateUniform(const UniformInfo *info, int count) override;
 
-	void sendTextures(const UniformInfo *info, graphics::Texture **textures, int count) override;
-	void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
-
-	void setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture) override;
-
 	void setMainTex(graphics::Texture *texture);
 
 	VkPipeline getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfigurationCore &configuration);
@@ -175,6 +170,9 @@ private:
 	void setTextureDescriptor(const UniformInfo *info, love::graphics::Texture *texture, int index);
 	void setBufferDescriptor(const UniformInfo *info, love::graphics::Buffer *buffer, int index);
 
+	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;
+
 	VkPipeline computePipeline = VK_NULL_HANDLE;
 
 	VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;