Add GEVulkanArrayTexture

This commit is contained in:
Benau 2022-08-20 15:44:38 +08:00
parent 694c1b6c29
commit 5aa70333ce
5 changed files with 337 additions and 21 deletions

View File

@ -71,6 +71,7 @@ set(GE_SOURCES
src/ge_texture.cpp
src/ge_vma.cpp
src/ge_vulkan_2d_renderer.cpp
src/ge_vulkan_array_texture.cpp
src/ge_vulkan_animated_mesh_scene_node.cpp
src/ge_vulkan_camera_scene_node.cpp
src/ge_vulkan_command_loader.cpp

View File

@ -0,0 +1,245 @@
#include "ge_vulkan_array_texture.hpp"
#include "ge_main.hpp"
#include "ge_mipmap_generator.hpp"
#include "ge_compressor_astc_4x4.hpp"
#include "ge_compressor_s3tc_bc3.hpp"
#include "ge_texture.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_features.hpp"
#include <IImageLoader.h>
#include <cassert>
namespace GE
{
// ============================================================================
std::vector<io::path> getPathList(const std::vector<GEVulkanTexture*>& tlist)
{
std::vector<io::path> list;
for (GEVulkanTexture* tex : tlist)
list.push_back(tex->getFullPath());
return list;
} // getPathList
// ============================================================================
GEVulkanArrayTexture::GEVulkanArrayTexture(const std::vector<io::path>& list,
VkImageViewType type,
std::function<void(video::IImage*,
unsigned)> image_mani)
: GEVulkanTexture()
{
if (list.empty())
throw std::runtime_error("empty texture list for array texture");
m_layer_count = list.size();
m_image_view_type = type;
m_vk = getVKDriver();
m_vulkan_device = m_vk->getDevice();
m_image = VK_NULL_HANDLE;
m_vma_allocation = VK_NULL_HANDLE;
m_has_mipmaps = true;
m_locked_data = NULL;
m_max_size = m_vk->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
m_internal_format = VK_FORMAT_R8G8B8A8_UNORM;
m_texture_size = 0;
m_size_lock.lock();
m_image_view_lock.lock();
GEVulkanCommandLoader::addMultiThreadingCommand(
[list, image_mani, this]()
{
reloadInternal(list, image_mani);
});
} // GEVulkanArrayTexture
// ----------------------------------------------------------------------------
GEVulkanArrayTexture::GEVulkanArrayTexture(
const std::vector<GEVulkanTexture*>& textures,
VkImageViewType type,
std::function<void(video::IImage*, unsigned)>
image_mani)
: GEVulkanArrayTexture(getPathList(textures), type,
image_mani)
{
} // GEVulkanArrayTexture
// ----------------------------------------------------------------------------
void GEVulkanArrayTexture::reloadInternal(const std::vector<io::path>& list,
std::function<void(video::IImage*,
unsigned)> image_mani)
{
VkDeviceSize image_size = 0;
VkDeviceSize mipmap_data_size = 0;
VkDeviceSize image_total_size = 0;
std::vector<video::IImage*> images;
std::vector<GEMipmapGenerator*> mipmaps;
VkBuffer staging_buffer = NULL;
VmaAllocation staging_buffer_allocation = NULL;
VmaAllocationCreateInfo staging_buffer_create_info = {};
staging_buffer_create_info.usage = VMA_MEMORY_USAGE_AUTO;
staging_buffer_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
staging_buffer_create_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
assert(m_layer_count == list.size());
uint8_t* mapped;
VkCommandBuffer command_buffer = VK_NULL_HANDLE;
unsigned offset = 0;
unsigned width = 0;
unsigned height = 0;
for (unsigned i = 0; i < list.size(); i++)
{
const io::path& fullpath = list[i];
video::IImageLoader* loader = NULL;
io::IReadFile* file = io::createReadFile(fullpath);
if (!file)
{
printf("Missing file %s in GEVulkanArrayTexture::reloadInternal",
fullpath.c_str());
goto destroy;
}
m_vk->createImageFromFile(file, &loader);
core::dimension2du dim;
if (!loader || !loader->getImageSize(file, &dim))
{
file->drop();
printf("Missing image loader for %s in "
"GEVulkanArrayTexture::reloadInternal", fullpath.c_str());
goto destroy;
}
file->drop();
if (m_image_view_type == VK_IMAGE_VIEW_TYPE_CUBE ||
m_image_view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
{
width = std::max({ width, dim.Width, dim.Height });
height = width;
}
else
{
width = std::max(width, dim.Width);
height = std::max(height, dim.Height);
}
}
m_size = core::dimension2du(width, height);
image_size = m_size.Width * m_size.Height * 4;
m_orig_size = m_size;
m_size_lock.unlock();
for (unsigned i = 0; i < list.size(); i++)
{
const io::path& fullpath = list[i];
video::IImage* texture_image = getResizedImageFullPath(fullpath,
m_max_size, NULL, &m_size);
if (texture_image == NULL)
goto destroy;
if (image_mani)
image_mani(texture_image, i);
uint8_t* texture_data = (uint8_t*)texture_image->lock();
bgraConversion(texture_data);
GEMipmapGenerator* mipmap_generator = NULL;
const bool normal_map = (std::string(fullpath.c_str()).find(
"_Normal.") != std::string::npos);
bool texture_compression = getGEConfig()->m_texture_compression;
if (texture_compression && GEVulkanFeatures::supportsASTC4x4())
{
if (i == 0)
{
image_size = get4x4CompressedTextureSize(m_size.Width,
m_size.Height);
m_internal_format = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
}
mipmap_generator = new GECompressorASTC4x4(texture_data, 4, m_size,
normal_map);
}
else if (texture_compression && GEVulkanFeatures::supportsS3TCBC3())
{
if (i == 0)
{
image_size = get4x4CompressedTextureSize(m_size.Width,
m_size.Height);
m_internal_format = VK_FORMAT_BC3_UNORM_BLOCK;
}
mipmap_generator = new GECompressorS3TCBC3(texture_data, 4, m_size,
normal_map);
}
else
{
mipmap_generator = new GEMipmapGenerator(texture_data, 4, m_size,
normal_map);
}
mipmap_data_size = mipmap_generator->getMipmapSizes();
if (mipmaps.empty())
{
image_total_size = image_size + mipmap_data_size;
image_total_size *= m_layer_count;
if (!m_vk->createBuffer(image_total_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info,
staging_buffer, staging_buffer_allocation))
{
goto destroy;
}
if (vmaMapMemory(m_vk->getVmaAllocator(),
staging_buffer_allocation, (void**)&mapped) != VK_SUCCESS)
{
goto destroy;
}
}
mipmaps.push_back(mipmap_generator);
for (GEImageLevel& level : mipmap_generator->getAllLevels())
{
memcpy(mapped, level.m_data, level.m_size);
mapped += level.m_size;
}
images.push_back(texture_image);
}
vmaUnmapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation);
vmaFlushAllocation(m_vk->getVmaAllocator(),
staging_buffer_allocation, 0, image_total_size);
if (!createImage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT))
goto destroy;
command_buffer = GEVulkanCommandLoader::beginSingleTimeCommands();
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
for (unsigned i = 0; i < list.size(); i++)
{
std::vector<GEImageLevel>& levels = mipmaps[i]->getAllLevels();
for (unsigned j = 0; j < levels.size(); j++)
{
GEImageLevel& level = levels[j];
copyBufferToImage(command_buffer, staging_buffer,
level.m_dim.Width, level.m_dim.Height, 0, 0, offset, j, i);
offset += level.m_size;
}
}
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
GEVulkanCommandLoader::endSingleTimeCommands(command_buffer);
createImageView(VK_IMAGE_ASPECT_COLOR_BIT);
destroy:
m_image_view_lock.unlock();
for (video::IImage* image : images)
image->drop();
for (GEMipmapGenerator* mipmap_generator : mipmaps)
delete mipmap_generator;
if (staging_buffer != VK_NULL_HANDLE)
{
vmaDestroyBuffer(m_vk->getVmaAllocator(), staging_buffer,
staging_buffer_allocation);
}
} // reloadInternal
}

View File

@ -0,0 +1,57 @@
#ifndef HEADER_GE_VULKAN_ARRAY_TEXTURE_HPP
#define HEADER_GE_VULKAN_ARRAY_TEXTURE_HPP
#include "ge_vulkan_texture.hpp"
namespace GE
{
class GEVulkanDriver;
class GEVulkanArrayTexture : public GEVulkanTexture
{
private:
void reloadInternal(const std::vector<io::path>& list,
std::function<void(video::IImage*, unsigned)>
image_mani);
public:
// ------------------------------------------------------------------------
GEVulkanArrayTexture(const std::vector<io::path>& full_path_list,
VkImageViewType type,
std::function<void(video::IImage*, unsigned)>
image_mani = nullptr);
// ------------------------------------------------------------------------
GEVulkanArrayTexture(const std::vector<GEVulkanTexture*>& textures,
VkImageViewType type,
std::function<void(video::IImage*, unsigned)>
image_mani = nullptr);
// ------------------------------------------------------------------------
virtual ~GEVulkanArrayTexture() {}
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0)
{ return NULL; }
// ------------------------------------------------------------------------
virtual void unlock() {}
// ------------------------------------------------------------------------
virtual video::E_DRIVER_TYPE getDriverType() const
{ return video::EDT_VULKAN; }
// ------------------------------------------------------------------------
virtual video::ECOLOR_FORMAT getColorFormat() const
{ return video::ECF_A8R8G8B8; }
// ------------------------------------------------------------------------
virtual u32 getPitch() const { return 0; }
// ------------------------------------------------------------------------
virtual bool hasMipMaps() const { return false; }
// ------------------------------------------------------------------------
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual unsigned int getTextureSize() const { return m_texture_size; }
// ------------------------------------------------------------------------
virtual void reload() {}
// ------------------------------------------------------------------------
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y) {}
}; // GEVulkanArrayTexture
}
#endif

View File

@ -28,9 +28,10 @@ GEVulkanTexture::GEVulkanTexture(const std::string& path,
m_locked_data(NULL),
m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE),
m_texture_size(0), m_disable_reload(false),
m_has_mipmaps(true), m_ondemand_load(false),
m_ondemand_loading(false),
m_texture_size(0), m_layer_count(1),
m_image_view_type(VK_IMAGE_VIEW_TYPE_2D),
m_disable_reload(false), m_has_mipmaps(true),
m_ondemand_load(false), m_ondemand_loading(false),
m_internal_format(VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
@ -75,9 +76,10 @@ GEVulkanTexture::GEVulkanTexture(video::IImage* img, const std::string& name)
m_locked_data(NULL),
m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE),
m_texture_size(0), m_disable_reload(true),
m_has_mipmaps(true), m_ondemand_load(false),
m_ondemand_loading(false),
m_texture_size(0), m_layer_count(1),
m_image_view_type(VK_IMAGE_VIEW_TYPE_2D),
m_disable_reload(true), m_has_mipmaps(true),
m_ondemand_load(false), m_ondemand_loading(false),
m_internal_format(VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
@ -102,9 +104,10 @@ GEVulkanTexture::GEVulkanTexture(const std::string& name, unsigned int size,
: video::ITexture(name.c_str()), m_image_mani(nullptr),
m_locked_data(NULL), m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE),
m_texture_size(0), m_disable_reload(true), m_has_mipmaps(true),
m_ondemand_load(false), m_ondemand_loading(false),
m_internal_format(single_channel ?
m_texture_size(0), m_layer_count(1),
m_image_view_type(VK_IMAGE_VIEW_TYPE_2D), m_disable_reload(true),
m_has_mipmaps(true), m_ondemand_load(false),
m_ondemand_loading(false), m_internal_format(single_channel ?
VK_FORMAT_R8_UNORM : VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
@ -229,14 +232,14 @@ bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
{
GEImageLevel& level = levels[i];
copyBufferToImage(command_buffer, staging_buffer,
level.m_dim.Width, level.m_dim.Height, 0, 0, offset, i);
level.m_dim.Width, level.m_dim.Height, 0, 0, offset, i, 0);
offset += level.m_size;
}
}
else
{
copyBufferToImage(command_buffer, staging_buffer, m_size.Width,
m_size.Height, 0, 0, 0, 0);
m_size.Height, 0, 0, 0, 0, 0);
}
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
@ -261,13 +264,16 @@ bool GEVulkanTexture::createImage(VkImageUsageFlags usage)
image_info.extent.height = m_size.Height;
image_info.extent.depth = 1;
image_info.mipLevels = getMipmapLevels();
image_info.arrayLayers = 1;
image_info.arrayLayers = m_layer_count;
image_info.format = m_internal_format;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = usage;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (m_image_view_type == VK_IMAGE_VIEW_TYPE_CUBE ||
m_image_view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
image_info.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
VmaAllocationCreateInfo alloc_info = {};
alloc_info.usage = VMA_MEMORY_USAGE_AUTO;
@ -295,7 +301,7 @@ void GEVulkanTexture::transitionImageLayout(VkCommandBuffer command_buffer,
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = getMipmapLevels();
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.layerCount = m_layer_count;
if (new_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
{
@ -376,7 +382,8 @@ void GEVulkanTexture::transitionImageLayout(VkCommandBuffer command_buffer,
// ----------------------------------------------------------------------------
void GEVulkanTexture::copyBufferToImage(VkCommandBuffer command_buffer,
VkBuffer buffer, u32 w, u32 h, s32 x,
s32 y, u32 offset, u32 mipmap_level)
s32 y, u32 offset, u32 mipmap_level,
u32 layer_level)
{
VkBufferImageCopy region = {};
region.bufferOffset = offset;
@ -384,7 +391,7 @@ void GEVulkanTexture::copyBufferToImage(VkCommandBuffer command_buffer,
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = mipmap_level;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.baseArrayLayer = layer_level;
region.imageSubresource.layerCount = 1;
region.imageOffset = {x, y, 0};
region.imageExtent = {w, h, 1};
@ -399,13 +406,13 @@ bool GEVulkanTexture::createImageView(VkImageAspectFlags aspect_flags)
VkImageViewCreateInfo view_info = {};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.image = m_image;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.viewType = m_image_view_type;
view_info.format = m_internal_format;
view_info.subresourceRange.aspectMask = aspect_flags;
view_info.subresourceRange.baseMipLevel = 0;
view_info.subresourceRange.levelCount = getMipmapLevels();
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 1;
view_info.subresourceRange.layerCount = m_layer_count;
if (isSingleChannel())
{
view_info.components.r = VK_COMPONENT_SWIZZLE_ONE;
@ -694,7 +701,7 @@ void GEVulkanTexture::updateTexture(void* data, video::ECOLOR_FORMAT format,
transitionImageLayout(command_buffer,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyBufferToImage(command_buffer, staging_buffer, w, h, x, y, 0, 0);
copyBufferToImage(command_buffer, staging_buffer, w, h, x, y, 0, 0, 0);
bool blit_mipmap = true;
if (isSingleChannel() && !GEVulkanFeatures::supportsR8Blit())

View File

@ -39,6 +39,10 @@ protected:
std::shared_ptr<std::atomic<VkImageView> > m_placeholder_view;
unsigned m_layer_count;
VkImageViewType m_image_view_type;
unsigned int m_texture_size;
const bool m_disable_reload;
@ -72,7 +76,7 @@ protected:
// ------------------------------------------------------------------------
void copyBufferToImage(VkCommandBuffer command_buffer, VkBuffer buffer,
u32 w, u32 h, s32 x, s32 y, u32 offset,
u32 mipmap_level);
u32 mipmap_level, u32 layer_level);
// ------------------------------------------------------------------------
void upload(uint8_t* data, bool generate_hq_mipmap = false);
// ------------------------------------------------------------------------
@ -114,8 +118,10 @@ protected:
return true;
}
// ------------------------------------------------------------------------
GEVulkanTexture() : video::ITexture(""), m_disable_reload(true),
m_ondemand_load(false), m_ondemand_loading(false) {}
GEVulkanTexture() : video::ITexture(""), m_layer_count(1),
m_image_view_type(VK_IMAGE_VIEW_TYPE_2D),
m_disable_reload(true), m_ondemand_load(false),
m_ondemand_loading(false) {}
public:
// ------------------------------------------------------------------------
GEVulkanTexture(const std::string& path,