149 lines
5.7 KiB
C++
149 lines
5.7 KiB
C++
#include "Images.hpp"
|
|
#include "Buffers.hpp"
|
|
#include "../Allocator.hpp"
|
|
#include "../../../../core/utils/logger.hpp"
|
|
|
|
using namespace render::vk;
|
|
|
|
Image::~Image() {
|
|
vkDestroyImage(Allocator::GetDefault()->getDevice(), ref, ALLOC);
|
|
vkDestroyImageView(Allocator::GetDefault()->getDevice(), view, ALLOC);
|
|
}
|
|
Texture::~Texture() {
|
|
vkDestroySampler(Allocator::GetDefault()->getDevice(), sampler, ALLOC);
|
|
}
|
|
|
|
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();
|
|
|
|
VkImageCreateInfo info{};
|
|
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
info.imageType = VK_IMAGE_TYPE_2D;
|
|
info.extent.width = req.size.width;
|
|
info.extent.height = req.size.height;
|
|
info.extent.depth = 1;
|
|
info.mipLevels = req.mipmapLevels;
|
|
info.arrayLayers = 1;
|
|
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;
|
|
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
info.flags = 0;
|
|
if (data) {
|
|
info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
}
|
|
|
|
if (vkCreateImage(device, &info, ALLOC, &out.ref) != VK_SUCCESS) {
|
|
LOG_E("Failed to create image");
|
|
return memory::GetNull();
|
|
}
|
|
out.offset = 0;
|
|
|
|
VkMemoryRequirements memRequirements;
|
|
vkGetImageMemoryRequirements(device, out.ref, &memRequirements);
|
|
|
|
auto memory = alloc->allocate(memRequirements, properties, true);
|
|
if (!memory || vkBindImageMemory(device, out.ref, memory->ref, memory->offset) != VK_SUCCESS) {
|
|
LOG_E("Failed to allocate image memory");
|
|
return memory::GetNull();
|
|
}
|
|
|
|
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, 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();
|
|
}
|
|
}
|
|
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.format);
|
|
viewInfo.subresourceRange.aspectMask = static_cast<VkImageAspectFlags>(req.aspect);
|
|
viewInfo.subresourceRange.baseMipLevel = 0;
|
|
viewInfo.subresourceRange.levelCount = req.mipmapLevels;
|
|
viewInfo.subresourceRange.baseArrayLayer = 0;
|
|
viewInfo.subresourceRange.layerCount = 1;
|
|
|
|
if (vkCreateImageView(device, &viewInfo, ALLOC, &out.view) != VK_SUCCESS) {
|
|
LOG_E("Failed to create texture image view!");
|
|
return memory::GetNull();
|
|
}
|
|
return memory;
|
|
}
|
|
//TODO: createImages
|
|
|
|
std::unique_ptr<Image> Image::Create(const requirement & req) {
|
|
vk::Image::info img;
|
|
auto mem = createImage(req, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, data_view(), img);
|
|
if(!mem) {
|
|
FATAL("Cannot create texture image");
|
|
}
|
|
return std::unique_ptr<Image>(new Image(img.ref, img.view, std::move(mem)));
|
|
}
|
|
|
|
std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sampling& props) {
|
|
auto device = Allocator::GetDefault()->getDevice();
|
|
|
|
std::vector<unsigned char> data;
|
|
auto header = [&] {
|
|
if (auto header = render::Image::Read(path, data)) {
|
|
return header.value();
|
|
}
|
|
FATAL("Cannot read texture");
|
|
}();
|
|
|
|
vk::Image::info img;
|
|
auto mem = createImage(requirement::Texture(header),
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, data, img);
|
|
if(!mem) {
|
|
FATAL("Cannot create texture image");
|
|
}
|
|
|
|
VkSamplerCreateInfo samplerInfo{};
|
|
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
|
samplerInfo.magFilter = props.magLinear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
|
samplerInfo.minFilter = props.minLinear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
|
|
|
const auto wrap = static_cast<VkSamplerAddressMode>(props.wrap);
|
|
samplerInfo.addressModeU = wrap;
|
|
samplerInfo.addressModeV = wrap;
|
|
samplerInfo.addressModeW = wrap;
|
|
|
|
auto maxAnisotropy = Allocator::GetDefault()->getCapabilities().maxAnisotropy;
|
|
if (maxAnisotropy && props.anisotropy > 0) {
|
|
samplerInfo.anisotropyEnable = VK_TRUE;
|
|
samplerInfo.maxAnisotropy = std::min<float>(maxAnisotropy.value(), 1 << (props.anisotropy-1));
|
|
} else {
|
|
samplerInfo.anisotropyEnable = VK_FALSE;
|
|
samplerInfo.maxAnisotropy = 1.f;
|
|
}
|
|
|
|
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
|
|
samplerInfo.unnormalizedCoordinates = VK_FALSE;
|
|
|
|
samplerInfo.compareEnable = VK_FALSE;
|
|
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
|
|
|
|
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 = props.mipmap ? header.mipmapLevels : 0.0f;
|
|
|
|
VkSampler sampler;
|
|
if (vkCreateSampler(device, &samplerInfo, ALLOC, &sampler) != VK_SUCCESS) {
|
|
FATAL("Failed to create texture sampler!");
|
|
}
|
|
|
|
return std::unique_ptr<Texture>(new Texture(sampler, img.view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, img.ref, std::move(mem)));
|
|
} |