1
0
Fork 0

WIP: Mipmap

This commit is contained in:
May B. 2020-10-05 19:13:33 +02:00
parent 68037d726c
commit c03759552b
6 changed files with 119 additions and 21 deletions

View File

@ -75,8 +75,10 @@ public:
properties props;
Layout layout;
Usage usage;
Aspect aspect;
// MAYBE: group layout usage aspect
bool mipMap;
bool linear = false;
Aspect aspect = Aspect::COLOR;
};
static std::optional<properties> Read(const std::string&, std::vector<unsigned char>& data);
@ -97,7 +99,7 @@ public:
bool minLinear = true;
Wrap wrap = Wrap::MIRRORED_REPEAT;
int anisotropy = 0;
//TODO: mipmap
bool mipMap = true;
};
/// Only supports dds files

View File

@ -14,7 +14,10 @@ const auto NO_DELETER = memory::Deleter(nullptr);
memory::ptr memory::GetNull() { return memory::ptr(nullptr, NO_DELETER); }
Allocator::Allocator(VkDevice device, const PhysicalDeviceInfo &info): physicalDevice(info.device),
capabilities({info.features.samplerAnisotropy == VK_TRUE}), device(device)
capabilities({
info.features.samplerAnisotropy == VK_TRUE ? info.properties.limits.maxSamplerAnisotropy : 1.f,
info.properties.limits.maxSamplerLodBias
}), device(device)
{
if(info.hasMemoryBudget()) {
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
@ -194,7 +197,7 @@ void Allocator::copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) {
submitCmd(transferBuffer, transferQueue);
}
void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipMap) {
beginCmd(graphicsBuffer);
VkImageMemoryBarrier barrier{};
@ -207,7 +210,7 @@ void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLay
barrier.image = image;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.levelCount = mipMap;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
@ -270,7 +273,79 @@ void Allocator::copyBufferToImage(VkBuffer src, VkImage dest, uint32_t width, ui
submitCmd(transferBuffer, transferQueue);
}
void Allocator::generateMipmaps(VkImage image, VkFormat format, uint32_t width, uint32_t height, uint32_t levels) {
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties);
if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
FATAL("Texture image format does not support linear blitting!");
}
beginCmd(graphicsBuffer);
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.image = image;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
int32_t mipWidth = width;
int32_t mipHeight = height;
for (uint32_t i = 1; i < levels; i++) {
barrier.subresourceRange.baseMipLevel = i - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(graphicsBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
VkImageBlit blit{};
blit.srcOffsets[0] = {0, 0, 0};
blit.srcOffsets[1] = {mipWidth, mipHeight, 1};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = i - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = 1;
blit.dstOffsets[0] = {0, 0, 0};
blit.dstOffsets[1] = {mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1};
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = i;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = 1;
vkCmdBlitImage(graphicsBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(graphicsBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
if (mipWidth > 1) mipWidth /= 2;
if (mipHeight > 1) mipHeight /= 2;
}
barrier.subresourceRange.baseMipLevel = levels - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(graphicsBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
submitCmd(graphicsBuffer, graphicsQueue);
}
std::optional<uint32_t> Allocator::findMemory(uint32_t typeFilter, VkMemoryPropertyFlags requirement, VkDeviceSize size) {
updateProperties();

View File

@ -37,13 +37,15 @@ public:
bool deallocate(const memory::area&);
void copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size);
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout);
void copyBufferToImage(VkBuffer src, VkImage dst, uint32_t width, uint32_t height);
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipMap = 1);
void generateMipmaps(VkImage image, VkFormat format, uint32_t width, uint32_t height, uint32_t levels);
void setTracyZone(const char* name);
void setTracyZone(const char *name);
struct Capabilities {
bool anisotropy;
float maxAnisotropy;
float maxLodBias;
};
constexpr VkDevice getDevice() const { return device; }

View File

@ -47,7 +47,7 @@ void CommandCenter::allocate(const std::vector<VkImageView>& views, const Pipeli
assert(freed);
depthbuffer = Image::Create({{{extent.height, extent.width}, 1, (Image::Format)depthFormat},
Image::Layout::DEPTH_STENCIL_ATTACHMENT, Image::Usage::DEPTH_STENCIL_ATTACHMENT, false, Image::Aspect::DEPTH});
Image::Layout::DEPTH_STENCIL_ATTACHMENT, Image::Usage::DEPTH_STENCIL_ATTACHMENT, Image::Aspect::DEPTH, false, false});
framebuffers.resize(views.size());

View File

@ -263,6 +263,7 @@ bool Renderer::Load(Window& window, const renderOptions& opt) {
volkLoadInstance(instance);
}
[[maybe_unused]]
const auto version = volkGetInstanceVersion();
LOG_D("Vulkan " << VK_VERSION_MAJOR(version) << '.' << VK_VERSION_MINOR(version) << '.' << VK_VERSION_PATCH(version) << ", GLSL precompiled");

View File

@ -2,6 +2,7 @@
#include "Buffers.hpp"
#include "../Allocator.hpp"
#include "../../../../core/utils/logger.hpp"
#include <cmath>
using namespace render::vk;
@ -13,17 +14,23 @@ Texture::~Texture() {
vkDestroySampler(Allocator::GetDefault()->getDevice(), sampler, ALLOC);
}
uint32_t getSizeMipMap(Image::frame size, bool generate = true) {
return generate ? 1 + std::floor(std::log2(std::max(size.width, size.height))) : 1;
}
memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags properties, const render::data_view data, Image::info& out) {
auto alloc = Allocator::GetDefault();
auto device = alloc->getDevice();
const auto mipMapLevels = getSizeMipMap(req.props.size, req.mipMap);
const auto mustGenerateMip = req.props.mipMap < mipMapLevels;
VkImageCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
info.imageType = VK_IMAGE_TYPE_2D;
info.extent.width = req.props.size.width;
info.extent.height = req.props.size.height;
info.extent.depth = 1;
info.mipLevels = req.props.mipMap; //TODO:
info.mipLevels = mipMapLevels;
info.arrayLayers = 1;
info.format = static_cast<VkFormat>(req.props.format);
info.tiling = req.linear ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
@ -32,9 +39,12 @@ memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags pro
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.flags = 0;
if (data) {
if (data || mustGenerateMip) {
info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (mustGenerateMip) {
info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (vkCreateImage(device, &info, ALLOC, &out.ref) != VK_SUCCESS) {
LOG_E("Failed to create image");
@ -54,15 +64,22 @@ memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags pro
if (data) {
if(auto staging = WritableBuffer::Create(data.size)) {
staging->write(data, 0);
alloc->transitionImageLayout(out.ref, info.format, info.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
alloc->transitionImageLayout(out.ref, info.format, info.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipMapLevels);
info.initialLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
alloc->copyBufferToImage(staging->getRef(), out.ref, info.extent.width, info.extent.height);
alloc->transitionImageLayout(out.ref, info.format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<VkImageLayout>(req.layout));
} else {
LOG_E("Cannot allocate staging memory");
return memory::GetNull();
}
}
if (mustGenerateMip) {
if(info.initialLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
LOG_E("Cannot generate mipmaps without initial data");
return memory::GetNull();
}
alloc->generateMipmaps(out.ref, info.format, info.extent.width, info.extent.height, info.mipLevels);
} else {
alloc->transitionImageLayout(out.ref, info.format, info.initialLayout, static_cast<VkImageLayout>(req.layout));
alloc->transitionImageLayout(out.ref, info.format, info.initialLayout, static_cast<VkImageLayout>(req.layout), mipMapLevels);
}
VkImageViewCreateInfo viewInfo{};
@ -72,7 +89,7 @@ memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags pro
viewInfo.format = static_cast<VkFormat>(req.props.format);
viewInfo.subresourceRange.aspectMask = static_cast<VkImageAspectFlags>(req.aspect);
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1; //TODO: mipmap
viewInfo.subresourceRange.levelCount = mipMapLevels;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
@ -105,7 +122,7 @@ std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sa
}();
vk::Image::info img;
auto mem = createImage({header.size, header.mipMap, header.format, Layout::SHADER_READ_ONLY, Usage::SAMPLED, false},
auto mem = createImage({header.size, header.mipMap, header.format, Layout::SHADER_READ_ONLY, Usage::SAMPLED, Aspect::COLOR, props.mipMap, false},
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, data, img);
if(!mem) {
FATAL("Cannot create texture image");
@ -121,9 +138,10 @@ std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sa
samplerInfo.addressModeV = wrap;
samplerInfo.addressModeW = wrap;
if (Allocator::GetDefault()->getCapabilities().anisotropy && props.anisotropy > 0) {
const auto maxAnisotropy = Allocator::GetDefault()->getCapabilities().maxAnisotropy;
if (maxAnisotropy > 1.f && props.anisotropy > 0) {
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = 1 << (props.anisotropy-1);
samplerInfo.maxAnisotropy = std::min<float>(maxAnisotropy, 1 << (props.anisotropy-1));
} else {
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.f;
@ -135,10 +153,10 @@ std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sa
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f; //TODO:
samplerInfo.mipmapMode = props.magLinear ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
samplerInfo.maxLod = props.mipMap ? getSizeMipMap(header.size) : 0.0f;
samplerInfo.mipLodBias = 0.0f; //TODO: check maxLodBias
VkSampler sampler;
if (vkCreateSampler(device, &samplerInfo, ALLOC, &sampler) != VK_SUCCESS) {