在Vulkan中,以下是这几个概念的简要说明以及它们之间的关系:
-
Texture2D
:- 纹理在vulkan API中并没有一个结构体来表示,它是着色器语言中(比如GLSL)的一种概念。在着色器中,
sampler2D
类型的变量表示一个二维纹理。
- 纹理在vulkan API中并没有一个结构体来表示,它是着色器语言中(比如GLSL)的一种概念。在着色器中,
-
VkImage
:- 它是Vulkan中表示图像的对象,负责存储图像的元数据(metadata),但不存储像素的具体值,比如宽度、高度、格式等。
- 类似于
VkBuffer
对象,VkImage 也只是一个句柄(handle),它本身并不包含实际的像素数据。相应地,你需要通过vkBindImageMemory()
函数将 VkImage 与实际的VkDeviceMemory
对象关联起来,以便在这块内存中存储图像的像素数据。具体可以参考一下这个文章 - 一旦完成关联,后续的
vkCmdCopyImage
等类似的指令,就以该VkImage作为句柄来对该图像数据进行引用了。当然,在事后资源释放时,需要分别销毁vulkan对象(VkDestroyImage
)以及释放设备内存(VkFreeMemory
)
在 Vulkan 中,对于很多对象(比如 VkBuffer 和 VkImage),数据存储和句柄是分离的,这为开发者提供了更多的灵活性。这样的设计允许你更精细地控制数据的存储位置、布局和访问权限。
-
VkImageView
:VkImageView
是对VkImage
的一个视图或描述。它指定了如何从底层的VkImage
中读取数据,包括图像的格式、范围等。在Vulkan中,你通常需要使用VkImageView
来对VkImage
进行采样操作。
-
VkSampler
:VkSampler
是一个对象,用于描述纹理采样的方式。它定义了纹理采样时的过滤方式、边界模式、各向异性过滤等参数。VkSampler
通常与VkImageView
一起用于纹理采样操作。
-
VkDeviceMemory
:VkDeviceMemory
用于表示 Vulkan 设备上的内存对象,它是用于存储VkImage
或其他 Vulkan 对象数据的实际内存。在 Vulkan 中,你需要显式地为图像数据分配GPU内存,完成数据的写入(从CPU端内存到GPU),然后将该内存绑定到相应的VkImage
对象上。
关系总结:
VkImage
是底层图像数据的表示,它包含像素数据和图像的元数据。VkImageView
是对VkImage
的描述,它提供了访问图像数据的视图,用于采样等操作。VkSampler
描述了纹理采样的方式,包括过滤方式、边界模式等。VkDeviceMemory
是分配给VkImage
或其他 Vulkan 对象的实际内存,用于存储底层数据。
在实际使用中,通常流程是:
创建 VkImage
对象
→
\to
→ 分配相应的 VkDeviceMemory
→
\to
→ 创建 VkImageView
和 VkSampler
以便在着色器中进行纹理采样操作。
一般在游戏引擎里,也会有个Texture2D的类,用来封装一些图像的操作和数据,如:
struct TextureSpecification
{
ImageFormat Format = ImageFormat::RGBA;
uint32_t Width = 1;
uint32_t Height = 1;
TextureWrap SamplerWrap = TextureWrap::Repeat;
TextureFilter SamplerFilter = TextureFilter::Linear;
bool GenerateMips = true;
bool SRGB = false;
bool Storage = false;
bool StoreLocally = false;
std::string DebugName;
};
class VulkanTexture2D : public Texture2D
{
public:
VulkanTexture2D(const TextureSpecification& specification, const std::filesystem::path& filepath);
VulkanTexture2D(const TextureSpecification& specification, Buffer data = Buffer());
~VulkanTexture2D() override;
virtual void Resize(const glm::uvec2& size) override;
virtual void Resize(uint32_t width, uint32_t height) override;
void Invalidate();
virtual ImageFormat GetFormat() const override { return m_Specification.Format; }
virtual uint32_t GetWidth() const override { return m_Specification.Width; }
virtual uint32_t GetHeight() const override { return m_Specification.Height; }
virtual glm::uvec2 GetSize() const override { return { m_Specification.Width, m_Specification.Height }; }
virtual void Bind(uint32_t slot = 0) const override;
virtual Ref<Image2D> GetImage() const override { return m_Image; }
virtual ResourceDescriptorInfo GetDescriptorInfo() const override { return m_Image.As<VulkanImage2D>()->GetDescriptorInfo(); }
const VkDescriptorImageInfo& GetDescriptorInfoVulkan() const { return *(VkDescriptorImageInfo*)GetDescriptorInfo(); }
void Lock() override;
void Unlock() override;
Buffer GetWriteableBuffer() override;
bool Loaded() const override { return m_ImageData; }
const std::filesystem::path& GetPath() const override;
uint32_t GetMipLevelCount() const override;
virtual std::pair<uint32_t, uint32_t> GetMipSize(uint32_t mip) const override;
void GenerateMips();
virtual uint64_t GetHash() const { return (uint64_t)m_Image.As<VulkanImage2D>()->GetDescriptorInfoVulkan().imageView; }
void CopyToHostBuffer(Buffer& buffer);
private:
std::filesystem::path m_Path;
TextureSpecification m_Specification;
Buffer m_ImageData; // CPU端内存,存放图像文件中读取的数据
Ref<Image2D> m_Image; // Image2D也是自定义的类型看下面
};
VulkanImage2D
这又是一层封装,核心内容是
- VkImage Image = nullptr;
- VkImageView ImageView = nullptr;
- VkSampler Sampler = nullptr;
- VmaAllocation MemoryAlloc = nullptr;
然后是其他的一些相关特性、操作的东西
class VulkanImage2D : public Image2D
{
public:
VulkanImage2D(const ImageSpecification& specification);
virtual ~VulkanImage2D() override;
virtual void Resize(const glm::uvec2& size) override
{
Resize(size.x, size.y);
}
virtual void Resize(const uint32_t width, const uint32_t height) override
{
m_Specification.Width = width;
m_Specification.Height = height;
Invalidate();
}
virtual void Invalidate() override;
virtual void Release() override;
virtual uint32_t GetWidth() const override { return m_Specification.Width; }
virtual uint32_t GetHeight() const override { return m_Specification.Height; }
virtual glm::uvec2 GetSize() const override { return { m_Specification.Width, m_Specification.Height };}
virtual float GetAspectRatio() const override { return (float)m_Specification.Width / (float)m_Specification.Height; }
virtual ImageSpecification& GetSpecification() override { return m_Specification; }
virtual const ImageSpecification& GetSpecification() const override { return m_Specification; }
void RT_Invalidate();
virtual void CreatePerLayerImageViews() override;
void RT_CreatePerLayerImageViews();
void RT_CreatePerSpecificLayerImageViews(const std::vector<uint32_t>& layerIndices);
virtual VkImageView GetLayerImageView(uint32_t layer)
{
HZ_CORE_ASSERT(layer < m_PerLayerImageViews.size());
return m_PerLayerImageViews[layer];
}
VkImageView GetMipImageView(uint32_t mip);
VkImageView RT_GetMipImageView(uint32_t mip);
VulkanImageInfo& GetImageInfo() { return m_Info; }
const VulkanImageInfo& GetImageInfo() const { return m_Info; }
virtual ResourceDescriptorInfo GetDescriptorInfo() const override { return (ResourceDescriptorInfo)&m_DescriptorImageInfo; }
const VkDescriptorImageInfo& GetDescriptorInfoVulkan() const { return *(VkDescriptorImageInfo*)GetDescriptorInfo(); }
virtual Buffer GetBuffer() const override { return m_ImageData; }
virtual Buffer& GetBuffer() override { return m_ImageData; }
virtual uint64_t GetHash() const override { return (uint64_t)m_Info.Image; }
void UpdateDescriptor();
// Debug
static const std::map<VkImage, WeakRef<VulkanImage2D>>& GetImageRefs();
void CopyToHostBuffer(Buffer& buffer);
private:
ImageSpecification m_Specification;
Buffer m_ImageData;
VkImage Image = nullptr;
VkImageView ImageView = nullptr;
VkSampler Sampler = nullptr;
VmaAllocation MemoryAlloc = nullptr;
VkDeviceSize m_GPUAllocationSize;
std::vector<VkImageView> m_PerLayerImageViews;
std::map<uint32_t, VkImageView> m_PerMipImageViews;
VkDescriptorImageInfo m_DescriptorImageInfo = {};
};