From dcea056fcbc88ce64676e648d6b61468bcc45781 Mon Sep 17 00:00:00 2001 From: niki Date: Mon, 1 Aug 2022 18:51:09 +0200 Subject: [PATCH] vulkan: properly implement samplers --- src/modules/graphics/Texture.h | 4 ++ src/modules/graphics/vulkan/Graphics.cpp | 54 ++++++++++++++++++++- src/modules/graphics/vulkan/Graphics.h | 9 ++++ src/modules/graphics/vulkan/Shader.cpp | 4 +- src/modules/graphics/vulkan/Texture.cpp | 37 +++----------- src/modules/graphics/vulkan/Texture.h | 7 ++- src/modules/graphics/vulkan/Vulkan.cpp | 61 ++++++++++++++++++++++++ src/modules/graphics/vulkan/Vulkan.h | 4 ++ 8 files changed, 142 insertions(+), 38 deletions(-) diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index 17402ac57..de2f7bbab 100644 --- a/src/modules/graphics/Texture.h +++ b/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 getConstants(WrapMode); + + bool operator==(const SamplerState& other) const { + return memcmp(this, &other, sizeof(SamplerState)) == 0; + } }; /** diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 7bf9f71bb..bf1c8decb 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/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(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); diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 330d6c456..94ca597db 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/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 graphicsPipelines; + std::unordered_map samplers; VkCommandPool commandPool = VK_NULL_HANDLE; std::vector commandBuffers; std::vector dataTransferCommandBuffers; diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index 41082ccfc..2e67ce941 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/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; } diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index 5389545a6..45731484a 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/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(); diff --git a/src/modules/graphics/vulkan/Texture.h b/src/modules/graphics/vulkan/Texture.h index d25e46f92..108fd3a85 100644 --- a/src/modules/graphics/vulkan/Texture.h +++ b/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); diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index 3a7054a81..6cb18c998 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/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{}; diff --git a/src/modules/graphics/vulkan/Vulkan.h b/src/modules/graphics/vulkan/Vulkan.h index 4e01157d8..3469873ba 100644 --- a/src/modules/graphics/vulkan/Vulkan.h +++ b/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,