Browse Source

vulkan: first iteration for generic uniforms

Until now we only considered the builtin uniform variables.
This commit attempts to make the code in the vulkan Shader
implementation more general, as to allow for custom uniforms.
This is not possible yet, but should be easier to implement now.
niki 3 years ago
parent
commit
f9ad1842b5
3 changed files with 325 additions and 121 deletions
  1. 38 0
      CMakeLists.txt
  2. 270 103
      src/modules/graphics/vulkan/Shader.cpp
  3. 17 18
      src/modules/graphics/vulkan/Shader.h

+ 38 - 0
CMakeLists.txt

@@ -1671,6 +1671,43 @@ endif()
 
 add_library(love_3p_physfs ${LOVE_SRC_3P_PHYSFS})
 
+#
+# spirv_cross
+#
+
+set(LOVE_SRC_3P_SPIRV_CROSS
+	src/libraries/spirv_cross/GLSL.std.450.h
+	src/libraries/spirv_cross/spirv_cfg.cpp
+	src/libraries/spirv_cross/spirv_cfg.hpp
+	src/libraries/spirv_cross/spirv_common.hpp
+	src/libraries/spirv_cross/spirv_cpp.cpp
+	src/libraries/spirv_cross/spirv_cpp.hpp
+	src/libraries/spirv_cross/spirv_cross_c.cpp
+	src/libraries/spirv_cross/spirv_cross_c.h
+	src/libraries/spirv_cross/spirv_cross_containers.hpp
+	src/libraries/spirv_cross/spirv_cross_error_handling.hpp
+	src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp
+	src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp
+	src/libraries/spirv_cross/spirv_cross_util.cpp
+	src/libraries/spirv_cross/spirv_cross_util.hpp
+	src/libraries/spirv_cross/spirv_cross.cpp
+	src/libraries/spirv_cross/spirv_cross.hpp
+	src/libraries/spirv_cross/spirv_glsl.cpp
+	src/libraries/spirv_cross/spirv_glsl.hpp
+	src/libraries/spirv_cross/spirv_hlsl.cpp
+	src/libraries/spirv_cross/spirv_hlsl.hpp
+	src/libraries/spirv_cross/spirv_msl.cpp
+	src/libraries/spirv_cross/spirv_msl.hpp
+	src/libraries/spirv_cross/spirv_parser.cpp
+	src/libraries/spirv_cross/spirv_parser.hpp
+	src/libraries/spirv_cross/spirv_reflect.cpp
+	src/libraries/spirv_cross/spirv_reflect.hpp
+	src/libraries/spirv_cross/spirv.h
+	src/libraries/spirv_cross/spirv.hpp
+)
+
+add_library(love_3p_spirv_cross ${LOVE_SRC_3P_SPIRV_CROSS})
+
 #
 # stb_image
 #
@@ -1752,6 +1789,7 @@ set(LOVE_3P
 	love_3p_lz4
 	love_3p_noise1234
 	love_3p_physfs
+	love_3p_spirv_cross
 	love_3p_wuff
 	love_3p_xxhash
 )

+ 270 - 103
src/modules/graphics/vulkan/Shader.cpp

@@ -3,7 +3,7 @@
 
 #include "libraries/glslang/glslang/Public/ShaderLang.h"
 #include "libraries/glslang/SPIRV/GlslangToSpv.h"
-#include "libraries/spirv_cross/spirv_cross.hpp"
+
 
 #include <vector>
 
@@ -150,12 +150,12 @@ Shader::Shader(StrongRef<love::graphics::ShaderStage> stages[])
 }
 
 bool Shader::loadVolatile() {
-	calculateUniformBufferSizeAligned();
 	compileShaders();
+	calculateUniformBufferSizeAligned();
 	createDescriptorSetLayout();
 	createPipelineLayout();
 	createStreamBuffers();
-	currentImage = 0;
+	currentFrame = 0;
 	count = 0;
 
 	return true;
@@ -201,42 +201,42 @@ static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* textur
 	return imageInfo;
 }
 
-void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
+void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t frameIndex) {
 	// detect wether a new frame has begun
-	if (currentImage != imageIndex) {
-		currentImage = imageIndex;
+	if (currentFrame != frameIndex) {
+		currentFrame = frameIndex;
 		count = 0;
 
 		// we needed more memory last frame, let's collapse all buffers into a single one.
-		if (streamBuffers.at(currentImage).size() > 1) {
+		if (streamBuffers.at(currentFrame).size() > 1) {
 			size_t newSize = 0;
-			for (auto streamBuffer : streamBuffers.at(currentImage)) {
+			for (auto streamBuffer : streamBuffers.at(currentFrame)) {
 				newSize += streamBuffer->getSize();
 				delete streamBuffer;
 			}
-			streamBuffers.at(currentImage).clear();
-			streamBuffers.at(currentImage).push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, newSize));
+			streamBuffers.at(currentFrame).clear();
+			streamBuffers.at(currentFrame).push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, newSize));
 		} 
 		// no collapse necessary, can just call nextFrame to reset the current (only) streambuffer
 		else {
-			streamBuffers.at(currentImage).at(0)->nextFrame();
+			streamBuffers.at(currentFrame).at(0)->nextFrame();
 		}
 	}
 	// still the same frame
 	else {
 		auto usedStreamBufferMemory = count * uniformBufferSizeAligned;
-		if (usedStreamBufferMemory >= streamBuffers.at(currentImage).back()->getSize()) {
+		if (usedStreamBufferMemory >= streamBuffers.at(currentFrame).back()->getSize()) {
 			// we ran out of memory in the current frame, need to allocate more.
-			streamBuffers.at(currentImage).push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned));
+			streamBuffers.at(currentFrame).push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned));
 			count = 0;
 		}
 	}
 
 	// additional data is always added onto the last stream buffer in the current frame
-	auto currentStreamBuffer = streamBuffers.at(currentImage).back();
+	auto currentStreamBuffer = streamBuffers.at(currentFrame).back();
 
 	auto mapInfo = currentStreamBuffer->map(uniformBufferSizeAligned);
-	memcpy(mapInfo.data, &uniformData, uniformBufferSizeAligned);
+	memcpy(mapInfo.data, localUniformStagingData.data(), uniformBufferSizeAligned);
 	currentStreamBuffer->unmap(uniformBufferSizeAligned);
 	currentStreamBuffer->markUsed(uniformBufferSizeAligned);
 
@@ -245,53 +245,42 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t image
 	bufferInfo.offset = count * uniformBufferSizeAligned;
 	bufferInfo.range = sizeof(BuiltinUniformData);
 	
-	auto mainTexImageInfo = createDescriptorImageInfo(mainTex);
-	auto ytextureImageInfo = createDescriptorImageInfo(ytexture);
-	auto cbtextureImageInfo = createDescriptorImageInfo(cbtexture);
-	auto crtextureImageInfo = createDescriptorImageInfo(crtexture);
-
-	std::array<VkWriteDescriptorSet, 5> descriptorWrite{};
-	descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-	descriptorWrite[0].dstSet = 0;
-	descriptorWrite[0].dstBinding = 0;
-	descriptorWrite[0].dstArrayElement = 0;
-	descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-	descriptorWrite[0].descriptorCount = 1;
-	descriptorWrite[0].pBufferInfo = &bufferInfo;
-
-	descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-	descriptorWrite[1].dstSet = 0;
-	descriptorWrite[1].dstBinding = 1;
-	descriptorWrite[1].dstArrayElement = 0;
-	descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	descriptorWrite[1].descriptorCount = 1;
-	descriptorWrite[1].pImageInfo = &mainTexImageInfo;				
-
-	descriptorWrite[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-	descriptorWrite[2].dstSet = 0;
-	descriptorWrite[2].dstBinding = 2;
-	descriptorWrite[2].dstArrayElement = 0;
-	descriptorWrite[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	descriptorWrite[2].descriptorCount = 1;
-	descriptorWrite[2].pImageInfo = &ytextureImageInfo;
-
-	descriptorWrite[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-	descriptorWrite[3].dstSet = 0;
-	descriptorWrite[3].dstBinding = 3;
-	descriptorWrite[3].dstArrayElement = 0;
-	descriptorWrite[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	descriptorWrite[3].descriptorCount = 1;
-	descriptorWrite[3].pImageInfo = &cbtextureImageInfo;
-
-	descriptorWrite[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-	descriptorWrite[4].dstSet = 0;
-	descriptorWrite[4].dstBinding = 4;
-	descriptorWrite[4].dstArrayElement = 0;
-	descriptorWrite[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	descriptorWrite[4].descriptorCount = 1;
-	descriptorWrite[4].pImageInfo = &crtextureImageInfo;
-
-	vkCmdPushDescriptorSet(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast<uint32_t>(descriptorWrite.size()), descriptorWrite.data());
+	std::vector<VkWriteDescriptorSet> descriptorWrite{};
+
+	// uniform buffer update always happens.
+	VkWriteDescriptorSet uniformWrite{};
+	uniformWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+	uniformWrite.dstSet = 0;
+	uniformWrite.dstBinding = builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW]->location;
+	uniformWrite.dstArrayElement = 0;
+	uniformWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+	uniformWrite.descriptorCount = 1;
+	uniformWrite.pBufferInfo = &bufferInfo;			
+
+	descriptorWrite.push_back(uniformWrite);
+
+	// update everything other than uniform buffers (since that's already taken care of.
+	for (const auto& [key, val] : uniformInfos) {
+		// fixme: other types.
+		if (val.baseType == UNIFORM_SAMPLER) {
+			VkWriteDescriptorSet write{};
+			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+			write.dstSet = 0;
+			write.dstBinding = val.location;
+			write.dstArrayElement = 0;
+			write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+			write.descriptorCount = 1;
+			const auto imageInfo =  createDescriptorImageInfo(val.textures[0]);	// fixme: arrays
+			write.pImageInfo = &imageInfo;
+
+			descriptorWrite.push_back(write);
+		}
+	}
+
+	vkCmdPushDescriptorSet(
+		commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, 
+		pipelineLayout, 0, 
+		static_cast<uint32_t>(descriptorWrite.size()), descriptorWrite.data());
 
 	count++;
 }
@@ -309,22 +298,89 @@ void Shader::attach() {
 }
 
 int Shader::getVertexAttributeIndex(const std::string& name) {
-	return vertexAttributeIndices.at(name);
+	return 0;
 }
 
 void Shader::calculateUniformBufferSizeAligned() {
 	gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
 	auto vgfx = (Graphics*)gfx;
 	auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
+	size_t size = localUniformStagingData.size();
 	uniformBufferSizeAligned = 
 		static_cast<VkDeviceSize>(
 			std::ceil(
-				static_cast<float>(sizeof(BuiltinUniformData)) / static_cast<float>(minAlignment)
+				static_cast<float>(size) / static_cast<float>(minAlignment)
 			)
 		)
 		* minAlignment;
 }
 
+void Shader::buildLocalUniforms(spirv_cross::Compiler& comp, const spirv_cross::SPIRType& type, size_t baseoff, const std::string& basename) {
+	using namespace spirv_cross;
+
+	const auto& membertypes = type.member_types;
+
+	for (size_t uindex = 0; uindex < membertypes.size(); uindex) {
+		const auto& memberType = comp.get_type(membertypes[uindex]);
+		size_t memberSize = comp.get_declared_struct_member_size(type, uindex);
+		size_t offset = baseoff + comp.type_struct_member_offset(type, uindex);
+
+		std::string name = basename + comp.get_member_name(type.self, uindex);
+
+		switch (memberType.basetype) {
+		case SPIRType::Struct:
+			name += ".";
+			buildLocalUniforms(comp, memberType, offset, name);
+			continue;
+		case SPIRType::Int:
+		case SPIRType::UInt:
+		case SPIRType::Float:
+			break;
+		default:
+			continue;
+		}
+
+		UniformInfo u = {};
+		u.name = name;
+		u.dataSize = memberSize;
+		u.count = memberType.array.empty() ? 1 : memberType.array[0];
+		u.components = 1;
+		u.data = localUniformStagingData.data() + offset;
+
+		if (memberType.columns == 1) {
+			if (memberType.basetype == SPIRType::Int) {
+				u.baseType = UNIFORM_INT;
+			}
+			else if (memberType.basetype == SPIRType::UInt) {
+				u.baseType = UNIFORM_UINT;
+			}
+			else {
+				u.baseType = UNIFORM_FLOAT;
+			}
+			u.components = memberType.vecsize;
+		}
+		else {
+			u.baseType = UNIFORM_MATRIX;
+			u.matrix.rows = memberType.vecsize;
+			u.matrix.columns = memberType.columns;
+		}
+
+		// fixme: initializer values
+
+		uniformInfos[u.name] = u;
+
+		BuiltinUniform builtin = BUILTIN_MAX_ENUM;
+		if (getConstant(u.name.c_str(), builtin)) {
+			if (builtin == BUILTIN_UNIFORMS_PER_DRAW) {
+				builtinUniformDataOffset = offset;
+			}
+			builtinUniformInfo[builtin] = &uniformInfos[u.name];
+		}
+
+		// update uniform.
+	}
+}
+
 void Shader::compileShaders() {
 	using namespace glslang;
 	using namespace spirv_cross;
@@ -335,7 +391,6 @@ void Shader::compileShaders() {
 	auto vgfx = (Graphics*)gfx;
 	device = vgfx->getDevice();
 
-	mainTex = vgfx->getDefaultTexture();
 	ytexture = vgfx->getDefaultTexture();
 	crtexture = vgfx->getDefaultTexture();
 	cbtexture = vgfx->getDefaultTexture();
@@ -385,6 +440,8 @@ void Shader::compileShaders() {
 		throw love::Exception("mapIO failed");
 	}
 
+	uniformInfos.clear();
+
 	for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
 		auto glslangStage = getGlslShaderType((ShaderStageType)i);
 		auto intermediate = program->getIntermediate(glslangStage);
@@ -424,49 +481,158 @@ void Shader::compileShaders() {
 		shaderStageInfo.pName = "main";
 
 		shaderStages.push_back(shaderStageInfo);
+
+		spirv_cross::CompilerGLSL comp(spirv);
+
+		// we only care about variables that are actually getting used.
+		auto active = comp.get_active_interface_variables();
+		auto shaderResources = comp.get_shader_resources(active);
+		comp.set_enabled_interface_variables(std::move(active));
+
+		std::string builtinUniformName = "love_UniformsPerDraw";
+
+		for (const auto& resource : shaderResources.uniform_buffers) {
+			size_t uniformBufferObjectSize = comp.get_declared_struct_size(comp.get_type(resource.base_type_id));
+
+			const auto& resourceType = comp.get_type(resource.type_id);
+			unsigned memberCount = resourceType.member_types.size();
+			for (unsigned i = 0; i < memberCount; i++) {
+				auto& type = comp.get_type(resourceType.member_types[i]);
+				auto baseType = type.basetype;
+				const std::string& name = comp.get_member_name(resourceType.self, i);
+
+				if (name == "gl_DefaultUniformBlock") {
+					auto defaultUniformBlockSize = comp.get_declared_struct_size(type);
+					localUniformStagingData.resize(defaultUniformBlockSize);
+
+					std::string basename("");
+					buildLocalUniforms(comp, type, 0, basename);
+				}
+				else if (name == builtinUniformName) {
+					UniformInfo u{};
+					u.name = name;
+					u.dataSize = sizeof(BuiltinUniformData);
+
+					localUniformStagingData.resize(u.dataSize);
+					builtinUniformDataOffset = 0;
+
+					u.count = type.array.empty() ? 1 : type.array[0];
+					u.components = 1;
+					u.data = localUniformStagingData.data();
+
+					if (type.columns == 1) {
+						if (type.basetype == SPIRType::Int) {
+							u.baseType = UNIFORM_INT;
+						}
+						else if (type.basetype == SPIRType::UInt) {
+							u.baseType = UNIFORM_UINT;
+						}
+						else {
+							u.baseType = UNIFORM_FLOAT;
+						}
+						u.components = type.vecsize;
+					}
+					else {
+						u.baseType = UNIFORM_MATRIX;
+						u.matrix.rows = type.vecsize;
+						u.matrix.columns = type.columns;
+					}
+
+					uniformInfos[u.name] = u;
+
+					builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW] = &uniformInfos[u.name];
+				}
+				else {
+					throw love::Exception("unimplemented: non default uniform blocks.");
+				}
+			}
+
+			for (const auto& r : shaderResources.sampled_images) {
+				const SPIRType& basetype = comp.get_type(r.base_type_id);
+				const SPIRType& type = comp.get_type(r.type_id);
+				const SPIRType& imagetype = comp.get_type(basetype.image.type);
+
+				graphics::Shader::UniformInfo info;
+				info.location = comp.get_decoration(r.id, spv::DecorationBinding);
+				info.baseType = UNIFORM_SAMPLER;
+				info.name = r.name;
+				info.count = type.array.empty() ? 1 : type.array[0];
+				info.isDepthSampler = type.image.depth;
+				info.components = 1;
+
+				switch (imagetype.basetype) {
+				case SPIRType::Float:
+					info.dataBaseType = DATA_BASETYPE_FLOAT;
+					break;
+				case SPIRType::Int:
+					info.dataBaseType = DATA_BASETYPE_INT;
+					break;
+				case SPIRType::UInt:
+					info.dataBaseType = DATA_BASETYPE_UINT;
+					break;
+				default:
+					break;
+				}
+
+				switch (basetype.image.dim) {
+				case spv::Dim2D:
+					info.textureType = basetype.image.arrayed ? TEXTURE_2D_ARRAY : TEXTURE_2D;
+					info.textures = new love::graphics::Texture * [info.count];
+					break;
+				case spv::Dim3D:
+					info.textureType = TEXTURE_VOLUME;
+					info.textures = new love::graphics::Texture * [info.count];
+					break;
+				case spv::DimCube:
+					if (basetype.image.arrayed) {
+						throw love::Exception("cubemap arrays are not currently supported");
+					}
+					info.textureType = TEXTURE_CUBE;
+					info.textures = new love::graphics::Texture * [info.count];
+					break;
+				case spv::DimBuffer:
+					throw love::Exception("dim buffers not implemented yet");
+				default:
+					throw love::Exception("unknown dim");
+				}
+
+				if (info.baseType == UNIFORM_SAMPLER) {
+					auto tex = vgfx->getDefaultTexture();
+					for (int i = 0; i < info.count; i++) {
+						info.textures[i] = tex;
+					}
+				}
+				// fixme
+				else if (info.baseType == UNIFORM_TEXELBUFFER) {
+					throw love::Exception("texel buffers not supported yet");
+				}
+
+				uniformInfos[r.name] = info;
+				BuiltinUniform builtin;
+				if (getConstant(r.name.c_str(), builtin)) {
+					builtinUniformInfo[builtin] = &uniformInfos[info.name];
+				}
+			}
+		}
 	}
 }
 
-// fixme: should generate this dynamically.
 void Shader::createDescriptorSetLayout() {
 	auto vgfx = (Graphics*)gfx;
 	vkCmdPushDescriptorSet = vgfx->getVkCmdPushDescriptorSetKHRFunctionPointer();
 
-	VkDescriptorSetLayoutBinding uboLayoutBinding{};
-	uboLayoutBinding.binding = 0;
-	uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-	uboLayoutBinding.descriptorCount = 1;
-	uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
-
-	VkDescriptorSetLayoutBinding samplerLayoutBinding{};
-	samplerLayoutBinding.binding = 1;
-	samplerLayoutBinding.descriptorCount = 1;
-	samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	samplerLayoutBinding.pImmutableSamplers = nullptr;
-	samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-
-	VkDescriptorSetLayoutBinding videoYBinding{};
-	videoYBinding.binding = 2;
-	videoYBinding.descriptorCount = 1;
-	videoYBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	videoYBinding.pImmutableSamplers = nullptr;
-	videoYBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-
-	VkDescriptorSetLayoutBinding videoCBBinding{};
-	videoCBBinding.binding = 3;
-	videoCBBinding.descriptorCount = 1;
-	videoCBBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	videoCBBinding.pImmutableSamplers = nullptr;
-	videoCBBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-
-	VkDescriptorSetLayoutBinding videoCRinding{};
-	videoCRinding.binding = 4;
-	videoCRinding.descriptorCount = 1;
-	videoCRinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	videoCRinding.pImmutableSamplers = nullptr;
-	videoCRinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-
-	std::array<VkDescriptorSetLayoutBinding, 5> bindings = { uboLayoutBinding, samplerLayoutBinding, videoYBinding, videoCBBinding, videoCRinding };
+	std::vector<VkDescriptorSetLayoutBinding> bindings;
+
+	for (auto const& [key, val] : uniformInfos) {
+		VkDescriptorSetLayoutBinding layoutBinding{};
+		layoutBinding.binding = val.location;
+		layoutBinding.descriptorType = val.baseType == UNIFORM_SAMPLER ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+		layoutBinding.descriptorCount = 1;	// is this correct?
+		layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;	// fixme: can we determine in what shader it got used?
+
+		bindings.push_back(layoutBinding);
+	}
+
 	VkDescriptorSetLayoutCreateInfo layoutInfo{};
 	layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
 	layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
@@ -506,11 +672,12 @@ void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cb
 }
 
 void Shader::setUniformData(BuiltinUniformData& data) {
-	uniformData = data;
+	char* ptr = (char*) builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW]->data + builtinUniformDataOffset;
+	memcpy(ptr, &data, sizeof(BuiltinUniformData));
 }
 
 void Shader::setMainTex(graphics::Texture* texture) {
-	mainTex = texture;
+	builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0] = texture;
 }
 } // vulkan
 } // graphics

+ 17 - 18
src/modules/graphics/vulkan/Shader.h

@@ -6,9 +6,12 @@
 #include "Vulkan.h"
 
 #include <vulkan/vulkan.h>
+#include "libraries/spirv_cross/spirv_reflect.hpp"
 
 #include <map>
+#include <memory>
 #include <iostream>
+#include <unordered_map>
 
 
 namespace love {
@@ -26,7 +29,7 @@ public:
 
 	const VkPipelineLayout getGraphicsPipelineLayout() const;
 
-	void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage);
+	void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentFrame);
 
 	void attach() override;
 
@@ -58,6 +61,11 @@ private:
 	void createDescriptorSetLayout();
 	void createPipelineLayout();
 	void createStreamBuffers();
+	void buildLocalUniforms(
+		spirv_cross::Compiler& comp, 
+		const spirv_cross::SPIRType& type, 
+		size_t baseoff, 
+		const std::string& basename);
 
 	VkDeviceSize uniformBufferSizeAligned;
 	PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
@@ -75,27 +83,18 @@ private:
 	Graphics* gfx;
 	VkDevice device;
 
-	std::map<std::string, int> vertexAttributeIndices = {
-		{ "VertexPosition", 0 },
-		{ "VertexTexCoord", 1 },
-		{ "VertexColor", 2 }
-	};
-
-	std::map<std::string, int> uniformBindings = {
-		{ "love_UniformsPerDraw", 0 },
-		{ "love_VideoYChannel", 1 },
-		{ "love_VideoCbChannel", 2 },
-		{ "love_VideoCrChannel", 3 },
-		{ "MainTex", 4 }
-	};
-
-	BuiltinUniformData uniformData;
-	graphics::Texture* mainTex;
+	std::unordered_map<std::string, graphics::Shader::UniformInfo> uniformInfos;
+	UniformInfo* builtinUniformInfo[BUILTIN_MAX_ENUM];
+
+	std::unique_ptr<StreamBuffer> uniformBufferObjectBuffer;
+	std::vector<uint8> localUniformStagingData;
+	size_t builtinUniformDataOffset;
+
 	graphics::Texture* ytexture;
 	graphics::Texture* cbtexture;
 	graphics::Texture* crtexture;
 
-	uint32_t currentImage;
+	uint32_t currentFrame;
 	// todo: give this variable a better name
 	uint32_t count;
 };