1
0
Fork 0

Vulkan lod mipmaps

This commit is contained in:
May B. 2020-10-06 16:32:05 +02:00
parent a408a475f5
commit 9465083742
8 changed files with 64 additions and 55 deletions

View File

@ -18,8 +18,8 @@ inline glm::vec4 fromHex(const std::string& str) {
} }
inline std::string toHexa(const glm::vec4& rgb) { inline std::string toHexa(const glm::vec4& rgb) {
std::ostringstream sstr; std::ostringstream sstr;
sstr << std::hex << std::setw(2) << std::setfill('0') << sstr << '#' << std::hex << std::setw(2) << std::setfill('0') <<
static_cast<int>(rgb.x * UCHAR_MAX) << static_cast<int>(rgb.y * UCHAR_MAX) << static_cast<int>(rgb.z * UCHAR_MAX) << std::endl; static_cast<int>(rgb.x * UCHAR_MAX) << static_cast<int>(rgb.y * UCHAR_MAX) << static_cast<int>(rgb.z * UCHAR_MAX);
return sstr.str(); return sstr.str();
} }

View File

@ -40,12 +40,12 @@ std::optional<Image::properties> Image::Read(const std::string& imagepath, std::
info.size.height = *(unsigned int*)&(header[8 ]); info.size.height = *(unsigned int*)&(header[8 ]);
info.size.width = *(unsigned int*)&(header[12]); info.size.width = *(unsigned int*)&(header[12]);
unsigned int linearSize = *(unsigned int*)&(header[16]); unsigned int linearSize = *(unsigned int*)&(header[16]);
info.mipMap = *(unsigned int*)&(header[24]); info.mipmapLevels = *(unsigned int*)&(header[24]);
unsigned int fourCC = *(unsigned int*)&(header[80]); unsigned int fourCC = *(unsigned int*)&(header[80]);
/* how big is it going to be including all mipmaps? */ /* how big is it going to be including all mipmaps? */
unsigned int bufsize = info.mipMap > 1 ? linearSize * 2 : linearSize; unsigned int bufsize = info.mipmapLevels > 1 ? linearSize * 2 : linearSize;
data.resize(bufsize); data.resize(bufsize);
fread(data.data(), 1, bufsize, fp); fread(data.data(), 1, bufsize, fp);
/* close the file pointer */ /* close the file pointer */
@ -53,9 +53,6 @@ std::optional<Image::properties> Image::Read(const std::string& imagepath, std::
switch(fourCC) switch(fourCC)
{ {
case FOURCC_DXT1:
info.format = Format::BC1;
break;
case FOURCC_DXT3: case FOURCC_DXT3:
info.format = Format::BC2; info.format = Format::BC2;
break; break;

View File

@ -17,15 +17,14 @@ public:
}; };
// NOTE: matches VkFormat // NOTE: matches VkFormat
enum class Format { enum class Format {
/// DXT1 RGBA SRGB
BC1 = 134,
/// DXT3 RGBA SRGB /// DXT3 RGBA SRGB
BC2 = 136, BC2 = 136,
/// DXT1 RGBA SRGB /// DXT5 RGBA SRGB
BC3 = 138, BC3 = 138,
// MAYBE: R8G8B8A8 // MAYBE: R8G8B8A8
// MAYBE: For HDR BC6H_SFLOAT = 144, // MAYBE: For HDR BC6H_SFLOAT = 144,
}; };
// NOTE: matches VkImageLayout // NOTE: matches VkImageLayout
enum class Layout { enum class Layout {
UNDEFINED = 0, UNDEFINED = 0,
@ -68,15 +67,20 @@ public:
struct properties { struct properties {
frame size; frame size;
uint32_t mipMap; uint32_t mipmapLevels;
Format format; Format format;
}; };
struct requirement { struct requirement: properties {
properties props; requirement(const properties& props, Layout layout, Usage usage, Aspect aspect,
bool optimal = true): properties(props), layout(layout), usage(usage),
aspect(aspect), optimal(optimal) { }
Layout layout; Layout layout;
Usage usage; Usage usage;
bool linear = false; Aspect aspect;
Aspect aspect = Aspect::COLOR; bool optimal;
static requirement Texture(const properties &props) {
return requirement(props, Layout::SHADER_READ_ONLY, Usage::SAMPLED, Aspect::COLOR); }
}; };
static std::optional<properties> Read(const std::string&, std::vector<unsigned char>& data); static std::optional<properties> Read(const std::string&, std::vector<unsigned char>& data);
@ -97,7 +101,7 @@ public:
bool minLinear = true; bool minLinear = true;
Wrap wrap = Wrap::MIRRORED_REPEAT; Wrap wrap = Wrap::MIRRORED_REPEAT;
int anisotropy = 0; int anisotropy = 0;
//TODO: mipmap bool mipmap = true;
}; };
/// Only supports dds files /// Only supports dds files

View File

@ -14,7 +14,8 @@ const auto NO_DELETER = memory::Deleter(nullptr);
memory::ptr memory::GetNull() { return memory::ptr(nullptr, NO_DELETER); } memory::ptr memory::GetNull() { return memory::ptr(nullptr, NO_DELETER); }
Allocator::Allocator(VkDevice device, const PhysicalDeviceInfo &info): physicalDevice(info.device), Allocator::Allocator(VkDevice device, const PhysicalDeviceInfo &info): physicalDevice(info.device),
capabilities({info.features.samplerAnisotropy == VK_TRUE}), device(device) capabilities({info.features.samplerAnisotropy == VK_TRUE ? std::make_optional(info.properties.limits.maxSamplerAnisotropy) : std::nullopt,
info.properties.limits.maxSamplerLodBias}), device(device)
{ {
if(info.hasMemoryBudget()) { if(info.hasMemoryBudget()) {
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
@ -194,7 +195,7 @@ void Allocator::copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) {
submitCmd(transferBuffer, transferQueue); 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 mipLevels) {
beginCmd(graphicsBuffer); beginCmd(graphicsBuffer);
VkImageMemoryBarrier barrier{}; VkImageMemoryBarrier barrier{};
@ -207,7 +208,7 @@ void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLay
barrier.image = image; barrier.image = image;
barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.levelCount = mipLevels;
barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1; barrier.subresourceRange.layerCount = 1;
@ -250,28 +251,33 @@ void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLay
submitCmd(graphicsBuffer, graphicsQueue); submitCmd(graphicsBuffer, graphicsQueue);
} }
void Allocator::copyBufferToImage(VkBuffer src, VkImage dest, uint32_t width, uint32_t height) { void Allocator::copyBufferToImage(VkBuffer src, VkImage dest, uint32_t width, uint32_t height, uint32_t mipLevels) {
beginCmd(transferBuffer); beginCmd(transferBuffer);
VkDeviceSize offset = 0;
std::vector<VkBufferImageCopy> regions{mipLevels};
for (size_t i = 0; i < mipLevels; i++) {
regions[i].bufferOffset = offset;
regions[i].bufferRowLength = std::max<uint32_t>(1, width >> i);
regions[i].bufferImageHeight = std::max<uint32_t>(1, height >> i);
VkBufferImageCopy region{}; regions[i].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.bufferOffset = 0; regions[i].imageSubresource.mipLevel = i;
region.bufferRowLength = 0; regions[i].imageSubresource.baseArrayLayer = 0;
region.bufferImageHeight = 0; regions[i].imageSubresource.layerCount = 1;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; regions[i].imageOffset = {0, 0, 0};
region.imageSubresource.mipLevel = 0; regions[i].imageExtent = {regions[i].bufferRowLength, regions[i].bufferImageHeight, 1};
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0}; //NOTE: may fail without width % 4 && height % 4
region.imageExtent = {width, height, 1}; offset += regions[i].imageExtent.height * regions[i].imageExtent.width * regions[i].imageExtent.depth;
}
vkCmdCopyBufferToImage(transferBuffer, src, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region); vkCmdCopyBufferToImage(transferBuffer, src, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels, regions.data());
submitCmd(transferBuffer, transferQueue); submitCmd(transferBuffer, transferQueue);
} }
std::optional<uint32_t> Allocator::findMemory(uint32_t typeFilter, VkMemoryPropertyFlags requirement, VkDeviceSize size) { std::optional<uint32_t> Allocator::findMemory(uint32_t typeFilter, VkMemoryPropertyFlags requirement, VkDeviceSize size) {
updateProperties(); updateProperties();
#if LOG_TRACE #if LOG_TRACE

View File

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

View File

@ -46,8 +46,8 @@ CommandCenter::~CommandCenter() {
void CommandCenter::allocate(const std::vector<VkImageView>& views, const Pipeline& pipe, VkExtent2D extent, const renderOptions& opt) { void CommandCenter::allocate(const std::vector<VkImageView>& views, const Pipeline& pipe, VkExtent2D extent, const renderOptions& opt) {
assert(freed); assert(freed);
depthbuffer = Image::Create({{{extent.height, extent.width}, 1, (Image::Format)depthFormat}, depthbuffer = Image::Create(Image::requirement({{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));
framebuffers.resize(views.size()); framebuffers.resize(views.size());

View File

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

View File

@ -20,13 +20,13 @@ memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags pro
VkImageCreateInfo info{}; VkImageCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
info.imageType = VK_IMAGE_TYPE_2D; info.imageType = VK_IMAGE_TYPE_2D;
info.extent.width = req.props.size.width; info.extent.width = req.size.width;
info.extent.height = req.props.size.height; info.extent.height = req.size.height;
info.extent.depth = 1; info.extent.depth = 1;
info.mipLevels = req.props.mipMap; //TODO: info.mipLevels = req.mipmapLevels;
info.arrayLayers = 1; info.arrayLayers = 1;
info.format = static_cast<VkFormat>(req.props.format); info.format = static_cast<VkFormat>(req.format);
info.tiling = req.linear ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; info.tiling = req.optimal ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
info.usage = static_cast<VkImageUsageFlags>(req.usage); info.usage = static_cast<VkImageUsageFlags>(req.usage);
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
@ -54,25 +54,24 @@ memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags pro
if (data) { if (data) {
if(auto staging = WritableBuffer::Create(data.size)) { if(auto staging = WritableBuffer::Create(data.size)) {
staging->write(data, 0); 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, req.mipmapLevels);
alloc->copyBufferToImage(staging->getRef(), out.ref, info.extent.width, info.extent.height); info.initialLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
alloc->transitionImageLayout(out.ref, info.format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<VkImageLayout>(req.layout)); alloc->copyBufferToImage(staging->getRef(), out.ref, info.extent.width, info.extent.height, req.mipmapLevels);
} else { } else {
LOG_E("Cannot allocate staging memory"); LOG_E("Cannot allocate staging memory");
return memory::GetNull(); return memory::GetNull();
} }
} 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), req.mipmapLevels);
VkImageViewCreateInfo viewInfo{}; VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = out.ref; viewInfo.image = out.ref;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = static_cast<VkFormat>(req.props.format); viewInfo.format = static_cast<VkFormat>(req.format);
viewInfo.subresourceRange.aspectMask = static_cast<VkImageAspectFlags>(req.aspect); viewInfo.subresourceRange.aspectMask = static_cast<VkImageAspectFlags>(req.aspect);
viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1; //TODO: mipmap viewInfo.subresourceRange.levelCount = req.mipmapLevels;
viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1; viewInfo.subresourceRange.layerCount = 1;
@ -105,7 +104,7 @@ std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sa
}(); }();
vk::Image::info img; vk::Image::info img;
auto mem = createImage({header.size, header.mipMap, header.format, Layout::SHADER_READ_ONLY, Usage::SAMPLED, false}, auto mem = createImage(requirement::Texture(header),
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, data, img); VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, data, img);
if(!mem) { if(!mem) {
FATAL("Cannot create texture image"); FATAL("Cannot create texture image");
@ -121,9 +120,10 @@ std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sa
samplerInfo.addressModeV = wrap; samplerInfo.addressModeV = wrap;
samplerInfo.addressModeW = wrap; samplerInfo.addressModeW = wrap;
if (Allocator::GetDefault()->getCapabilities().anisotropy && props.anisotropy > 0) { auto maxAnisotropy = Allocator::GetDefault()->getCapabilities().maxAnisotropy;
if (maxAnisotropy && props.anisotropy > 0) {
samplerInfo.anisotropyEnable = VK_TRUE; samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = 1 << (props.anisotropy-1); samplerInfo.maxAnisotropy = std::min<float>(maxAnisotropy.value(), 1 << (props.anisotropy-1));
} else { } else {
samplerInfo.anisotropyEnable = VK_FALSE; samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.f; samplerInfo.maxAnisotropy = 1.f;
@ -135,10 +135,10 @@ std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sa
samplerInfo.compareEnable = VK_FALSE; samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.mipmapMode = props.minLinear ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.mipLodBias = 0.0f; //TODO: samplerInfo.mipLodBias = 0.0f; //TODO:
samplerInfo.minLod = 0.0f; samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f; samplerInfo.maxLod = props.mipmap ? header.mipmapLevels : 0.0f;
VkSampler sampler; VkSampler sampler;
if (vkCreateSampler(device, &samplerInfo, ALLOC, &sampler) != VK_SUCCESS) { if (vkCreateSampler(device, &samplerInfo, ALLOC, &sampler) != VK_SUCCESS) {