Browse Source

vulkan: move descriptor set logic into Shader

Using the extension VK_KHR_push_descriptor makes the code a lot easier.
Using it makes us able to set set descriptor sets dynamically.
niki 3 years ago
parent
commit
fb6457f57d

+ 20 - 172
src/modules/graphics/vulkan/Graphics.cpp

@@ -34,7 +34,10 @@ namespace love {
 			};
 			};
 
 
 			const std::vector<const char*> deviceExtensions = {
 			const std::vector<const char*> deviceExtensions = {
-				VK_KHR_SWAPCHAIN_EXTENSION_NAME
+				VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+
+				// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_push_descriptor.html
+				VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME
 			};
 			};
 
 
 #ifdef NDEBUG
 #ifdef NDEBUG
@@ -207,12 +210,10 @@ namespace love {
 				createSwapChain();
 				createSwapChain();
 				createImageViews();
 				createImageViews();
 				createDefaultShaders();
 				createDefaultShaders();
-				createDescriptorSetLayout();
 				createCommandPool();
 				createCommandPool();
 				createCommandBuffers();
 				createCommandBuffers();
 				createDefaultTexture();
 				createDefaultTexture();
 				createQuadIndexBuffer();
 				createQuadIndexBuffer();
-				createDescriptorPool();
 				createSyncObjects();
 				createSyncObjects();
 				startRecordingGraphicsCommands();
 				startRecordingGraphicsCommands();
 				currentFrame = 0;
 				currentFrame = 0;
@@ -520,10 +521,6 @@ namespace love {
 				}
 				}
 			}
 			}
 
 
-			void Graphics::setTexture(graphics::Texture* texture) {
-				currentTexture = texture;
-			}
-
 			void Graphics::updatedBatchedDrawBuffers() {
 			void Graphics::updatedBatchedDrawBuffers() {
 				batchedDrawState.vb[0] = batchedDrawBuffers[currentFrame].vertexBuffer1;
 				batchedDrawState.vb[0] = batchedDrawBuffers[currentFrame].vertexBuffer1;
 				batchedDrawState.vb[0]->nextFrame();
 				batchedDrawState.vb[0]->nextFrame();
@@ -533,30 +530,12 @@ namespace love {
 				batchedDrawState.indexBuffer->nextFrame();
 				batchedDrawState.indexBuffer->nextFrame();
 			}
 			}
 
 
-			VkDescriptorSet* Graphics::getDescriptorSet(int currentFrame) {
-				DecriptorSetConfiguration config{};
-				config.texture = currentTexture;
-				config.buffer = getUniformBuffer();
-				for (auto i = 0; i < descriptorSetsMap.size(); i++) {
-					if (descriptorSetsMap[i].first == config) {
-						return &descriptorSetsMap[i].second[currentFrame];
-					}
-				}
-				auto descriptorSets = createDescriptorSets(config);
-				descriptorSetsMap.push_back(std::make_pair(config, descriptorSets));
-				return &descriptorSetsMap.back().second[currentFrame];
+			uint32_t Graphics::getNumImagesInFlight() const {
+				return MAX_FRAMES_IN_FLIGHT;
 			}
 			}
 
 
-			graphics::StreamBuffer* Graphics::getUniformBuffer() {
-				auto data = getCurrentBuiltinUniformData();
-				for (auto &it : uniformBufferMap) {
-					if (it.first == data) {
-						return it.second;
-					}
-				}
-				auto buffer = createUniformBufferFromData(data);
-				uniformBufferMap.push_back(std::make_pair(data, buffer));
-				return buffer;
+			const PFN_vkCmdPushDescriptorSetKHR Graphics::getVkCmdPushDescriptorSetKHRFunctionPointer() const {
+				return vkCmdPushDescriptorSet;
 			}
 			}
 
 
 			VkCommandBuffer Graphics::beginSingleTimeCommands() {
 			VkCommandBuffer Graphics::beginSingleTimeCommands() {
@@ -629,15 +608,6 @@ namespace love {
 				return data;
 				return data;
 			}
 			}
 
 
-			graphics::StreamBuffer* Graphics::createUniformBufferFromData(graphics::Shader::BuiltinUniformData data) {
-				auto buffer = newStreamBuffer(BUFFERUSAGE_UNIFORM, sizeof(data));
-				auto mappedInfo = buffer->map(0);
-				memcpy(mappedInfo.data, &data, sizeof(data));
-				buffer->unmap(0);
-
-				return buffer;
-			}
-
 			void Graphics::createVulkanInstance() {
 			void Graphics::createVulkanInstance() {
 				if (enableValidationLayers && !checkValidationSupport()) {
 				if (enableValidationLayers && !checkValidationSupport()) {
 					throw love::Exception("validation layers requested, but not available");
 					throw love::Exception("validation layers requested, but not available");
@@ -893,6 +863,11 @@ namespace love {
 
 
 				vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
 				vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
 				vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
 				vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
+
+				vkCmdPushDescriptorSet = (PFN_vkCmdPushDescriptorSetKHR) vkGetDeviceProcAddr(device, "vkCmdPushDescriptorSetKHR");
+				if (!vkCmdPushDescriptorSet) {
+					throw love::Exception("could not get a valid function pointer for vkCmdPushDescriptorSetKHR");
+				}
 			}
 			}
 
 
 			void Graphics::initVMA() {
 			void Graphics::initVMA() {
@@ -1082,114 +1057,6 @@ namespace love {
 				}
 				}
 			}
 			}
 
 
-			void Graphics::createDescriptorSetLayout() {
-				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;
-
-				std::array<VkDescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
-				VkDescriptorSetLayoutCreateInfo layoutInfo{};
-				layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
-				layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
-				layoutInfo.pBindings = bindings.data();
-
-				if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
-					throw love::Exception("failed to create descriptor set layout");
-				}
-			}
-
-			void Graphics::createDescriptorPool() {
-				std::array<VkDescriptorPoolSize, 2> poolSizes{};
-				poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-				poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
-				poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-				poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
-
-				VkDescriptorPoolCreateInfo poolInfo{};
-				poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
-				poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
-				poolInfo.pPoolSizes = poolSizes.data();
-				// FIXME: When using more than 128 textures at once we will run out of memory.
-				// we probably want to reuse descriptors per flight image
-				// and use multiple pools in case of too many allocations
-				poolInfo.maxSets = 128 * static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
-
-				if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
-					throw love::Exception("failed to create descriptor pool");
-				}
-			}
-
-			std::vector<VkDescriptorSet> Graphics::createDescriptorSets(DecriptorSetConfiguration config) {
-				std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout);
-				VkDescriptorSetAllocateInfo allocInfo{};
-				allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
-				allocInfo.descriptorPool = descriptorPool;
-				allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
-				allocInfo.pSetLayouts = layouts.data();
-
-				std::vector<VkDescriptorSet> newDescriptorSets;
-
-				newDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
-				VkResult result = vkAllocateDescriptorSets(device, &allocInfo, newDescriptorSets.data());
-				if (result != VK_SUCCESS) {
-					switch (result) {
-					case VK_ERROR_OUT_OF_HOST_MEMORY:
-						throw love::Exception("failed to allocate descriptor sets: out of host memory");
-					case VK_ERROR_OUT_OF_DEVICE_MEMORY:
-						throw love::Exception("failed to allocate descriptor sets: out of device memory");
-					case VK_ERROR_FRAGMENTED_POOL:
-						throw love::Exception("failed to allocate descriptor sets: fragmented pool");
-					case VK_ERROR_OUT_OF_POOL_MEMORY:
-						throw love::Exception("failed to allocate descriptor sets: out of pool memory");
-					default:
-						throw love::Exception("failed to allocate descriptor sets");
-					}
-				}
-
-				for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
-					VkDescriptorBufferInfo bufferInfo{};
-					bufferInfo.buffer = (VkBuffer)config.buffer->getHandle();
-					bufferInfo.offset = 0;
-					bufferInfo.range = sizeof(graphics::Shader::BuiltinUniformData);
-
-					VkDescriptorImageInfo imageInfo{};
-					imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
-					Texture* vkTexture = (Texture*)config.texture;
-					imageInfo.imageView = vkTexture->getImageView();
-					imageInfo.sampler = vkTexture->getSampler();
-
-					std::array<VkWriteDescriptorSet, 2> descriptorWrite{};
-					descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-					descriptorWrite[0].dstSet = newDescriptorSets[i];
-					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 = newDescriptorSets[i];
-					descriptorWrite[1].dstBinding = 1;
-					descriptorWrite[1].dstArrayElement = 0;
-					descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-					descriptorWrite[1].descriptorCount = 1;
-					descriptorWrite[1].pImageInfo = &imageInfo;
-
-					vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrite.size()), descriptorWrite.data(), 0, nullptr);
-				}
-
-				return newDescriptorSets;
-			}
-
 			void Graphics::createVulkanVertexFormat(
 			void Graphics::createVulkanVertexFormat(
 				VertexAttributes vertexAttributes, 
 				VertexAttributes vertexAttributes, 
 				bool& useConstantVertexColor,
 				bool& useConstantVertexColor,
@@ -1305,16 +1172,19 @@ namespace love {
 					offsets.push_back((VkDeviceSize)0);
 					offsets.push_back((VkDeviceSize)0);
 				}
 				}
 
 
+				auto currentUniformData = getCurrentBuiltinUniformData();
+				configuration.shader->setUniformData(currentUniformData);
 				if (texture == nullptr) {
 				if (texture == nullptr) {
-					setTexture(standardTexture.get());
+					configuration.shader->setMainTex(standardTexture.get());
 				}
 				}
 				else {
 				else {
-					setTexture(texture);
+					configuration.shader->setMainTex(texture);
 				}
 				}
+				configuration.shader->setVideoTextures(standardTexture.get(), standardTexture.get(), standardTexture.get());
 
 
 				ensureGraphicsPipelineConfiguration(configuration);
 				ensureGraphicsPipelineConfiguration(configuration);
 
 
-				vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, getDescriptorSet(currentFrame), 0, nullptr);
+				configuration.shader->cmdPushDescriptorSets(commandBuffers.at(imageIndex), currentFrame);
 				vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast<uint32_t>(bufferVector.size()), bufferVector.data(), offsets.data());
 				vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast<uint32_t>(bufferVector.size()), bufferVector.data(), offsets.data());
 			}
 			}
 
 
@@ -1451,17 +1321,6 @@ namespace love {
 				colorBlending.blendConstants[2] = 0.0f;
 				colorBlending.blendConstants[2] = 0.0f;
 				colorBlending.blendConstants[3] = 0.0f;
 				colorBlending.blendConstants[3] = 0.0f;
 
 
-				VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
-				pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
-				pipelineLayoutInfo.setLayoutCount = 1;
-				pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
-				pipelineLayoutInfo.pushConstantRangeCount = 0;
-
-				if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
-					throw love::Exception("failed to create pipeline layout");
-				}
-				graphicsPipelineLayouts.push_back(pipelineLayout);
-
 				VkFormat framebufferOutputFormat = configuration.framebufferFormat;
 				VkFormat framebufferOutputFormat = configuration.framebufferFormat;
 
 
 				VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{};
 				VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{};
@@ -1481,7 +1340,7 @@ namespace love {
 				pipelineInfo.pDepthStencilState = nullptr;
 				pipelineInfo.pDepthStencilState = nullptr;
 				pipelineInfo.pColorBlendState = &colorBlending;
 				pipelineInfo.pColorBlendState = &colorBlending;
 				pipelineInfo.pDynamicState = nullptr;
 				pipelineInfo.pDynamicState = nullptr;
-				pipelineInfo.layout = pipelineLayout;
+				pipelineInfo.layout = configuration.shader->getGraphicsPipelineLayout();
 				pipelineInfo.subpass = 0;
 				pipelineInfo.subpass = 0;
 				pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 				pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 				pipelineInfo.basePipelineIndex = -1;
 				pipelineInfo.basePipelineIndex = -1;
@@ -1663,7 +1522,6 @@ namespace love {
 					vkDestroyFence(device, inFlightFences[i], nullptr);
 					vkDestroyFence(device, inFlightFences[i], nullptr);
 				}
 				}
 
 
-				vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
 				vkDestroyCommandPool(device, commandPool, nullptr);
 				vkDestroyCommandPool(device, commandPool, nullptr);
 				vkDestroyDevice(device, nullptr);
 				vkDestroyDevice(device, nullptr);
 				vkDestroySurfaceKHR(instance, surface, nullptr);
 				vkDestroySurfaceKHR(instance, surface, nullptr);
@@ -1678,19 +1536,10 @@ namespace love {
 				}
 				}
 				graphicsPipelines.clear();
 				graphicsPipelines.clear();
 				currentGraphicsPipeline = VK_NULL_HANDLE;
 				currentGraphicsPipeline = VK_NULL_HANDLE;
-				for (const auto pipelineLayout : graphicsPipelineLayouts) {
-					vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
-				}
-				graphicsPipelineLayouts.clear();
 				for (size_t i = 0; i < swapChainImageViews.size(); i++) {
 				for (size_t i = 0; i < swapChainImageViews.size(); i++) {
 					vkDestroyImageView(device, swapChainImageViews[i], nullptr);
 					vkDestroyImageView(device, swapChainImageViews[i], nullptr);
 				}
 				}
 				vkDestroySwapchainKHR(device, swapChain, nullptr);
 				vkDestroySwapchainKHR(device, swapChain, nullptr);
-				for (auto p : uniformBufferMap) {
-					delete p.second;
-				}
-				uniformBufferMap.clear();
-				descriptorSetsMap.clear();
 			}
 			}
 
 
 			void Graphics::recreateSwapChain() {
 			void Graphics::recreateSwapChain() {
@@ -1700,7 +1549,6 @@ namespace love {
 
 
 				createSwapChain();
 				createSwapChain();
 				createImageViews();
 				createImageViews();
-				createDescriptorPool();
 				createCommandBuffers();
 				createCommandBuffers();
 				startRecordingGraphicsCommands();
 				startRecordingGraphicsCommands();
 			}
 			}

+ 4 - 20
src/modules/graphics/vulkan/Graphics.h

@@ -37,15 +37,6 @@ namespace love {
 				friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
 				friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
 			};
 			};
 
 
-			struct DecriptorSetConfiguration {
-				graphics::Texture* texture;
-				graphics::StreamBuffer* buffer;
-
-				bool operator==(const DecriptorSetConfiguration& conf2) {
-					return this->texture == conf2.texture && this->buffer == conf2.buffer;
-				}
-			};
-
 			struct BatchedDrawBuffers {
 			struct BatchedDrawBuffers {
 				StreamBuffer* vertexBuffer1;
 				StreamBuffer* vertexBuffer1;
 				StreamBuffer* vertexBuffer2;
 				StreamBuffer* vertexBuffer2;
@@ -125,6 +116,9 @@ namespace love {
 				VkCommandBuffer beginSingleTimeCommands();
 				VkCommandBuffer beginSingleTimeCommands();
 				void endSingleTimeCommands(VkCommandBuffer);
 				void endSingleTimeCommands(VkCommandBuffer);
 
 
+				uint32_t getNumImagesInFlight() const;
+				const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const;
+
 			protected:
 			protected:
 				graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { 
 				graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { 
 					return new ShaderStage(this, stage, source, gles, cachekey); 
 					return new ShaderStage(this, stage, source, gles, cachekey); 
@@ -155,9 +149,6 @@ namespace love {
 				void createSwapChain();
 				void createSwapChain();
 				void createImageViews();
 				void createImageViews();
 				void createDefaultShaders();
 				void createDefaultShaders();
-				void createDescriptorSetLayout();
-				void createDescriptorPool();
-				std::vector<VkDescriptorSet> createDescriptorSets(DecriptorSetConfiguration);
 				VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
 				VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
 				void createCommandPool();
 				void createCommandPool();
 				void createCommandBuffers();
 				void createCommandBuffers();
@@ -170,12 +161,8 @@ namespace love {
 				void startRecordingGraphicsCommands();
 				void startRecordingGraphicsCommands();
 				void endRecordingGraphicsCommands();
 				void endRecordingGraphicsCommands();
 				void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
 				void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
-				graphics::StreamBuffer* createUniformBufferFromData(graphics::Shader::BuiltinUniformData);
 				graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
 				graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
-				void setTexture(graphics::Texture* texture);
 				void updatedBatchedDrawBuffers();
 				void updatedBatchedDrawBuffers();
-				VkDescriptorSet* getDescriptorSet(int currentFrame);
-				graphics::StreamBuffer* getUniformBuffer();
 				void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
 				void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
 				void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
 				void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
 				void startRenderPass(Texture*, uint32_t w, uint32_t h);
 				void startRenderPass(Texture*, uint32_t w, uint32_t h);
@@ -192,11 +179,9 @@ namespace love {
 				VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED;
 				VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED;
 				VkExtent2D swapChainExtent = VkExtent2D();
 				VkExtent2D swapChainExtent = VkExtent2D();
 				std::vector<VkImageView> swapChainImageViews;
 				std::vector<VkImageView> swapChainImageViews;
-				VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
 				VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
 				VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
 				VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
 				VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
 				std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines;	// FIXME improve performance by using a hash map
 				std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines;	// FIXME improve performance by using a hash map
-				std::vector<VkPipelineLayout> graphicsPipelineLayouts;
 				VkCommandPool commandPool = VK_NULL_HANDLE;
 				VkCommandPool commandPool = VK_NULL_HANDLE;
 				std::vector<VkCommandBuffer> commandBuffers;
 				std::vector<VkCommandBuffer> commandBuffers;
 				VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
 				VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
@@ -204,6 +189,7 @@ namespace love {
 				std::vector<VkSemaphore> renderFinishedSemaphores;
 				std::vector<VkSemaphore> renderFinishedSemaphores;
 				std::vector<VkFence> inFlightFences;
 				std::vector<VkFence> inFlightFences;
 				std::vector<VkFence> imagesInFlight;
 				std::vector<VkFence> imagesInFlight;
+				PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
 				size_t currentFrame = 0;
 				size_t currentFrame = 0;
 				uint32_t imageIndex = 0;
 				uint32_t imageIndex = 0;
 				bool framebufferResized = false;
 				bool framebufferResized = false;
@@ -214,8 +200,6 @@ namespace love {
 				// and we can't (or shouldn't) update the contents of the buffers while they're still in flight / being rendered.
 				// and we can't (or shouldn't) update the contents of the buffers while they're still in flight / being rendered.
 				std::vector<BatchedDrawBuffers> batchedDrawBuffers;
 				std::vector<BatchedDrawBuffers> batchedDrawBuffers;
 				graphics::Texture* currentTexture = nullptr;
 				graphics::Texture* currentTexture = nullptr;
-				std::vector<std::pair<graphics::Shader::BuiltinUniformData, graphics::StreamBuffer*>> uniformBufferMap;
-				std::vector<std::pair<DecriptorSetConfiguration, std::vector<VkDescriptorSet>>> descriptorSetsMap;
 				VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
 				VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
 
 
 				// render pass variables.
 				// render pass variables.

+ 215 - 23
src/modules/graphics/vulkan/Shader.cpp

@@ -117,6 +117,8 @@ namespace love {
 				}
 				}
 			};
 			};
 
 
+			static const uint32_t STREAMBUFFER_SIZE = 1024;
+
 			static VkShaderStageFlagBits getStageBit(ShaderStageType type) {
 			static VkShaderStageFlagBits getStageBit(ShaderStageType type) {
 				switch (type) {
 				switch (type) {
 				case SHADERSTAGE_VERTEX:
 				case SHADERSTAGE_VERTEX:
@@ -148,12 +150,150 @@ namespace love {
 			}
 			}
 
 
 			bool Shader::loadVolatile() {
 			bool Shader::loadVolatile() {
+				compileShaders();
+				createDescriptorSetLayout();
+				createPipelineLayout();
+				createStreamBuffers();
+				currentImage = 0;
+				count = 0;
+
+				return true;
+			}
+
+			void Shader::unloadVolatile() {
+				if (shaderModules.size() == 0) {
+					return;
+				}
+
+				auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
+				auto device = gfx->getDevice();
+				// fixme: we shouldn't do a greedy wait here.
+				vkDeviceWaitIdle(device);
+				for (const auto shaderModule : shaderModules) {
+					vkDestroyShaderModule(device, shaderModule, nullptr);
+				}
+				shaderModules.clear();
+				shaderStages.clear();
+				vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
+				vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
+				for (const auto streamBuffer : streamBuffers) {
+					delete streamBuffer;
+				}
+				streamBuffers.clear();
+			}
+
+			const std::vector<VkPipelineShaderStageCreateInfo>& Shader::getShaderStages() const {
+				return shaderStages;
+			}
+
+			const VkPipelineLayout Shader::getGraphicsPipelineLayout() const {
+				return pipelineLayout;
+			}
+
+			static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* texture) {
+				VkDescriptorImageInfo imageInfo{};
+				imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+				Texture* vkTexture = (Texture*)texture;
+				imageInfo.imageView = vkTexture->getImageView();
+				imageInfo.sampler = vkTexture->getSampler();
+				return imageInfo;
+			}
+
+			void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
+				if (currentImage != imageIndex) {
+					currentImage = imageIndex;
+					count = 0;
+				}
+				else {
+					if (count >= STREAMBUFFER_SIZE) {
+						throw love::Exception("uniform stream buffer: out of memory (fixme: resize)");
+					}
+				}
+
+				auto mapInfo = streamBuffers[currentImage]->map(sizeof(BuiltinUniformData));
+				memcpy(mapInfo.data, &uniformData, sizeof(BuiltinUniformData));
+				streamBuffers[currentImage]->unmap(sizeof(BuiltinUniformData));
+				streamBuffers[currentImage]->markUsed(sizeof(BuiltinUniformData));
+
+				VkDescriptorBufferInfo bufferInfo{};
+				bufferInfo.buffer = (VkBuffer)streamBuffers[currentImage]->getHandle();
+				bufferInfo.offset = count * sizeof(BuiltinUniformData);
+				bufferInfo.range = sizeof(graphics::Shader::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());
+			}
+
+			Shader::~Shader() {
+				unloadVolatile();
+			}
+
+			void Shader::attach() {
+				if (Shader::current != this) {
+					Graphics::flushBatchedDrawsGlobal();
+					Shader::current = this;
+					Vulkan::shaderSwitch();
+				}
+			}
+
+			int Shader::getVertexAttributeIndex(const std::string& name) {
+				return vertexAttributeIndices.at(name);
+			}
+
+			void Shader::compileShaders() {
 				using namespace glslang;
 				using namespace glslang;
 				using namespace spirv_cross;
 				using namespace spirv_cross;
 
 
 				TProgram* program = new TProgram();
 				TProgram* program = new TProgram();
 
 
-				auto gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
+				gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
+				auto vgfx = (Graphics*)gfx;
+				device = vgfx->getDevice();
 
 
 				for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
 				for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
 					if (!stages[i])
 					if (!stages[i])
@@ -240,40 +380,92 @@ namespace love {
 
 
 					shaderStages.push_back(shaderStageInfo);
 					shaderStages.push_back(shaderStageInfo);
 				}
 				}
+			}
 
 
-				return true;
+			// 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 };
+				VkDescriptorSetLayoutCreateInfo layoutInfo{};
+				layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+				layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
+				layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
+				layoutInfo.pBindings = bindings.data();
+
+				if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
+					throw love::Exception("failed to create descriptor set layout");
+				}
 			}
 			}
 
 
-			void Shader::unloadVolatile() {
-				if (shaderModules.size() == 0) {
-					return;
+			void Shader::createPipelineLayout() {
+				VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
+				pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+				pipelineLayoutInfo.setLayoutCount = 1;
+				pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
+				pipelineLayoutInfo.pushConstantRangeCount = 0;
+
+				if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
+					throw love::Exception("failed to create pipeline layout");
 				}
 				}
+			}
 
 
-				auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
-				auto device = gfx->getDevice();
-				// fixme: we shouldn't do a greedy wait here.
-				vkDeviceWaitIdle(device);
-				for (const auto shaderModule : shaderModules) {
-					vkDestroyShaderModule(device, shaderModule, nullptr);
+			void Shader::createStreamBuffers() {
+				auto vgfx = (Graphics*)gfx;
+				const auto numImagesInFlight = vgfx->getNumImagesInFlight();
+				streamBuffers.resize(numImagesInFlight);
+				for (uint32_t i = 0; i < numImagesInFlight; i++) {
+					streamBuffers[i] = new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_SIZE * sizeof(BuiltinUniformData));
 				}
 				}
-				shaderModules.clear();
-				shaderStages.clear();
 			}
 			}
 
 
-			Shader::~Shader() {
-				unloadVolatile();
+			void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) {
+				this->ytexture = ytexture;
+				this->cbtexture = cbtexture;
+				this->crtexture = crtexture;
 			}
 			}
 
 
-			void Shader::attach() {
-				if (Shader::current != this) {
-					Graphics::flushBatchedDrawsGlobal();
-					Shader::current = this;
-					Vulkan::shaderSwitch();
-				}
+			void Shader::setUniformData(BuiltinUniformData& data) {
+				uniformData = data;
 			}
 			}
 
 
-			int Shader::getVertexAttributeIndex(const std::string& name) {
-				return vertexAttributeIndices.at(name);
+			void Shader::setMainTex(graphics::Texture* texture) {
+				mainTex = texture;
 			}
 			}
 		}
 		}
 	}
 	}

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

@@ -21,9 +21,11 @@ namespace love {
 				bool loadVolatile() override;
 				bool loadVolatile() override;
 				void unloadVolatile() override;
 				void unloadVolatile() override;
 
 
-				const std::vector<VkPipelineShaderStageCreateInfo>& getShaderStages() const {
-					return shaderStages;
-				}
+				const std::vector<VkPipelineShaderStageCreateInfo>& getShaderStages() const;
+
+				const VkPipelineLayout getGraphicsPipelineLayout() const;
+
+				void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage);
 
 
 				void attach() override;
 				void attach() override;
 
 
@@ -43,11 +45,29 @@ namespace love {
 
 
 				bool hasUniform(const std::string& name) const override { return false; }
 				bool hasUniform(const std::string& name) const override { return false; }
 
 
-				void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override {}
+				void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override;
+
+				// fixme: use normal methods for this in the future.
+				void setUniformData(BuiltinUniformData& data);
+				void setMainTex(graphics::Texture* texture);
 
 
 			private:
 			private:
+				void compileShaders();
+				void createDescriptorSetLayout();
+				void createPipelineLayout();
+				void createStreamBuffers();
+
+				PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet;
+
+				VkDescriptorSetLayout descriptorSetLayout;
+				VkPipelineLayout pipelineLayout;
+
+				std::vector<StreamBuffer*> streamBuffers;
+
 				std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
 				std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
 				std::vector<VkShaderModule> shaderModules;
 				std::vector<VkShaderModule> shaderModules;
+				Graphics* gfx;
+				VkDevice device;
 
 
 				std::map<std::string, int> vertexAttributeIndices = {
 				std::map<std::string, int> vertexAttributeIndices = {
 					{ "VertexPosition", 0 },
 					{ "VertexPosition", 0 },
@@ -63,7 +83,14 @@ namespace love {
 					{ "MainTex", 4 }
 					{ "MainTex", 4 }
 				};
 				};
 
 
-				std::map<std::string, UniformInfo> uniforms;
+				BuiltinUniformData uniformData;
+				graphics::Texture* mainTex;
+				graphics::Texture* ytexture;
+				graphics::Texture* cbtexture;
+				graphics::Texture* crtexture;
+
+				uint32_t currentImage;
+				uint32_t count;
 			};
 			};
 		}
 		}
 	}
 	}