mirror of
https://github.com/love2d/love.git
synced 2026-08-14 10:13:46 +02:00
vulkan: properly implement samplers
This commit is contained in:
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user