Browse Source

Merge pull request #2037 from nikeinikei/main

vk: unify local uniform buffer
Sasha Szpakowski 1 year ago
parent
commit
f3887361e5

+ 25 - 6
src/modules/graphics/vulkan/Graphics.cpp

@@ -21,6 +21,7 @@
 #include "common/Exception.h"
 #include "common/pixelformat.h"
 #include "common/version.h"
+#include "common/memory.h"
 #include "window/Window.h"
 #include "Buffer.h"
 #include "Graphics.h"
@@ -153,6 +154,7 @@ Graphics::~Graphics()
 {
 	defaultConstantTexCoord.set(nullptr);
 	defaultConstantColor.set(nullptr);
+	localUniformBuffer.set(nullptr);
 
 	Volatile::unloadAll();
 	cleanup();
@@ -650,6 +652,9 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 		createSyncObjects();
 	}
 
+	if (localUniformBuffer == nullptr)
+		localUniformBuffer.set(new StreamBuffer(this, BUFFERUSAGE_UNIFORM, 1024 * 512 * 1), Acquire::NORETAIN);
+
 	beginFrame();
 
 	if (createBaseObjects)
@@ -1302,9 +1307,11 @@ void Graphics::beginFrame()
 
 	Vulkan::resetShaderSwitches();
 
-	for (const auto shader : usedShadersInFrame)
+	for (const auto &shader : usedShadersInFrame)
 		shader->newFrame();
 	usedShadersInFrame.clear();
+
+	localUniformBuffer->nextFrame();
 }
 
 void Graphics::startRecordingGraphicsCommands()
@@ -1330,11 +1337,6 @@ void Graphics::endRecordingGraphicsCommands() {
 		throw love::Exception("failed to record command buffer");
 }
 
-const VkDeviceSize Graphics::getMinUniformBufferOffsetAlignment() const
-{
-	return minUniformBufferOffsetAlignment;
-}
-
 VkCommandBuffer Graphics::getCommandBufferForDataTransfer()
 {
 	if (renderPassState.active)
@@ -2869,6 +2871,23 @@ int Graphics::getVsync() const
 	return vsync;
 }
 
+void Graphics::mapLocalUniformData(void *data, size_t size, VkDescriptorBufferInfo &bufferInfo)
+{
+	size_t alignedSize = alignUp(size, minUniformBufferOffsetAlignment);
+
+	if (localUniformBuffer->getUsableSize() < alignedSize)
+		localUniformBuffer.set(new StreamBuffer(this, BUFFERUSAGE_UNIFORM, localUniformBuffer->getSize() * 2), Acquire::NORETAIN);
+
+	auto mapInfo = localUniformBuffer->map(size);
+	memcpy(mapInfo.data, data, size);
+
+	bufferInfo.buffer = (VkBuffer)localUniformBuffer->getHandle();
+	bufferInfo.offset = localUniformBuffer->unmap(size);
+	bufferInfo.range = size;
+
+	localUniformBuffer->markUsed(alignedSize);
+}
+
 void Graphics::createColorResources()
 {
 	if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)

+ 2 - 1
src/modules/graphics/vulkan/Graphics.h

@@ -316,7 +316,6 @@ public:
 	void queueCleanUp(std::function<void()> cleanUp);
 	void addReadbackCallback(std::function<void()> callback);
 	void submitGpuCommands(SubmitMode, void *screenshotCallbackData = nullptr);
-	const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
 	VkSampler getCachedSampler(const SamplerState &sampler);
 	void setComputeShader(Shader *computeShader);
 	graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
@@ -325,6 +324,7 @@ public:
 	VkSampleCountFlagBits getMsaaCount(int requestedMsaa) const;
 	void setVsync(int vsync);
 	int getVsync() const;
+	void mapLocalUniformData(void *data, size_t size, VkDescriptorBufferInfo &bufferInfo);
 
 	uint32 getDeviceApiVersion() const { return deviceApiVersion; }
 
@@ -444,6 +444,7 @@ private:
 	VmaAllocator vmaAllocator = VK_NULL_HANDLE;
 	StrongRef<love::graphics::Buffer> defaultConstantColor;
 	StrongRef<love::graphics::Buffer> defaultConstantTexCoord;
+	StrongRef<StreamBuffer> localUniformBuffer;
 	// functions that need to be called to cleanup objects that were needed for rendering a frame.
 	// We need a vector for each frame in flight.
 	std::vector<std::vector<std::function<void()>>> cleanUpFunctions;

+ 1 - 58
src/modules/graphics/vulkan/Shader.cpp

@@ -36,7 +36,6 @@ namespace graphics
 namespace vulkan
 {
 
-static const uint32_t STREAMBUFFER_DEFAULT_SIZE = 16;
 static const uint32_t DESCRIPTOR_POOL_SIZE = 1000;
 
 class BindingMapper
@@ -157,14 +156,11 @@ bool Shader::loadVolatile()
 		builtinUniformInfo[i] = nullptr;
 
 	compileShaders();
-	calculateUniformBufferSizeAligned();
 	createDescriptorSetLayout();
 	createPipelineLayout();
 	createDescriptorPoolSizes();
-	createStreamBuffers();
 	descriptorPools.resize(MAX_FRAMES_IN_FLIGHT);
 	currentFrame = 0;
-	currentUsedUniformStreamBuffersCount = 0;
 	newFrame();
 
 	return true;
@@ -189,12 +185,8 @@ void Shader::unloadVolatile()
 			vkDestroyPipeline(device, computePipeline, nullptr);
 	});
 
-	for (const auto streamBuffer : streamBuffers)
-		streamBuffer->release();
-
 	shaderModules.clear();
 	shaderStages.clear();
-	streamBuffers.clear();
 	descriptorPools.clear();
 }
 
@@ -217,23 +209,8 @@ void Shader::newFrame()
 {
 	currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
 
-	currentUsedUniformStreamBuffersCount = 0;
 	currentDescriptorPool = 0;
 
-	if (streamBuffers.size() > 1)
-	{
-		size_t newSize = 0;
-		for (auto streamBuffer : streamBuffers)
-		{
-			newSize += streamBuffer->getSize();
-			streamBuffer->release();
-		}
-		streamBuffers.clear();
-		streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, newSize));
-	}
-	else if (streamBuffers.size() == 1)
-		streamBuffers.at(0)->nextFrame();
-
 	for (VkDescriptorPool pool : descriptorPools[currentFrame])
 		vkResetDescriptorPool(device, pool, 0);
 }
@@ -246,13 +223,6 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
 
 	if (!localUniformData.empty())
 	{
-		auto usedStreamBufferMemory = currentUsedUniformStreamBuffersCount * uniformBufferSizeAligned;
-		if (usedStreamBufferMemory >= streamBuffers.back()->getSize())
-		{
-			streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned));
-			currentUsedUniformStreamBuffersCount = 0;
-		}
-
 		if (builtinUniformDataOffset.hasValue)
 		{
 			auto builtinData = vgfx->getCurrentBuiltinUniformData();
@@ -260,19 +230,7 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
 			memcpy(dst, &builtinData, sizeof(builtinData));
 		}
 
-		auto currentStreamBuffer = streamBuffers.back();
-
-		auto mapInfo = currentStreamBuffer->map(uniformBufferSizeAligned);
-		memcpy(mapInfo.data, localUniformData.data(), localUniformData.size());
-		auto offset = currentStreamBuffer->unmap(uniformBufferSizeAligned);
-		currentStreamBuffer->markUsed(uniformBufferSizeAligned);
-
-		VkDescriptorBufferInfo &bufferInfo = descriptorBuffers[bufferIndex++];
-		bufferInfo.buffer = (VkBuffer)currentStreamBuffer->getHandle();
-		bufferInfo.offset = offset;
-		bufferInfo.range = localUniformData.size();
-
-		currentUsedUniformStreamBuffersCount++;
+		vgfx->mapLocalUniformData(localUniformData.data(), localUniformData.size(), descriptorBuffers[bufferIndex++]);
 	}
 
 	// TODO: iteration order must match the order at the end of compileShaders right now.
@@ -436,14 +394,6 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
 	}
 }
 
-void Shader::calculateUniformBufferSizeAligned()
-{
-	auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
-	size_t size = localUniformStagingData.size();
-	auto factor = static_cast<VkDeviceSize>(std::ceil(static_cast<float>(size) / static_cast<float>(minAlignment)));
-	uniformBufferSizeAligned = factor * minAlignment;
-}
-
 void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
 {
 	using namespace spirv_cross;
@@ -978,13 +928,6 @@ void Shader::createDescriptorPoolSizes()
 	}
 }
 
-void Shader::createStreamBuffers()
-{
-	size_t size = STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned;
-	if (size > 0)
-		streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, size));
-}
-
 void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture)
 {
 	std::array<graphics::Texture*, 3> textures = {

+ 0 - 8
src/modules/graphics/vulkan/Shader.h

@@ -88,27 +88,20 @@ public:
 	void setMainTex(graphics::Texture *texture);
 
 private:
-	void calculateUniformBufferSizeAligned();
 	void compileShaders();
 	void createDescriptorSetLayout();
 	void createPipelineLayout();
 	void createDescriptorPoolSizes();
-	void createStreamBuffers();
 	void buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename);
 	void createDescriptorPool();
 	VkDescriptorSet allocateDescriptorSet();
 
-	VkDeviceSize uniformBufferSizeAligned;
-
 	VkPipeline computePipeline;
 
 	VkDescriptorSetLayout descriptorSetLayout;
 	VkPipelineLayout pipelineLayout;
 	std::vector<VkDescriptorPoolSize> descriptorPoolSizes;
 
-	// we don't know how much memory we need per frame for the uniform buffer descriptors
-	// we keep a vector of stream buffers that gets dynamically increased if more memory is needed
-	std::vector<StreamBuffer*> streamBuffers;
 	std::vector<std::vector<VkDescriptorPool>> descriptorPools;
 
 	std::vector<VkDescriptorBufferInfo> descriptorBuffers;
@@ -135,7 +128,6 @@ private:
 	std::unordered_map<std::string, int> attributes;
 
 	uint32_t currentFrame;
-	uint32_t currentUsedUniformStreamBuffersCount;
 	uint32_t currentDescriptorPool;
 };