Files
KytyPS5/src/graphics/host_gpu/renderer/image/imageView.cpp
T

413 lines
15 KiB
C++

#include "graphics/host_gpu/renderer/image/imageView.h"
#include "common/assert.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/image/image.h"
#include <mutex>
namespace Libs::Graphics {
namespace {
[[nodiscard]] bool IsComponentSwizzle(vk::ComponentSwizzle swizzle) {
switch (swizzle) {
case vk::ComponentSwizzle::eIdentity:
case vk::ComponentSwizzle::eZero:
case vk::ComponentSwizzle::eOne:
case vk::ComponentSwizzle::eR:
case vk::ComponentSwizzle::eG:
case vk::ComponentSwizzle::eB:
case vk::ComponentSwizzle::eA: return true;
default: return false;
}
}
[[nodiscard]] bool IsCompatibleViewFormat(vk::Format image_format, vk::Format view_format) {
return ImageViewOps::FormatsCompatible(image_format, view_format);
}
[[nodiscard]] bool IsStencilViewFormat(vk::Format format) {
switch (format) {
case vk::Format::eS8Uint:
case vk::Format::eR8Uint:
case vk::Format::eR8Unorm: return true;
default: return false;
}
}
[[nodiscard]] bool IsDepthViewFormat(vk::Format format) {
switch (format) {
case vk::Format::eD16Unorm:
case vk::Format::eR16Unorm:
case vk::Format::eD32Sfloat:
case vk::Format::eR32Sfloat:
case vk::Format::eR32Uint: return true;
default: return false;
}
}
[[nodiscard]] bool IsValidViewType(const VulkanImage& image, const ImageViewInfo& info) {
switch (image.image_type) {
case vk::ImageType::e1D:
if (info.type != vk::ImageViewType::e1D && info.type != vk::ImageViewType::e1DArray) {
return false;
}
return info.type != vk::ImageViewType::e1D || info.layer_count == 1;
case vk::ImageType::e2D:
switch (info.type) {
case vk::ImageViewType::e2D: return info.layer_count == 1;
case vk::ImageViewType::e2DArray: return true;
case vk::ImageViewType::eCube:
return static_cast<bool>(image.flags &
vk::ImageCreateFlagBits::eCubeCompatible) &&
info.base_layer % 6 == 0 && info.layer_count == 6;
case vk::ImageViewType::eCubeArray:
return static_cast<bool>(image.flags &
vk::ImageCreateFlagBits::eCubeCompatible) &&
info.base_layer % 6 == 0 && info.layer_count % 6 == 0;
default: return false;
}
case vk::ImageType::e3D:
switch (info.type) {
case vk::ImageViewType::e3D: return info.base_layer == 0 && info.layer_count == 1;
case vk::ImageViewType::e2D:
return static_cast<bool>(image.flags &
vk::ImageCreateFlagBits::e2DArrayCompatible) &&
info.level_count == 1 && info.layer_count == 1;
case vk::ImageViewType::e2DArray:
return static_cast<bool>(image.flags &
vk::ImageCreateFlagBits::e2DArrayCompatible) &&
info.level_count == 1;
default: return false;
}
default: return false;
}
}
[[nodiscard]] bool IsValidAspect(const VulkanImage& image, vk::ImageAspectFlags aspect) {
const auto depth_format = DepthAspectTransferFormat(image.format);
if (depth_format == vk::Format::eUndefined) {
return aspect == vk::ImageAspectFlagBits::eColor;
}
const auto supported = ImageViewOps::DepthAspectMask(image.format);
return static_cast<bool>(aspect) && !(aspect & ~supported);
}
} // namespace
namespace ImageViewOps {
namespace {
enum CompatibilityClass : uint32_t {
None = 0,
Bit8 = 1u << 0,
Bit16 = 1u << 1,
Bit24 = 1u << 2,
Bit32 = 1u << 3,
Bit48 = 1u << 4,
Bit64 = 1u << 5,
Bit96 = 1u << 6,
Bit128 = 1u << 7,
Bit192 = 1u << 8,
Bit256 = 1u << 9,
Bc1Rgb = 1u << 10,
Bc1Rgba = 1u << 11,
Bc2 = 1u << 12,
Bc3 = 1u << 13,
Bc4 = 1u << 14,
Bc5 = 1u << 15,
Bc6h = 1u << 16,
Bc7 = 1u << 17,
D16 = 1u << 18,
D16S8 = 1u << 19,
D24 = 1u << 20,
D24S8 = 1u << 21,
D32 = 1u << 22,
D32S8 = 1u << 23,
S8 = 1u << 24,
};
[[nodiscard]] uint32_t FormatClass(vk::Format format) noexcept {
switch (format) {
case vk::Format::eR4G4UnormPack8:
case vk::Format::eR8Sint:
case vk::Format::eR8Snorm:
case vk::Format::eR8Srgb:
case vk::Format::eR8Sscaled:
case vk::Format::eR8Uint:
case vk::Format::eR8Unorm:
case vk::Format::eR8Uscaled: return Bit8;
case vk::Format::eA1R5G5B5UnormPack16:
case vk::Format::eA4B4G4R4UnormPack16:
case vk::Format::eA4R4G4B4UnormPack16:
case vk::Format::eB4G4R4A4UnormPack16:
case vk::Format::eB5G5R5A1UnormPack16:
case vk::Format::eB5G6R5UnormPack16:
case vk::Format::eR10X6UnormPack16:
case vk::Format::eR12X4UnormPack16:
case vk::Format::eR16Sfloat:
case vk::Format::eR16Sint:
case vk::Format::eR16Snorm:
case vk::Format::eR16Sscaled:
case vk::Format::eR16Uint:
case vk::Format::eR16Unorm:
case vk::Format::eR16Uscaled:
case vk::Format::eR4G4B4A4UnormPack16:
case vk::Format::eR5G5B5A1UnormPack16:
case vk::Format::eR5G6B5UnormPack16:
case vk::Format::eR8G8Sint:
case vk::Format::eR8G8Snorm:
case vk::Format::eR8G8Srgb:
case vk::Format::eR8G8Sscaled:
case vk::Format::eR8G8Uint:
case vk::Format::eR8G8Unorm:
case vk::Format::eR8G8Uscaled: return Bit16;
case vk::Format::eB8G8R8Sint:
case vk::Format::eB8G8R8Snorm:
case vk::Format::eB8G8R8Srgb:
case vk::Format::eB8G8R8Sscaled:
case vk::Format::eB8G8R8Uint:
case vk::Format::eB8G8R8Unorm:
case vk::Format::eB8G8R8Uscaled:
case vk::Format::eR8G8B8Sint:
case vk::Format::eR8G8B8Snorm:
case vk::Format::eR8G8B8Srgb:
case vk::Format::eR8G8B8Sscaled:
case vk::Format::eR8G8B8Uint:
case vk::Format::eR8G8B8Unorm:
case vk::Format::eR8G8B8Uscaled: return Bit24;
case vk::Format::eA2B10G10R10SintPack32:
case vk::Format::eA2B10G10R10SnormPack32:
case vk::Format::eA2B10G10R10SscaledPack32:
case vk::Format::eA2B10G10R10UintPack32:
case vk::Format::eA2B10G10R10UnormPack32:
case vk::Format::eA2B10G10R10UscaledPack32:
case vk::Format::eA2R10G10B10SintPack32:
case vk::Format::eA2R10G10B10SnormPack32:
case vk::Format::eA2R10G10B10SscaledPack32:
case vk::Format::eA2R10G10B10UintPack32:
case vk::Format::eA2R10G10B10UnormPack32:
case vk::Format::eA2R10G10B10UscaledPack32:
case vk::Format::eA8B8G8R8SintPack32:
case vk::Format::eA8B8G8R8SnormPack32:
case vk::Format::eA8B8G8R8SrgbPack32:
case vk::Format::eA8B8G8R8SscaledPack32:
case vk::Format::eA8B8G8R8UintPack32:
case vk::Format::eA8B8G8R8UnormPack32:
case vk::Format::eA8B8G8R8UscaledPack32:
case vk::Format::eB10G11R11UfloatPack32:
case vk::Format::eB8G8R8A8Sint:
case vk::Format::eB8G8R8A8Snorm:
case vk::Format::eB8G8R8A8Srgb:
case vk::Format::eB8G8R8A8Sscaled:
case vk::Format::eB8G8R8A8Uint:
case vk::Format::eB8G8R8A8Unorm:
case vk::Format::eB8G8R8A8Uscaled:
case vk::Format::eE5B9G9R9UfloatPack32:
case vk::Format::eR10X6G10X6Unorm2Pack16:
case vk::Format::eR12X4G12X4Unorm2Pack16:
case vk::Format::eR16G16Sfloat:
case vk::Format::eR16G16Sint:
case vk::Format::eR16G16Snorm:
case vk::Format::eR16G16Sscaled:
case vk::Format::eR16G16Uint:
case vk::Format::eR16G16Unorm:
case vk::Format::eR16G16Uscaled:
case vk::Format::eR32Sfloat:
case vk::Format::eR32Sint:
case vk::Format::eR32Uint:
case vk::Format::eR8G8B8A8Sint:
case vk::Format::eR8G8B8A8Snorm:
case vk::Format::eR8G8B8A8Srgb:
case vk::Format::eR8G8B8A8Sscaled:
case vk::Format::eR8G8B8A8Uint:
case vk::Format::eR8G8B8A8Unorm:
case vk::Format::eR8G8B8A8Uscaled: return Bit32;
case vk::Format::eR16G16B16Sfloat:
case vk::Format::eR16G16B16Sint:
case vk::Format::eR16G16B16Snorm:
case vk::Format::eR16G16B16Sscaled:
case vk::Format::eR16G16B16Uint:
case vk::Format::eR16G16B16Unorm:
case vk::Format::eR16G16B16Uscaled: return Bit48;
case vk::Format::eR16G16B16A16Sfloat:
case vk::Format::eR16G16B16A16Sint:
case vk::Format::eR16G16B16A16Snorm:
case vk::Format::eR16G16B16A16Sscaled:
case vk::Format::eR16G16B16A16Uint:
case vk::Format::eR16G16B16A16Unorm:
case vk::Format::eR16G16B16A16Uscaled:
case vk::Format::eR32G32Sfloat:
case vk::Format::eR32G32Sint:
case vk::Format::eR32G32Uint:
case vk::Format::eR64Sfloat:
case vk::Format::eR64Sint:
case vk::Format::eR64Uint: return Bit64;
case vk::Format::eR32G32B32Sfloat:
case vk::Format::eR32G32B32Sint:
case vk::Format::eR32G32B32Uint: return Bit96;
case vk::Format::eR32G32B32A32Sfloat:
case vk::Format::eR32G32B32A32Sint:
case vk::Format::eR32G32B32A32Uint:
case vk::Format::eR64G64Sfloat:
case vk::Format::eR64G64Sint:
case vk::Format::eR64G64Uint: return Bit128;
case vk::Format::eR64G64B64Sfloat:
case vk::Format::eR64G64B64Sint:
case vk::Format::eR64G64B64Uint: return Bit192;
case vk::Format::eR64G64B64A64Sfloat:
case vk::Format::eR64G64B64A64Sint:
case vk::Format::eR64G64B64A64Uint: return Bit256;
case vk::Format::eBc1RgbSrgbBlock:
case vk::Format::eBc1RgbUnormBlock: return Bc1Rgb | Bit64;
case vk::Format::eBc1RgbaSrgbBlock:
case vk::Format::eBc1RgbaUnormBlock: return Bc1Rgba | Bit64;
case vk::Format::eBc2SrgbBlock:
case vk::Format::eBc2UnormBlock: return Bc2 | Bit128;
case vk::Format::eBc3SrgbBlock:
case vk::Format::eBc3UnormBlock: return Bc3 | Bit128;
case vk::Format::eBc4SnormBlock:
case vk::Format::eBc4UnormBlock: return Bc4 | Bit64;
case vk::Format::eBc5SnormBlock:
case vk::Format::eBc5UnormBlock: return Bc5 | Bit128;
case vk::Format::eBc6HSfloatBlock:
case vk::Format::eBc6HUfloatBlock: return Bc6h | Bit128;
case vk::Format::eBc7SrgbBlock:
case vk::Format::eBc7UnormBlock: return Bc7 | Bit128;
case vk::Format::eD16Unorm: return D16;
case vk::Format::eD16UnormS8Uint: return D16S8;
case vk::Format::eX8D24UnormPack32: return D24;
case vk::Format::eD24UnormS8Uint: return D24S8;
case vk::Format::eD32Sfloat: return D32;
case vk::Format::eD32SfloatS8Uint: return D32S8;
case vk::Format::eS8Uint: return S8;
default: return None;
}
}
} // namespace
vk::ImageAspectFlags DepthAspectMask(vk::Format format) {
switch (format) {
case vk::Format::eD16Unorm:
case vk::Format::eD32Sfloat: return vk::ImageAspectFlagBits::eDepth;
case vk::Format::eD16UnormS8Uint:
case vk::Format::eD24UnormS8Uint:
case vk::Format::eD32SfloatS8Uint:
return vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
default: EXIT("unsupported depth/stencil image format: %d\n", static_cast<int>(format));
}
}
bool FormatsCompatible(vk::Format base, vk::Format view) noexcept {
if (base == view) {
return true;
}
const auto base_class = FormatClass(base);
const auto view_class = FormatClass(view);
return view_class != None && (base_class & view_class) == view_class;
}
} // namespace ImageViewOps
vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
const auto& image = backing;
auto normalized = view_info;
const bool is_storage = static_cast<bool>(normalized.usage & vk::ImageUsageFlagBits::eStorage);
normalized.aspect = FullAspectMask(image.format);
if (normalized.aspect & vk::ImageAspectFlagBits::eDepth &&
IsDepthViewFormat(normalized.format)) {
normalized.format = image.format;
normalized.aspect = vk::ImageAspectFlagBits::eDepth;
}
if (normalized.aspect & vk::ImageAspectFlagBits::eStencil &&
IsStencilViewFormat(normalized.format)) {
normalized.format = image.format;
normalized.aspect = vk::ImageAspectFlagBits::eStencil;
}
normalized.usage = is_storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlags {};
const bool format_compatible = normalized.format != vk::Format::eUndefined &&
IsCompatibleViewFormat(image.format, normalized.format);
const bool slice_view =
image.image_type == vk::ImageType::e3D && (normalized.type == vk::ImageViewType::e2D ||
normalized.type == vk::ImageViewType::e2DArray);
const bool levels_valid = normalized.level_count != 0 &&
normalized.base_level < image.mip_levels &&
normalized.level_count <= image.mip_levels - normalized.base_level;
const auto view_layers = slice_view && levels_valid
? std::max(image.extent.depth >> normalized.base_level, 1u)
: image.layers;
const bool ranges_valid = levels_valid && normalized.layer_count != 0 &&
normalized.base_layer < view_layers &&
normalized.layer_count <= view_layers - normalized.base_layer;
const bool mapping_valid =
IsComponentSwizzle(normalized.mapping.r) && IsComponentSwizzle(normalized.mapping.g) &&
IsComponentSwizzle(normalized.mapping.b) && IsComponentSwizzle(normalized.mapping.a);
if (image.image == nullptr || !format_compatible || !ranges_valid || !mapping_valid ||
!IsValidViewType(image, normalized) || !IsValidAspect(image, normalized.aspect)) {
EXIT("invalid image view: image_format=%d view_format=%d type=%d aspect=0x%x "
"mip=%u+%u layer=%u+%u usage=0x%x image_levels=%u image_layers=%u\n",
static_cast<int>(image.format), static_cast<int>(normalized.format),
static_cast<int>(normalized.type),
static_cast<vk::ImageAspectFlags::MaskType>(normalized.aspect), normalized.base_level,
normalized.level_count, normalized.base_layer, normalized.layer_count,
static_cast<vk::ImageUsageFlags::MaskType>(normalized.usage), image.mip_levels,
image.layers);
}
std::lock_guard lock(views.mutex);
for (const auto& cached: views.views) {
if (cached.info == normalized) {
return cached.view;
}
}
vk::ImageViewUsageCreateInfo usage {};
usage.sType = vk::StructureType::eImageViewUsageCreateInfo;
usage.usage = image.usage;
if (!is_storage) {
usage.usage &= ~vk::ImageUsageFlagBits::eStorage;
}
vk::ImageViewCreateInfo create {};
create.sType = vk::StructureType::eImageViewCreateInfo;
create.pNext = &usage;
create.image = image.image;
create.viewType = normalized.type;
create.format = normalized.format;
create.components = normalized.mapping;
create.subresourceRange.aspectMask = normalized.aspect;
create.subresourceRange.baseMipLevel = normalized.base_level;
create.subresourceRange.levelCount = normalized.level_count;
create.subresourceRange.baseArrayLayer = normalized.base_layer;
create.subresourceRange.layerCount = normalized.layer_count;
vk::ImageView view = nullptr;
const auto result = m_graphics->device.createImageView(&create, nullptr, &view);
if (result != vk::Result::eSuccess || view == nullptr) {
EXIT("failed to create image view: result=%d image_format=%d view_format=%d type=%d "
"aspect=0x%x mip=%u+%u layer=%u+%u usage=0x%x\n",
static_cast<int>(result), static_cast<int>(image.format),
static_cast<int>(view_info.format), static_cast<int>(view_info.type),
static_cast<vk::ImageAspectFlags::MaskType>(view_info.aspect), view_info.base_level,
view_info.level_count, view_info.base_layer, view_info.layer_count,
static_cast<vk::ImageUsageFlags::MaskType>(view_info.usage));
}
views.views.push_back({normalized, view});
return view;
}
} // namespace Libs::Graphics