forked from suyu/suyu
vk_texture_cache: Support image store on sRGB images with VkImageViewUsageCreateInfo
Vulkan 1.0 didn't support creating sRGB image views on an ABGR8 VkImage with storage usage bits. VK_KHR_maintenance2 addressed this allowing to reduce the usage bits on a VkImageView. To allow image store on non-sRGB image views when the VkImage is created with sRGB, always create VkImages without sRGB and add the sRGB format on the view.
This commit is contained in:
parent
8be9e5b48b
commit
6b00443bc1
3 changed files with 72 additions and 38 deletions
|
@ -110,8 +110,8 @@ VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_
|
||||||
} // namespace Sampler
|
} // namespace Sampler
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
constexpr u32 Attachable = 1 << 0;
|
||||||
enum : u32 { Attachable = 1, Storage = 2 };
|
constexpr u32 Storage = 1 << 1;
|
||||||
|
|
||||||
struct FormatTuple {
|
struct FormatTuple {
|
||||||
VkFormat format; ///< Vulkan format
|
VkFormat format; ///< Vulkan format
|
||||||
|
@ -222,22 +222,27 @@ constexpr bool IsZetaFormat(PixelFormat pixel_format) {
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format) {
|
FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with_srgb,
|
||||||
ASSERT(static_cast<std::size_t>(pixel_format) < std::size(tex_format_tuples));
|
PixelFormat pixel_format) {
|
||||||
|
ASSERT(static_cast<size_t>(pixel_format) < std::size(tex_format_tuples));
|
||||||
auto tuple = tex_format_tuples[static_cast<std::size_t>(pixel_format)];
|
FormatTuple tuple = tex_format_tuples[static_cast<size_t>(pixel_format)];
|
||||||
if (tuple.format == VK_FORMAT_UNDEFINED) {
|
if (tuple.format == VK_FORMAT_UNDEFINED) {
|
||||||
UNIMPLEMENTED_MSG("Unimplemented texture format with pixel format={}", pixel_format);
|
UNIMPLEMENTED_MSG("Unimplemented texture format with pixel format={}", pixel_format);
|
||||||
return {VK_FORMAT_A8B8G8R8_UNORM_PACK32, true, true};
|
return FormatInfo{VK_FORMAT_A8B8G8R8_UNORM_PACK32, true, true};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use A8B8G8R8_UNORM on hardware that doesn't support ASTC natively
|
// Use A8B8G8R8_UNORM on hardware that doesn't support ASTC natively
|
||||||
if (!device.IsOptimalAstcSupported() && VideoCore::Surface::IsPixelFormatASTC(pixel_format)) {
|
if (!device.IsOptimalAstcSupported() && VideoCore::Surface::IsPixelFormatASTC(pixel_format)) {
|
||||||
const bool is_srgb = VideoCore::Surface::IsPixelFormatSRGB(pixel_format);
|
const bool is_srgb = with_srgb && VideoCore::Surface::IsPixelFormatSRGB(pixel_format);
|
||||||
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
if (is_srgb) {
|
||||||
|
tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32;
|
||||||
|
} else {
|
||||||
|
tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||||
|
tuple.usage |= Storage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const bool attachable = tuple.usage & Attachable;
|
const bool attachable = (tuple.usage & Attachable) != 0;
|
||||||
const bool storage = tuple.usage & Storage;
|
const bool storage = (tuple.usage & Storage) != 0;
|
||||||
|
|
||||||
VkFormatFeatureFlags usage{};
|
VkFormatFeatureFlags usage{};
|
||||||
switch (format_type) {
|
switch (format_type) {
|
||||||
|
|
|
@ -35,7 +35,15 @@ struct FormatInfo {
|
||||||
bool storage;
|
bool storage;
|
||||||
};
|
};
|
||||||
|
|
||||||
FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format);
|
/**
|
||||||
|
* Returns format properties supported in the host
|
||||||
|
* @param device Host device
|
||||||
|
* @param format_type Type of image the buffer will use
|
||||||
|
* @param with_srgb True when the format can be sRGB when converted to another format (ASTC)
|
||||||
|
* @param pixel_format Guest pixel format to describe
|
||||||
|
*/
|
||||||
|
[[nodiscard]] FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with_srgb,
|
||||||
|
PixelFormat pixel_format);
|
||||||
|
|
||||||
VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage);
|
VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage);
|
||||||
|
|
||||||
|
|
|
@ -95,20 +95,12 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) {
|
[[nodiscard]] VkImageUsageFlags ImageUsageFlags(const MaxwellToVK::FormatInfo& info,
|
||||||
const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, info.format);
|
PixelFormat format) {
|
||||||
VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
|
||||||
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
|
|
||||||
info.size.width == info.size.height) {
|
|
||||||
flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
|
||||||
}
|
|
||||||
if (info.type == ImageType::e3D) {
|
|
||||||
flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
|
||||||
}
|
|
||||||
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
if (format_info.attachable) {
|
if (info.attachable) {
|
||||||
switch (VideoCore::Surface::GetFormatType(info.format)) {
|
switch (VideoCore::Surface::GetFormatType(format)) {
|
||||||
case VideoCore::Surface::SurfaceType::ColorTexture:
|
case VideoCore::Surface::SurfaceType::ColorTexture:
|
||||||
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||||
break;
|
break;
|
||||||
|
@ -120,9 +112,33 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||||
UNREACHABLE_MSG("Invalid surface type");
|
UNREACHABLE_MSG("Invalid surface type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (format_info.storage) {
|
if (info.storage) {
|
||||||
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
||||||
}
|
}
|
||||||
|
return usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the preferred format for a VkImage
|
||||||
|
[[nodiscard]] PixelFormat StorageFormat(PixelFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case PixelFormat::A8B8G8R8_SRGB:
|
||||||
|
return PixelFormat::A8B8G8R8_UNORM;
|
||||||
|
default:
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) {
|
||||||
|
const PixelFormat format = StorageFormat(info.format);
|
||||||
|
const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format);
|
||||||
|
VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||||
|
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
|
||||||
|
info.size.width == info.size.height) {
|
||||||
|
flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||||
|
}
|
||||||
|
if (info.type == ImageType::e3D) {
|
||||||
|
flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
||||||
|
}
|
||||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
|
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
|
||||||
return VkImageCreateInfo{
|
return VkImageCreateInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||||
|
@ -130,17 +146,16 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.imageType = ConvertImageType(info.type),
|
.imageType = ConvertImageType(info.type),
|
||||||
.format = format_info.format,
|
.format = format_info.format,
|
||||||
.extent =
|
.extent{
|
||||||
{
|
.width = info.size.width >> samples_x,
|
||||||
.width = info.size.width >> samples_x,
|
.height = info.size.height >> samples_y,
|
||||||
.height = info.size.height >> samples_y,
|
.depth = info.size.depth,
|
||||||
.depth = info.size.depth,
|
},
|
||||||
},
|
|
||||||
.mipLevels = static_cast<u32>(info.resources.levels),
|
.mipLevels = static_cast<u32>(info.resources.levels),
|
||||||
.arrayLayers = static_cast<u32>(info.resources.layers),
|
.arrayLayers = static_cast<u32>(info.resources.layers),
|
||||||
.samples = ConvertSampleCount(info.num_samples),
|
.samples = ConvertSampleCount(info.num_samples),
|
||||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||||
.usage = usage,
|
.usage = ImageUsageFlags(format_info, format),
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
.queueFamilyIndexCount = 0,
|
.queueFamilyIndexCount = 0,
|
||||||
.pQueueFamilyIndices = nullptr,
|
.pQueueFamilyIndices = nullptr,
|
||||||
|
@ -209,10 +224,11 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||||
|
|
||||||
[[nodiscard]] VkAttachmentDescription AttachmentDescription(const Device& device,
|
[[nodiscard]] VkAttachmentDescription AttachmentDescription(const Device& device,
|
||||||
const ImageView* image_view) {
|
const ImageView* image_view) {
|
||||||
const auto pixel_format = image_view->format;
|
using MaxwellToVK::SurfaceFormat;
|
||||||
|
const PixelFormat pixel_format = image_view->format;
|
||||||
return VkAttachmentDescription{
|
return VkAttachmentDescription{
|
||||||
.flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
|
.flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
|
||||||
.format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, pixel_format).format,
|
.format = SurfaceFormat(device, FormatType::Optimal, true, pixel_format).format,
|
||||||
.samples = image_view->Samples(),
|
.samples = image_view->Samples(),
|
||||||
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||||
|
@ -860,11 +876,16 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||||
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
|
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const VkFormat vk_format =
|
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
|
||||||
MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, format).format;
|
const VkFormat vk_format = format_info.format;
|
||||||
|
const VkImageViewUsageCreateInfo image_view_usage{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.usage = ImageUsageFlags(format_info, format),
|
||||||
|
};
|
||||||
const VkImageViewCreateInfo create_info{
|
const VkImageViewCreateInfo create_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = &image_view_usage,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.image = image.Handle(),
|
.image = image.Handle(),
|
||||||
.viewType = VkImageViewType{},
|
.viewType = VkImageViewType{},
|
||||||
|
@ -954,7 +975,7 @@ vk::ImageView ImageView::MakeDepthStencilView(VkImageAspectFlags aspect_mask) {
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.image = image_handle,
|
.image = image_handle,
|
||||||
.viewType = ImageViewType(type),
|
.viewType = ImageViewType(type),
|
||||||
.format = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, format).format,
|
.format = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format).format,
|
||||||
.components{
|
.components{
|
||||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
|
|
Loading…
Reference in a new issue