diff --git a/src/client/config.hpp b/src/client/config.hpp index 44ebea9..15e9953 100644 --- a/src/client/config.hpp +++ b/src/client/config.hpp @@ -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(rgb.x * UCHAR_MAX) << static_cast(rgb.y * UCHAR_MAX) << static_cast(rgb.z * UCHAR_MAX) << std::endl; + sstr << '#' << std::hex << std::setw(2) << std::setfill('0') << + static_cast(rgb.x * UCHAR_MAX) << static_cast(rgb.y * UCHAR_MAX) << static_cast(rgb.z * UCHAR_MAX); return sstr.str(); } diff --git a/src/client/render/api/Images.cpp b/src/client/render/api/Images.cpp index be4e2e9..0f34fd1 100644 --- a/src/client/render/api/Images.cpp +++ b/src/client/render/api/Images.cpp @@ -40,12 +40,12 @@ std::optional 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::Read(const std::string& imagepath, std:: switch(fourCC) { - case FOURCC_DXT1: - info.format = Format::BC1; - break; case FOURCC_DXT3: info.format = Format::BC2; break; diff --git a/src/client/render/api/Images.hpp b/src/client/render/api/Images.hpp index 8c3dc4d..d615570 100644 --- a/src/client/render/api/Images.hpp +++ b/src/client/render/api/Images.hpp @@ -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 Read(const std::string&, std::vector& 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 diff --git a/src/client/render/vk/Allocator.cpp b/src/client/render/vk/Allocator.cpp index 9a87c22..3c30208 100644 --- a/src/client/render/vk/Allocator.cpp +++ b/src/client/render/vk/Allocator.cpp @@ -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) { +void Allocator::copyBufferToImage(VkBuffer src, VkImage dest, uint32_t width, uint32_t height, uint32_t mipLevels) { + beginCmd(transferBuffer); + + VkDeviceSize offset = 0; + std::vector regions{mipLevels}; + for (size_t i = 0; i < mipLevels; i++) { + regions[i].bufferOffset = offset; + regions[i].bufferRowLength = std::max(1, width >> i); + regions[i].bufferImageHeight = std::max(1, height >> i); - VkBufferImageCopy region{}; - region.bufferOffset = 0; - region.bufferRowLength = 0; - region.bufferImageHeight = 0; + regions[i].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + regions[i].imageSubresource.mipLevel = i; + regions[i].imageSubresource.baseArrayLayer = 0; + regions[i].imageSubresource.layerCount = 1; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = 0; - region.imageSubresource.baseArrayLayer = 0; - region.imageSubresource.layerCount = 1; + regions[i].imageOffset = {0, 0, 0}; + regions[i].imageExtent = {regions[i].bufferRowLength, regions[i].bufferImageHeight, 1}; - region.imageOffset = {0, 0, 0}; - region.imageExtent = {width, height, 1}; - - vkCmdCopyBufferToImage(transferBuffer, src, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + //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 Allocator::findMemory(uint32_t typeFilter, VkMemoryPropertyFlags requirement, VkDeviceSize size) { updateProperties(); #if LOG_TRACE diff --git a/src/client/render/vk/Allocator.hpp b/src/client/render/vk/Allocator.hpp index f05c31d..daeaac3 100644 --- a/src/client/render/vk/Allocator.hpp +++ b/src/client/render/vk/Allocator.hpp @@ -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 maxAnisotropy; + float maxLodBias; }; constexpr VkDevice getDevice() const { return device; } diff --git a/src/client/render/vk/CommandCenter.cpp b/src/client/render/vk/CommandCenter.cpp index 491e30f..4f15cf5 100644 --- a/src/client/render/vk/CommandCenter.cpp +++ b/src/client/render/vk/CommandCenter.cpp @@ -46,8 +46,8 @@ CommandCenter::~CommandCenter() { void CommandCenter::allocate(const std::vector& 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()); diff --git a/src/client/render/vk/Renderer.cpp b/src/client/render/vk/Renderer.cpp index b9f94c4..8388886 100644 --- a/src/client/render/vk/Renderer.cpp +++ b/src/client/render/vk/Renderer.cpp @@ -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"); diff --git a/src/client/render/vk/api/Images.cpp b/src/client/render/vk/api/Images.cpp index e22354c..33686b2 100644 --- a/src/client/render/vk/api/Images.cpp +++ b/src/client/render/vk/api/Images.cpp @@ -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(req.props.format); - info.tiling = req.linear ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; + info.format = static_cast(req.format); + info.tiling = req.optimal ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; info.usage = static_cast(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(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(req.layout)); } + alloc->transitionImageLayout(out.ref, info.format, info.initialLayout, static_cast(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(req.props.format); + viewInfo.format = static_cast(req.format); viewInfo.subresourceRange.aspectMask = static_cast(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::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::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(maxAnisotropy.value(), 1 << (props.anisotropy-1)); } else { samplerInfo.anisotropyEnable = VK_FALSE; samplerInfo.maxAnisotropy = 1.f; @@ -135,10 +135,10 @@ std::unique_ptr 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) {