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})
 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
 # stb_image
 #
 #
@@ -1752,6 +1789,7 @@ set(LOVE_3P
 	love_3p_lz4
 	love_3p_lz4
 	love_3p_noise1234
 	love_3p_noise1234
 	love_3p_physfs
 	love_3p_physfs
+	love_3p_spirv_cross
 	love_3p_wuff
 	love_3p_wuff
 	love_3p_xxhash
 	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/glslang/Public/ShaderLang.h"
 #include "libraries/glslang/SPIRV/GlslangToSpv.h"
 #include "libraries/glslang/SPIRV/GlslangToSpv.h"
-#include "libraries/spirv_cross/spirv_cross.hpp"
+
 
 
 #include <vector>
 #include <vector>
 
 
@@ -150,12 +150,12 @@ Shader::Shader(StrongRef<love::graphics::ShaderStage> stages[])
 }
 }
 
 
 bool Shader::loadVolatile() {
 bool Shader::loadVolatile() {
-	calculateUniformBufferSizeAligned();
 	compileShaders();
 	compileShaders();
+	calculateUniformBufferSizeAligned();
 	createDescriptorSetLayout();
 	createDescriptorSetLayout();
 	createPipelineLayout();
 	createPipelineLayout();
 	createStreamBuffers();
 	createStreamBuffers();
-	currentImage = 0;
+	currentFrame = 0;
 	count = 0;
 	count = 0;
 
 
 	return true;
 	return true;
@@ -201,42 +201,42 @@ static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* textur
 	return imageInfo;
 	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
 	// detect wether a new frame has begun
-	if (currentImage != imageIndex) {
-		currentImage = imageIndex;
+	if (currentFrame != frameIndex) {
+		currentFrame = frameIndex;
 		count = 0;
 		count = 0;
 
 
 		// we needed more memory last frame, let's collapse all buffers into a single one.
 		// 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;
 			size_t newSize = 0;
-			for (auto streamBuffer : streamBuffers.at(currentImage)) {
+			for (auto streamBuffer : streamBuffers.at(currentFrame)) {
 				newSize += streamBuffer->getSize();
 				newSize += streamBuffer->getSize();
 				delete streamBuffer;
 				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
 		// no collapse necessary, can just call nextFrame to reset the current (only) streambuffer
 		else {
 		else {
-			streamBuffers.at(currentImage).at(0)->nextFrame();
+			streamBuffers.at(currentFrame).at(0)->nextFrame();
 		}
 		}
 	}
 	}
 	// still the same frame
 	// still the same frame
 	else {
 	else {
 		auto usedStreamBufferMemory = count * uniformBufferSizeAligned;
 		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.
 			// 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;
 			count = 0;
 		}
 		}
 	}
 	}
 
 
 	// additional data is always added onto the last stream buffer in the current frame
 	// 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);
 	auto mapInfo = currentStreamBuffer->map(uniformBufferSizeAligned);
-	memcpy(mapInfo.data, &uniformData, uniformBufferSizeAligned);
+	memcpy(mapInfo.data, localUniformStagingData.data(), uniformBufferSizeAligned);
 	currentStreamBuffer->unmap(uniformBufferSizeAligned);
 	currentStreamBuffer->unmap(uniformBufferSizeAligned);
 	currentStreamBuffer->markUsed(uniformBufferSizeAligned);
 	currentStreamBuffer->markUsed(uniformBufferSizeAligned);
 
 
@@ -245,53 +245,42 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t image
 	bufferInfo.offset = count * uniformBufferSizeAligned;
 	bufferInfo.offset = count * uniformBufferSizeAligned;
 	bufferInfo.range = sizeof(BuiltinUniformData);
 	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++;
 	count++;
 }
 }
@@ -309,22 +298,89 @@ void Shader::attach() {
 }
 }
 
 
 int Shader::getVertexAttributeIndex(const std::string& name) {
 int Shader::getVertexAttributeIndex(const std::string& name) {
-	return vertexAttributeIndices.at(name);
+	return 0;
 }
 }
 
 
 void Shader::calculateUniformBufferSizeAligned() {
 void Shader::calculateUniformBufferSizeAligned() {
 	gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
 	gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
 	auto vgfx = (Graphics*)gfx;
 	auto vgfx = (Graphics*)gfx;
 	auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
 	auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
+	size_t size = localUniformStagingData.size();
 	uniformBufferSizeAligned = 
 	uniformBufferSizeAligned = 
 		static_cast<VkDeviceSize>(
 		static_cast<VkDeviceSize>(
 			std::ceil(
 			std::ceil(
-				static_cast<float>(sizeof(BuiltinUniformData)) / static_cast<float>(minAlignment)
+				static_cast<float>(size) / static_cast<float>(minAlignment)
 			)
 			)
 		)
 		)
 		* 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() {
 void Shader::compileShaders() {
 	using namespace glslang;
 	using namespace glslang;
 	using namespace spirv_cross;
 	using namespace spirv_cross;
@@ -335,7 +391,6 @@ void Shader::compileShaders() {
 	auto vgfx = (Graphics*)gfx;
 	auto vgfx = (Graphics*)gfx;
 	device = vgfx->getDevice();
 	device = vgfx->getDevice();
 
 
-	mainTex = vgfx->getDefaultTexture();
 	ytexture = vgfx->getDefaultTexture();
 	ytexture = vgfx->getDefaultTexture();
 	crtexture = vgfx->getDefaultTexture();
 	crtexture = vgfx->getDefaultTexture();
 	cbtexture = vgfx->getDefaultTexture();
 	cbtexture = vgfx->getDefaultTexture();
@@ -385,6 +440,8 @@ void Shader::compileShaders() {
 		throw love::Exception("mapIO failed");
 		throw love::Exception("mapIO failed");
 	}
 	}
 
 
+	uniformInfos.clear();
+
 	for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
 	for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
 		auto glslangStage = getGlslShaderType((ShaderStageType)i);
 		auto glslangStage = getGlslShaderType((ShaderStageType)i);
 		auto intermediate = program->getIntermediate(glslangStage);
 		auto intermediate = program->getIntermediate(glslangStage);
@@ -424,49 +481,158 @@ void Shader::compileShaders() {
 		shaderStageInfo.pName = "main";
 		shaderStageInfo.pName = "main";
 
 
 		shaderStages.push_back(shaderStageInfo);
 		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() {
 void Shader::createDescriptorSetLayout() {
 	auto vgfx = (Graphics*)gfx;
 	auto vgfx = (Graphics*)gfx;
 	vkCmdPushDescriptorSet = vgfx->getVkCmdPushDescriptorSetKHRFunctionPointer();
 	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{};
 	VkDescriptorSetLayoutCreateInfo layoutInfo{};
 	layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
 	layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
 	layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
 	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) {
 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) {
 void Shader::setMainTex(graphics::Texture* texture) {
-	mainTex = texture;
+	builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0] = texture;
 }
 }
 } // vulkan
 } // vulkan
 } // graphics
 } // graphics

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

@@ -6,9 +6,12 @@
 #include "Vulkan.h"
 #include "Vulkan.h"
 
 
 #include <vulkan/vulkan.h>
 #include <vulkan/vulkan.h>
+#include "libraries/spirv_cross/spirv_reflect.hpp"
 
 
 #include <map>
 #include <map>
+#include <memory>
 #include <iostream>
 #include <iostream>
+#include <unordered_map>
 
 
 
 
 namespace love {
 namespace love {
@@ -26,7 +29,7 @@ public:
 
 
 	const VkPipelineLayout getGraphicsPipelineLayout() const;
 	const VkPipelineLayout getGraphicsPipelineLayout() const;
 
 
-	void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage);
+	void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentFrame);
 
 
 	void attach() override;
 	void attach() override;
 
 
@@ -58,6 +61,11 @@ private:
 	void createDescriptorSetLayout();
 	void createDescriptorSetLayout();
 	void createPipelineLayout();
 	void createPipelineLayout();
 	void createStreamBuffers();
 	void createStreamBuffers();
+	void buildLocalUniforms(
+		spirv_cross::Compiler& comp, 
+		const spirv_cross::SPIRType& type, 
+		size_t baseoff, 
+		const std::string& basename);
 
 
 	VkDeviceSize uniformBufferSizeAligned;
 	VkDeviceSize uniformBufferSizeAligned;
 	PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
 	PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
@@ -75,27 +83,18 @@ private:
 	Graphics* gfx;
 	Graphics* gfx;
 	VkDevice device;
 	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* ytexture;
 	graphics::Texture* cbtexture;
 	graphics::Texture* cbtexture;
 	graphics::Texture* crtexture;
 	graphics::Texture* crtexture;
 
 
-	uint32_t currentImage;
+	uint32_t currentFrame;
 	// todo: give this variable a better name
 	// todo: give this variable a better name
 	uint32_t count;
 	uint32_t count;
 };
 };