Add ondemand texture loading for vulkan

This commit is contained in:
Benau 2022-08-12 13:07:53 +08:00
parent eace871c66
commit e0636495f0
9 changed files with 229 additions and 69 deletions

View File

@ -13,15 +13,20 @@ irr::video::ITexture* createFontTexture(const std::string& name,
unsigned size, bool single_channel);
irr::video::ITexture* createTexture(irr::video::IImage* img,
const std::string& name);
irr::core::dimension2d<irr::u32> getResizingTarget(
const irr::core::dimension2d<irr::u32>& orig_size,
const irr::core::dimension2d<irr::u32>& max_size);
irr::video::IImage* getResizedImage(const std::string& path,
const irr::core::dimension2d<irr::u32>& max_size,
irr::core::dimension2d<irr::u32>* orig_size = NULL);
irr::video::IImage* getResizedImageFullPath(const irr::io::path& fullpath,
const irr::core::dimension2d<irr::u32>& max_size,
irr::core::dimension2d<irr::u32>* orig_size = NULL);
irr::core::dimension2d<irr::u32>* orig_size = NULL,
const irr::core::dimension2d<irr::u32>* target_size = NULL);
irr::video::IImage* getResizedImage(irr::io::IReadFile* file,
const irr::core::dimension2d<irr::u32>& max_size,
irr::core::dimension2d<irr::u32>* orig_size = NULL);
irr::core::dimension2d<irr::u32>* orig_size = NULL,
const irr::core::dimension2d<irr::u32>* target_size = NULL);
irr::video::ITexture* createTexture(const std::string& path,
std::function<void(irr::video::IImage*)> image_mani = nullptr);
}; // GE

View File

@ -10,6 +10,7 @@ namespace irr
}
#include <array>
#include <atomic>
#include <map>
#include <memory>
#include <vector>
@ -21,12 +22,14 @@ enum GEVulkanSampler : unsigned;
class GEVulkanTextureDescriptor
{
typedef std::array<std::shared_ptr<VkImageView>,
typedef std::array<std::shared_ptr<std::atomic<VkImageView> >,
_IRR_MATERIAL_MAX_TEXTURES_> TextureList;
std::map<TextureList, int> m_texture_list;
std::shared_ptr<VkImageView> m_white_image, m_transparent_image;
std::shared_ptr<std::atomic<VkImageView> > m_white_image;
std::shared_ptr<std::atomic<VkImageView> > m_transparent_image;
VkDescriptorSetLayout m_descriptor_set_layout;
@ -68,7 +71,7 @@ public:
{
for (auto& t : p.first)
{
if (*(t.get()) == VK_NULL_HANDLE)
if (t.get()->load() == VK_NULL_HANDLE)
{
has_deleted_image_view = true;
break;

View File

@ -23,15 +23,32 @@ video::IImage* getResizedImage(const std::string& path,
return image;
} // getResizedImage
// ----------------------------------------------------------------------------
core::dimension2du getResizingTarget(const core::dimension2du& orig_size,
const core::dimension2du& max_size)
{
bool has_npot = !getGEConfig()->m_disable_npot_texture &&
getDriver()->queryFeature(video::EVDF_TEXTURE_NPOT);
core::dimension2du tex_size = orig_size.getOptimalSize(!has_npot);
if (tex_size.Width > max_size.Width)
tex_size.Width = max_size.Width;
if (tex_size.Height > max_size.Height)
tex_size.Height = max_size.Height;
return tex_size;
} // getResizingTarget
// ----------------------------------------------------------------------------
video::IImage* getResizedImageFullPath(const io::path& fullpath,
const core::dimension2d<u32>& max_size,
core::dimension2d<u32>* orig_size)
core::dimension2d<u32>* orig_size,
const core::dimension2d<u32>* target_size)
{
io::IReadFile* file = io::createReadFile(fullpath);
if (file == NULL)
return NULL;
video::IImage* texture_image = getResizedImage(file, max_size, orig_size);
video::IImage* texture_image = getResizedImage(file, max_size, orig_size,
target_size);
file->drop();
return texture_image;
} // getResizedImageFullPath
@ -39,7 +56,8 @@ video::IImage* getResizedImageFullPath(const io::path& fullpath,
// ----------------------------------------------------------------------------
video::IImage* getResizedImage(irr::io::IReadFile* file,
const core::dimension2du& max_size,
core::dimension2d<u32>* orig_size)
core::dimension2d<u32>* orig_size,
const core::dimension2d<u32>* target_size)
{
video::IImage* image = getDriver()->createImageFromFile(file);
if (image == NULL)
@ -48,14 +66,11 @@ video::IImage* getResizedImage(irr::io::IReadFile* file,
*orig_size = image->getDimension();
core::dimension2du img_size = image->getDimension();
bool has_npot = !getGEConfig()->m_disable_npot_texture &&
getDriver()->queryFeature(video::EVDF_TEXTURE_NPOT);
core::dimension2du tex_size = img_size.getOptimalSize(!has_npot);
if (tex_size.Width > max_size.Width)
tex_size.Width = max_size.Width;
if (tex_size.Height > max_size.Height)
tex_size.Height = max_size.Height;
core::dimension2du tex_size;
if (target_size)
tex_size = *target_size;
else
tex_size = getResizingTarget(img_size, max_size);
if (image->getColorFormat() != video::ECF_A8R8G8B8 ||
tex_size != img_size)

View File

@ -39,7 +39,7 @@ public:
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u64 getTextureHandler() const
{ return (u64)*(m_image_view.get()); }
{ return (u64)(m_image_view.get()->load()); }
// ------------------------------------------------------------------------
virtual unsigned int getTextureSize() const { return m_texture_size; }
// ------------------------------------------------------------------------
@ -48,7 +48,7 @@ public:
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y) {}
// ------------------------------------------------------------------------
virtual std::shared_ptr<VkImageView> getImageView() const
virtual std::shared_ptr<std::atomic<VkImageView> > getImageView() const
{ return m_image_view; }
}; // GEVulkanDepthTexture

View File

@ -36,7 +36,7 @@ public:
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u64 getTextureHandler() const
{ return (u64)*(m_image_view.get()); }
{ return (u64)(m_image_view.get()->load()); }
// ------------------------------------------------------------------------
virtual unsigned int getTextureSize() const { return m_texture_size; }
// ------------------------------------------------------------------------
@ -45,7 +45,7 @@ public:
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y) {}
// ------------------------------------------------------------------------
virtual std::shared_ptr<VkImageView> getImageView() const
virtual std::shared_ptr<std::atomic<VkImageView> > getImageView() const
{ return m_image_view; }
// ------------------------------------------------------------------------
void createRTT();

View File

@ -15,7 +15,9 @@ extern "C"
#include <mipmap/imgresize.h>
}
#include <cassert>
#include <IAttributes.h>
#include <IImageLoader.h>
#include <limits>
namespace GE
@ -27,7 +29,8 @@ GEVulkanTexture::GEVulkanTexture(const std::string& path,
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_has_mipmaps(true), m_ondemand_load(false),
m_ondemand_loading(false),
m_internal_format(VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
@ -40,6 +43,24 @@ GEVulkanTexture::GEVulkanTexture(const std::string& path,
return;
}
if (m_ondemand_load)
{
video::IImageLoader* loader = NULL;
io::IReadFile* file = io::createReadFile(m_full_path);
getDriver()->createImageFromFile(file, &loader);
if (loader && loader->getImageSize(file, &m_orig_size))
{
m_size = getResizingTarget(m_orig_size, m_max_size);
if (m_size.Width < 4 || m_size.Height < 4)
m_has_mipmaps = false;
setPlaceHolderView();
}
else
LoadingFailed = true;
file->drop();
return;
}
m_size_lock.lock();
m_image_view_lock.lock();
GEVulkanCommandLoader::addMultiThreadingCommand(
@ -53,7 +74,8 @@ GEVulkanTexture::GEVulkanTexture(video::IImage* img, const std::string& name)
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_has_mipmaps(true), m_ondemand_load(false),
m_ondemand_loading(false),
m_internal_format(VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
@ -79,6 +101,7 @@ GEVulkanTexture::GEVulkanTexture(const std::string& name, unsigned int size,
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 ?
VK_FORMAT_R8_UNORM : VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
@ -100,8 +123,7 @@ GEVulkanTexture::GEVulkanTexture(const std::string& name, unsigned int size,
// ----------------------------------------------------------------------------
GEVulkanTexture::~GEVulkanTexture()
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
waitImageView();
if (m_image_view || m_image != VK_NULL_HANDLE ||
m_vma_allocation != VK_NULL_HANDLE)
@ -390,12 +412,18 @@ bool GEVulkanTexture::createImageView(VkImageAspectFlags aspect_flags)
view_info.components.a = VK_COMPONENT_SWIZZLE_R;
}
std::shared_ptr<VkImageView> image_view = std::make_shared<VkImageView>();
auto image_view = std::make_shared<std::atomic<VkImageView> >();
VkImageView view_ptr = NULL;
VkResult result = vkCreateImageView(m_vulkan_device, &view_info, NULL,
image_view.get());
&view_ptr);
if (result == VK_SUCCESS)
{
image_view.get()->store(view_ptr);
m_image_view = image_view;
if (m_placeholder_view)
m_placeholder_view.get()->store(VK_NULL_HANDLE);
m_ondemand_loading.store(false);
return true;
}
return false;
@ -406,12 +434,16 @@ void GEVulkanTexture::clearVulkanData()
{
if (m_image_view)
{
vkDestroyImageView(m_vulkan_device, *m_image_view.get(), NULL);
*(m_image_view.get()) = VK_NULL_HANDLE;
vkDestroyImageView(m_vulkan_device, m_image_view.get()->load(), NULL);
m_image_view.get()->store(VK_NULL_HANDLE);
m_image_view.reset();
}
if (m_image != VK_NULL_HANDLE)
{
vmaDestroyImage(m_vk->getVmaAllocator(), m_image, m_vma_allocation);
m_image = VK_NULL_HANDLE;
m_vma_allocation = VK_NULL_HANDLE;
}
} // clearVulkanData
// ----------------------------------------------------------------------------
@ -422,17 +454,30 @@ void GEVulkanTexture::reloadInternal()
clearVulkanData();
video::IImage* texture_image = getResizedImageFullPath(m_full_path,
m_max_size, &m_orig_size);
video::IImage* texture_image = NULL;
if (m_ondemand_load)
{
texture_image = getResizedImageFullPath(m_full_path, m_max_size,
NULL, &m_size);
if (texture_image == NULL)
{
throw std::runtime_error(
"Missing texture_image in getResizedImageFullPath");
}
}
else
{
texture_image = getResizedImageFullPath(m_full_path, m_max_size,
&m_orig_size);
if (texture_image == NULL)
{
throw std::runtime_error(
"Missing texture_image in getResizedImageFullPath");
}
m_size = texture_image->getDimension();
if (m_size.Width < 4 || m_size.Height < 4)
m_has_mipmaps = false;
}
m_size_lock.unlock();
if (m_image_mani)
@ -492,7 +537,7 @@ uint8_t* GEVulkanTexture::getTextureData()
return NULL;
video::IImage* texture_image = getResizedImageFullPath(m_full_path,
m_max_size, &m_orig_size);
m_max_size, NULL, &m_size);
if (texture_image == NULL)
return NULL;
texture_image->setDeleteMemory(false);
@ -501,8 +546,8 @@ uint8_t* GEVulkanTexture::getTextureData()
return data;
}
m_image_view_lock.lock();
m_image_view_lock.unlock();
if (!waitImageView())
return NULL;
VkBuffer buffer;
VmaAllocation buffer_allocation;
@ -567,8 +612,8 @@ cleanup:
void GEVulkanTexture::updateTexture(void* data, video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
if (!waitImageView())
return;
VkBuffer staging_buffer;
VmaAllocation staging_buffer_allocation;
@ -743,14 +788,19 @@ void GEVulkanTexture::bgraConversion(uint8_t* img_data)
//-----------------------------------------------------------------------------
void GEVulkanTexture::reload()
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
if (!waitImageView())
return;
if (m_image_view || m_image != VK_NULL_HANDLE ||
m_vma_allocation != VK_NULL_HANDLE)
m_vk->waitIdle();
if (!m_disable_reload)
if (m_ondemand_load)
{
clearVulkanData();
setPlaceHolderView();
}
else if (!m_disable_reload)
{
m_size_lock.lock();
m_image_view_lock.lock();
@ -759,4 +809,35 @@ void GEVulkanTexture::reload()
}
} // reload
//-----------------------------------------------------------------------------
void GEVulkanTexture::setPlaceHolderView()
{
auto tex = static_cast<GEVulkanTexture*>(m_vk->getTransparentTexture());
auto image_view = std::make_shared<std::atomic<VkImageView> >();
image_view.get()->store((VkImageView)tex->getTextureHandler());
if (m_placeholder_view)
m_placeholder_view.get()->store(VK_NULL_HANDLE);
m_placeholder_view = image_view;
} // setPlaceHolderView
//-----------------------------------------------------------------------------
std::shared_ptr<std::atomic<VkImageView> > GEVulkanTexture::getImageViewLive() const
{
assert(m_ondemand_load && m_placeholder_view);
if (m_ondemand_loading.load() == false)
{
if (m_image_view)
return m_image_view;
else
{
GEVulkanTexture* tex = const_cast<GEVulkanTexture*>(this);
GEVulkanCommandLoader::addMultiThreadingCommand(
std::bind(&GEVulkanTexture::reloadInternal, tex));
m_ondemand_loading.store(true);
return m_placeholder_view;
}
}
return m_placeholder_view;
} // getImageViewLive
}

View File

@ -7,6 +7,7 @@
#include "ge_spin_lock.hpp"
#include <algorithm>
#include <atomic>
#include <cmath>
#include <functional>
#include <memory>
@ -34,7 +35,9 @@ protected:
VmaAllocation m_vma_allocation;
std::shared_ptr<VkImageView> m_image_view;
std::shared_ptr<std::atomic<VkImageView> > m_image_view;
std::shared_ptr<std::atomic<VkImageView> > m_placeholder_view;
unsigned int m_texture_size;
@ -42,6 +45,10 @@ protected:
bool m_has_mipmaps;
bool m_ondemand_load;
mutable std::atomic<bool> m_ondemand_loading;
GESpinLock m_size_lock;
GESpinLock m_image_view_lock;
@ -87,7 +94,28 @@ protected:
bool isSingleChannel() const
{ return m_internal_format == VK_FORMAT_R8_UNORM; }
// ------------------------------------------------------------------------
GEVulkanTexture() : video::ITexture(""), m_disable_reload(true) {}
void setPlaceHolderView();
// ------------------------------------------------------------------------
std::shared_ptr<std::atomic<VkImageView> > getImageViewLive() const;
// ------------------------------------------------------------------------
bool waitImageView()
{
if (!m_ondemand_load)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
}
else
{
while (m_ondemand_loading.load());
if (m_image == VK_NULL_HANDLE)
return false;
}
return true;
}
// ------------------------------------------------------------------------
GEVulkanTexture() : video::ITexture(""), m_disable_reload(true),
m_ondemand_load(false), m_ondemand_loading(false) {}
public:
// ------------------------------------------------------------------------
GEVulkanTexture(const std::string& path,
@ -113,16 +141,22 @@ public:
}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getOriginalSize() const
{
if (!m_ondemand_load)
{
m_size_lock.lock();
m_size_lock.unlock();
}
return m_orig_size;
}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getSize() const
{
if (!m_ondemand_load)
{
m_size_lock.lock();
m_size_lock.unlock();
}
return m_size;
}
// ------------------------------------------------------------------------
@ -139,16 +173,27 @@ public:
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u64 getTextureHandler() const
{
if (!m_ondemand_load)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
return m_image_view ? (u64)*(m_image_view.get()) : 0;
return m_image_view ? (u64)(m_image_view.get()->load()) : 0;
}
else
{
auto image_view = getImageViewLive();
return (u64)(image_view.get()->load());
}
}
// ------------------------------------------------------------------------
virtual unsigned int getTextureSize() const
{
if (!m_ondemand_load)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
}
return m_texture_size;
}
// ------------------------------------------------------------------------
@ -157,12 +202,17 @@ public:
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y);
// ------------------------------------------------------------------------
virtual std::shared_ptr<VkImageView> getImageView() const
virtual std::shared_ptr<std::atomic<VkImageView> > getImageView() const
{
if (!m_ondemand_load)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
return m_image_view;
}
else
return getImageViewLive();
}
// ------------------------------------------------------------------------
VkFormat getInternalFormat() const { return m_internal_format; }
}; // GEVulkanTexture

View File

@ -131,9 +131,9 @@ void GEVulkanTextureDescriptor::updateDescriptor()
VkDescriptorImageInfo info;
info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
info.sampler = m_vk->getSampler(m_sampler_use);
info.imageView = *(p.first[i].get());
info.imageView = p.first[i].get()->load();
if (info.imageView == VK_NULL_HANDLE)
info.imageView = *m_transparent_image.get();
info.imageView = m_transparent_image.get()->load();
image_infos[p.second * m_max_layer + i] = info;
}
}
@ -143,7 +143,7 @@ void GEVulkanTextureDescriptor::updateDescriptor()
{
VkDescriptorImageInfo dummy_info;
dummy_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
dummy_info.imageView = *m_transparent_image.get();
dummy_info.imageView = m_transparent_image.get()->load();
dummy_info.sampler = m_vk->getSampler(m_sampler_use);
image_infos.resize(m_max_texture_list * m_max_layer, dummy_info);
}

View File

@ -1298,13 +1298,19 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file, video::IImageLoade
{
if (SurfaceLoader[i]->isALoadableFileExtension(file->getFileName()))
{
// reset file position which might have changed due to previous loadImage calls
file->seek(0);
if (loader)
{
if (SurfaceLoader[i]->isALoadableFileFormat(file))
{
file->seek(0);
*loader = SurfaceLoader[i];
return 0;
}
// reset file position which might have changed due to previous loadImage calls
file->seek(0);
else
continue;
}
image = SurfaceLoader[i]->loadImage(file);
if (image)
return image;
@ -1318,12 +1324,12 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file, video::IImageLoade
file->seek(0);
if (SurfaceLoader[i]->isALoadableFileFormat(file))
{
file->seek(0);
if (loader)
{
*loader = SurfaceLoader[i];
return 0;
}
file->seek(0);
image = SurfaceLoader[i]->loadImage(file);
if (image)
return image;