Files
iwa/include/iwa/image.hpp
2024-04-06 14:11:26 +02:00

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)