diff options
| author | lizzie <lizzie@eden-emu.dev> | 2026-05-15 22:08:09 +0200 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-05-15 22:08:09 +0200 |
| commit | 4d49341918981a782395fe379a8e6846e1fd11b2 (patch) | |
| tree | 6d09831d70ef4898dd5da9db4e1bf72200c504da | |
| parent | 2f0f8a979c88b1ded8b5cce9f9bee1a59bb2508c (diff) | |
[vk, opengl] recognize and use ETC2 (if available) textures natively (#3237)
this makes it so VK and OGL backends map the NVIDIA's ETC2 into VK_FORMAT_ETC-whatever and GL_ETC-whatever remaps, instead of using the default fallback for AR8G8B8. in short, just make the ETC2 textures be submitted as ETC2 instead of being submit as A8R8G8B8.
Signed-off-by: lizzie lizzie@eden-emu.dev
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3237
Reviewed-by: Ghost <>
Reviewed-by: crueter <crueter@eden-emu.dev>
| -rw-r--r-- | src/video_core/renderer_vulkan/maxwell_to_vk.cpp | 11 | ||||
| -rw-r--r-- | src/video_core/surface.cpp | 19 | ||||
| -rw-r--r-- | src/video_core/surface.h | 9 | ||||
| -rw-r--r-- | src/video_core/texture_cache/format_lookup_table.cpp | 14 | ||||
| -rw-r--r-- | src/video_core/texture_cache/formatter.h | 210 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_device.h | 5 |
6 files changed, 60 insertions, 208 deletions
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 930f06daf6..77ef8bf7bc 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -217,6 +217,12 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_UNORM_BLOCK, 0, ASTC_2D_6X5_UNORM) \ SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_SRGB_BLOCK, 0, ASTC_2D_6X5_SRGB) \ SURFACE_FORMAT_ELEM(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, 0, E5B9G9R9_FLOAT) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, 0, ETC2_RGB_UNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, 0, ETC2_RGBA_UNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, 0, ETC2_RGB_PTA_UNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \ /* Depth formats */ \ SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \ SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \ @@ -253,8 +259,8 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with break; } } - // Transcode on hardware that doesn't support BCn natively if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) { + // Transcode on hardware that doesn't support BCn natively if (pixel_format == PixelFormat::BC4_SNORM) { tuple.format = VK_FORMAT_R8_SNORM; } else if (pixel_format == PixelFormat::BC4_UNORM) { @@ -270,6 +276,9 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with } else { tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; } + } else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) { + // Transcode on hardware that doesn't support ETC2 natively + tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32; } bool const attachable = (tuple.usage & usage_attachable) != 0; bool const storage = (tuple.usage & usage_storage) != 0; diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 1998849e84..69c1720943 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2014 Citra Emulator Project @@ -336,6 +336,20 @@ bool IsPixelFormatBCn(PixelFormat format) { } } +bool IsPixelFormatETC2(PixelFormat format) { + switch (format) { + case PixelFormat::ETC2_RGB_UNORM: + case PixelFormat::ETC2_RGBA_UNORM: + case PixelFormat::ETC2_RGB_PTA_UNORM: + case PixelFormat::ETC2_RGB_SRGB: + case PixelFormat::ETC2_RGBA_SRGB: + case PixelFormat::ETC2_RGB_PTA_SRGB: + return true; + default: + return false; + } +} + bool IsPixelFormatSRGB(PixelFormat format) { switch (format) { case PixelFormat::A8B8G8R8_SRGB: @@ -344,6 +358,9 @@ bool IsPixelFormatSRGB(PixelFormat format) { case PixelFormat::BC2_SRGB: case PixelFormat::BC3_SRGB: case PixelFormat::BC7_SRGB: + case PixelFormat::ETC2_RGB_SRGB: + case PixelFormat::ETC2_RGBA_SRGB: + case PixelFormat::ETC2_RGB_PTA_SRGB: case PixelFormat::ASTC_2D_4X4_SRGB: case PixelFormat::ASTC_2D_8X8_SRGB: case PixelFormat::ASTC_2D_8X5_SRGB: diff --git a/src/video_core/surface.h b/src/video_core/surface.h index a97bcb39b5..c659a977c1 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -111,6 +111,12 @@ namespace VideoCore::Surface { PIXEL_FORMAT_ELEM(ASTC_2D_6X5_UNORM, 6, 5, 128) \ PIXEL_FORMAT_ELEM(ASTC_2D_6X5_SRGB, 6, 5, 128) \ PIXEL_FORMAT_ELEM(E5B9G9R9_FLOAT, 1, 1, 32) \ + PIXEL_FORMAT_ELEM(ETC2_RGB_UNORM, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(ETC2_RGBA_UNORM, 4, 4, 128) \ + PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_UNORM, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(ETC2_RGB_SRGB, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(ETC2_RGBA_SRGB, 4, 4, 128) \ + PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_SRGB, 4, 4, 64) \ /* Depth formats */ \ PIXEL_FORMAT_ELEM(D32_FLOAT, 1, 1, 32) \ PIXEL_FORMAT_ELEM(D16_UNORM, 1, 1, 16) \ @@ -181,8 +187,6 @@ constexpr u32 BitsPerBlock(PixelFormat format) noexcept { } } -#undef PIXEL_FORMAT_LIST - /// Returns the sizer in bytes of the specified pixel format constexpr u32 BytesPerBlock(PixelFormat pixel_format) { return BitsPerBlock(pixel_format) / CHAR_BIT; @@ -198,6 +202,7 @@ SurfaceType GetFormatType(PixelFormat pixel_format); bool HasAlpha(PixelFormat pixel_format); bool IsPixelFormatASTC(PixelFormat format); bool IsPixelFormatBCn(PixelFormat format); +bool IsPixelFormatETC2(PixelFormat format); bool IsPixelFormatSRGB(PixelFormat format); bool IsPixelFormatInteger(PixelFormat format); bool IsPixelFormatSignedInteger(PixelFormat format); diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index a9e03b375d..ea68a1c063 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -191,6 +191,20 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::BC6H_SFLOAT; case Hash(TextureFormat::BC6H_U16, FLOAT): return PixelFormat::BC6H_UFLOAT; + /* ETC2 */ + case Hash(TextureFormat::ETC2_RGB, UNORM, LINEAR): + return PixelFormat::ETC2_RGB_UNORM; + case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, LINEAR): + return PixelFormat::ETC2_RGB_PTA_UNORM; + case Hash(TextureFormat::ETC2_RGBA, UNORM, LINEAR): + return PixelFormat::ETC2_RGBA_UNORM; + case Hash(TextureFormat::ETC2_RGB, UNORM, SRGB): + return PixelFormat::ETC2_RGB_SRGB; + case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, SRGB): + return PixelFormat::ETC2_RGB_PTA_SRGB; + case Hash(TextureFormat::ETC2_RGBA, UNORM, SRGB): + return PixelFormat::ETC2_RGBA_SRGB; + /* ASTC */ case Hash(TextureFormat::ASTC_2D_4X4, UNORM, LINEAR): return PixelFormat::ASTC_2D_4X4_UNORM; case Hash(TextureFormat::ASTC_2D_4X4, UNORM, SRGB): diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index 33c32645a2..3e2e749274 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -17,210 +20,9 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str using VideoCore::Surface::PixelFormat; const string_view name = [format] { switch (format) { - case PixelFormat::A8B8G8R8_UNORM: - return "A8B8G8R8_UNORM"; - case PixelFormat::A8B8G8R8_SNORM: - return "A8B8G8R8_SNORM"; - case PixelFormat::A8B8G8R8_SINT: - return "A8B8G8R8_SINT"; - case PixelFormat::A8B8G8R8_UINT: - return "A8B8G8R8_UINT"; - case PixelFormat::R5G6B5_UNORM: - return "R5G6B5_UNORM"; - case PixelFormat::B5G6R5_UNORM: - return "B5G6R5_UNORM"; - case PixelFormat::A1R5G5B5_UNORM: - return "A1R5G5B5_UNORM"; - case PixelFormat::A2B10G10R10_UNORM: - return "A2B10G10R10_UNORM"; - case PixelFormat::A2B10G10R10_UINT: - return "A2B10G10R10_UINT"; - case PixelFormat::A2R10G10B10_UNORM: - return "A2R10G10B10_UNORM"; - case PixelFormat::A1B5G5R5_UNORM: - return "A1B5G5R5_UNORM"; - case PixelFormat::A5B5G5R1_UNORM: - return "A5B5G5R1_UNORM"; - case PixelFormat::R8_UNORM: - return "R8_UNORM"; - case PixelFormat::R8_SNORM: - return "R8_SNORM"; - case PixelFormat::R8_SINT: - return "R8_SINT"; - case PixelFormat::R8_UINT: - return "R8_UINT"; - case PixelFormat::R16G16B16A16_FLOAT: - return "R16G16B16A16_FLOAT"; - case PixelFormat::R16G16B16A16_UNORM: - return "R16G16B16A16_UNORM"; - case PixelFormat::R16G16B16A16_SNORM: - return "R16G16B16A16_SNORM"; - case PixelFormat::R16G16B16A16_SINT: - return "R16G16B16A16_SINT"; - case PixelFormat::R16G16B16A16_UINT: - return "R16G16B16A16_UINT"; - case PixelFormat::B10G11R11_FLOAT: - return "B10G11R11_FLOAT"; - case PixelFormat::R32G32B32A32_UINT: - return "R32G32B32A32_UINT"; - case PixelFormat::BC1_RGBA_UNORM: - return "BC1_RGBA_UNORM"; - case PixelFormat::BC2_UNORM: - return "BC2_UNORM"; - case PixelFormat::BC3_UNORM: - return "BC3_UNORM"; - case PixelFormat::BC4_UNORM: - return "BC4_UNORM"; - case PixelFormat::BC4_SNORM: - return "BC4_SNORM"; - case PixelFormat::BC5_UNORM: - return "BC5_UNORM"; - case PixelFormat::BC5_SNORM: - return "BC5_SNORM"; - case PixelFormat::BC7_UNORM: - return "BC7_UNORM"; - case PixelFormat::BC6H_UFLOAT: - return "BC6H_UFLOAT"; - case PixelFormat::BC6H_SFLOAT: - return "BC6H_SFLOAT"; - case PixelFormat::ASTC_2D_4X4_UNORM: - return "ASTC_2D_4X4_UNORM"; - case PixelFormat::B8G8R8A8_UNORM: - return "B8G8R8A8_UNORM"; - case PixelFormat::R32G32B32A32_FLOAT: - return "R32G32B32A32_FLOAT"; - case PixelFormat::R32G32B32A32_SINT: - return "R32G32B32A32_SINT"; - case PixelFormat::R32G32_FLOAT: - return "R32G32_FLOAT"; - case PixelFormat::R32G32_SINT: - return "R32G32_SINT"; - case PixelFormat::R32_FLOAT: - return "R32_FLOAT"; - case PixelFormat::R16_FLOAT: - return "R16_FLOAT"; - case PixelFormat::R16_UNORM: - return "R16_UNORM"; - case PixelFormat::R16_SNORM: - return "R16_SNORM"; - case PixelFormat::R16_UINT: - return "R16_UINT"; - case PixelFormat::R16_SINT: - return "R16_SINT"; - case PixelFormat::R16G16_UNORM: - return "R16G16_UNORM"; - case PixelFormat::R16G16_FLOAT: - return "R16G16_FLOAT"; - case PixelFormat::R16G16_UINT: - return "R16G16_UINT"; - case PixelFormat::R16G16_SINT: - return "R16G16_SINT"; - case PixelFormat::R16G16_SNORM: - return "R16G16_SNORM"; - case PixelFormat::R32G32B32_FLOAT: - return "R32G32B32_FLOAT"; - case PixelFormat::A8B8G8R8_SRGB: - return "A8B8G8R8_SRGB"; - case PixelFormat::R8G8_UNORM: - return "R8G8_UNORM"; - case PixelFormat::R8G8_SNORM: - return "R8G8_SNORM"; - case PixelFormat::R8G8_SINT: - return "R8G8_SINT"; - case PixelFormat::R8G8_UINT: - return "R8G8_UINT"; - case PixelFormat::R32G32_UINT: - return "R32G32_UINT"; - case PixelFormat::R16G16B16X16_FLOAT: - return "R16G16B16X16_FLOAT"; - case PixelFormat::R32_UINT: - return "R32_UINT"; - case PixelFormat::R32_SINT: - return "R32_SINT"; - case PixelFormat::ASTC_2D_8X8_UNORM: - return "ASTC_2D_8X8_UNORM"; - case PixelFormat::ASTC_2D_8X5_UNORM: - return "ASTC_2D_8X5_UNORM"; - case PixelFormat::ASTC_2D_5X4_UNORM: - return "ASTC_2D_5X4_UNORM"; - case PixelFormat::B8G8R8A8_SRGB: - return "B8G8R8A8_SRGB"; - case PixelFormat::BC1_RGBA_SRGB: - return "BC1_RGBA_SRGB"; - case PixelFormat::BC2_SRGB: - return "BC2_SRGB"; - case PixelFormat::BC3_SRGB: - return "BC3_SRGB"; - case PixelFormat::BC7_SRGB: - return "BC7_SRGB"; - case PixelFormat::A4B4G4R4_UNORM: - return "A4B4G4R4_UNORM"; - case PixelFormat::G4R4_UNORM: - return "G4R4_UNORM"; - case PixelFormat::ASTC_2D_4X4_SRGB: - return "ASTC_2D_4X4_SRGB"; - case PixelFormat::ASTC_2D_8X8_SRGB: - return "ASTC_2D_8X8_SRGB"; - case PixelFormat::ASTC_2D_8X5_SRGB: - return "ASTC_2D_8X5_SRGB"; - case PixelFormat::ASTC_2D_5X4_SRGB: - return "ASTC_2D_5X4_SRGB"; - case PixelFormat::ASTC_2D_5X5_UNORM: - return "ASTC_2D_5X5_UNORM"; - case PixelFormat::ASTC_2D_5X5_SRGB: - return "ASTC_2D_5X5_SRGB"; - case PixelFormat::ASTC_2D_10X8_UNORM: - return "ASTC_2D_10X8_UNORM"; - case PixelFormat::ASTC_2D_10X8_SRGB: - return "ASTC_2D_10X8_SRGB"; - case PixelFormat::ASTC_2D_6X6_UNORM: - return "ASTC_2D_6X6_UNORM"; - case PixelFormat::ASTC_2D_6X6_SRGB: - return "ASTC_2D_6X6_SRGB"; - case PixelFormat::ASTC_2D_10X6_UNORM: - return "ASTC_2D_10X6_UNORM"; - case PixelFormat::ASTC_2D_10X6_SRGB: - return "ASTC_2D_10X6_SRGB"; - case PixelFormat::ASTC_2D_10X5_UNORM: - return "ASTC_2D_10X5_UNORM"; - case PixelFormat::ASTC_2D_10X5_SRGB: - return "ASTC_2D_10X5_SRGB"; - case PixelFormat::ASTC_2D_10X10_UNORM: - return "ASTC_2D_10X10_UNORM"; - case PixelFormat::ASTC_2D_10X10_SRGB: - return "ASTC_2D_10X10_SRGB"; - case PixelFormat::ASTC_2D_12X10_UNORM: - return "ASTC_2D_12X10_UNORM"; - case PixelFormat::ASTC_2D_12X10_SRGB: - return "ASTC_2D_12X10_SRGB"; - case PixelFormat::ASTC_2D_12X12_UNORM: - return "ASTC_2D_12X12_UNORM"; - case PixelFormat::ASTC_2D_12X12_SRGB: - return "ASTC_2D_12X12_SRGB"; - case PixelFormat::ASTC_2D_8X6_UNORM: - return "ASTC_2D_8X6_UNORM"; - case PixelFormat::ASTC_2D_8X6_SRGB: - return "ASTC_2D_8X6_SRGB"; - case PixelFormat::ASTC_2D_6X5_UNORM: - return "ASTC_2D_6X5_UNORM"; - case PixelFormat::ASTC_2D_6X5_SRGB: - return "ASTC_2D_6X5_SRGB"; - case PixelFormat::E5B9G9R9_FLOAT: - return "E5B9G9R9_FLOAT"; - case PixelFormat::D32_FLOAT: - return "D32_FLOAT"; - case PixelFormat::D16_UNORM: - return "D16_UNORM"; - case PixelFormat::X8_D24_UNORM: - return "X8_D24_UNORM"; - case PixelFormat::S8_UINT: - return "S8_UINT"; - case PixelFormat::D24_UNORM_S8_UINT: - return "D24_UNORM_S8_UINT"; - case PixelFormat::S8_UINT_D24_UNORM: - return "S8_UINT_D24_UNORM"; - case PixelFormat::D32_FLOAT_S8_UINT: - return "D32_FLOAT_S8_UINT"; +#define PIXEL_FORMAT_ELEM(NAME, ...) case PixelFormat::NAME: return #NAME; + PIXEL_FORMAT_LIST +#undef PIXEL_FORMAT_ELEM case PixelFormat::MaxDepthStencilFormat: case PixelFormat::Invalid: return "Invalid"; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 13fe1b371f..7f2c29519f 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -363,6 +363,11 @@ public: return features.features.textureCompressionBC; } + /// Returns true if ETC2 is natively supported. + bool IsOptimalEtc2Supported() const { + return features.features.textureCompressionETC2; + } + /// Returns true if descriptor aliasing is natively supported. bool IsDescriptorAliasingSupported() const { return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; |
