Browse Source

vulkan: implement storage and texel buffers

niki 2 years ago
parent
commit
ccf8cb45c9

+ 22 - 8
src/modules/graphics/vulkan/Buffer.cpp

@@ -15,6 +15,7 @@ static VkBufferUsageFlags getUsageBit(BufferUsage mode)
 	case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
 	case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
 	case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+	case BUFFERUSAGE_TEXEL: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
 	default:
 		throw love::Exception("unsupported BufferUsage mode");
 	}
@@ -47,16 +48,27 @@ bool Buffer::loadVolatile()
 	VkBufferCreateInfo bufferInfo{};
 	bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
 	bufferInfo.size = getSize();
-	if (dataUsage == BUFFERDATAUSAGE_READBACK)
-		bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
-	else
-		bufferInfo.usage = getVulkanUsageFlags(usageFlags);
+	bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | getVulkanUsageFlags(usageFlags);
 
 	VmaAllocationCreateInfo allocCreateInfo = {};
 	allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
 	allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
 
-	vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
+	auto result = vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo);
+	if (result != VK_SUCCESS)
+		throw love::Exception("failed to create buffer");
+
+	if (usageFlags & BUFFERUSAGE_TEXEL)
+	{
+		VkBufferViewCreateInfo bufferViewInfo{};
+		bufferViewInfo.buffer = buffer;
+		bufferViewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
+		bufferViewInfo.format = Vulkan::getVulkanVertexFormat(getDataMember(0).decl.format);
+		bufferViewInfo.range = VK_WHOLE_SIZE;
+
+		if (vkCreateBufferView(vgfx->getDevice(), &bufferViewInfo, nullptr, &bufferView) != VK_SUCCESS)
+			throw love::Exception("failed to create texel buffer view");
+	}
 
 	return true;
 }
@@ -69,12 +81,15 @@ void Buffer::unloadVolatile()
 	auto device = vgfx->getDevice();
 
 	vgfx->queueCleanUp(
-		[device=device, allocator=allocator, buffer=buffer, allocation=allocation](){
+		[device=device, allocator=allocator, buffer=buffer, allocation=allocation, bufferView=bufferView](){
 		vkDeviceWaitIdle(device);
 		vmaDestroyBuffer(allocator, buffer, allocation);
+		if (bufferView)
+			vkDestroyBufferView(device, bufferView, nullptr);
 	});
 
 	buffer = VK_NULL_HANDLE;
+	bufferView = VK_NULL_HANDLE;
 }
 
 Buffer::~Buffer()
@@ -89,8 +104,7 @@ ptrdiff_t Buffer::getHandle() const
 
 ptrdiff_t Buffer::getTexelBufferHandle() const
 {
-	throw love::Exception("unimplemented Buffer::getTexelBufferHandle");
-	return (ptrdiff_t) nullptr;	// todo ?
+	return (ptrdiff_t) bufferView;
 }
 
 void* Buffer::map(MapType map, size_t offset, size_t size)

+ 1 - 0
src/modules/graphics/vulkan/Buffer.h

@@ -36,6 +36,7 @@ public:
 private:
 	// todo use a staging buffer for improved performance
 	VkBuffer buffer = VK_NULL_HANDLE;
+	VkBufferView bufferView = VK_NULL_HANDLE;
 	Graphics *vgfx = nullptr;
 	VmaAllocator allocator;
 	VmaAllocation allocation;

+ 86 - 68
src/modules/graphics/vulkan/Shader.cpp

@@ -270,63 +270,7 @@ void Shader::newFrame(uint32_t frameIndex)
 
 	currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
 
-	// update everything other than uniform buffers
-	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 = currentDescriptorSet;
-			write.dstBinding = val.location;
-			write.dstArrayElement = 0;
-			write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-			write.descriptorCount = val.count;
-
-			std::vector<VkDescriptorImageInfo> imageInfos;
-
-			for (int i = 0; i < val.count; i++)
-			{
-				auto vkTexture = dynamic_cast<Texture*>(val.textures[i]);
-
-				VkDescriptorImageInfo imageInfo{};
-				imageInfo.imageLayout = vkTexture->getImageLayout();
-				imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
-				imageInfo.sampler = (VkSampler)vkTexture->getSamplerHandle();
-
-				imageInfos.push_back(imageInfo);
-			}
-
-			write.pImageInfo = imageInfos.data();
-
-			vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
-		}
-		if (val.baseType == UNIFORM_STORAGETEXTURE) {
-			VkWriteDescriptorSet write{};
-			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-			write.dstSet = currentDescriptorSet;
-			write.dstBinding = val.location;
-			write.dstArrayElement = 0;
-			write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
-			write.descriptorCount = val.count;
-
-			std::vector<VkDescriptorImageInfo> imageInfos;
-
-			for (int i = 0; i < val.count; i++)
-			{
-				auto vkTexture = dynamic_cast<Texture*>(val.textures[i]);
-
-				VkDescriptorImageInfo imageInfo{};
-				imageInfo.imageLayout = vkTexture->getImageLayout();
-				imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
-
-				imageInfos.push_back(imageInfo);
-			}
-
-			write.pImageInfo = imageInfos.data();
-
-			vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
-		}
-	}
+	initDescriptorSet();
 }
 
 void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint bindPoint)
@@ -409,6 +353,8 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
 		descriptorSetsVector.at(currentFrame).push_back(allocateDescriptorSet());
 
 	currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
+
+	initDescriptorSet();
 }
 
 Shader::~Shader()
@@ -465,12 +411,17 @@ static bool usesLocalUniformData(const graphics::Shader::UniformInfo *info)
 
 void Shader::updateUniform(const UniformInfo *info, int count)
 {
-	if (current == this)
+	updateUniform(info, count, false);
+}
+
+void Shader::updateUniform(const UniformInfo* info, int count, bool internal)
+{
+	if (!internal && current == this)
 		Graphics::flushBatchedDrawsGlobal();
 
 	if (usesLocalUniformData(info))
 		memcpy(localUniformData.data(), localUniformStagingData.data(), localUniformStagingData.size());
-	else if (info->baseType == UNIFORM_SAMPLER || info->baseType == UNIFORM_STORAGETEXTURE)
+	if (info->baseType == UNIFORM_SAMPLER || info->baseType == UNIFORM_STORAGETEXTURE)
 	{
 		bool isSampler = info->baseType == UNIFORM_SAMPLER;
 
@@ -495,10 +446,58 @@ void Shader::updateUniform(const UniformInfo *info, int count)
 		write.dstSet = currentDescriptorSet;
 		write.dstBinding = info->location;
 		write.dstArrayElement = 0;
-		write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+		if (isSampler)
+			write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+		else
+			write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
 		write.descriptorCount = static_cast<uint32_t>(count);
 		write.pImageInfo = imageInfos.data();
 
+		vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
+	}
+	if (info->baseType == UNIFORM_STORAGEBUFFER)
+	{
+		VkWriteDescriptorSet write{};
+		write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+		write.dstSet = currentDescriptorSet;
+		write.dstBinding = info->location;
+		write.dstArrayElement = 0;
+		write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+		write.descriptorCount = info->count;
+
+		std::vector<VkDescriptorBufferInfo> bufferInfos;
+
+		for (int i = 0; i < info->count; i++)
+		{
+			VkDescriptorBufferInfo bufferInfo{};
+			bufferInfo.buffer = (VkBuffer)info->buffers[i]->getHandle();;
+			bufferInfo.offset = 0;
+			bufferInfo.range = info->buffers[i]->getSize();
+
+			bufferInfos.push_back(bufferInfo);
+		}
+
+		write.pBufferInfo = bufferInfos.data();
+
+		vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
+	}
+	if (info->baseType == UNIFORM_TEXELBUFFER)
+	{
+		VkWriteDescriptorSet write{};
+		write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+		write.dstSet = currentDescriptorSet;
+		write.dstBinding = info->location;
+		write.dstArrayElement = 0;
+		write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+		write.descriptorCount = info->count;
+
+		std::vector<VkBufferView> bufferViews;
+
+		for (int i = 0; i < info->count; i++)
+			bufferViews.push_back((VkBufferView)info->buffers[i]->getTexelBufferHandle());
+
+		write.pTexelBufferView = bufferViews.data();
+
 		vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
 	}
 }
@@ -515,6 +514,18 @@ void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures,
 	}
 }
 
+void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
+{
+	for (int i = 0; i < count; i++)
+	{
+		auto oldBuffer = info->buffers[i];
+		info->buffers[i] = buffers[i];
+		info->buffers[i]->retain();
+		if (oldBuffer)
+			oldBuffer->release();
+	}
+}
+
 void Shader::calculateUniformBufferSizeAligned()
 {
 	auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
@@ -525,6 +536,13 @@ void Shader::calculateUniformBufferSizeAligned()
 	uniformBufferSizeAligned = factor * minAlignment;
 }
 
+void Shader::initDescriptorSet()
+{
+	for (const auto &[key, val] : uniformInfos)
+		if (Vulkan::getDescriptorType(val.baseType) != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
+			updateUniform(&val, val.count, true);
+}
+
 void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
 {
 	using namespace spirv_cross;
@@ -880,6 +898,12 @@ void Shader::createDescriptorSetLayout()
 {
 	std::vector<VkDescriptorSetLayoutBinding> bindings;
 
+	VkShaderStageFlags stageFlags;
+	if (isCompute)
+		stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+	else
+		stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
+
 	for (auto const &[key, val] : uniformInfos)
 	{
 		auto type = Vulkan::getDescriptorType(val.baseType);
@@ -890,10 +914,7 @@ void Shader::createDescriptorSetLayout()
 			layoutBinding.binding = val.location;
 			layoutBinding.descriptorType = type;
 			layoutBinding.descriptorCount = val.count;
-			if (isCompute)
-				layoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
-			else
-				layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
+			layoutBinding.stageFlags = stageFlags;
 
 			bindings.push_back(layoutBinding);
 		}
@@ -905,10 +926,7 @@ void Shader::createDescriptorSetLayout()
 		uniformBinding.binding = uniformLocation;
 		uniformBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 		uniformBinding.descriptorCount = 1;
-		if (isCompute)
-			uniformBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
-		else
-			uniformBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
+		uniformBinding.stageFlags = stageFlags;
 		bindings.push_back(uniformBinding);
 	}
 

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

@@ -61,7 +61,7 @@ public:
 	void updateUniform(const UniformInfo *info, int count) override;
 
 	void sendTextures(const UniformInfo *info, graphics::Texture **textures, int count) override;
-	void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override {}
+	void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
 
 	bool hasUniform(const std::string &name) const override;
 
@@ -81,6 +81,8 @@ private:
 		const spirv_cross::SPIRType &type, 
 		size_t baseoff, 
 		const std::string &basename);
+	void initDescriptorSet();
+	void updateUniform(const UniformInfo* info, int count, bool internal);
 
 	VkDescriptorSet allocateDescriptorSet();