Browse Source

vulkan: properly implement samplers

niki 3 years ago
parent
commit
dcea056fcb

+ 4 - 0
src/modules/graphics/Texture.h

@@ -139,6 +139,10 @@ struct SamplerState
 	static bool getConstant(const char *in, WrapMode &out);
 	static bool getConstant(WrapMode in, const char *&out);
 	static std::vector<std::string> getConstants(WrapMode);
+
+	bool operator==(const SamplerState& other) const {
+		return memcmp(this, &other, sizeof(SamplerState)) == 0;
+	}
 };
 
 /**

+ 53 - 1
src/modules/graphics/vulkan/Graphics.cpp

@@ -1220,7 +1220,7 @@ void Graphics::startRenderPass(Texture* texture, uint32_t w, uint32_t h) {
 	colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 	colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 	if (texture) {
-		colorAttachmentInfo.imageView = texture->getImageView();
+		colorAttachmentInfo.imageView = (VkImageView)texture->getRenderTargetHandle();
 		auto vulkanFormat = Vulkan::getTextureFormat(texture->getPixelFormat());
 		currentFramebufferOutputFormat = vulkanFormat.internalFormat;
 
@@ -1262,6 +1262,53 @@ void Graphics::endRenderPass() {
 	}
 }
 
+VkSampler Graphics::createSampler(const SamplerState& samplerState) {
+	VkPhysicalDeviceProperties properties{};
+	vkGetPhysicalDeviceProperties(physicalDevice, &properties);
+
+	// fixme: determine actual values
+	VkSamplerCreateInfo samplerInfo{};
+	samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+	samplerInfo.magFilter = Vulkan::getFilter(samplerState.magFilter);
+	samplerInfo.minFilter = Vulkan::getFilter(samplerState.minFilter);
+	samplerInfo.addressModeU = Vulkan::getWrapMode(samplerState.wrapU);
+	samplerInfo.addressModeV = Vulkan::getWrapMode(samplerState.wrapV);
+	samplerInfo.addressModeW = Vulkan::getWrapMode(samplerState.wrapW);
+	samplerInfo.anisotropyEnable = VK_TRUE;
+	samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;	// fixme: samplerState.maxAnisotropy
+	samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
+	samplerInfo.unnormalizedCoordinates = VK_FALSE;
+	if (samplerState.depthSampleMode.hasValue) {
+		samplerInfo.compareEnable = VK_TRUE;
+		samplerInfo.compareOp = Vulkan::getCompareOp(samplerState.depthSampleMode.value);
+	} else {
+		samplerInfo.compareEnable = VK_FALSE;
+		samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
+	}
+	samplerInfo.mipmapMode = Vulkan::getMipMapMode(samplerState.mipmapFilter);
+	samplerInfo.mipLodBias = samplerState.lodBias;
+	samplerInfo.minLod = 0.0f;	// fixme: samplerState.minLod
+	samplerInfo.maxLod = 0.0f;	// fixme: samplerState.maxLod
+
+	VkSampler sampler;
+	if (vkCreateSampler(device, &samplerInfo, nullptr, &sampler) != VK_SUCCESS) {
+		throw love::Exception("failed to create sampler");
+	}
+
+	return sampler;
+}
+
+VkSampler Graphics::getCachedSampler(const SamplerState& samplerState) {
+	auto it = samplers.find(samplerState);
+	if (it != samplers.end()) {
+		return it->second;
+	} else {
+		VkSampler sampler = createSampler(samplerState);
+		samplers.insert({samplerState, sampler});
+		return sampler;
+	}
+}
+
 VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
 	auto &shaderStages = configuration.shader->getShaderStages();
 
@@ -1496,6 +1543,11 @@ void Graphics::cleanup() {
 	vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
 	vkFreeCommandBuffers(device, commandPool, MAX_FRAMES_IN_FLIGHT, dataTransferCommandBuffers.data());
 
+	for (auto const& p : samplers) {
+		vkDestroySampler(device, p.second, nullptr);
+	}
+	samplers.clear();
+
 	// fixme: maybe we should clean up some pipelines if they haven't been used in a while.
 	for (auto const& p : graphicsPipelines) {
 		vkDestroyPipeline(device, p.second, nullptr);

+ 9 - 0
src/modules/graphics/vulkan/Graphics.h

@@ -53,6 +53,12 @@ struct GraphicsPipelineConfigurationHasher {
 	}
 };
 
+struct SamplerStateHasher {
+	size_t operator()(const SamplerState &samplerState) const {
+		return XXH32(&samplerState, sizeof(SamplerState), 0);
+	}
+};
+
 struct BatchedDrawBuffers {
 	StreamBuffer* vertexBuffer1;
 	StreamBuffer* vertexBuffer2;
@@ -139,6 +145,7 @@ public:
 	const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const;
 	const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
 	graphics::Texture* getDefaultTexture() const;
+	VkSampler getCachedSampler(const SamplerState&);
 
 protected:
 	graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { 
@@ -192,6 +199,7 @@ private:
 	void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
 	void startRenderPass(Texture*, uint32_t w, uint32_t h);
 	void endRenderPass();
+	VkSampler createSampler(const SamplerState&);
 
 	VkInstance instance = VK_NULL_HANDLE;
 	VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
@@ -207,6 +215,7 @@ private:
 	VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
 	VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
 	std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
+	std::unordered_map<SamplerState, VkSampler, SamplerStateHasher> samplers;
 	VkCommandPool commandPool = VK_NULL_HANDLE;
 	std::vector<VkCommandBuffer> commandBuffers;
 	std::vector<VkCommandBuffer> dataTransferCommandBuffers;

+ 2 - 2
src/modules/graphics/vulkan/Shader.cpp

@@ -196,8 +196,8 @@ static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* textur
 	VkDescriptorImageInfo imageInfo{};
 	imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
 	Texture* vkTexture = (Texture*)texture;
-	imageInfo.imageView = vkTexture->getImageView();
-	imageInfo.sampler = vkTexture->getSampler();
+	imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
+	imageInfo.sampler = (VkSampler)vkTexture->getSamplerHandle();
 	return imageInfo;
 }
 

+ 6 - 31
src/modules/graphics/vulkan/Texture.cpp

@@ -83,7 +83,7 @@ bool Texture::loadVolatile() {
 		}
 	}
 	createTextureImageView();
-	createTextureSampler();
+	textureSampler = vgfx->getCachedSampler(samplerState);
 
 	return true;
 }
@@ -94,12 +94,10 @@ void Texture::unloadVolatile() {
 
 	vgfx->queueCleanUp([
 		device = device, 
-		textureSampler = textureSampler, 
 		textureImageView = textureImageView, 
 		allocator = allocator, 
 		textureImage = textureImage, 
 		textureImageAllocation = textureImageAllocation] () {
-		vkDestroySampler(device, textureSampler, nullptr);
 		vkDestroyImageView(device, textureImageView, nullptr);
 		vmaDestroyImage(allocator, textureImage, textureImageAllocation);
 	});
@@ -111,6 +109,11 @@ Texture::~Texture() {
 	unloadVolatile();
 }
 
+void Texture::setSamplerState(const SamplerState &s) {
+	love::graphics::Texture::setSamplerState(s);
+	textureSampler = vgfx->getCachedSampler(s);
+}
+
 void Texture::createTextureImageView() {
 	auto vulkanFormat = Vulkan::getTextureFormat(format);
 
@@ -134,34 +137,6 @@ void Texture::createTextureImageView() {
 	}
 }
 
-void Texture::createTextureSampler() {
-	auto physicalDevice = vgfx->getPhysicalDevice();
-	VkPhysicalDeviceProperties properties{};
-	vkGetPhysicalDeviceProperties(physicalDevice, &properties);
-
-	VkSamplerCreateInfo samplerInfo{};
-	samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
-	samplerInfo.magFilter = VK_FILTER_LINEAR;
-	samplerInfo.minFilter = VK_FILTER_LINEAR;
-	samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
-	samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
-	samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
-	samplerInfo.anisotropyEnable = VK_TRUE;
-	samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
-	samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
-	samplerInfo.unnormalizedCoordinates = VK_FALSE;
-	samplerInfo.compareEnable = VK_FALSE;
-	samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
-	samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
-	samplerInfo.mipLodBias = 0.0f;
-	samplerInfo.minLod = 0.0f;
-	samplerInfo.maxLod = 0.0f;
-
-	if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
-		throw love::Exception("failed to create texture sampler");
-	}
-}
-
 void Texture::clear(bool white) {
 	auto commandBuffer = vgfx->beginSingleTimeCommands();
 

+ 3 - 4
src/modules/graphics/vulkan/Texture.h

@@ -20,10 +20,12 @@ public:
 	virtual bool loadVolatile() override;
 	virtual void unloadVolatile() override;
 
+	void setSamplerState(const SamplerState &s) override;
+
 	void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override;
 	void copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override;
 
-	ptrdiff_t getRenderTargetHandle() const override { return (ptrdiff_t)textureImage; };
+	ptrdiff_t getRenderTargetHandle() const override { return (ptrdiff_t)textureImageView; };
 	ptrdiff_t getSamplerHandle() const override { return (ptrdiff_t)textureSampler; };
 
 	void uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) override;
@@ -32,12 +34,9 @@ public:
 
 	int getMSAA() const override { return 0; };
 	ptrdiff_t getHandle() const override { return (ptrdiff_t)textureImage; }
-	VkImageView getImageView() const { return textureImageView; }
-	VkSampler getSampler() const { return textureSampler; }
 
 private:
 	void createTextureImageView();
-	void createTextureSampler();
 	void clear(bool white);
 
 	VkClearColorValue getClearValue(bool white);

+ 61 - 0
src/modules/graphics/vulkan/Vulkan.cpp

@@ -489,6 +489,67 @@ VkPolygonMode Vulkan::getPolygonMode(bool wireframe) {
 	}
 }
 
+VkFilter Vulkan::getFilter(SamplerState::FilterMode mode) {
+	switch (mode) {
+	case SamplerState::FILTER_LINEAR:
+		return VK_FILTER_LINEAR;
+	case SamplerState::FILTER_NEAREST:
+		return VK_FILTER_NEAREST;
+	default:
+		throw love::Exception("unkonwn filter mode");
+	}
+}
+
+VkSamplerAddressMode Vulkan::getWrapMode(SamplerState::WrapMode mode) {
+	switch (mode) {
+		//fixme: not accounting for different clamps (how does that work in vulkan?)
+	case SamplerState::WRAP_CLAMP:
+	case SamplerState::WRAP_CLAMP_ZERO:
+	case SamplerState::WRAP_CLAMP_ONE:
+		return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+	case SamplerState::WRAP_REPEAT:
+		return VK_SAMPLER_ADDRESS_MODE_REPEAT;
+	case SamplerState::WRAP_MIRRORED_REPEAT:
+		return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
+	default:
+		throw love::Exception("unknown wrap mode");
+	}
+}
+
+VkCompareOp Vulkan::getCompareOp(CompareMode mode) {
+	switch (mode) {
+	case COMPARE_LESS:
+		return VK_COMPARE_OP_LESS;
+	case COMPARE_LEQUAL:
+		return VK_COMPARE_OP_LESS_OR_EQUAL;
+	case COMPARE_EQUAL:
+		return VK_COMPARE_OP_EQUAL;
+	case COMPARE_GEQUAL:
+		return VK_COMPARE_OP_GREATER_OR_EQUAL;
+	case COMPARE_GREATER:
+		return VK_COMPARE_OP_GREATER;
+	case COMPARE_NOTEQUAL:
+		return VK_COMPARE_OP_NOT_EQUAL;
+	case COMPARE_ALWAYS:
+		return VK_COMPARE_OP_ALWAYS;
+	case COMPARE_NEVER:
+		return VK_COMPARE_OP_NEVER;
+	default:
+		throw love::Exception("unknown compare mode");
+	}
+}
+
+VkSamplerMipmapMode Vulkan::getMipMapMode(SamplerState::MipmapFilterMode mode) {
+	switch (mode) {
+	case SamplerState::MIPMAP_FILTER_NEAREST:
+		return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+	case SamplerState::MIPMAP_FILTER_NONE:
+	case SamplerState::MIPMAP_FILTER_LINEAR:
+	default:
+		return VK_SAMPLER_MIPMAP_MODE_LINEAR;
+	}
+}
+
 void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout,
 	uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount) {
 	VkImageMemoryBarrier barrier{};

+ 4 - 0
src/modules/graphics/vulkan/Vulkan.h

@@ -44,6 +44,10 @@ public:
 	static VkImageType getImageType(TextureType);
 	static VkImageViewType getImageViewType(TextureType);
 	static VkPolygonMode getPolygonMode(bool wireframe);
+	static VkFilter getFilter(SamplerState::FilterMode);
+	static VkSamplerAddressMode getWrapMode(SamplerState::WrapMode);
+	static VkCompareOp getCompareOp(CompareMode);
+	static VkSamplerMipmapMode getMipMapMode(SamplerState::MipmapFilterMode);
 
 	static void cmdTransitionImageLayout(
 		VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout,