Add GEVulkanDepthTexture

This commit is contained in:
Benau 2022-07-10 14:48:07 +08:00
parent 7d94bf1f6f
commit 27b5770ea9
7 changed files with 208 additions and 41 deletions

View File

@ -29,6 +29,7 @@ set(GE_SOURCES
src/ge_vulkan_2d_renderer.cpp
src/ge_vulkan_camera_scene_node.cpp
src/ge_vulkan_command_loader.cpp
src/ge_vulkan_depth_texture.cpp
src/ge_vulkan_driver.cpp
src/ge_vulkan_dynamic_buffer.cpp
src/ge_vulkan_features.cpp

View File

@ -22,6 +22,7 @@ using namespace video;
namespace GE
{
class GEVulkanDepthTexture;
class GEVulkanMeshCache;
enum GEVulkanSampler : unsigned
{
@ -335,6 +336,10 @@ namespace GE
void waitIdle();
void setDisableWaitIdle(bool val) { m_disable_wait_idle = val; }
IrrlichtDevice* getIrrlichtDevice() const { return m_irrlicht_device; }
GEVulkanDepthTexture* getDepthTexture() const { return m_depth_texture; }
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates,
VkImageTiling tiling,
VkFormatFeatureFlags features);
private:
struct SwapChainSupportDetails
{
@ -452,6 +457,7 @@ namespace GE
bool m_disable_wait_idle;
IrrlichtDevice* m_irrlicht_device;
GEVulkanDepthTexture* m_depth_texture;
void createInstance(SDL_Window* window);
void findPhysicalDevice();

View File

@ -0,0 +1,39 @@
#include "ge_vulkan_depth_texture.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
namespace GE
{
GEVulkanDepthTexture::GEVulkanDepthTexture(GEVulkanDriver* vk,
const core::dimension2d<u32>& size)
: GEVulkanTexture()
{
m_vk = vk;
m_vulkan_device = m_vk->getDevice();
m_image = VK_NULL_HANDLE;
m_image_memory = VK_NULL_HANDLE;
m_image_view = VK_NULL_HANDLE;
m_has_mipmaps = false;
m_locked_data = NULL;
m_size = m_orig_size = m_max_size = size;
m_texture_size = m_size.Width * m_size.Height * 4;
std::vector<VkFormat> preferred =
{
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT
};
m_internal_format = m_vk->findSupportedFormat(preferred,
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
if (!createImage(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))
throw std::runtime_error("createImage failed for depth texture");
if (!createImageView(VK_IMAGE_ASPECT_DEPTH_BIT))
throw std::runtime_error("createImageView failed for depth texture");
} // GEVulkanDepthTexture
}

View File

@ -0,0 +1,53 @@
#ifndef HEADER_GE_VULKAN_DEPTH_TEXTURE_HPP
#define HEADER_GE_VULKAN_DEPTH_TEXTURE_HPP
#include "ge_vulkan_texture.hpp"
namespace GE
{
class GEVulkanDriver;
class GEVulkanDepthTexture : public GEVulkanTexture
{
public:
// ------------------------------------------------------------------------
GEVulkanDepthTexture(GEVulkanDriver* vk,
const core::dimension2d<u32>& size);
// ------------------------------------------------------------------------
virtual ~GEVulkanDepthTexture() {}
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0)
{ return NULL; }
// ------------------------------------------------------------------------
virtual void unlock() {}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getOriginalSize() const
{ return m_orig_size; }
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getSize() const { return m_size; }
// ------------------------------------------------------------------------
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 u64 getTextureHandler() const { return (u64)m_image_view; }
// ------------------------------------------------------------------------
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) {}
}; // GEVulkanDepthTexture
}
#endif

View File

@ -5,6 +5,7 @@
#include "ge_vulkan_2d_renderer.hpp"
#include "ge_vulkan_camera_scene_node.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_depth_texture.hpp"
#include "ge_vulkan_features.hpp"
#include "ge_vulkan_mesh_cache.hpp"
#include "ge_vulkan_shader_manager.hpp"
@ -459,7 +460,8 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params,
io::IFileSystem* io, SDL_Window* window,
IrrlichtDevice* device)
: CNullDriver(io, core::dimension2d<u32>(0, 0)),
m_params(params), m_irrlicht_device(device)
m_params(params), m_irrlicht_device(device),
m_depth_texture(NULL)
{
m_vk.reset(new VK());
m_physical_device = VK_NULL_HANDLE;
@ -577,6 +579,11 @@ GEVulkanDriver::~GEVulkanDriver()
// ----------------------------------------------------------------------------
void GEVulkanDriver::destroyVulkan()
{
if (m_depth_texture)
{
m_depth_texture->drop();
m_depth_texture = NULL;
}
if (m_white_texture)
{
m_white_texture->drop();
@ -1158,6 +1165,10 @@ found_mode:
throw std::runtime_error("vkCreateImageView failed");
m_vk->swap_chain_image_views.push_back(swap_chain_image_view);
}
m_depth_texture = new GEVulkanDepthTexture(this,
core::dimension2du(m_swap_chain_extent.width,
m_swap_chain_extent.height));
} // createSwapChain
// ----------------------------------------------------------------------------
@ -1283,21 +1294,43 @@ void GEVulkanDriver::createRenderPass()
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentDescription depth_attachment = {};
depth_attachment.format = m_depth_texture->getInternalFormat();
depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_attachment_ref = {};
depth_attachment_ref.attachment = 1;
depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
subpass.pDepthStencilAttachment = &depth_attachment_ref;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
std::array<VkAttachmentDescription, 1> attachments = { color_attachment };
std::array<VkAttachmentDescription, 2> attachments =
{
color_attachment,
depth_attachment
};
VkRenderPassCreateInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = (uint32_t)(attachments.size());
@ -1320,9 +1353,9 @@ void GEVulkanDriver::createFramebuffers()
const std::vector<VkImageView>& image_views = m_vk->swap_chain_image_views;
for (unsigned int i = 0; i < image_views.size(); i++)
{
std::array<VkImageView, 1> attachments =
std::array<VkImageView, 2> attachments =
{
image_views[i]
image_views[i], (VkImageView)m_depth_texture->getTextureHandler()
};
VkFramebufferCreateInfo framebuffer_info = {};
@ -1999,6 +2032,11 @@ void GEVulkanDriver::unpauseRendering()
void GEVulkanDriver::destroySwapChainRelated(bool handle_surface)
{
waitIdle();
if (m_depth_texture)
{
m_depth_texture->drop();
m_depth_texture = NULL;
}
for (VkFramebuffer& framebuffer : m_vk->swap_chain_framebuffers)
vkDestroyFramebuffer(m_vk->device, framebuffer, NULL);
m_vk->swap_chain_framebuffers.clear();
@ -2106,6 +2144,25 @@ void GEVulkanDriver::buildCommandBuffers()
vkEndCommandBuffer(getCurrentCommandBuffer());
} // buildCommandBuffers
// ----------------------------------------------------------------------------
VkFormat GEVulkanDriver::findSupportedFormat(const std::vector<VkFormat>& candidates,
VkImageTiling tiling,
VkFormatFeatureFlags features)
{
for (VkFormat format : candidates)
{
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(m_physical_device, format, &props);
if (tiling == VK_IMAGE_TILING_LINEAR &&
(props.linearTilingFeatures & features) == features)
return format;
else if (tiling == VK_IMAGE_TILING_OPTIMAL &&
(props.optimalTilingFeatures & features) == features)
return format;
}
throw std::runtime_error("failed to find supported format!");
}
}
namespace irr

View File

@ -25,8 +25,9 @@ GEVulkanTexture::GEVulkanTexture(const std::string& path,
m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_image_memory(VK_NULL_HANDLE),
m_image_view(VK_NULL_HANDLE), m_texture_size(0),
m_disable_reload(false), m_single_channel(false),
m_has_mipmaps(true)
m_disable_reload(false), m_has_mipmaps(true),
m_internal_format(VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
m_max_size = getDriver()->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
@ -50,8 +51,9 @@ GEVulkanTexture::GEVulkanTexture(video::IImage* img, const std::string& name)
m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_image_memory(VK_NULL_HANDLE),
m_image_view(VK_NULL_HANDLE), m_texture_size(0),
m_disable_reload(true), m_single_channel(false),
m_has_mipmaps(true)
m_disable_reload(true), m_has_mipmaps(true),
m_internal_format(VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
if (!img)
{
@ -75,12 +77,14 @@ GEVulkanTexture::GEVulkanTexture(const std::string& name, unsigned int size,
m_locked_data(NULL), m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_image_memory(VK_NULL_HANDLE),
m_image_view(VK_NULL_HANDLE), m_texture_size(0),
m_disable_reload(true), m_single_channel(single_channel),
m_has_mipmaps(true)
m_disable_reload(true), m_has_mipmaps(true),
m_internal_format(single_channel ?
VK_FORMAT_R8_UNORM : VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
if (m_single_channel && !GEVulkanFeatures::supportsR8Blit())
if (isSingleChannel() && !GEVulkanFeatures::supportsR8Blit())
m_has_mipmaps = false;
else if (!m_single_channel && !GEVulkanFeatures::supportsRGBA8Blit())
else if (!isSingleChannel() && !GEVulkanFeatures::supportsRGBA8Blit())
m_has_mipmaps = false;
m_orig_size.Width = size;
@ -88,7 +92,7 @@ GEVulkanTexture::GEVulkanTexture(const std::string& name, unsigned int size,
m_size = m_orig_size;
std::vector<uint8_t> data;
data.resize(size * size * (m_single_channel ? 1 : 4), 0);
data.resize(size * size * (isSingleChannel() ? 1 : 4), 0);
upload(data.data());
} // GEVulkanTexture
@ -100,7 +104,7 @@ GEVulkanTexture::~GEVulkanTexture()
if (m_image_view != VK_NULL_HANDLE || m_image != VK_NULL_HANDLE ||
m_image_memory != VK_NULL_HANDLE)
getVKDriver()->waitIdle();
m_vk->waitIdle();
clearVulkanData();
} // ~GEVulkanTexture
@ -114,7 +118,7 @@ bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
getMipmapSizes();
VkDeviceSize mipmap_data_size = 0;
unsigned channels = (m_single_channel ? 1 : 4);
unsigned channels = (isSingleChannel() ? 1 : 4);
VkDeviceSize image_size = m_size.Width * m_size.Height * channels;
if (generate_hq_mipmap)
{
@ -131,7 +135,7 @@ bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
VkBuffer staging_buffer;
VkDeviceMemory staging_buffer_memory;
bool success = getVKDriver()->createBuffer(image_total_size,
bool success = m_vk->createBuffer(image_total_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer,
@ -205,8 +209,7 @@ bool GEVulkanTexture::createImage(VkImageUsageFlags usage)
image_info.extent.depth = 1;
image_info.mipLevels = getMipmapLevels();
image_info.arrayLayers = 1;
image_info.format =
(m_single_channel ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8G8B8A8_UNORM);
image_info.format = m_internal_format;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = usage;
@ -223,7 +226,7 @@ bool GEVulkanTexture::createImage(VkImageUsageFlags usage)
vkGetImageMemoryRequirements(m_vulkan_device, m_image, &mem_requirements);
VkPhysicalDeviceMemoryProperties mem_properties;
vkGetPhysicalDeviceMemoryProperties(getVKDriver()->getPhysicalDevice(),
vkGetPhysicalDeviceMemoryProperties(m_vk->getPhysicalDevice(),
&mem_properties);
uint32_t memory_type_index = std::numeric_limits<uint32_t>::max();
@ -378,14 +381,13 @@ bool GEVulkanTexture::createImageView(VkImageAspectFlags aspect_flags)
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.format =
(m_single_channel ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8G8B8A8_UNORM);
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;
if (m_single_channel)
if (isSingleChannel())
{
view_info.components.r = VK_COMPONENT_SWIZZLE_ONE;
view_info.components.g = VK_COMPONENT_SWIZZLE_ONE;
@ -453,14 +455,14 @@ void GEVulkanTexture::upload(uint8_t* data, bool generate_hq_mipmap)
return;
if (!createImageView(VK_IMAGE_ASPECT_COLOR_BIT))
return;
m_texture_size = m_size.Width * m_size.Height * (m_single_channel ? 1 : 4);
m_texture_size = m_size.Width * m_size.Height * (isSingleChannel() ? 1 : 4);
} // upload
// ----------------------------------------------------------------------------
void* GEVulkanTexture::lock(video::E_TEXTURE_LOCK_MODE mode, u32 mipmap_level)
{
uint8_t* texture_data = getTextureData();
if (m_single_channel)
if (isSingleChannel())
{
m_locked_data = new uint8_t[m_size.Width * m_size.Height * 4]();
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
@ -488,8 +490,8 @@ uint8_t* GEVulkanTexture::getTextureData()
VkBuffer buffer;
VkDeviceMemory buffer_memory;
VkDeviceSize image_size =
m_size.Width * m_size.Height * (m_single_channel ? 1 : 4);
if (!getVKDriver()->createBuffer(image_size,
m_size.Width * m_size.Height * (isSingleChannel() ? 1 : 4);
if (!m_vk->createBuffer(image_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer, buffer_memory))
@ -548,12 +550,12 @@ void GEVulkanTexture::updateTexture(void* data, video::ECOLOR_FORMAT format,
VkBuffer staging_buffer;
VkDeviceMemory staging_buffer_memory;
if (m_single_channel)
if (isSingleChannel())
{
if (format == video::ECF_R8)
{
unsigned image_size = w * h;
if (!getVKDriver()->createBuffer(image_size,
if (!m_vk->createBuffer(image_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer,
@ -572,7 +574,7 @@ void GEVulkanTexture::updateTexture(void* data, video::ECOLOR_FORMAT format,
if (format == video::ECF_R8)
{
unsigned image_size = w * h * 4;
if (!getVKDriver()->createBuffer(w * h * 4,
if (!m_vk->createBuffer(w * h * 4,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer,
@ -593,7 +595,7 @@ void GEVulkanTexture::updateTexture(void* data, video::ECOLOR_FORMAT format,
else if (format == video::ECF_A8R8G8B8)
{
unsigned image_size = w * h * 4;
if (!getVKDriver()->createBuffer(w * h * 4,
if (!m_vk->createBuffer(w * h * 4,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer,
@ -622,9 +624,9 @@ void GEVulkanTexture::updateTexture(void* data, video::ECOLOR_FORMAT format,
copyBufferToImage(command_buffer, staging_buffer, w, h, x, y, 0, 0);
bool blit_mipmap = true;
if (m_single_channel && !GEVulkanFeatures::supportsR8Blit())
if (isSingleChannel() && !GEVulkanFeatures::supportsR8Blit())
blit_mipmap = false;
else if (!m_single_channel && !GEVulkanFeatures::supportsRGBA8Blit())
else if (!isSingleChannel() && !GEVulkanFeatures::supportsRGBA8Blit())
blit_mipmap = false;
if (blit_mipmap)
{
@ -720,7 +722,7 @@ void GEVulkanTexture::reload()
if (m_image_view != VK_NULL_HANDLE || m_image != VK_NULL_HANDLE ||
m_image_memory != VK_NULL_HANDLE)
getVKDriver()->waitIdle();
m_vk->waitIdle();
if (!m_disable_reload)
{
@ -745,7 +747,7 @@ void GEVulkanTexture::generateHQMipmap(void* in,
2/*hopcount*/, 2.0f/*alpha*/, 1.0f/*amplifynormal*/,
0.0f/*normalsustainfactor*/);
unsigned channels = (m_single_channel ? 1 : 4);
unsigned channels = (isSingleChannel() ? 1 : 4);
#ifdef DEBUG
int ret = imBuildMipmapCascade(&cascade, in, mms[0].first.Width,
mms[0].first.Height, 1/*layercount*/, channels,

View File

@ -16,9 +16,10 @@ using namespace irr;
namespace GE
{
class GEVulkanDriver;
class GEVulkanTexture : public video::ITexture
{
private:
protected:
core::dimension2d<u32> m_size, m_orig_size, m_max_size;
std::function<void(video::IImage*)> m_image_mani;
@ -37,8 +38,6 @@ private:
const bool m_disable_reload;
const bool m_single_channel;
bool m_has_mipmaps;
GESpinLock m_size_lock;
@ -47,6 +46,10 @@ private:
io::path m_full_path;
VkFormat m_internal_format;
GEVulkanDriver* m_vk;
// ------------------------------------------------------------------------
bool createTextureImage(uint8_t* texture_data, bool generate_hq_mipmap);
// ------------------------------------------------------------------------
@ -98,11 +101,15 @@ private:
// ------------------------------------------------------------------------
unsigned getMipmapLevels() const
{
if (!hasMipMaps())
if (!m_has_mipmaps)
return 1;
return std::floor(std::log2(std::max(m_size.Width, m_size.Height))) + 1;
}
// ------------------------------------------------------------------------
bool isSingleChannel() const
{ return m_internal_format == VK_FORMAT_R8_UNORM; }
// ------------------------------------------------------------------------
GEVulkanTexture() : video::ITexture(""), m_disable_reload(true) {}
public:
// ------------------------------------------------------------------------
GEVulkanTexture(const std::string& path,
@ -171,6 +178,8 @@ public:
// ------------------------------------------------------------------------
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y);
// ------------------------------------------------------------------------
VkFormat getInternalFormat() const { return m_internal_format; }
}; // GEVulkanTexture
}