271 lines
9.9 KiB
C++
271 lines
9.9 KiB
C++
|
|
#pragma once
|
|
|
|
#ifndef IWA_IMAGE_HPP_INCLUDED
|
|
#define IWA_IMAGE_HPP_INCLUDED
|
|
|
|
#include <mijin/async/coroutine.hpp>
|
|
#include <mijin/util/bitflags.hpp>
|
|
#include <mijin/util/flag.hpp>
|
|
#include "iwa/device_memory.hpp"
|
|
#include "iwa/object.hpp"
|
|
#include "iwa/vkwrapper.hpp"
|
|
#include "iwa/util/vkutil.hpp"
|
|
|
|
namespace iwa
|
|
{
|
|
inline constexpr std::uint32_t MAX_MIP_LEVELS = std::numeric_limits<std::uint32_t>::max();
|
|
|
|
static constexpr vk::ComponentMapping DEFAULT_COMPONENT_MAPPING = {
|
|
.r = vk::ComponentSwizzle::eIdentity,
|
|
.g = vk::ComponentSwizzle::eIdentity,
|
|
.b = vk::ComponentSwizzle::eIdentity,
|
|
.a = vk::ComponentSwizzle::eIdentity
|
|
};
|
|
|
|
static constexpr vk::ImageSubresourceRange DEFAULT_SUBRESOURCE_RANGE = {
|
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
.baseMipLevel = 0,
|
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = VK_REMAINING_ARRAY_LAYERS
|
|
};
|
|
|
|
static constexpr vk::ImageSubresourceRange DEFAULT_DEPTH_SUBRESOURCE_RANGE = {
|
|
.aspectMask = vk::ImageAspectFlagBits::eDepth,
|
|
.baseMipLevel = 0,
|
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = VK_REMAINING_ARRAY_LAYERS
|
|
};
|
|
|
|
static constexpr vk::ImageSubresourceLayers DEFAULT_SUBRESOURCE_LAYERS = {
|
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
.mipLevel = 0,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1
|
|
};
|
|
|
|
struct ImageTransition
|
|
{
|
|
vk::PipelineStageFlags stages;
|
|
vk::ImageLayout layout;
|
|
vk::AccessFlags access;
|
|
vk::ImageSubresourceRange subResourceRange = {
|
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
.baseMipLevel = 0,
|
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = VK_REMAINING_ARRAY_LAYERS
|
|
};
|
|
};
|
|
|
|
static constexpr ImageTransition IMAGE_TRANSITION_FRAGMENT_READ = {
|
|
.stages = vk::PipelineStageFlagBits::eFragmentShader,
|
|
.layout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
|
.access = vk::AccessFlagBits::eShaderRead
|
|
};
|
|
|
|
static constexpr ImageTransition IMAGE_TRANSITION_COMPUTE_WRITE = {
|
|
.stages = vk::PipelineStageFlagBits::eComputeShader,
|
|
.layout = vk::ImageLayout::eGeneral,
|
|
.access = vk::AccessFlagBits::eShaderWrite
|
|
};
|
|
|
|
static constexpr ImageTransition IMAGE_TRANSITION_COMPUTE_READ = {
|
|
.stages = vk::PipelineStageFlagBits::eComputeShader,
|
|
.layout = vk::ImageLayout::eGeneral,
|
|
.access = vk::AccessFlagBits::eShaderRead
|
|
};
|
|
|
|
static constexpr ImageTransition IMAGE_TRANSITION_COLOR_ATTACHMENT = {
|
|
.stages = vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
|
.layout = vk::ImageLayout::eColorAttachmentOptimal,
|
|
.access = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead
|
|
};
|
|
|
|
static constexpr ImageTransition IMAGE_TRANSITION_TRANSFER_READ = {
|
|
.stages = vk::PipelineStageFlagBits::eTransfer,
|
|
.layout = vk::ImageLayout::eTransferSrcOptimal,
|
|
.access = vk::AccessFlagBits::eTransferRead
|
|
};
|
|
|
|
static constexpr ImageTransition IMAGE_TRANSITION_TRANSFER_WRITE = {
|
|
.stages = vk::PipelineStageFlagBits::eTransfer,
|
|
.layout = vk::ImageLayout::eTransferDstOptimal,
|
|
.access = vk::AccessFlagBits::eTransferWrite
|
|
};
|
|
|
|
struct ImageCreationArgs
|
|
{
|
|
vk::ImageCreateFlags flags = {};
|
|
vk::ImageType imageType = vk::ImageType::e2D;
|
|
vk::Format format = vk::Format::eR8G8B8A8Unorm;
|
|
vk::Extent3D extent = {};
|
|
uint32_t mipLevels = 1;
|
|
uint32_t arrayLayers = 1;
|
|
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1;
|
|
vk::ImageTiling tiling = vk::ImageTiling::eOptimal;
|
|
vk::ImageUsageFlags usage = {};
|
|
vk::SharingMode sharingMode = vk::SharingMode::eExclusive;
|
|
std::vector<std::uint32_t> queueFamilyIndices;
|
|
vk::ImageLayout initialLayout = vk::ImageLayout::eUndefined;
|
|
};
|
|
|
|
struct ImageWrapArgs
|
|
{
|
|
vk::Image handle;
|
|
vk::ImageType type;
|
|
vk::Format format;
|
|
vk::ImageUsageFlags usage;
|
|
vk::Extent3D size;
|
|
unsigned mipLevels = 1;
|
|
};
|
|
|
|
struct ImageFromBitmapArgs
|
|
{
|
|
const class Bitmap* bitmap;
|
|
vk::ImageCreateFlags flags = {};
|
|
vk::ImageTiling tiling = vk::ImageTiling::eOptimal;
|
|
vk::ImageUsageFlags usage = {};
|
|
vk::SharingMode sharingMode = vk::SharingMode::eExclusive;
|
|
std::vector<std::uint32_t> queueFamilyIndices;
|
|
vk::ImageLayout initialLayout = vk::ImageLayout::eUndefined;
|
|
};
|
|
|
|
struct ImageViewCreationArgs
|
|
{
|
|
vk::ImageViewCreateFlags flags;
|
|
vk::ImageViewType viewType = vk::ImageViewType::e2D;
|
|
vk::Format format = vk::Format::eUndefined;
|
|
vk::ComponentMapping components = DEFAULT_COMPONENT_MAPPING;
|
|
vk::ImageSubresourceRange subresourceRange = DEFAULT_SUBRESOURCE_RANGE;
|
|
};
|
|
|
|
MIJIN_DEFINE_FLAG(ResetLayout);
|
|
|
|
class Image : public Object<Image, BaseObject, class Device>, public MixinVulkanObject<vk::Image>
|
|
{
|
|
private:
|
|
vk::ImageCreateFlags mFlags;
|
|
vk::ImageType mType;
|
|
vk::Format mFormat;
|
|
vk::ImageTiling mTiling;
|
|
vk::ImageUsageFlags mUsage;
|
|
vk::Extent3D mSize;
|
|
unsigned mArrayLayers;
|
|
unsigned mMipLevels;
|
|
bool mWrapped = false;
|
|
ObjectPtr<DeviceMemory> mMemory;
|
|
|
|
vk::ImageLayout currentLayout = vk::ImageLayout::eUndefined;
|
|
vk::PipelineStageFlags lastUsageStages = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
vk::AccessFlags lastAccess = {};
|
|
public:
|
|
Image(ObjectPtr<class Device> owner, ImageCreationArgs args);
|
|
Image(ObjectPtr<class Device> owner, ImageWrapArgs args);
|
|
~Image() noexcept override;
|
|
|
|
[[nodiscard]] vk::ImageType getType() const noexcept { return mType; }
|
|
[[nodiscard]] vk::Format getFormat() const noexcept { return mFormat; }
|
|
[[nodiscard]] vk::ImageUsageFlags getUsage() const noexcept { return mUsage; }
|
|
[[nodiscard]] const vk::Extent3D& getSize() const noexcept { return mSize; }
|
|
[[nodiscard]] unsigned getArrayLayers() const noexcept { return mArrayLayers; }
|
|
[[nodiscard]] unsigned getMipLevels() const noexcept { return mMipLevels; }
|
|
|
|
void allocateMemory();
|
|
void bindMemory(ObjectPtr<DeviceMemory> memory, vk::DeviceSize offset = 0);
|
|
void resetUsage(ResetLayout resetLayout = ResetLayout::NO) noexcept;
|
|
void applyTransition(vk::CommandBuffer cmdBuffer, const ImageTransition& transition);
|
|
|
|
[[nodiscard]] ObjectPtr<class ImageView> createImageView(const ImageViewCreationArgs& args = {});
|
|
|
|
mijin::Task<> c_doTransition(const ImageTransition& transition);
|
|
mijin::Task<> c_upload(const void* data, std::size_t bytes, vk::Extent3D bufferImageSize, vk::Offset3D imageOffset, unsigned baseLayer = 0, unsigned layerCount = 1);
|
|
mijin::Task<> c_upload(const class Bitmap& bitmap, vk::Offset3D imageOffset = {}, unsigned baseLayer = 0, unsigned layerCount = 1);
|
|
mijin::Task<> c_blitFrom(Image& srcImage, std::vector<vk::ImageBlit> regions, vk::Filter filter = vk::Filter::eNearest);
|
|
mijin::Task<> c_blitFrom(const class Bitmap& bitmap, std::vector<vk::ImageBlit> regions, vk::Filter filter = vk::Filter::eNearest);
|
|
mijin::Task<> c_copyFrom(Image& srcImage, std::vector<vk::ImageCopy> regions);
|
|
private:
|
|
[[nodiscard]] std::uint32_t clampMipLevels(std::uint32_t levels) const;
|
|
void generateMipMaps(vk::CommandBuffer cmdBuffer);
|
|
|
|
public:
|
|
static mijin::Task<ObjectPtr<Image>> c_create(ObjectPtr<class Device> owner, ImageFromBitmapArgs args);
|
|
};
|
|
|
|
class ImageView : public Object<ImageView, BaseObject, Image>, public MixinVulkanObject<vk::ImageView>
|
|
{
|
|
public:
|
|
ImageView(ObjectPtr<Image> owner, const ImageViewCreationArgs& args = {});
|
|
~ImageView() noexcept override;
|
|
};
|
|
|
|
struct SamplerCreationOptions : mijin::BitFlags<SamplerCreationOptions>
|
|
{
|
|
std::uint8_t anisotropyEnable : 1 = 0;
|
|
std::uint8_t compareEnable : 1 = 0;
|
|
std::uint8_t unnormalizedCoordinates : 1 = 0;
|
|
};
|
|
|
|
struct SamplerCreationArgs
|
|
{
|
|
vk::SamplerCreateFlags flags = {};
|
|
SamplerCreationOptions options;
|
|
vk::Filter magFilter = vk::Filter::eLinear;
|
|
vk::Filter minFilter = vk::Filter::eNearest;
|
|
vk::SamplerMipmapMode mipmapMode = vk::SamplerMipmapMode::eLinear;
|
|
vk::SamplerAddressMode addressModeU = vk::SamplerAddressMode::eRepeat;
|
|
vk::SamplerAddressMode addressModeV = vk::SamplerAddressMode::eRepeat;
|
|
vk::SamplerAddressMode addressModeW = vk::SamplerAddressMode::eRepeat;
|
|
float mipLodBias = 0.f;
|
|
float maxAnisotropy = 1.f;
|
|
vk::CompareOp compareOp = vk::CompareOp::eAlways;
|
|
float minLod = 0.f;
|
|
float maxLod = VK_LOD_CLAMP_NONE;
|
|
vk::BorderColor borderColor = vk::BorderColor::eFloatTransparentBlack;
|
|
};
|
|
|
|
class Sampler : public Object<Sampler, BaseObject, class Device>, public MixinVulkanObject<vk::Sampler>
|
|
{
|
|
public:
|
|
Sampler(ObjectPtr<class Device> owner, const SamplerCreationArgs& args = {});
|
|
~Sampler() noexcept override;
|
|
};
|
|
|
|
inline vk::ImageSubresourceRange defaultDepthSubresourceRange(vk::Format format, bool withStencil = true)
|
|
{
|
|
return {
|
|
.aspectMask = vk::ImageAspectFlagBits::eDepth | (withStencil && isStencilFormat(format) ? vk::ImageAspectFlagBits::eStencil : vk::ImageAspectFlagBits()),
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1, // not really any miplevels for depth textures
|
|
.baseArrayLayer = 0,
|
|
.layerCount = VK_REMAINING_ARRAY_LAYERS
|
|
};
|
|
}
|
|
|
|
inline vk::ImageSubresourceLayers defaultDepthSubresourceLayers(vk::Format format)
|
|
{
|
|
return {
|
|
.aspectMask = vk::ImageAspectFlagBits::eDepth | (isStencilFormat(format) ? vk::ImageAspectFlagBits::eStencil : vk::ImageAspectFlagBits()),
|
|
.mipLevel = 0,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = VK_REMAINING_ARRAY_LAYERS
|
|
};
|
|
}
|
|
|
|
|
|
inline ImageTransition imageTransitionDepthAttachment(vk::Format depthFormat)
|
|
{
|
|
return {
|
|
.stages = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests,
|
|
.layout = isStencilFormat(depthFormat) ? vk::ImageLayout::eDepthAttachmentStencilReadOnlyOptimal : vk::ImageLayout::eDepthAttachmentOptimal,
|
|
.access = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
|
.subResourceRange = defaultDepthSubresourceRange(depthFormat)
|
|
};
|
|
}
|
|
|
|
} // namespace iwa
|
|
|
|
#endif // !included(IWA_IMAGE_HPP_INCLUDED)
|