Browse Source

vulkan: dynamically increase size of available memory for uniforms

niki 3 years ago
parent
commit
845fd16b4b
2 changed files with 40 additions and 12 deletions
  1. 35 11
      src/modules/graphics/vulkan/Shader.cpp
  2. 5 1
      src/modules/graphics/vulkan/Shader.h

+ 35 - 11
src/modules/graphics/vulkan/Shader.cpp

@@ -117,7 +117,7 @@ static const TBuiltInResource defaultTBuiltInResource = {
 	}
 };
 
-static const uint32_t STREAMBUFFER_SIZE = 1024;
+static const uint32_t STREAMBUFFER_DEFAULT_SIZE = 16;
 
 static VkShaderStageFlagBits getStageBit(ShaderStageType type) {
 	switch (type) {
@@ -174,8 +174,10 @@ void Shader::unloadVolatile() {
 		vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
 		vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
 	});
-	for (const auto streamBuffer : streamBuffers) {
-		delete streamBuffer;
+	for (const auto streamBufferVector : streamBuffers) {
+		for (const auto streamBuffer : streamBufferVector) {
+			delete streamBuffer;
+		}
 	}
 	shaderModules.clear();
 	shaderStages.clear();
@@ -200,24 +202,46 @@ static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* textur
 }
 
 void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
+	// detect wether a new frame has begun
 	if (currentImage != imageIndex) {
 		currentImage = imageIndex;
 		count = 0;
-		streamBuffers[currentImage]->nextFrame();
+
+		// we needed more memory last frame, let's collapse all buffers into a single one.
+		if (streamBuffers.at(currentImage).size() > 1) {
+			size_t newSize = 0;
+			for (auto streamBuffer : streamBuffers.at(currentImage)) {
+				newSize += streamBuffer->getSize();
+				delete streamBuffer;
+			}
+			streamBuffers.at(currentImage).clear();
+			streamBuffers.at(currentImage).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();
+		}
 	}
+	// still the same frame
 	else {
-		if (count >= STREAMBUFFER_SIZE) {
-			throw love::Exception("uniform stream buffer: out of memory (fixme: resize)");
+		auto usedStreamBufferMemory = count * uniformBufferSizeAligned;
+		if (usedStreamBufferMemory >= streamBuffers.at(currentImage).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));
+			count = 0;
 		}
 	}
 
-	auto mapInfo = streamBuffers[currentImage]->map(uniformBufferSizeAligned);
+	// additional data is always added onto the last stream buffer in the current frame
+	auto currentStreamBuffer = streamBuffers.at(currentImage).back();
+
+	auto mapInfo = currentStreamBuffer->map(uniformBufferSizeAligned);
 	memcpy(mapInfo.data, &uniformData, uniformBufferSizeAligned);
-	streamBuffers[currentImage]->unmap(uniformBufferSizeAligned);
-	streamBuffers[currentImage]->markUsed(uniformBufferSizeAligned);
+	currentStreamBuffer->unmap(uniformBufferSizeAligned);
+	currentStreamBuffer->markUsed(uniformBufferSizeAligned);
 
 	VkDescriptorBufferInfo bufferInfo{};
-	bufferInfo.buffer = (VkBuffer)streamBuffers[currentImage]->getHandle();
+	bufferInfo.buffer = (VkBuffer)currentStreamBuffer->getHandle();
 	bufferInfo.offset = count * uniformBufferSizeAligned;
 	bufferInfo.range = sizeof(BuiltinUniformData);
 	
@@ -471,7 +495,7 @@ void Shader::createStreamBuffers() {
 	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 * uniformBufferSizeAligned);
+		streamBuffers[i].push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned));
 	}
 }
 

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

@@ -64,7 +64,10 @@ private:
 	VkDescriptorSetLayout descriptorSetLayout;
 	VkPipelineLayout pipelineLayout;
 
-	std::vector<StreamBuffer*> streamBuffers;
+	// we don't know how much memory we need per frame for the uniform buffer descriptors
+	// we keep a vector of stream buffers per frame in flight
+	// that gets dynamically increased if more memory is needed
+	std::vector<std::vector<StreamBuffer*>> streamBuffers;
 
 	std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
 	std::vector<VkShaderModule> shaderModules;
@@ -92,6 +95,7 @@ private:
 	graphics::Texture* crtexture;
 
 	uint32_t currentImage;
+	// todo: give this variable a better name
 	uint32_t count;
 };
 }