Commit 94650837 authored by Shu's avatar Shu 🍠
Browse files

Vulkan lod mipmaps

parent a408a475
......@@ -18,8 +18,8 @@ inline glm::vec4 fromHex(const std::string& str) {
}
inline std::string toHexa(const glm::vec4& rgb) {
std::ostringstream sstr;
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;
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);
return sstr.str();
}
......
......@@ -40,12 +40,12 @@ std::optional<Image::properties> Image::Read(const std::string& imagepath, std::
info.size.height = *(unsigned int*)&(header[8 ]);
info.size.width = *(unsigned int*)&(header[12]);
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]);
/* 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);
fread(data.data(), 1, bufsize, fp);
/* close the file pointer */
......@@ -53,9 +53,6 @@ std::optional<Image::properties> Image::Read(const std::string& imagepath, std::
switch(fourCC)
{
case FOURCC_DXT1:
info.format = Format::BC1;
break;
case FOURCC_DXT3:
info.format = Format::BC2;
break;
......
......@@ -17,15 +17,14 @@ public:
};
// NOTE: matches VkFormat
enum class Format {
/// DXT1 RGBA SRGB
BC1 = 134,
/// DXT3 RGBA SRGB
BC2 = 136,
/// DXT1 RGBA SRGB
/// DXT5 RGBA SRGB
BC3 = 138,
// MAYBE: R8G8B8A8
// MAYBE: For HDR BC6H_SFLOAT = 144,
};
// NOTE: matches VkImageLayout
enum class Layout {
UNDEFINED = 0,
......@@ -68,15 +67,20 @@ public:
struct properties {
frame size;
uint32_t mipMap;
uint32_t mipmapLevels;
Format format;
};
struct requirement {
properties props;
struct requirement: properties {
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;
Usage usage;
bool linear = false;
Aspect aspect = Aspect::COLOR;
Aspect aspect;
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);
......@@ -97,7 +101,7 @@ public:
bool minLinear = true;
Wrap wrap = Wrap::MIRRORED_REPEAT;
int anisotropy = 0;
//TODO: mipmap
bool mipmap = true;
};
/// Only supports dds files
......
......@@ -14,7 +14,8 @@ 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 ? std::make_optional(info.properties.limits.maxSamplerAnisotropy) : std::nullopt,
info.properties.limits.maxSamplerLodBias}), device(device)
{
if(info.hasMemoryBudget()) {
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);
}
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);
VkImageMemoryBarrier barrier{};
......@@ -207,7 +208,7 @@ void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLay
barrier.image = image;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.levelCount = mipLevels;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
......@@ -250,28 +251,33 @@ void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLay
submitCmd(graphicsBuffer, graphicsQueue);
}
void Allocator::copyBufferToImage(VkBuffer src, VkImage dest, uint32_t width, uint32_t height) {
beginCmd(transferBuffer);
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0};
region.imageExtent = {width, height, 1};
void Allocator::copyBufferToImage(VkBuffer src, VkImage dest, uint32_t width, uint32_t height, uint32_t mipLevels) {
vkCmdCopyBufferToImage(transferBuffer, src, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
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);
regions[i].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
regions[i].imageSubresource.mipLevel = i;
regions[i].imageSubresource.baseArrayLayer = 0;
regions[i].imageSubresource.layerCount = 1;
regions[i].imageOffset = {0, 0, 0};
regions[i].imageExtent = {regions[i].bufferRowLength, regions[i].bufferImageHeight, 1};
//NOTE: may fail without width % 4 && height % 4
offset += regions[i].imageExtent.height * regions[i].imageExtent.width * regions[i].imageExtent.depth;
}
vkCmdCopyBufferToImage(transferBuffer, src, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels, regions.data());
submitCmd(transferBuffer, transferQueue);
}
std::optional<uint32_t> Allocator::findMemory(uint32_t typeFilter, VkMemoryPropertyFlags requirement, VkDeviceSize size) {
updateProperties();
#if LOG_TRACE
......
......@@ -37,13 +37,14 @@ 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 mipLevels);
void copyBufferToImage(VkBuffer src, VkImage dst, uint32_t width, uint32_t height, uint32_t mipLevels = 1);
void setTracyZone(const char* name);
struct Capabilities {
bool anisotropy;
std::optional<float> maxAnisotropy;
float maxLodBias;
};
constexpr VkDevice getDevice() const { return device; }
......
......@@ -46,8 +46,8 @@ CommandCenter::~CommandCenter() {
void CommandCenter::allocate(const std::vector<VkImageView>& views, const Pipeline& pipe, VkExtent2D extent, const renderOptions& opt) {
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});
depthbuffer = Image::Create(Image::requirement({{extent.height, extent.width}, 1, (Image::Format)depthFormat},
Image::Layout::DEPTH_STENCIL_ATTACHMENT, Image::Usage::DEPTH_STENCIL_ATTACHMENT, Image::Aspect::DEPTH));
framebuffers.resize(views.size());
......
......@@ -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");
......
......@@ -20,13 +20,13 @@ memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags pro
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.width = req.size.width;
info.extent.height = req.size.height;
info.extent.depth = 1;
info.mipLevels = req.props.mipMap; //TODO:
info.mipLevels = req.mipmapLevels;
info.arrayLayers = 1;
info.format = static_cast<VkFormat>(req.props.format);
info.tiling = req.linear ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
info.format = static_cast<VkFormat>(req.format);
info.tiling = req.optimal ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
info.usage = static_cast<VkImageUsageFlags>(req.usage);
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
......@@ -54,25 +54,24 @@ 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->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));
alloc->transitionImageLayout(out.ref, info.format, info.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, req.mipmapLevels);
info.initialLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
alloc->copyBufferToImage(staging->getRef(), out.ref, info.extent.width, info.extent.height, req.mipmapLevels);
} else {
LOG_E("Cannot allocate staging memory");
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{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = out.ref;
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.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1; //TODO: mipmap
viewInfo.subresourceRange.levelCount = req.mipmapLevels;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
......@@ -105,7 +104,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(requirement::Texture(header),
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, data, img);
if(!mem) {
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.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.maxAnisotropy = 1 << (props.anisotropy-1);
samplerInfo.maxAnisotropy = std::min<float>(maxAnisotropy.value(), 1 << (props.anisotropy-1));
} else {
samplerInfo.anisotropyEnable = VK_FALSE;
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.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.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
samplerInfo.maxLod = props.mipmap ? header.mipmapLevels : 0.0f;
VkSampler sampler;
if (vkCreateSampler(device, &samplerInfo, ALLOC, &sampler) != VK_SUCCESS) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment