diff --git a/antora/modules/ROOT/nav.adoc b/antora/modules/ROOT/nav.adoc index 882837cb7..0ac0f56cf 100644 --- a/antora/modules/ROOT/nav.adoc +++ b/antora/modules/ROOT/nav.adoc @@ -90,6 +90,7 @@ ** xref:samples/performance/command_buffer_usage/README.adoc[Command buffer usage] ** xref:samples/performance/constant_data/README.adoc[Constant data] ** xref:samples/performance/descriptor_management/README.adoc[Descriptor management] +** xref:samples/performance/image_compression_control/README.adoc[Image compression control] ** xref:samples/performance/layout_transitions/README.adoc[Layout transitions] ** xref:samples/performance/msaa/README.adoc[MSAA] ** xref:samples/performance/multithreading_render_passes/README.adoc[Multithreading render passes] diff --git a/framework/common/strings.cpp b/framework/common/strings.cpp index 857bdc9c2..fb6aca69f 100644 --- a/framework/common/strings.cpp +++ b/framework/common/strings.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2018-2023, Arm Limited and Contributors +/* Copyright (c) 2018-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -332,6 +332,309 @@ const std::string to_string(VkFormat format) return "VK_FORMAT_D24_UNORM_S8_UINT"; case VK_FORMAT_D32_SFLOAT_S8_UINT: return "VK_FORMAT_D32_SFLOAT_S8_UINT"; + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + return "VK_FORMAT_BC1_RGB_UNORM_BLOCK"; + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + return "VK_FORMAT_BC1_RGB_SRGB_BLOCK"; + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + return "VK_FORMAT_BC1_RGBA_UNORM_BLOCK"; + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: + return "VK_FORMAT_BC1_RGBA_SRGB_BLOCK"; + case VK_FORMAT_BC2_UNORM_BLOCK: + return "VK_FORMAT_BC2_UNORM_BLOCK"; + case VK_FORMAT_BC2_SRGB_BLOCK: + return "VK_FORMAT_BC2_SRGB_BLOCK"; + case VK_FORMAT_BC3_UNORM_BLOCK: + return "VK_FORMAT_BC3_UNORM_BLOCK"; + case VK_FORMAT_BC3_SRGB_BLOCK: + return "VK_FORMAT_BC3_SRGB_BLOCK"; + case VK_FORMAT_BC4_UNORM_BLOCK: + return "VK_FORMAT_BC4_UNORM_BLOCK"; + case VK_FORMAT_BC4_SNORM_BLOCK: + return "VK_FORMAT_BC4_SNORM_BLOCK"; + case VK_FORMAT_BC5_UNORM_BLOCK: + return "VK_FORMAT_BC5_UNORM_BLOCK"; + case VK_FORMAT_BC5_SNORM_BLOCK: + return "VK_FORMAT_BC5_SNORM_BLOCK"; + case VK_FORMAT_BC6H_UFLOAT_BLOCK: + return "VK_FORMAT_BC6H_UFLOAT_BLOCK"; + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + return "VK_FORMAT_BC6H_SFLOAT_BLOCK"; + case VK_FORMAT_BC7_UNORM_BLOCK: + return "VK_FORMAT_BC7_UNORM_BLOCK"; + case VK_FORMAT_BC7_SRGB_BLOCK: + return "VK_FORMAT_BC7_SRGB_BLOCK"; + case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + return "VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK"; + case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + return "VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK"; + case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + return "VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOK "; + case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + return "VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK"; + case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + return "VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK"; + case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + return "VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK"; + case VK_FORMAT_EAC_R11_UNORM_BLOCK: + return "VK_FORMAT_EAC_R11_UNORM_BLOCK"; + case VK_FORMAT_EAC_R11_SNORM_BLOCK: + return "VK_FORMAT_EAC_R11_SNORM_BLOCK"; + case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: + return "VK_FORMAT_EAC_R11G11_UNORM_BLOCK"; + case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: + return "VK_FORMAT_EAC_R11G11_SNORM_BLOCK"; + case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: + return "VK_FORMAT_ASTC_4x4_UNORM_BLOCK"; + case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: + return "VK_FORMAT_ASTC_4x4_SRGB_BLOCK"; + case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: + return "VK_FORMAT_ASTC_5x4_UNORM_BLOCK"; + case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: + return "VK_FORMAT_ASTC_5x4_SRGB_BLOCK"; + case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: + return "VK_FORMAT_ASTC_5x5_UNORM_BLOCK"; + case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: + return "VK_FORMAT_ASTC_5x5_SRGB_BLOCK"; + case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: + return "VK_FORMAT_ASTC_6x5_UNORM_BLOCK"; + case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: + return "VK_FORMAT_ASTC_6x5_SRGB_BLOCK"; + case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: + return "VK_FORMAT_ASTC_6x6_UNORM_BLOCK"; + case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: + return "VK_FORMAT_ASTC_6x6_SRGB_BLOCK"; + case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: + return "VK_FORMAT_ASTC_8x5_UNORM_BLOCK"; + case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: + return "VK_FORMAT_ASTC_8x5_SRGB_BLOCK"; + case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: + return "VK_FORMAT_ASTC_8x6_UNORM_BLOCK"; + case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: + return "VK_FORMAT_ASTC_8x6_SRGB_BLOCK"; + case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: + return "VK_FORMAT_ASTC_8x8_UNORM_BLOCK"; + case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: + return "VK_FORMAT_ASTC_8x8_SRGB_BLOCK"; + case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: + return "VK_FORMAT_ASTC_10x5_UNORM_BLOCK"; + case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: + return "VK_FORMAT_ASTC_10x5_SRGB_BLOCK"; + case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: + return "VK_FORMAT_ASTC_10x6_UNORM_BLOCK"; + case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: + return "VK_FORMAT_ASTC_10x6_SRGB_BLOCK"; + case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: + return "VK_FORMAT_ASTC_10x8_UNORM_BLOCK"; + case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: + return "VK_FORMAT_ASTC_10x8_SRGB_BLOCK"; + case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: + return "VK_FORMAT_ASTC_10x10_UNORM_BLOCK"; + case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: + return "VK_FORMAT_ASTC_10x10_SRGB_BLOCK"; + case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: + return "VK_FORMAT_ASTC_12x10_UNORM_BLOCK"; + case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: + return "VK_FORMAT_ASTC_12x10_SRGB_BLOCK"; + case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: + return "VK_FORMAT_ASTC_12x12_UNORM_BLOCK"; + case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: + return "VK_FORMAT_ASTC_12x12_SRGB_BLOCK"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G8B8G8R8_422_UNORM: + return "VK_FORMAT_G8B8G8R8_422_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_B8G8R8G8_422_UNORM: + return "VK_FORMAT_B8G8R8G8_422_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: + return "VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: + return "VK_FORMAT_G8_B8R8_2PLANE_420_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM: + return "VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM: + return "VK_FORMAT_G8_B8R8_2PLANE_422_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM: + return "VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_R10X6_UNORM_PACK16: + return "VK_FORMAT_R10X6_UNORM_PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_R10X6G10X6_UNORM_2PACK16: + return "VK_FORMAT_R10X6G10X6_UNORM_2PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: + return "VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: + return "VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: + return "VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16: + return "VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: + return "VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16: + return "VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16: + return "VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16: + return "VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_R12X4_UNORM_PACK16: + return "VK_FORMAT_R12X4_UNORM_PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_R12X4G12X4_UNORM_2PACK16: + return "VK_FORMAT_R12X4G12X4_UNORM_2PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: + return "VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: + return "VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: + return "VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16: + return "VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16: + return "VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16: + return "VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16: + return "VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16: + return "VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G16B16G16R16_422_UNORM: + return "VK_FORMAT_G16B16G16R16_422_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_B16G16R16G16_422_UNORM: + return "VK_FORMAT_B16G16R16G16_422_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM: + return "VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM: + return "VK_FORMAT_G16_B16R16_2PLANE_420_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM: + return "VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM: + return "VK_FORMAT_G16_B16R16_2PLANE_422_UNORM"; + // Provided by VK_VERSION_1_1 + case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM: + return "VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM: + return "VK_FORMAT_G8_B8R8_2PLANE_444_UNORM"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16: + return "VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16: + return "VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM: + return "VK_FORMAT_G16_B16R16_2PLANE_444_UNORM"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_A4R4G4B4_UNORM_PACK16: + return "VK_FORMAT_A4R4G4B4_UNORM_PACK16"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_A4B4G4R4_UNORM_PACK16: + return "VK_FORMAT_A4B4G4R4_UNORM_PACK16"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK"; + // Provided by VK_VERSION_1_3 + case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK: + return "VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK"; + // Provided by VK_IMG_format_pvrtc + case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: + return "VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG"; + // Provided by VK_IMG_format_pvrtc + case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: + return "VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG"; + // Provided by VK_IMG_format_pvrtc + case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: + return "VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG"; + // Provided by VK_IMG_format_pvrtc + case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: + return "VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG"; + // Provided by VK_IMG_format_pvrtc + case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: + return "VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG"; + // Provided by VK_IMG_format_pvrtc + case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: + return "VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG"; + // Provided by VK_IMG_format_pvrtc + case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: + return "VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG"; + // Provided by VK_IMG_format_pvrtc + case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: + return "VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG"; + // Provided by VK_NV_optical_flow + case VK_FORMAT_R16G16_S10_5_NV: + return "VK_FORMAT_R16G16_S10_5_NV"; + // Provided by VK_KHR_maintenance5 + case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR: + return "VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR"; + // Provided by VK_KHR_maintenance5 + case VK_FORMAT_A8_UNORM_KHR: + return "VK_FORMAT_A8_UNORM_KHR"; case VK_FORMAT_UNDEFINED: return "VK_FORMAT_UNDEFINED"; default: @@ -1241,7 +1544,17 @@ const std::string image_usage_to_string(VkImageUsageFlags flags) {VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, "VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT"}, {VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, "VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT"}, {VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, "VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT"}, - {VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, "VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT"}}); + {VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, "VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT"}, + {VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR, "VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR"}, + {VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR, "VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR"}, + {VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR, "VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR"}, + {VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT, "VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT"}, + {VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, "VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR"}, + {VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT, "VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT"}, + {VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT, "VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT"}, + {VK_IMAGE_USAGE_INVOCATION_MASK_BIT_HUAWEI, "VK_IMAGE_USAGE_INVOCATION_MASK_BIT_HUAWEI"}, + {VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM, "VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM"}, + {VK_IMAGE_USAGE_SAMPLE_BLOCK_MATCH_BIT_QCOM, "VK_IMAGE_USAGE_SAMPLE_BLOCK_MATCH_BIT_QCOM"}}); } const std::string image_aspect_to_string(VkImageAspectFlags flags) @@ -1274,6 +1587,45 @@ const std::string color_component_to_string(VkColorComponentFlags flags) {VK_COLOR_COMPONENT_A_BIT, "A"}}); } +const std::string image_compression_flags_to_string(VkImageCompressionFlagsEXT flags) +{ + return 0 == flags ? "VK_IMAGE_COMPRESSION_DEFAULT_EXT" : + to_string(flags, + {{VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT"}, + {VK_IMAGE_COMPRESSION_DISABLED_EXT, "VK_IMAGE_COMPRESSION_DISABLED_EXT"}}); +} + +const std::string image_compression_fixed_rate_flags_to_string(VkImageCompressionFixedRateFlagsEXT flags) +{ + return 0 == flags ? "VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT" : + to_string(flags, + {{VK_IMAGE_COMPRESSION_FIXED_RATE_1BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_1BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_3BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_3BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_4BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_4BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_5BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_5BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_6BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_6BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_7BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_7BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_8BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_8BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_9BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_9BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_10BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_10BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_11BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_11BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_12BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_12BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_13BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_13BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_14BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_14BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_15BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_15BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_16BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_16BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_17BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_17BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_18BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_18BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_19BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_19BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_20BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_20BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_21BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_21BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_22BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_22BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_23BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_23BPC_BIT_EXT"}, + {VK_IMAGE_COMPRESSION_FIXED_RATE_24BPC_BIT_EXT, "VK_IMAGE_COMPRESSION_FIXED_RATE_24BPC_BIT_EXT"}}); +} + std::vector split(const std::string &input, char delim) { std::vector tokens; diff --git a/framework/common/strings.h b/framework/common/strings.h index 2b1a28b13..13fd8abed 100644 --- a/framework/common/strings.h +++ b/framework/common/strings.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018-2023, Arm Limited and Contributors +/* Copyright (c) 2018-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -103,112 +103,112 @@ std::string to_string(VkExtent2D format); /** * @brief Helper function to convert VkSampleCountFlagBits to a string * @param flags Vulkan sample count flags to convert - * @return const std::string + * @return const std::string */ const std::string to_string(VkSampleCountFlagBits flags); /** - * @brief Helper function to convert VkImageTiling to a string + * @brief Helper function to convert VkImageTiling to a string * @param tiling Vulkan VkImageTiling to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkImageTiling tiling); /** - * @brief Helper function to convert VkImageType to a string + * @brief Helper function to convert VkImageType to a string * @param type Vulkan VkImageType to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkImageType type); /** - * @brief Helper function to convert VkBlendFactor to a string + * @brief Helper function to convert VkBlendFactor to a string * @param blend Vulkan VkBlendFactor to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkBlendFactor blend); /** - * @brief Helper function to convert VkVertexInputRate to a string + * @brief Helper function to convert VkVertexInputRate to a string * @param rate Vulkan VkVertexInputRate to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkVertexInputRate rate); /** - * @brief Helper function to convert VkBool32 to a string + * @brief Helper function to convert VkBool32 to a string * @param state Vulkan VkBool32 to convert - * @return The string to return + * @return The string to return */ const std::string to_string_vk_bool(VkBool32 state); /** - * @brief Helper function to convert VkPrimitiveTopology to a string + * @brief Helper function to convert VkPrimitiveTopology to a string * @param topology Vulkan VkPrimitiveTopology to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkPrimitiveTopology topology); /** - * @brief Helper function to convert VkFrontFace to a string + * @brief Helper function to convert VkFrontFace to a string * @param face Vulkan VkFrontFace to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkFrontFace face); /** - * @brief Helper function to convert VkPolygonMode to a string + * @brief Helper function to convert VkPolygonMode to a string * @param mode Vulkan VkPolygonMode to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkPolygonMode mode); /** - * @brief Helper function to convert VkCompareOp to a string + * @brief Helper function to convert VkCompareOp to a string * @param operation Vulkan VkCompareOp to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkCompareOp operation); /** - * @brief Helper function to convert VkStencilOp to a string + * @brief Helper function to convert VkStencilOp to a string * @param operation Vulkan VkStencilOp to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkStencilOp operation); /** - * @brief Helper function to convert VkLogicOp to a string + * @brief Helper function to convert VkLogicOp to a string * @param operation Vulkan VkLogicOp to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkLogicOp operation); /** - * @brief Helper function to convert VkBlendOp to a string + * @brief Helper function to convert VkBlendOp to a string * @param operation Vulkan VkBlendOp to convert - * @return The string to return + * @return The string to return */ const std::string to_string(VkBlendOp operation); /** - * @brief Helper function to convert AlphaMode to a string + * @brief Helper function to convert AlphaMode to a string * @param mode Vulkan AlphaMode to convert - * @return The string to return + * @return The string to return */ const std::string to_string(sg::AlphaMode mode); /** - * @brief Helper function to convert bool to a string + * @brief Helper function to convert bool to a string * @param flag Vulkan bool to convert (true/false) - * @return The string to return + * @return The string to return */ const std::string to_string(bool flag); /** - * @brief Helper function to convert ShaderResourceType to a string + * @brief Helper function to convert ShaderResourceType to a string * @param type Vulkan ShaderResourceType to convert - * @return The string to return + * @return The string to return */ const std::string to_string(ShaderResourceType type); @@ -229,7 +229,7 @@ inline const std::string to_string(uint32_t bitmask, const std::map fixed_rate_compression_flags_to_vector(VkImageCompressionFixedRateFlagsEXT flags) +{ + const std::vector all_flags = {VK_IMAGE_COMPRESSION_FIXED_RATE_1BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_3BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_4BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_5BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_6BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_7BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_8BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_9BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_10BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_11BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_12BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_13BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_14BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_15BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_16BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_17BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_18BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_19BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_20BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_21BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_22BPC_BIT_EXT, + VK_IMAGE_COMPRESSION_FIXED_RATE_23BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_24BPC_BIT_EXT}; + + std::vector flags_vector; + + for (size_t i = 0; i < all_flags.size(); i++) + { + if (all_flags[i] & flags) + { + flags_vector.push_back(all_flags[i]); + } + } + + return flags_vector; +} + +VkImageCompressionPropertiesEXT query_supported_fixed_rate_compression(VkPhysicalDevice gpu, const VkImageCreateInfo &create_info) +{ + VkImageCompressionPropertiesEXT supported_compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT}; + + VkImageCompressionControlEXT compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT}; + compression_control.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT; + + VkPhysicalDeviceImageFormatInfo2 image_format_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2}; + image_format_info.format = create_info.format; + image_format_info.type = create_info.imageType; + image_format_info.tiling = create_info.tiling; + image_format_info.usage = create_info.usage; + image_format_info.pNext = &compression_control; + + VkImageFormatProperties2 image_format_properties{VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2}; + image_format_properties.pNext = &supported_compression_properties; + + vkGetPhysicalDeviceImageFormatProperties2KHR(gpu, &image_format_info, &image_format_properties); + + return supported_compression_properties; +} + +VkImageCompressionPropertiesEXT query_applied_compression(VkDevice device, VkImage image) +{ + VkImageSubresource2EXT image_subresource{VK_STRUCTURE_TYPE_IMAGE_SUBRESOURCE_2_KHR}; + image_subresource.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_subresource.imageSubresource.mipLevel = 0; + image_subresource.imageSubresource.arrayLayer = 0; + + VkImageCompressionPropertiesEXT compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT}; + VkSubresourceLayout2EXT subresource_layout{VK_STRUCTURE_TYPE_SUBRESOURCE_LAYOUT_2_KHR}; + subresource_layout.pNext = &compression_properties; + + vkGetImageSubresourceLayout2EXT(device, image, &image_subresource, &subresource_layout); + + return compression_properties; +} + VkSurfaceFormatKHR select_surface_format(VkPhysicalDevice gpu, VkSurfaceKHR surface, std::vector const &preferred_formats) { uint32_t surface_format_count; diff --git a/framework/common/vk_common.h b/framework/common/vk_common.h index 2b6ab236f..b6a8b2ec7 100644 --- a/framework/common/vk_common.h +++ b/framework/common/vk_common.h @@ -257,6 +257,15 @@ void image_layout_transition(VkCommandBuffer VkImageLayout old_layout, VkImageLayout new_layout); +/** + * @brief Helper functions for compression controls + */ +std::vector fixed_rate_compression_flags_to_vector(VkImageCompressionFixedRateFlagsEXT flags); + +VkImageCompressionPropertiesEXT query_supported_fixed_rate_compression(VkPhysicalDevice gpu, const VkImageCreateInfo &create_info); + +VkImageCompressionPropertiesEXT query_applied_compression(VkDevice device, VkImage image); + /** * @brief Load and store info for a render pass attachment. */ diff --git a/framework/core/device.cpp b/framework/core/device.cpp index 5dd42f2b9..3f03124ce 100644 --- a/framework/core/device.cpp +++ b/framework/core/device.cpp @@ -75,21 +75,6 @@ Device::Device(PhysicalDevice &gpu, } // Check extensions to enable Vma Dedicated Allocation - uint32_t device_extension_count; - VK_CHECK(vkEnumerateDeviceExtensionProperties(gpu.get_handle(), nullptr, &device_extension_count, nullptr)); - device_extensions = std::vector(device_extension_count); - VK_CHECK(vkEnumerateDeviceExtensionProperties(gpu.get_handle(), nullptr, &device_extension_count, device_extensions.data())); - - // Display supported extensions - if (device_extensions.size() > 0) - { - LOGD("Device supports the following extensions:"); - for (auto &extension : device_extensions) - { - LOGD(" \t{}", extension.extensionName); - } - } - bool can_get_memory_requirements = is_extension_supported("VK_KHR_get_memory_requirements2"); bool has_dedicated_allocation = is_extension_supported("VK_KHR_dedicated_allocation"); @@ -228,10 +213,7 @@ Device::~Device() bool Device::is_extension_supported(const std::string &requested_extension) const { - return std::find_if(device_extensions.begin(), device_extensions.end(), - [requested_extension](auto &device_extension) { - return std::strcmp(device_extension.extensionName, requested_extension.c_str()) == 0; - }) != device_extensions.end(); + return gpu.is_extension_supported(requested_extension); } bool Device::is_enabled(const char *extension) const diff --git a/framework/core/device.h b/framework/core/device.h index 62c55bf4f..4b17efdd8 100644 --- a/framework/core/device.h +++ b/framework/core/device.h @@ -219,8 +219,6 @@ class Device : public core::VulkanResource std::unique_ptr debug_utils; - std::vector device_extensions; - std::vector enabled_extensions{}; std::vector> queues; diff --git a/framework/core/hpp_device.cpp b/framework/core/hpp_device.cpp index 7ce71f559..159572713 100644 --- a/framework/core/hpp_device.cpp +++ b/framework/core/hpp_device.cpp @@ -75,18 +75,6 @@ HPPDevice::HPPDevice(vkb::core::HPPPhysicalDevice &gpu, } // Check extensions to enable Vma Dedicated Allocation - device_extensions = gpu.get_handle().enumerateDeviceExtensionProperties(); - - // Display supported extensions - if (device_extensions.size() > 0) - { - LOGD("HPPDevice supports the following extensions:"); - for (auto &extension : device_extensions) - { - LOGD(" \t{}", extension.extensionName.data()); - } - } - bool can_get_memory_requirements = is_extension_supported(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); bool has_dedicated_allocation = is_extension_supported(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); @@ -204,9 +192,7 @@ HPPDevice::~HPPDevice() bool HPPDevice::is_extension_supported(std::string const &requested_extension) const { - return std::find_if(device_extensions.begin(), - device_extensions.end(), - [requested_extension](auto &device_extension) { return std::strcmp(device_extension.extensionName, requested_extension.c_str()) == 0; }) != device_extensions.end(); + return gpu.is_extension_supported(requested_extension); } bool HPPDevice::is_enabled(std::string const &extension) const diff --git a/framework/core/hpp_device.h b/framework/core/hpp_device.h index 342a2f106..5410a51eb 100644 --- a/framework/core/hpp_device.h +++ b/framework/core/hpp_device.h @@ -133,8 +133,6 @@ class HPPDevice : public vkb::core::HPPVulkanResource std::unique_ptr debug_utils; - std::vector device_extensions; - std::vector enabled_extensions{}; std::vector> queues; diff --git a/framework/core/hpp_physical_device.cpp b/framework/core/hpp_physical_device.cpp index 695c6c639..14995263d 100644 --- a/framework/core/hpp_physical_device.cpp +++ b/framework/core/hpp_physical_device.cpp @@ -34,6 +34,18 @@ HPPPhysicalDevice::HPPPhysicalDevice(HPPInstance &instance, vk::PhysicalDevice p LOGI("Found GPU: {}", properties.deviceName.data()); queue_family_properties = physical_device.getQueueFamilyProperties(); + + device_extensions = physical_device.enumerateDeviceExtensionProperties(); + + // Display supported extensions + if (device_extensions.size() > 0) + { + LOGD("HPPDevice supports the following extensions:"); + for (auto &extension : device_extensions) + { + LOGD(" \t{}", extension.extensionName.data()); + } + } } DriverVersion HPPPhysicalDevice::get_driver_version() const @@ -69,6 +81,13 @@ void *HPPPhysicalDevice::get_extension_feature_chain() const return last_requested_extension_feature; } +bool HPPPhysicalDevice::is_extension_supported(const std::string &requested_extension) const +{ + return std::find_if(device_extensions.begin(), + device_extensions.end(), + [requested_extension](auto &device_extension) { return std::strcmp(device_extension.extensionName, requested_extension.c_str()) == 0; }) != device_extensions.end(); +} + const vk::PhysicalDeviceFeatures &HPPPhysicalDevice::get_features() const { return features; diff --git a/framework/core/hpp_physical_device.h b/framework/core/hpp_physical_device.h index 6356e4426..7650c76d0 100644 --- a/framework/core/hpp_physical_device.h +++ b/framework/core/hpp_physical_device.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -53,8 +53,8 @@ class HPPPhysicalDevice HPPPhysicalDevice &operator=(HPPPhysicalDevice &&) = delete; /** - * @return The version of the driver - */ + * @return The version of the driver + */ DriverVersion get_driver_version() const; /** @@ -63,6 +63,8 @@ class HPPPhysicalDevice */ void *get_extension_feature_chain() const; + bool is_extension_supported(const std::string &requested_extension) const; + const vk::PhysicalDeviceFeatures &get_features() const; vk::PhysicalDevice get_handle() const; @@ -72,12 +74,12 @@ class HPPPhysicalDevice const vk::PhysicalDeviceMemoryProperties &get_memory_properties() const; /** - * @brief Checks that a given memory type is supported by the GPU - * @param bits The memory requirement type bits - * @param properties The memory property to search for - * @param memory_type_found True if found, false if not found - * @returns The memory type index of the found memory type - */ + * @brief Checks that a given memory type is supported by the GPU + * @param bits The memory requirement type bits + * @param properties The memory property to search for + * @param memory_type_found True if found, false if not found + * @returns The memory type index of the found memory type + */ uint32_t get_memory_type(uint32_t bits, vk::MemoryPropertyFlags properties, vk::Bool32 *memory_type_found = nullptr) const; const vk::PhysicalDeviceProperties &get_properties() const; @@ -167,6 +169,9 @@ class HPPPhysicalDevice // The features that this GPU supports vk::PhysicalDeviceFeatures features; + // The extensions that this GPU supports + std::vector device_extensions; + // The GPU properties vk::PhysicalDeviceProperties properties; diff --git a/framework/core/image.cpp b/framework/core/image.cpp index 68c97d77c..11611c7ae 100644 --- a/framework/core/image.cpp +++ b/framework/core/image.cpp @@ -194,5 +194,18 @@ std::unordered_set &Image::get_views() return views; } +VkDeviceSize Image::get_image_required_size() const +{ + VkMemoryRequirements memory_requirements; + + vkGetImageMemoryRequirements(device->get_handle(), handle, &memory_requirements); + + return memory_requirements.size; +} + +VkImageCompressionPropertiesEXT Image::get_applied_compression() const +{ + return query_applied_compression(device->get_handle(), handle); +} } // namespace core } // namespace vkb diff --git a/framework/core/image.h b/framework/core/image.h index 139d3d09f..48fb78e24 100644 --- a/framework/core/image.h +++ b/framework/core/image.h @@ -119,6 +119,16 @@ struct ImageBuilder : public allocated::Builder return *this; } + template + ImageBuilder &with_extension(ExtensionType &extension) + { + extension.pNext = create_info.pNext; + + create_info.pNext = &extension; + + return *this; + } + Image build(const Device &device) const; ImagePtr build_unique(const Device &device) const; }; @@ -179,6 +189,10 @@ class Image : public allocated::Allocated std::unordered_set &get_views(); + VkDeviceSize get_image_required_size() const; + + VkImageCompressionPropertiesEXT get_applied_compression() const; + private: /// Image views referring to this image VkImageCreateInfo create_info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; diff --git a/framework/core/physical_device.cpp b/framework/core/physical_device.cpp index 1a4a54997..8404e1bad 100644 --- a/framework/core/physical_device.cpp +++ b/framework/core/physical_device.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2020-2022, Arm Limited and Contributors +/* Copyright (c) 2020-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -33,6 +33,21 @@ PhysicalDevice::PhysicalDevice(Instance &instance, VkPhysicalDevice physical_dev vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_properties_count, nullptr); queue_family_properties = std::vector(queue_family_properties_count); vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_properties_count, queue_family_properties.data()); + + uint32_t device_extension_count; + VK_CHECK(vkEnumerateDeviceExtensionProperties(get_handle(), nullptr, &device_extension_count, nullptr)); + device_extensions = std::vector(device_extension_count); + VK_CHECK(vkEnumerateDeviceExtensionProperties(get_handle(), nullptr, &device_extension_count, device_extensions.data())); + + // Display supported extensions + if (device_extensions.size() > 0) + { + LOGD("Device supports the following extensions:"); + for (auto &extension : device_extensions) + { + LOGD(" \t{}", extension.extensionName); + } + } } Instance &PhysicalDevice::get_instance() const @@ -52,6 +67,14 @@ VkBool32 PhysicalDevice::is_present_supported(VkSurfaceKHR surface, uint32_t que return present_supported; } +bool PhysicalDevice::is_extension_supported(const std::string &requested_extension) const +{ + return std::find_if(device_extensions.begin(), device_extensions.end(), + [requested_extension](auto &device_extension) { + return std::strcmp(device_extension.extensionName, requested_extension.c_str()) == 0; + }) != device_extensions.end(); +} + const VkFormatProperties PhysicalDevice::get_format_properties(VkFormat format) const { VkFormatProperties format_properties; diff --git a/framework/core/physical_device.h b/framework/core/physical_device.h index 7c3b6b37e..225647ab7 100644 --- a/framework/core/physical_device.h +++ b/framework/core/physical_device.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2020-2023, Arm Limited and Contributors +/* Copyright (c) 2020-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -45,6 +45,8 @@ class PhysicalDevice VkBool32 is_present_supported(VkSurfaceKHR surface, uint32_t queue_family_index) const; + bool is_extension_supported(const std::string &extension) const; + const VkFormatProperties get_format_properties(VkFormat format) const; VkPhysicalDevice get_handle() const; @@ -158,6 +160,9 @@ class PhysicalDevice // The features that this GPU supports VkPhysicalDeviceFeatures features{}; + // The extensions that this GPU supports + std::vector device_extensions; + // The GPU properties VkPhysicalDeviceProperties properties; diff --git a/framework/core/swapchain.cpp b/framework/core/swapchain.cpp index b4e5fec84..9d2e738af 100644 --- a/framework/core/swapchain.cpp +++ b/framework/core/swapchain.cpp @@ -19,6 +19,7 @@ #include "core/util/logging.hpp" #include "device.h" +#include "image.h" namespace vkb { @@ -149,7 +150,7 @@ inline VkSurfaceFormatKHR choose_surface_format( } } - // If nothing found, default the first supporte surface format + // If nothing found, default to the first supported surface format surface_format_it = available_surface_formats.begin(); LOGW("(Swapchain) Surface format ({}) not supported. Selecting ({}).", to_string(requested_surface_format), to_string(*surface_format_it)); } @@ -286,7 +287,9 @@ Swapchain::Swapchain(Swapchain &old_swapchain, const VkExtent2D &extent) : extent, old_swapchain.properties.image_count, old_swapchain.properties.pre_transform, - old_swapchain.image_usage_flags} + old_swapchain.image_usage_flags, + old_swapchain.requested_compression, + old_swapchain.requested_compression_fixed_rate} {} Swapchain::Swapchain(Swapchain &old_swapchain, const uint32_t image_count) : @@ -299,7 +302,9 @@ Swapchain::Swapchain(Swapchain &old_swapchain, const uint32_t image_count) : old_swapchain.properties.extent, image_count, old_swapchain.properties.pre_transform, - old_swapchain.image_usage_flags} + old_swapchain.image_usage_flags, + old_swapchain.requested_compression, + old_swapchain.requested_compression_fixed_rate} {} Swapchain::Swapchain(Swapchain &old_swapchain, const std::set &image_usage_flags) : @@ -312,7 +317,9 @@ Swapchain::Swapchain(Swapchain &old_swapchain, const std::set const &present_mode_priority_list, - const std::vector &surface_format_priority_list, - const VkExtent2D &extent, - const uint32_t image_count, - const VkSurfaceTransformFlagBitsKHR transform, - const std::set &image_usage_flags) : +Swapchain::Swapchain(Swapchain &old_swapchain, const VkImageCompressionFlagsEXT requested_compression, const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate) : + Swapchain{old_swapchain, + old_swapchain.device, + old_swapchain.surface, + old_swapchain.properties.present_mode, + old_swapchain.present_mode_priority_list, + old_swapchain.surface_format_priority_list, + old_swapchain.properties.extent, + old_swapchain.properties.image_count, + old_swapchain.properties.pre_transform, + old_swapchain.image_usage_flags, + requested_compression, + requested_compression_fixed_rate} +{} + +Swapchain::Swapchain(Device &device, + VkSurfaceKHR surface, + const VkPresentModeKHR present_mode, + std::vector const &present_mode_priority_list, + const std::vector &surface_format_priority_list, + const VkExtent2D &extent, + const uint32_t image_count, + const VkSurfaceTransformFlagBitsKHR transform, + const std::set &image_usage_flags, + const VkImageCompressionFlagsEXT requested_compression, + const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate) : Swapchain{*this, device, surface, present_mode, present_mode_priority_list, surface_format_priority_list, extent, image_count, transform, image_usage_flags} { } -Swapchain::Swapchain(Swapchain &old_swapchain, - Device &device, - VkSurfaceKHR surface, - const VkPresentModeKHR present_mode, - std::vector const &present_mode_priority_list, - const std::vector &surface_format_priority_list, - const VkExtent2D &extent, - const uint32_t image_count, - const VkSurfaceTransformFlagBitsKHR transform, - const std::set &image_usage_flags) : +Swapchain::Swapchain(Swapchain &old_swapchain, + Device &device, + VkSurfaceKHR surface, + const VkPresentModeKHR present_mode, + std::vector const &present_mode_priority_list, + const std::vector &surface_format_priority_list, + const VkExtent2D &extent, + const uint32_t image_count, + const VkSurfaceTransformFlagBitsKHR transform, + const std::set &image_usage_flags, + const VkImageCompressionFlagsEXT requested_compression, + const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate) : device{device}, - surface{surface} + surface{surface}, + requested_compression{requested_compression}, + requested_compression_fixed_rate{requested_compression_fixed_rate} { this->present_mode_priority_list = present_mode_priority_list; this->surface_format_priority_list = surface_format_priority_list; @@ -377,9 +407,9 @@ Swapchain::Swapchain(Swapchain &old_swapchain, VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(this->device.get_gpu().get_handle(), surface, &present_mode_count, present_modes.data())); LOGI("Surface supports the following present modes:"); - for (auto &present_mode : present_modes) + for (auto &pm : present_modes) { - LOGI(" \t{}", to_string(present_mode)); + LOGI(" \t{}", to_string(pm)); } // Chose best properties based on surface capabilities @@ -414,6 +444,35 @@ Swapchain::Swapchain(Swapchain &old_swapchain, create_info.oldSwapchain = properties.old_swapchain; create_info.surface = surface; + auto fixed_rate_flags = requested_compression_fixed_rate; + VkImageCompressionControlEXT compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT}; + compression_control.flags = requested_compression; + if (device.is_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME)) + { + create_info.pNext = &compression_control; + + if (VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT == requested_compression) + { + // Do not support compression for multi-planar formats + compression_control.compressionControlPlaneCount = 1; + compression_control.pFixedRateFlags = &fixed_rate_flags; + } + else if (VK_IMAGE_COMPRESSION_DISABLED_EXT == requested_compression) + { + LOGW("(Swapchain) Disabling default (lossless) compression, which can negatively impact performance") + } + } + else + { + if (VK_IMAGE_COMPRESSION_DEFAULT_EXT != requested_compression) + { + LOGW("(Swapchain) Compression cannot be controlled because VK_EXT_image_compression_control_swapchain is not enabled") + + this->requested_compression = VK_IMAGE_COMPRESSION_DEFAULT_EXT; + this->requested_compression_fixed_rate = VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT; + } + } + VkResult result = vkCreateSwapchainKHR(device.get_handle(), &create_info, nullptr, &handle); if (result != VK_SUCCESS) @@ -427,6 +486,32 @@ Swapchain::Swapchain(Swapchain &old_swapchain, images.resize(image_available); VK_CHECK(vkGetSwapchainImagesKHR(device.get_handle(), handle, &image_available, images.data())); + + if (device.is_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME) && + VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT == requested_compression) + { + // Check if fixed-rate compression was applied + const auto applied_compression_fixed_rate = vkb::query_applied_compression(device.get_handle(), images[0]).imageCompressionFixedRateFlags; + + if (applied_compression_fixed_rate != requested_compression_fixed_rate) + { + LOGW("(Swapchain) Requested fixed-rate compression ({}) was not applied, instead images use {}", + image_compression_fixed_rate_flags_to_string(requested_compression_fixed_rate), + image_compression_fixed_rate_flags_to_string(applied_compression_fixed_rate)); + + this->requested_compression_fixed_rate = applied_compression_fixed_rate; + + if (VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT == applied_compression_fixed_rate) + { + this->requested_compression = VK_IMAGE_COMPRESSION_DEFAULT_EXT; + } + } + else + { + LOGI("(Swapchain) Applied fixed-rate compression: {}", + image_compression_fixed_rate_flags_to_string(applied_compression_fixed_rate)); + } + } } Swapchain::~Swapchain() @@ -481,6 +566,11 @@ VkFormat Swapchain::get_format() const return properties.surface_format.format; } +VkSurfaceFormatKHR Swapchain::get_surface_format() const +{ + return properties.surface_format; +} + const std::vector &Swapchain::get_images() const { return images; @@ -505,4 +595,56 @@ VkPresentModeKHR Swapchain::get_present_mode() const { return properties.present_mode; } + +VkImageCompressionFlagsEXT Swapchain::get_applied_compression() const +{ + return vkb::query_applied_compression(device.get_handle(), get_images()[0]).imageCompressionFlags; +} + +std::vector Swapchain::query_supported_fixed_rate_compression(Device &device, const VkSurfaceKHR &surface) +{ + std::vector surface_format_compression_list; + + if (device.is_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME)) + { + if (device.get_gpu().get_instance().is_enabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) + { + VkPhysicalDeviceSurfaceInfo2KHR surface_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR}; + surface_info.surface = surface; + + uint32_t surface_format_count{0U}; + + VK_CHECK(vkGetPhysicalDeviceSurfaceFormats2KHR(device.get_gpu().get_handle(), &surface_info, &surface_format_count, nullptr)); + + std::vector surface_formats; + surface_formats.resize(surface_format_count, {VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR}); + + std::vector compression_properties; + compression_properties.resize(surface_format_count, {VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT}); + + for (uint32_t i = 0; i < surface_format_count; i++) + { + surface_formats[i].pNext = &compression_properties[i]; + } + + VK_CHECK(vkGetPhysicalDeviceSurfaceFormats2KHR(device.get_gpu().get_handle(), &surface_info, &surface_format_count, surface_formats.data())); + + surface_format_compression_list.reserve(surface_format_count); + for (uint32_t i = 0; i < surface_format_count; i++) + { + surface_format_compression_list.push_back({surface_formats[i], compression_properties[i]}); + } + } + else + { + LOGW("(Swapchain) To query fixed-rate compression support, instance extension VK_KHR_get_surface_capabilities2 must be enabled") + } + } + else + { + LOGW("(Swapchain) To query fixed-rate compression support, device extension VK_EXT_image_compression_control_swapchain must be enabled") + } + + return surface_format_compression_list; +} } // namespace vkb diff --git a/framework/core/swapchain.h b/framework/core/swapchain.h index 63fb55862..2e78aa811 100644 --- a/framework/core/swapchain.h +++ b/framework/core/swapchain.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2023, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -60,7 +60,7 @@ class Swapchain /** * @brief Constructor to create a swapchain by changing the image usage - * only and preserving the configuration from the old swapchain. + * only and preserving the configuration from the old swapchain. */ Swapchain(Swapchain &old_swapchain, const std::set &image_usage_flags); @@ -70,36 +70,46 @@ class Swapchain */ Swapchain(Swapchain &swapchain, const VkExtent2D &extent, const VkSurfaceTransformFlagBitsKHR transform); + /** + * @brief Constructor to create a swapchain by changing the compression settings + * only and preserving the configuration from the old swapchain. + */ + Swapchain(Swapchain &swapchain, const VkImageCompressionFlagsEXT requested_compression, const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate); + /** * @brief Constructor to create a swapchain. */ - Swapchain(Device &device, - VkSurfaceKHR surface, - const VkPresentModeKHR present_mode, - const std::vector &present_mode_priority_list = {VK_PRESENT_MODE_FIFO_KHR, - VK_PRESENT_MODE_MAILBOX_KHR}, - const std::vector &surface_format_priority_list = {{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}, - const VkExtent2D &extent = {}, - const uint32_t image_count = 3, - const VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, - const std::set &image_usage_flags = {VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT}); + Swapchain(Device &device, + VkSurfaceKHR surface, + const VkPresentModeKHR present_mode, + const std::vector &present_mode_priority_list = {VK_PRESENT_MODE_FIFO_KHR, + VK_PRESENT_MODE_MAILBOX_KHR}, + const std::vector &surface_format_priority_list = {{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}, + const VkExtent2D &extent = {}, + const uint32_t image_count = 3, + const VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + const std::set &image_usage_flags = {VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT}, + const VkImageCompressionFlagsEXT requested_compression = VK_IMAGE_COMPRESSION_DEFAULT_EXT, + const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate = VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT); /** * @brief Constructor to create a swapchain from the old swapchain * by configuring all parameters. */ - Swapchain(Swapchain &old_swapchain, - Device &device, - VkSurfaceKHR surface, - const VkPresentModeKHR present_mode, - const std::vector &present_mode_priority_list = {VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_MAILBOX_KHR}, - const std::vector &surface_format_priority_list = {{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}, - const VkExtent2D &extent = {}, - const uint32_t image_count = 3, - const VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, - const std::set &image_usage_flags = {VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT}); + Swapchain(Swapchain &old_swapchain, + Device &device, + VkSurfaceKHR surface, + const VkPresentModeKHR present_mode, + const std::vector &present_mode_priority_list = {VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_MAILBOX_KHR}, + const std::vector &surface_format_priority_list = {{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}, + const VkExtent2D &extent = {}, + const uint32_t image_count = 3, + const VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + const std::set &image_usage_flags = {VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT}, + const VkImageCompressionFlagsEXT requested_compression = VK_IMAGE_COMPRESSION_DEFAULT_EXT, + const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate = VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT); Swapchain(const Swapchain &) = delete; @@ -123,6 +133,8 @@ class Swapchain VkFormat get_format() const; + VkSurfaceFormatKHR get_surface_format() const; + const std::vector &get_images() const; VkSurfaceTransformFlagBitsKHR get_transform() const; @@ -133,6 +145,20 @@ class Swapchain VkPresentModeKHR get_present_mode() const; + VkImageCompressionFlagsEXT get_applied_compression() const; + + /** + * Helper functions for compression controls + */ + + struct SurfaceFormatCompression + { + VkSurfaceFormat2KHR surface_format{VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR}; + VkImageCompressionPropertiesEXT compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT}; + }; + + static std::vector query_supported_fixed_rate_compression(Device &device, const VkSurfaceKHR &surface); + private: Device &device; @@ -159,5 +185,9 @@ class Swapchain {VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; std::set image_usage_flags; + + VkImageCompressionFlagsEXT requested_compression{VK_IMAGE_COMPRESSION_DEFAULT_EXT}; + + VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate{VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT}; }; } // namespace vkb diff --git a/framework/rendering/render_context.cpp b/framework/rendering/render_context.cpp index 004a88ff5..ebf2b8e42 100644 --- a/framework/rendering/render_context.cpp +++ b/framework/rendering/render_context.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2023, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -175,6 +175,21 @@ void RenderContext::update_swapchain(const VkExtent2D &extent, const VkSurfaceTr recreate(); } +void RenderContext::update_swapchain(const VkImageCompressionFlagsEXT compression, const VkImageCompressionFixedRateFlagsEXT compression_fixed_rate) +{ + if (!swapchain) + { + LOGW("Can't update the swapchains compression in headless mode, skipping."); + return; + } + + device.get_resource_cache().clear_framebuffers(); + + swapchain = std::make_unique(*swapchain, compression, compression_fixed_rate); + + recreate(); +} + void RenderContext::recreate() { LOGI("Recreated swapchain"); diff --git a/framework/rendering/render_context.h b/framework/rendering/render_context.h index 44798653d..dfe917ce6 100644 --- a/framework/rendering/render_context.h +++ b/framework/rendering/render_context.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2023, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -120,6 +120,13 @@ class RenderContext */ void update_swapchain(const VkExtent2D &extent, const VkSurfaceTransformFlagBitsKHR transform); + /** + * @brief Updates the swapchain's compression settings, if a swapchain exists + * @param compression The compression to use for swapchain images (default, fixed-rate, none) + * @param compression_fixed_rate The rate to use, if compression is fixed-rate + */ + void update_swapchain(const VkImageCompressionFlagsEXT compression, const VkImageCompressionFixedRateFlagsEXT compression_fixed_rate); + /** * @returns True if a valid swapchain exists in the RenderContext */ diff --git a/framework/vulkan_sample.h b/framework/vulkan_sample.h index 92bd3e713..53f8de229 100644 --- a/framework/vulkan_sample.h +++ b/framework/vulkan_sample.h @@ -253,21 +253,23 @@ class VulkanSample : public vkb::Application */ void create_render_context(const std::vector &surface_priority_list); - DeviceType &get_device(); - DeviceType const &get_device() const; - GuiType &get_gui(); - GuiType const &get_gui() const; - InstanceType &get_instance(); - InstanceType const &get_instance() const; - RenderPipelineType &get_render_pipeline(); - RenderPipelineType const &get_render_pipeline() const; - sg::Scene &get_scene(); - StatsType &get_stats(); - SurfaceType get_surface() const; - bool has_device() const; - bool has_gui() const; - bool has_render_pipeline() const; - bool has_scene(); + DeviceType &get_device(); + DeviceType const &get_device() const; + GuiType &get_gui(); + GuiType const &get_gui() const; + InstanceType &get_instance(); + InstanceType const &get_instance() const; + RenderPipelineType &get_render_pipeline(); + RenderPipelineType const &get_render_pipeline() const; + sg::Scene &get_scene(); + StatsType &get_stats(); + SurfaceType get_surface() const; + std::vector &get_surface_priority_list(); + std::vector const &get_surface_priority_list() const; + bool has_device() const; + bool has_gui() const; + bool has_render_pipeline() const; + bool has_scene(); /** * @brief Loads the scene @@ -391,6 +393,13 @@ class VulkanSample : public vkb::Application */ vk::SurfaceKHR surface; + /** + * @brief A list of surface formats in order of priority (vector[0] has high priority, vector[size-1] has low priority) + */ + std::vector surface_priority_list = { + {vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}, + {vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}}; + /** * @brief The configuration of the sample */ @@ -470,8 +479,6 @@ inline std::unique_ptr::InstanceType> VulkanS template inline void VulkanSample::create_render_context() { - auto surface_priority_list = std::vector{{vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}, - {vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}}; create_render_context_impl(surface_priority_list); } @@ -532,11 +539,13 @@ inline void VulkanSample::draw_impl(vkb::core::HPPCommandBuffer &co memory_barrier.dst_stage_mask = vk::PipelineStageFlagBits::eColorAttachmentOutput; command_buffer.image_memory_barrier(views[0], memory_barrier); + render_target.set_layout(0, memory_barrier.new_layout); // Skip 1 as it is handled later as a depth-stencil attachment for (size_t i = 2; i < views.size(); ++i) { command_buffer.image_memory_barrier(views[i], memory_barrier); + render_target.set_layout(static_cast(i), memory_barrier.new_layout); } } @@ -550,6 +559,7 @@ inline void VulkanSample::draw_impl(vkb::core::HPPCommandBuffer &co memory_barrier.dst_stage_mask = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests; command_buffer.image_memory_barrier(views[1], memory_barrier); + render_target.set_layout(1, memory_barrier.new_layout); } if constexpr (bindingType == BindingType::Cpp) @@ -570,6 +580,7 @@ inline void VulkanSample::draw_impl(vkb::core::HPPCommandBuffer &co memory_barrier.dst_stage_mask = vk::PipelineStageFlagBits::eBottomOfPipe; command_buffer.image_memory_barrier(views[0], memory_barrier); + render_target.set_layout(0, memory_barrier.new_layout); } } @@ -690,6 +701,32 @@ inline typename VulkanSample::GuiType const &VulkanSample +inline std::vector::SurfaceFormatType> &VulkanSample::get_surface_priority_list() +{ + if constexpr (bindingType == BindingType::Cpp) + { + return surface_priority_list; + } + else + { + return reinterpret_cast &>(surface_priority_list); + } +} + +template +inline std::vector::SurfaceFormatType> const &VulkanSample::get_surface_priority_list() const +{ + if constexpr (bindingType == BindingType::Cpp) + { + return surface_priority_list; + } + else + { + return reinterpret_cast const &>(surface_priority_list); + } +} + template inline typename VulkanSample::InstanceType &VulkanSample::get_instance() { diff --git a/samples/README.adoc b/samples/README.adoc index e7be87ccc..bfee2abb5 100644 --- a/samples/README.adoc +++ b/samples/README.adoc @@ -46,4 +46,4 @@ include::./extensions/README.adoc[] include::./tooling/README.adoc[] [[general-samples]] -include::./general/README.adoc[] \ No newline at end of file +include::./general/README.adoc[] diff --git a/samples/performance/README.adoc b/samples/performance/README.adoc index 05271ae35..1cfa2d994 100644 --- a/samples/performance/README.adoc +++ b/samples/performance/README.adoc @@ -53,6 +53,11 @@ This sample will explore a few options to improve both descriptor and buffer man A transcoded version of the Performance sample <> that illustrates the usage of the C{pp} bindings of vulkan provided by vulkan.hpp. +=== xref:./performance/image_compression_control/README.adoc[Image compression control] + +This sample shows how to use the extensions https://docs.vulkan.org/spec/latest/appendices/extensions.html#VK_EXT_image_compression_control[`VK_EXT_image_compression_control`] and https://docs.vulkan.org/spec/latest/appendices/extensions.html#VK_EXT_image_compression_control_swapchain[`VK_EXT_image_compression_control_swapchain`] to select between different levels of image compression. +The UI shows the impact compression has on image size and bandwidth, illustrating the benefits of fixed-rate (visually lossless) compression. + === xref:./{performance_samplespath}layout_transitions/README.adoc[Layout transitions] Vulkan requires the application to manage image layouts, so that all render pass attachments are in the correct layout when the render pass begins. diff --git a/samples/performance/afbc/README.adoc b/samples/performance/afbc/README.adoc index 24539d35a..fb7bbac95 100644 --- a/samples/performance/afbc/README.adoc +++ b/samples/performance/afbc/README.adoc @@ -1,5 +1,5 @@ //// -- Copyright (c) 2019-2023, Arm Limited and Contributors +- Copyright (c) 2019-2024, Arm Limited and Contributors - - SPDX-License-Identifier: Apache-2.0 - @@ -159,3 +159,5 @@ for compute on a specific image). *Debugging* * To test if AFBC is enabled or disabled, you can use a profiler such as Streamline and record the bandwidth values when AFBC is enabled or when AFBC is disabled. +* You may also use the extension https://docs.vulkan.org/spec/latest/appendices/extensions.html#VK_EXT_image_compression_control[`VK_EXT_image_compression_control`] to query if AFBC is enabled. +See the xref:/samples/performance/image_compression_control/README.adoc[Image Compression Control sample] for more details. diff --git a/samples/performance/afbc/afbc.cpp b/samples/performance/afbc/afbc.cpp index b236271e5..5e93e648a 100644 --- a/samples/performance/afbc/afbc.cpp +++ b/samples/performance/afbc/afbc.cpp @@ -35,6 +35,9 @@ AFBCSample::AFBCSample() { + // Extension that may be used to query if AFBC is enabled + add_device_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME, true); + auto &config = get_configuration(); config.insert(0, afbc_enabled, false); @@ -117,6 +120,12 @@ void AFBCSample::draw_gui() get_gui().show_options_window( /* body = */ [this]() { ImGui::Checkbox("Enable AFBC", &afbc_enabled); + + if (get_device().is_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME)) + { + ImGui::SameLine(); + ImGui::Text("(%s)", vkb::image_compression_flags_to_string(get_render_context().get_swapchain().get_applied_compression()).c_str()); + } }, /* lines = */ 1); } diff --git a/samples/performance/image_compression_control/CMakeLists.txt b/samples/performance/image_compression_control/CMakeLists.txt new file mode 100644 index 000000000..db2639854 --- /dev/null +++ b/samples/performance/image_compression_control/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright (c) 2024, Arm Limited and Contributors +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 the "License"; +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH) +get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME) + +add_sample( + ID ${FOLDER_NAME} + CATEGORY ${CATEGORY_NAME} + AUTHOR "Arm" + NAME "Image Compression Control" + DESCRIPTION "Save memory footprint and bandwidth with visually lossless compression") diff --git a/samples/performance/image_compression_control/README.adoc b/samples/performance/image_compression_control/README.adoc new file mode 100644 index 000000000..83731a640 --- /dev/null +++ b/samples/performance/image_compression_control/README.adoc @@ -0,0 +1,291 @@ +//// +- Copyright (c) 2024, The Khronos Group +- Copyright (c) 2024, Arm Limited and Contributors +- +- SPDX-License-Identifier: Apache-2.0 +- +- Licensed under the Apache License, Version 2.0 the "License"; +- you may not use this file except in compliance with the License. +- You may obtain a copy of the License at +- +- http://www.apache.org/licenses/LICENSE-2.0 +- +- Unless required by applicable law or agreed to in writing, software +- distributed under the License is distributed on an "AS IS" BASIS, +- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +- See the License for the specific language governing permissions and +- limitations under the License. +- +//// + +//// +The following block adds linkage to this repo in the Vulkan docs site project. It's only visible if the file is viewed via the Antora framework. +//// + +ifdef::site-gen-antora[] +TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/performance/image_compression_control[Khronos Vulkan samples github repository]. +endif::[] + += Image Compression Control + +== Overview + +This sample shows how to allow a Vulkan application to control the compression of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImage.html[`VkImage`] elements, in particular a framebuffer attachment and the swapchain. +This requires enabling and using the extensions https://docs.vulkan.org/spec/latest/appendices/extensions.html#VK_EXT_image_compression_control[`VK_EXT_image_compression_control`] and https://docs.vulkan.org/spec/latest/appendices/extensions.html#VK_EXT_image_compression_control_swapchain[`VK_EXT_image_compression_control_swapchain`] respectively. +Applications that use compression generally perform better thanks to the reduced memory footprint and bandwidth. + +In this sample the use-case is a simple post-processing effect (https://en.wikipedia.org/wiki/Chromatic_aberration[chromatic aberration]) applied to the output color of the forward-rendering pass. +Since the color output needs to be saved to main memory and read again by the post-processing pass, this an opportunity to improve performance by using image compression. + +The sample allows toggling between: + +* "Default": lossless compression (e.g. xref:/samples/performance/afbc/README.adoc[AFBC]). + +* "Fixed-rate": visually lossless compression (e.g. https://learn.arm.com/learning-paths/smartphones-and-mobile/afrc/[AFRC]). +A low/high level of compression can be specified in this case. + +* "None": disable all compression, which is usually not recommended except for debugging purposes. + +The compression settings will be applied to both the color attachment and the swapchain, if the extensions are supported. +The on-screen hardware counters show the impact each option has on bandwidth and on the memory footprint. + +image::./images/image_compression_control.png[Image Compression Control sample, 900, align="center"] + +[.text-center] +__Sponza scene with default (lossless) compression__ + +=== Default compression [[default_compression]] + +This is lossless compression that devices can enable transparently to save performance where possible. +Vulkan applications do not need to explicitly enable this sort of compression. + +Devices with Arm GPUs implement Arm Frame Buffer Compression (AFBC), which uses variable bitrate encoding: the image is compressed in blocks (e.g. 4x4 pixels) and, depending on their composition, a different bitrate will be used. +This means that the bandwidth savings depend on the image being compressed, and the compression ratio applied to each block. +On average, high compression ratios are often obtained, as shown in the xref:/samples/performance/afbc/README.adoc[AFBC sample]. + +=== Fixed-rate compression [[fixed_rate_compression]] + +On the other hand, a compression scheme may use a constant bitrate for all blocks, and in this case the compression is defined as fixed-rate. +This means that in some cases a block will lose some information, and thus the compression is lossy. +Therefore the device may not enable it automatically, and the developer must opt-in using the Vulkan image compression control extensions. + +Recent devices with Arm GPUs support Arm Fixed Rate Compression (AFRC), which achieve high quality results even with the highest compression ratios. +For instance, see below for images saved from a Pixel 8 device: + +[.center, cols="a,a,a"] +|=== +| Default compression +| 2BPC Fixed-rate compression +| Pixel difference +| image::./images/default.png[Default compression, 400] +| image::./images/fixed_rate_2BPC.png[Fixed-rate compression, 400] +| image::./images/compare.png[Pixel difference, 400] +|=== +[.text-center] +__Space Module scene compression comparison__ + +Since the difference is not noticeable with the naked eye, this is sometimes referred to as "visually lossless" compression. +Software like https://imagemagick.org/script/compare.php[imagemagick allows to compare] the images and obtain a https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio[PSNR] quality estimation, in this case with the high value of 49.8dB: + +[,shell] +---- +$ magick compare -metric PSNR default.png fixed_rate_2BPC.png compare.png +49.8487 (0.498487) +---- + +There are some performance benefits associated with fixed-rate compression, as described below. + +=== Memory footprint savings + +Images compressed with a fixed-rate will always consume less memory. +In this case, an image compressed with a 2BPC bitrate results in a 65% reduction compared to uncompressed. + +image::./images/footprint.png[Memory footprint savings, 700, align="center"] + +In this case, the slightly larger size of images compressed with AFBC is expected, as variable bitrates require enough space for the worse case (uncompressed) as well as some extra storage for compression-related metadata. + +=== Bandwidth savings + +The sample allows to observe an estimate of bytes being written out to main memory. +On this device the write bandwidth difference between uncompressed and fixed-rate compression is approximately 38%: + +image::./images/bandwidth.png[Bandwidth savings, 700, align="center"] + +Bandwidth savings coming from image compression depend on the pixels being compressed. +Moving the camera and showing different distribution of colors in the frame changes the results. +Be sure to profile your application and verify which compression scheme is optimal in each case. +For instance, images with a high proportion of solid color (e.g. normals or material properties) may be more optimally compressed with variable bitrates than with fixed-rate. +This is the case for the Space Module scene shown above. + +== VK_EXT_image_compression_control + +This sample enables the https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_image_compression_control.html[`VK_EXT_image_compression_control`] extension and requests the relevant device feature, https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceImageCompressionControlFeaturesEXT.html[`imageCompressionControl`] +This extension abstracts how applications choose a fixed compression rate, in terms of "minimum number of bits per component (BPC)". + +=== Query for image compression support + +To query if a particular image supports fixed-rate compression, add a https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionPropertiesEXT.html[`VkImageCompressionPropertiesEXT`] to the `pNext` chain of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageFormatProperties2KHR.html[`VkImageFormatProperties2`], and call https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetPhysicalDeviceImageFormatProperties2KHR.html[`vkGetPhysicalDeviceImageFormatProperties2KHR`]: + +[,cpp] +---- +VkImageCompressionPropertiesEXT supported_compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT}; + +VkImageCompressionControlEXT compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT}; +compression_control.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT; + +VkPhysicalDeviceImageFormatInfo2 image_format_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2}; +image_format_info.format = VK_FORMAT_R8G8B8_UNORM; +image_format_info.type = VK_IMAGE_TYPE_2D; +image_format_info.tiling = VK_IMAGE_TILING_OPTIMAL; +image_format_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; +image_format_info.pNext = &compression_control; + +VkImageFormatProperties2 image_format_properties{VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2}; +image_format_properties.pNext = &supported_compression_properties; + +vkGetPhysicalDeviceImageFormatProperties2KHR(device.get_gpu().get_handle(), &image_format_info, &image_format_properties); +---- + +In the Vulkan Samples framework, this happens in the https://github.com/KhronosGroup/Vulkan-Samples/blob/main/framework/common/vk_common.cpp[`vkb::query_supported_fixed_rate_compression`] function. + +Then inspect the values written to the `imageCompressionFixedRateFlags` component of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionPropertiesEXT.html[`VkImageCompressionPropertiesEXT`]. +If fixed-rate compression is supported, the flags will indicate which levels may be selected for this image, for instance https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionFixedRateFlagBitsEXT.html[`VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT`] or https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionFixedRateFlagBitsEXT.html[`VK_IMAGE_COMPRESSION_FIXED_RATE_5BPC_BIT_EXT`]. +The sample will use the minimum BPC available for its high compression setting, and the maximum BPC available for its low compression setting. + +image::./images/fixed_rate_levels.png[Image Compression Control sample, 900, align="center"] + +[.text-center] +__Fixed-rate options__ + +=== Request image compression + +To request fixed-rate compression, provide a https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionControlEXT.html[`VkImageCompressionControlEXT`] to the `pNext` chain of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCreateInfo.html[`VkImageCreateInfo`]: + +[,cpp] +---- +VkImageCompressionFixedRateFlagsEXT fixed_rate_flags_array[1] = {VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT}; + +VkImageCompressionControlEXT compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT}; +compression_control.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT; +compression_control.compressionControlPlaneCount = 1; +compression_control.pFixedRateFlags = &fixed_rate_flags_array[0]; + +VkImageCreateInfo image_info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; +image_info.format = VK_FORMAT_R8G8B8_UNORM; +image_info.imageType = VK_IMAGE_TYPE_2D; +image_info.tiling = VK_IMAGE_TILING_OPTIMAL; +image_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; +image_info.pNext = &compression_control; + +vkCreateImage(device, &image_info, nullptr, &new_image); +---- + +Note that, instead of using https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionFlagBitsEXT.html[`VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT`], one may use https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionFlagBitsEXT.html[`VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT`], and in that case it would not be necessary to provide a specific set of `pFixedRateFlags`. + +In the Vulkan Samples framework, this happens in the https://github.com/KhronosGroup/Vulkan-Samples/blob/main/framework/core/image.cpp[`core::Image`] constructor. + +=== Verify image compression [[verify_image_compression]] + +To query which compression was applied, if any, once a https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImage.html[`VkImage`] has been created, add a https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionPropertiesEXT.html[`VkImageCompressionPropertiesEXT`] to the `pNext` chain of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageSubresource2EXT.html[`VkImageSubresource2EXT`], and call https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetImageSubresourceLayout2EXT.html[`vkGetImageSubresourceLayout2EXT`]: + +[,cpp] +---- +VkImageCompressionPropertiesEXT compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT}; + +VkSubresourceLayout2EXT subresource_layout{VK_STRUCTURE_TYPE_SUBRESOURCE_LAYOUT_2_KHR}; +subresource_layout.pNext = &compression_properties; + +VkImageSubresource2EXT image_subresource{VK_STRUCTURE_TYPE_IMAGE_SUBRESOURCE_2_KHR}; +image_subresource.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +image_subresource.imageSubresource.mipLevel = 0; +image_subresource.imageSubresource.arrayLayer = 0; + +vkGetImageSubresourceLayout2EXT(device, image, &image_subresource, &subresource_layout); +---- + +Then inspect the values written to the `imageCompressionFlags` and `imageCompressionFixedRateFlags` components of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionPropertiesEXT.html[`VkImageCompressionPropertiesEXT`]. +In the Vulkan Samples framework, this happens in the https://github.com/KhronosGroup/Vulkan-Samples/blob/main/framework/core/image.cpp[`core::Image::query_applied_compression`] function. + + +== VK_EXT_image_compression_control_swapchain + +Compression control for swapchain images is similar, but it requires the https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_image_compression_control_swapchain.html[`VK_EXT_image_compression_control_swapchain`] extension and the https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT.html[`imageCompressionControlSwapchain`] device feature to be enabled. +These depend on the https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_image_compression_control.html[`VK_EXT_image_compression_control`] being available and enabled too. + +=== Query for surface compression support + +To query if the surface supports fixed-rate compression, add a https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionPropertiesEXT.html[`VkImageCompressionPropertiesEXT`] to the `pNext` chain of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageFormatProperties2KHR.html[`VkImageFormatProperties2`], and call https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetPhysicalDeviceImageFormatProperties2KHR.html[`vkGetPhysicalDeviceImageFormatProperties2KHR`]: + +[,cpp] +---- +VkPhysicalDeviceSurfaceInfo2KHR surface_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR}; +surface_info.surface = surface; + +uint32_t surface_format_count{0U}; + +vkGetPhysicalDeviceSurfaceFormats2KHR(device, &surface_info, &surface_format_count, nullptr); + +std::vector surface_formats; +surface_formats.resize(surface_format_count, {VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR}); + +std::vector compression_properties; +compression_properties.resize(surface_format_count, {VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT}); + +for (uint32_t i = 0; i < surface_format_count; i++) +{ + surface_formats[i].pNext = &compression_properties[i]; +} + +vkGetPhysicalDeviceSurfaceFormats2KHR(device, &surface_info, &surface_format_count, surface_formats.data()); +---- + +Then inspect the values written to the `imageCompressionFixedRateFlags` component of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionPropertiesEXT.html[`VkImageCompressionPropertiesEXT`], associated to a particular https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSurfaceFormat2KHR.html[`VkSurfaceFormat2KHR`]. +In the Vulkan Samples framework, this happens in the https://github.com/KhronosGroup/Vulkan-Samples/blob/main/framework/core/swapchain.cpp[`Swapchain::query_supported_fixed_rate_compression`] function. + +=== Request surface compression + +To request fixed-rate compression, provide a https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionControlEXT.html[`VkImageCompressionControlEXT`] to the `pNext` chain of https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSwapchainCreateInfoKHR.html[`VkSwapchainCreateInfoKHR`]: + +[,cpp] +---- +VkImageCompressionFixedRateFlagsEXT fixed_rate_flags_array[1] = {VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT}; + +VkImageCompressionControlEXT compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT}; +compression_control.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT; +compression_control.compressionControlPlaneCount = 1; +compression_control.pFixedRateFlags = &fixed_rate_flags_array[0]; + +VkSwapchainCreateInfoKHR create_info{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; +create_info.surface = surface; +create_info.pNext = &compression_control; + +vkCreateSwapchainKHR(device, &create_info, nullptr, &new_swapchain); +---- + +Similarly to regular images, https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionFlagBitsEXT.html[`VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT`] may be used instead. + +In the Vulkan Samples framework, this happens in the https://github.com/KhronosGroup/Vulkan-Samples/blob/main/framework/core/swapchain.cpp[`Swapchain`] constructor. + +=== Verify surface compression + +To verify that compression was applied to the swapchain images, use the same method as described for a regular https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImage.html[`VkImage`] in <>. +No need to enable https://docs.vulkan.org/spec/latest/appendices/extensions.html#VK_EXT_image_compression_control_swapchain[`VK_EXT_image_compression_control_swapchain`] for this. + +In the Vulkan Samples framework, this happens in the https://github.com/KhronosGroup/Vulkan-Samples/blob/main/framework/core/swapchain.cpp[`Swapchain::get_applied_compression`] function. + +Note that even if the surface supports fixed-rate compression and the extensions are enabled, the surface might not be compressed. +The most likely reason is that, even though the GPU supports it, other IP components in the system (e.g. the Display) do not support it, and therefore images are not compressed. + +== Disabling fixed-rate compression + +As explained above, the `flags` in https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionControlEXT.html[`VkImageCompressionControlEXT`] control the compression scheme selection for images. +Take care not to accidentally disable <> when disabling <>. +That is, ensure that https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionFlagBitsEXT.html[`VK_IMAGE_COMPRESSION_DEFAULT_EXT`] is used by default, rather than https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCompressionFlagBitsEXT.html[`VK_IMAGE_COMPRESSION_DISABLED_EXT`], which disables all compression, negatively impacting performance. + +== Conclusion + +https://docs.vulkan.org/spec/latest/appendices/extensions.html#VK_EXT_image_compression_control[`VK_EXT_image_compression_control`] allows applications to check if default compression is enabled. +It also provides the mechanism to request lossy (fixed-rate) compression where appropriate (https://docs.vulkan.org/spec/latest/appendices/extensions.html#VK_EXT_image_compression_control_swapchain[`VK_EXT_image_compression_control_swapchain`] is required for swapchain images). + +Fixed-rate compression guarantees the most efficient memory footprint and can result in substantially reduced memory bandwidth, without sacrificing image quality. +Bandwidth reductions can in turn result in performance improvements and power savings. diff --git a/samples/performance/image_compression_control/image_compression_control.cpp b/samples/performance/image_compression_control/image_compression_control.cpp new file mode 100644 index 000000000..3f1633e9b --- /dev/null +++ b/samples/performance/image_compression_control/image_compression_control.cpp @@ -0,0 +1,582 @@ +/* Copyright (c) 2024, Arm Limited and Contributors + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "image_compression_control.h" + +#include "common/vk_common.h" +#include "gltf_loader.h" +#include "gui.h" +#include "platform/platform.h" +#include "rendering/postprocessing_renderpass.h" +#include "rendering/subpasses/forward_subpass.h" +#include "stats/stats.h" + +ImageCompressionControlSample::ImageCompressionControlSample() +{ + // Extensions of interest in this sample (optional) + add_device_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME, true); + add_device_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME, true); + + // Extension dependency requirements (given that instance API version is 1.0.0) + add_instance_extension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, true); + + auto &config = get_configuration(); + + // Batch mode will test the toggle between different compression modes + config.insert(0, static_cast(gui_target_compression), 0); + config.insert(1, static_cast(gui_target_compression), 1); + config.insert(2, static_cast(gui_target_compression), 2); +} + +void ImageCompressionControlSample::request_gpu_features(vkb::PhysicalDevice &gpu) +{ + if (gpu.is_extension_supported(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME)) + { + auto &compression_control_features = gpu.request_extension_features(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT); + + compression_control_features.imageCompressionControl = VK_TRUE; + } + + if (gpu.is_extension_supported(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME)) + { + auto &compression_control_swapchain_features = gpu.request_extension_features(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT); + + compression_control_swapchain_features.imageCompressionControlSwapchain = VK_TRUE; + } +} + +bool ImageCompressionControlSample::prepare(const vkb::ApplicationOptions &options) +{ + if (!VulkanSample::prepare(options)) + { + return false; + } + + load_scene("scenes/sponza/Sponza01.gltf"); + + auto &camera_node = vkb::add_free_camera(get_scene(), "main_camera", get_render_context().get_surface_extent()); + camera = dynamic_cast(&camera_node.get_component()); + + vkb::ShaderSource scene_vs("base.vert"); + vkb::ShaderSource scene_fs("base.frag"); + auto scene_subpass = std::make_unique(get_render_context(), std::move(scene_vs), std::move(scene_fs), get_scene(), *camera); + scene_subpass->set_output_attachments({(int) Attachments::Color}); + + // Forward rendering pass + auto render_pipeline = std::make_unique(); + render_pipeline->add_subpass(std::move(scene_subpass)); + render_pipeline->set_load_store(scene_load_store); + set_render_pipeline(std::move(render_pipeline)); + + // Post-processing pass (chromatic aberration) + vkb::ShaderSource postprocessing_vs("postprocessing/postprocessing.vert"); + postprocessing_pipeline = std::make_unique(get_render_context(), std::move(postprocessing_vs)); + postprocessing_pipeline->add_pass().add_subpass(vkb::ShaderSource("postprocessing/chromatic_aberration.frag")); + + // Trigger recreation of Swapchain and render targets, with initial compression parameters + update_render_targets(); + + get_stats().request_stats({vkb::StatIndex::frame_times, + vkb::StatIndex::gpu_ext_write_bytes}); + + create_gui(*window, &get_stats()); + + // Hide GUI compression options other than default if the required extension is not supported + if (!get_device().is_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME)) + { + for (int i = 0; i < (int) TargetCompression::Count; i++) + { + if (static_cast(i) != TargetCompression::Default) + { + gui_skip_compression_values.insert(static_cast(i)); + } + } + } + + return true; +} + +void ImageCompressionControlSample::create_render_context() +{ + /** + * The framework expects a prioritized list of surface formats. For this sample, include + * only those that can be compressed. + */ + std::vector surface_formats_that_support_compression; + + /** + * To query for compression support, VK_EXT_image_compression_control_swapchain allows to + * add a VkImageCompressionPropertiesEXT to the pNext chain of VkSurfaceFormat2KHR when + * calling vkGetPhysicalDeviceSurfaceFormats2KHR. + * See the implementation of this helper function in vkb::Swapchain. + */ + auto surface_compression_properties_list = vkb::Swapchain::query_supported_fixed_rate_compression(get_device(), get_surface()); + + LOGI("The following surface formats support compression:"); + for (auto &surface_compression_properties : surface_compression_properties_list) + { + if (surface_compression_properties.compression_properties.imageCompressionFixedRateFlags != VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT) + { + VkSurfaceFormatKHR surface_format = {surface_compression_properties.surface_format.surfaceFormat.format, + surface_compression_properties.surface_format.surfaceFormat.colorSpace}; + + LOGI(" \t{}:\t{}", + vkb::to_string(surface_format), + vkb::image_compression_fixed_rate_flags_to_string(surface_compression_properties.compression_properties.imageCompressionFixedRateFlags)); + + surface_formats_that_support_compression.push_back(surface_format); + } + } + + if (surface_formats_that_support_compression.empty()) + { + LOGI(" \tNo surface formats support fixed-rate compression"); + + // Fall-back to default surface format priority list + VulkanSample::create_render_context(); + } + else + { + // Filter default list to those formats that support compression + std::vector new_surface_priority_list; + for (auto const &surface_priority : get_surface_priority_list()) + { + auto it = std::find_if(surface_formats_that_support_compression.begin(), surface_formats_that_support_compression.end(), + [&](VkSurfaceFormatKHR &sf) { return surface_priority.format == sf.format && + surface_priority.colorSpace == sf.colorSpace; }); + if (it != surface_formats_that_support_compression.end()) + { + new_surface_priority_list.push_back(*it); + + surface_formats_that_support_compression.erase(it); + } + } + + // In case there is no overlap, append any formats that support compression but were not in the default list + for (auto &remaining_format : surface_formats_that_support_compression) + { + new_surface_priority_list.push_back(remaining_format); + } + + VulkanSample::create_render_context(new_surface_priority_list); + } + + /** + * At this point, a Swapchain has been created using the first supported format in the list above. Save a list of + * its corresponding supported compression rates (if any). + */ + const auto &selected_surface_format = get_render_context().get_swapchain().get_surface_format(); + for (size_t i = 0; i < surface_compression_properties_list.size(); i++) + { + if (selected_surface_format.format == surface_compression_properties_list[i].surface_format.surfaceFormat.format && + selected_surface_format.colorSpace == surface_compression_properties_list[i].surface_format.surfaceFormat.colorSpace) + { + supported_fixed_rate_flags_swapchain = vkb::fixed_rate_compression_flags_to_vector( + surface_compression_properties_list[i].compression_properties.imageCompressionFixedRateFlags); + break; + } + } +} + +void ImageCompressionControlSample::prepare_render_context() +{ + get_render_context().prepare(1, std::bind(&ImageCompressionControlSample::create_render_target, this, std::placeholders::_1)); +} + +std::unique_ptr ImageCompressionControlSample::create_render_target(vkb::core::Image &&swapchain_image) +{ + /** + * The render passes will use 3 attachments: Color, Depth and Swapchain. + * This sample allows to control the compression of the color and Swapchain attachments. + * The Swapchain has already been created by the RenderContext. Here, create color and depth images. + */ + color_image_info.imageType = VK_IMAGE_TYPE_2D; + color_image_info.extent = swapchain_image.get_extent(); + color_image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + color_image_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + + // The first time this function is called, choose a compressible format for the color attachment. + if (VK_FORMAT_UNDEFINED == color_image_info.format) + { + // Query fixed-rate compression support for a few candidate formats + std::vector format_list{VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_R8G8B8_SNORM, VK_FORMAT_R8G8B8_SRGB, + VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_B8G8R8_SNORM, VK_FORMAT_B8G8R8_SRGB, + VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8A8_SRGB, + VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM, VK_FORMAT_B8G8R8A8_SRGB}; + + VkFormat chosen_format{VK_FORMAT_UNDEFINED}; + if (get_device().is_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME)) + { + for (auto &candidate_format : format_list) + { + color_image_info.format = candidate_format; + + /** + * To query for compression support, VK_EXT_image_compression_control allows to + * add a VkImageCompressionPropertiesEXT to the pNext chain of VkImageFormatProperties2 when + * calling vkGetPhysicalDeviceImageFormatProperties2KHR. + * See the implementation of this helper function in vkb::core::Image. + */ + VkImageCompressionPropertiesEXT supported_compression_properties = vkb::query_supported_fixed_rate_compression(get_device().get_gpu().get_handle(), color_image_info); + + auto fixed_rate_flags = vkb::fixed_rate_compression_flags_to_vector(supported_compression_properties.imageCompressionFixedRateFlags); + + VkImageFormatProperties format_properties; + auto result = vkGetPhysicalDeviceImageFormatProperties(get_device().get_gpu().get_handle(), + color_image_info.format, + color_image_info.imageType, + color_image_info.tiling, + color_image_info.usage, + 0, // no create flags + &format_properties); + + // Pick the first format that supports at least 2 or more levels of fixed-rate compression, otherwise + // pick the first format that supports at least 1 level + if (result != VK_ERROR_FORMAT_NOT_SUPPORTED && + fixed_rate_flags.size() > 0) + { + if ((VK_FORMAT_UNDEFINED == chosen_format) || (fixed_rate_flags.size() > 1)) + { + chosen_format = candidate_format; + supported_fixed_rate_flags_color = fixed_rate_flags; + } + + if (fixed_rate_flags.size() > 1) + { + break; + } + } + } + } + + // Fallback if no fixed-rate compressible format was found + color_image_info.format = (VK_FORMAT_UNDEFINED != chosen_format) ? chosen_format : swapchain_image.get_format(); + LOGI("Chosen color format: {}", vkb::to_string(color_image_info.format)); + + // Hide GUI fixed-rate compression option if chosen format does not support it + if (supported_fixed_rate_flags_color.empty()) + { + gui_skip_compression_values.insert(TargetCompression::FixedRate); + + LOGW("Color image does not support fixed-rate compression. Possible reasons:"); + LOGW("\t- Its format may not be supported (format = {})", vkb::to_string(color_image_info.format)); + if (color_image_info.usage & VK_IMAGE_USAGE_STORAGE_BIT) + { + LOGW("\t- It is a storage image (usage = {})", vkb::image_usage_to_string(color_image_info.usage)); + } + if (color_image_info.samples > 1) + { + LOGW("\t- It is a multi-sampled image (sample count = {})", vkb::to_string(color_image_info.samples)); + } + } + } + + vkb::core::Image depth_image{get_device(), + vkb::core::ImageBuilder(color_image_info.extent) + .with_format(vkb::get_suitable_depth_format(get_device().get_gpu().get_handle())) + .with_usage(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) + .with_vma_usage(VMA_MEMORY_USAGE_GPU_ONLY)}; + + vkb::core::ImageBuilder color_image_builder(color_image_info.extent); + color_image_builder.with_format(color_image_info.format) + .with_usage(color_image_info.usage) + .with_tiling(color_image_info.tiling) + .with_vma_usage(VMA_MEMORY_USAGE_GPU_ONLY); + + // Prepare compression control structure + VkImageCompressionControlEXT color_compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT}; + + if (VK_IMAGE_COMPRESSION_DEFAULT_EXT != compression_flag) + { + color_compression_control.flags = compression_flag; + + if (VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT == compression_flag && + VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT != compression_fixed_rate_flag_color) + { + color_compression_control.compressionControlPlaneCount = 1; + color_compression_control.pFixedRateFlags = &compression_fixed_rate_flag_color; + } + + color_image_builder.with_extension(color_compression_control); + } + + vkb::core::Image color_image{get_device(), color_image_builder}; + + if (VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT == compression_flag) + { + /** + * To verify that the requested compression was indeed applied, the framework core::Image class + * provides a helper function that uses the new structures introduced by VK_EXT_image_compression_control. + * It calls vkGetImageSubresourceLayout2EXT with a VkImageSubresource2EXT that includes a + * VkImageCompressionPropertiesEXT structure in its pNext chain. + * This structure is then filled with the applied compression flags. + */ + LOGI("Applied fixed-rate compression for color ({}): {}", vkb::to_string(color_image_info.format), + vkb::image_compression_fixed_rate_flags_to_string(color_image.get_applied_compression().imageCompressionFixedRateFlags)); + } + + // Update memory footprint values in GUI (displayed in MB) + const float bytes_in_mb = 1024 * 1024; + footprint_swapchain = swapchain_image.get_image_required_size() / bytes_in_mb; + footprint_color = color_image.get_image_required_size() / bytes_in_mb; + + scene_load_store.clear(); + std::vector images; + + // Attachment 0 - Swapchain - Not used in the scene render pass, output of postprocessing + images.push_back(std::move(swapchain_image)); + scene_load_store.push_back({VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE}); + + // Attachment 1 - Attachments::Depth - Transient, used only in the scene render pass + images.push_back(std::move(depth_image)); + scene_load_store.push_back({VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE}); + + // Attachment 2 - Attachments::Color - Output of the scene render pass, input of postprocessing + images.push_back(std::move(color_image)); + scene_load_store.push_back({VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE}); + + return std::make_unique(std::move(images)); +} + +void ImageCompressionControlSample::update(float delta_time) +{ + elapsed_time += delta_time; + + if ((gui_target_compression != last_gui_target_compression) || + (gui_fixed_rate_compression_level != last_gui_fixed_rate_compression_level)) + { + update_render_targets(); + + last_gui_target_compression = gui_target_compression; + last_gui_fixed_rate_compression_level = gui_fixed_rate_compression_level; + } + + VulkanSample::update(delta_time); +} + +void ImageCompressionControlSample::update_render_targets() +{ + /** + * Define the compression flags that will be used to select the compression + * level of the color and Swapchain images. In the framework, this will be + * handled by the vkb::core::Image and vkb::Swapchain constructors. + * The extensions introduce a new VkImageCompressionControlEXT struct, which + * collects compression settings, and that can be provided to the pNext + * list of VkImageCreateInfo and VkSwapchainCreateInfoKHR. + */ + switch (gui_target_compression) + { + case TargetCompression::FixedRate: + { + compression_flag = VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT; + break; + } + case TargetCompression::None: + { + compression_flag = VK_IMAGE_COMPRESSION_DISABLED_EXT; + break; + } + case TargetCompression::Default: + default: + { + compression_flag = VK_IMAGE_COMPRESSION_DEFAULT_EXT; + break; + } + } + + /** + * Select the minimum (higher compression) or maximum (lower compression) bitrate supported, + * which might be different for the color and the Swapchain. + */ + auto fixed_rate_compression_level = static_cast(gui_fixed_rate_compression_level); + compression_fixed_rate_flag_color = select_fixed_rate_compression_flag(supported_fixed_rate_flags_color, fixed_rate_compression_level); + compression_fixed_rate_flag_swapchain = select_fixed_rate_compression_flag(supported_fixed_rate_flags_swapchain, fixed_rate_compression_level); + + // Recreate the Swapchain, which also triggers recreation of render targets + get_device().wait_idle(); + get_render_context().update_swapchain(compression_flag, compression_fixed_rate_flag_swapchain); +} + +VkImageCompressionFixedRateFlagBitsEXT ImageCompressionControlSample::select_fixed_rate_compression_flag( + std::vector &supported_fixed_rate_flags, + FixedRateCompressionLevel compression_level) +{ + if (!supported_fixed_rate_flags.empty()) + { + switch (compression_level) + { + case FixedRateCompressionLevel::High: + { + return supported_fixed_rate_flags.front(); + } + case FixedRateCompressionLevel::Low: + { + return supported_fixed_rate_flags.back(); + } + default: + { + LOGW("Unknown fixed-rate compression level {}", static_cast(compression_level)); + break; + } + } + } + + return VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT; +} + +void ImageCompressionControlSample::render(vkb::CommandBuffer &command_buffer) +{ + // Scene (forward rendering) pass + VulkanSample::render(command_buffer); + + command_buffer.end_render_pass(); + + /** + * Post processing pass, which applies a simple chromatic aberration effect. + * The effect is animated, using elapsed time, for two reasons: + * 1. Allows to visualize the scene with and without the effect. + * 2. Reduces the effect of transaction elimination, a useful feature that + * reduces bandwidth but may hide the bandwidth benefits of compression, + * the focus of this sample. + */ + auto &postprocessing_pass = postprocessing_pipeline->get_pass(0); + postprocessing_pass.set_uniform_data(sin(elapsed_time)); + + auto &postprocessing_subpass = postprocessing_pass.get_subpass(0); + postprocessing_subpass.bind_sampled_image("color_sampler", (int) Attachments::Color); + + postprocessing_pipeline->draw(command_buffer, get_render_context().get_active_frame().get_render_target()); +} + +namespace +{ + +/** + * @brief Helper function to generate a GUI drop-down options menu + */ +template +inline T generate_combo(T current_value, const char *combo_label, const std::unordered_map &enum_to_string, float item_width, const std::set *skip_values = nullptr) +{ + ImGui::PushItemWidth(item_width); + + T new_enum_value = current_value; + + const auto &search_it = enum_to_string.find(current_value); + const char *selected_value = search_it != enum_to_string.end() ? search_it->second.c_str() : ""; + + if (ImGui::BeginCombo(combo_label, selected_value)) + { + for (const auto &it : enum_to_string) + { + if (skip_values && std::find(skip_values->begin(), skip_values->end(), it.first) != skip_values->end()) + { + continue; + } + + const bool is_selected = it.first == current_value; + + if (ImGui::Selectable(it.second.c_str(), is_selected)) + { + new_enum_value = it.first; + } + + if (is_selected) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + + return new_enum_value; +} +} // namespace + +void ImageCompressionControlSample::draw_gui() +{ + const bool landscape = camera->get_aspect_ratio() > 1.0f; + uint32_t lines = 3; + + if (landscape) + { + lines--; + } + + get_gui().show_options_window( + [&]() { + const auto window_width = ImGui::GetWindowWidth(); + + /** + * Select compression scheme from those available. Some options may be hidden if the extension(s) are not supported, + * or if the chosen color format does not support fixed-rate compression. + */ + ImGui::Text("Compression:"); + ImGui::SameLine(); + + const TargetCompression compression = generate_combo(gui_target_compression, "##compression", + {{TargetCompression::FixedRate, "Fixed-rate"}, {TargetCompression::None, "None"}, {TargetCompression::Default, "Default"}}, + window_width * 0.2f, + &gui_skip_compression_values); + + gui_target_compression = compression; + + if (compression == TargetCompression::FixedRate && supported_fixed_rate_flags_color.size() > 1) + { + /** + * Select level of fixed-rate compression from those available. It is assumed that the Swapchain can only be compressed + * if the color attachment can be compressed too, given that we select similar formats and the Swapchain compression control + * extension depends on the general image compression control extension. + */ + ImGui::SameLine(); + ImGui::Text("Level:"); + + ImGui::SameLine(); + const FixedRateCompressionLevel compression_level = generate_combo(gui_fixed_rate_compression_level, "##compression-level", + {{FixedRateCompressionLevel::High, "High"}, {FixedRateCompressionLevel::Low, "Low"}}, + window_width * 0.2f); + + gui_fixed_rate_compression_level = compression_level; + } + + if (landscape) + { + ImGui::SameLine(); + } + + if (gui_skip_compression_values.size() >= (int) TargetCompression::Count - 1) + { + // Single or no compression options available on this device + ImGui::Text("(Extensions are not supported)"); + } + else + { + // Indicate if the Swapchain compression matches that of the color attachment + ImGui::Text("(Swapchain is %s affected)", get_render_context().get_swapchain().get_applied_compression() == compression_flag ? "also" : "not"); + } + + /** + * Display the memory footprint of the configurable targets, which will be lower if fixed-rate compression is selected. + */ + ImGui::Text("Color attachment (%.1f MB), Swapchain (%.1f MB)", footprint_color, footprint_swapchain); + }, + lines); +} + +std::unique_ptr> create_image_compression_control() +{ + return std::make_unique(); +} diff --git a/samples/performance/image_compression_control/image_compression_control.h b/samples/performance/image_compression_control/image_compression_control.h new file mode 100644 index 000000000..49780b77e --- /dev/null +++ b/samples/performance/image_compression_control/image_compression_control.h @@ -0,0 +1,189 @@ +/* Copyright (c) 2024, Arm Limited and Contributors + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "rendering/postprocessing_pipeline.h" +#include "rendering/render_pipeline.h" +#include "scene_graph/components/camera.h" +#include "scene_graph/components/perspective_camera.h" +#include "vulkan_sample.h" + +/** + * @brief Image Compression Control Sample + * + * This sample shows how to use the extensions VK_EXT_image_compression_control + * and VK_EXT_image_compression_control_swapchain to select between + * different levels of image compression. + * + * The UI shows the impact compression has on image size and bandwidth, + * illustrating the benefits of fixed-rate (visually lossless) compression. + */ +class ImageCompressionControlSample : public vkb::VulkanSample +{ + public: + ImageCompressionControlSample(); + + virtual bool prepare(const vkb::ApplicationOptions &options) override; + + virtual void request_gpu_features(vkb::PhysicalDevice &gpu) override; + + virtual ~ImageCompressionControlSample() = default; + + virtual void update(float delta_time) override; + + virtual void render(vkb::CommandBuffer &command_buffer) override; + + void draw_gui() override; + + private: + vkb::sg::PerspectiveCamera *camera{nullptr}; + + virtual void prepare_render_context() override; + + virtual void create_render_context() override; + + std::unique_ptr create_render_target(vkb::core::Image &&swapchain_image); + + enum class Attachments : int + { + Swapchain = 0, + Depth = 1, + Color = 2, + }; + + /** + * @brief Postprocessing pipeline + * In this sample, a postprocessing pass applies a simple chromatic aberration effect + * to the output of the forward rendering pass. Since this color output needs to + * be saved to main memory, it is a simple use case for compressing the image and + * saving bandwidth and memory footprint, as illustrated in the sample. + */ + std::unique_ptr postprocessing_pipeline{}; + + /** + * @brief Load/store operations of the forward rendering pass attachments + * Used to specify that the color output must be stored to main memory. + */ + std::vector scene_load_store{}; + + /** + * @brief Color image parameters + * These are the properties of the color output image. + */ + VkImageCreateInfo color_image_info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + + /** + * @brief Compression scheme + * By default, many implementations support lossless compression. + * Some also support fixed-rate compression, which is generally visually lossless. + * This flag can also be used to disable all compression, which is not recommended. + */ + VkImageCompressionFlagsEXT compression_flag{VK_IMAGE_COMPRESSION_DEFAULT_EXT}; + + /** + * @brief Possible compression schemes + */ + enum class TargetCompression : int + { + Default = 0, + FixedRate = 1, + None = 2, + + // Enum value count + Count = 3 + }; + + /** + * @brief Compression rate for the color image + * If fixed-rate compression is selected, this specifies the bitrate per component. + */ + VkImageCompressionFixedRateFlagsEXT compression_fixed_rate_flag_color{}; + + /** + * @brief Compression rate for the Swapchain image + */ + VkImageCompressionFixedRateFlagsEXT compression_fixed_rate_flag_swapchain{}; + + /** + * @brief Supported values for compression rate + * These depend on the properties (format and usage) of the color image. + */ + std::vector supported_fixed_rate_flags_color{}; + + /** + * @brief Supported values for compression rate + * These depend on the properties (format and usage) of the Swapchain image. + */ + std::vector supported_fixed_rate_flags_swapchain{}; + + /** + * @brief Possible fixed-rate compression levels + */ + enum FixedRateCompressionLevel : int + { + High = 0, // Highest compression available (smallest bitrate) + Low = 1, // Lowest compression available (largest bitrate) + }; + + /** + * @brief Selects the compression bitrate given a list of supported values and a compression level + */ + VkImageCompressionFixedRateFlagBitsEXT select_fixed_rate_compression_flag(std::vector &supported_fixed_rate_flags, + FixedRateCompressionLevel compression_level); + + /** + * @brief Size (in MB) of the color image (output of the forward rendering pass) + */ + float footprint_color{}; + + /** + * @brief Size (in MB) of the Swapchain image (output of the postprocessing pass) + */ + float footprint_swapchain{}; + + /** + * @brief Time + * Used to animate the chromatic aberration effect. + */ + float elapsed_time{}; + + /** + * @brief Updates compression settings based on user GUI input. + * Triggers recreation of the Swapchain and other render targets. + */ + void update_render_targets(); + + /** + * @brief Unsupported compression schemes + * Used to reduce the GUI controls if, for instance, the required + * extensions are not supported by the device. + */ + std::set gui_skip_compression_values; + + /* Helpers for managing GUI input */ + + TargetCompression gui_target_compression{TargetCompression::Default}; + + TargetCompression last_gui_target_compression{gui_target_compression}; + + FixedRateCompressionLevel gui_fixed_rate_compression_level{FixedRateCompressionLevel::High}; + + FixedRateCompressionLevel last_gui_fixed_rate_compression_level{gui_fixed_rate_compression_level}; +}; + +std::unique_ptr> create_image_compression_control(); diff --git a/samples/performance/image_compression_control/images/bandwidth.png b/samples/performance/image_compression_control/images/bandwidth.png new file mode 100644 index 000000000..6a5172a01 Binary files /dev/null and b/samples/performance/image_compression_control/images/bandwidth.png differ diff --git a/samples/performance/image_compression_control/images/compare.png b/samples/performance/image_compression_control/images/compare.png new file mode 100644 index 000000000..19b589ceb Binary files /dev/null and b/samples/performance/image_compression_control/images/compare.png differ diff --git a/samples/performance/image_compression_control/images/default.png b/samples/performance/image_compression_control/images/default.png new file mode 100644 index 000000000..1b438e465 Binary files /dev/null and b/samples/performance/image_compression_control/images/default.png differ diff --git a/samples/performance/image_compression_control/images/fixed_rate_2BPC.png b/samples/performance/image_compression_control/images/fixed_rate_2BPC.png new file mode 100644 index 000000000..9ac65c6a8 Binary files /dev/null and b/samples/performance/image_compression_control/images/fixed_rate_2BPC.png differ diff --git a/samples/performance/image_compression_control/images/fixed_rate_levels.png b/samples/performance/image_compression_control/images/fixed_rate_levels.png new file mode 100644 index 000000000..b3ac9b455 Binary files /dev/null and b/samples/performance/image_compression_control/images/fixed_rate_levels.png differ diff --git a/samples/performance/image_compression_control/images/footprint.png b/samples/performance/image_compression_control/images/footprint.png new file mode 100644 index 000000000..5a297e16e Binary files /dev/null and b/samples/performance/image_compression_control/images/footprint.png differ diff --git a/samples/performance/image_compression_control/images/image_compression_control.png b/samples/performance/image_compression_control/images/image_compression_control.png new file mode 100644 index 000000000..e08764a3c Binary files /dev/null and b/samples/performance/image_compression_control/images/image_compression_control.png differ diff --git a/shaders/postprocessing/chromatic_aberration.frag b/shaders/postprocessing/chromatic_aberration.frag new file mode 100644 index 000000000..b18c3535e --- /dev/null +++ b/shaders/postprocessing/chromatic_aberration.frag @@ -0,0 +1,49 @@ +#version 450 +/* Copyright (c) 2024, Arm Limited and Contributors + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +precision highp float; + +layout(set = 0, binding = 1) uniform sampler2D color_sampler; + +layout(location = 0) in vec2 in_uv; + +layout(location = 0) out vec4 o_color; + +layout(set = 0, binding = 0) uniform PostprocessingUniform +{ + float sin_elapsed_time; +} +postprocessing_uniform; + +#define SCALE 0.002 + +#define RED_OFFSET 3 +#define GREEN_OFFSET -1 +#define BLUE_OFFSET 2 + +void main(void) +{ + o_color = vec4(1.0); + + const vec2 center_coord = vec2(0.5); + const vec2 direction = (in_uv - center_coord) * postprocessing_uniform.sin_elapsed_time * SCALE; + + o_color.r = texture(color_sampler, in_uv - (direction * vec2(RED_OFFSET))).r; + o_color.g = texture(color_sampler, in_uv - (direction * vec2(GREEN_OFFSET))).g; + o_color.b = texture(color_sampler, in_uv - (direction * vec2(BLUE_OFFSET))).b; +}