585 lines
24 KiB
C++
585 lines
24 KiB
C++
/* Copyright (c) 2024-2025, 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<vkb::IntSetting>(0, static_cast<int>(gui_target_compression), 0);
|
|
config.insert<vkb::IntSetting>(1, static_cast<int>(gui_target_compression), 1);
|
|
config.insert<vkb::IntSetting>(2, static_cast<int>(gui_target_compression), 2);
|
|
}
|
|
|
|
void ImageCompressionControlSample::request_gpu_features(vkb::PhysicalDevice &gpu)
|
|
{
|
|
if (gpu.is_extension_supported(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME))
|
|
{
|
|
REQUEST_REQUIRED_FEATURE(gpu,
|
|
VkPhysicalDeviceImageCompressionControlFeaturesEXT,
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT,
|
|
imageCompressionControl);
|
|
}
|
|
|
|
if (gpu.is_extension_supported(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME))
|
|
{
|
|
REQUEST_OPTIONAL_FEATURE(gpu,
|
|
VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT,
|
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT,
|
|
imageCompressionControlSwapchain);
|
|
}
|
|
}
|
|
|
|
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<vkb::sg::PerspectiveCamera *>(&camera_node.get_component<vkb::sg::Camera>());
|
|
|
|
vkb::ShaderSource scene_vs("base.vert.spv");
|
|
vkb::ShaderSource scene_fs("base.frag.spv");
|
|
auto scene_subpass = std::make_unique<vkb::ForwardSubpass>(get_render_context(), std::move(scene_vs), std::move(scene_fs), get_scene(), *camera);
|
|
scene_subpass->set_output_attachments({static_cast<int>(Attachments::Color)});
|
|
|
|
// Forward rendering pass
|
|
auto render_pipeline = std::make_unique<vkb::RenderPipeline>();
|
|
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.spv");
|
|
postprocessing_pipeline = std::make_unique<vkb::PostProcessingPipeline>(get_render_context(), std::move(postprocessing_vs));
|
|
postprocessing_pipeline->add_pass().add_subpass(vkb::ShaderSource("postprocessing/chromatic_aberration.frag.spv"));
|
|
|
|
// 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_extension_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME))
|
|
{
|
|
for (int i = 0; i < static_cast<int>(TargetCompression::Count); i++)
|
|
{
|
|
if (static_cast<TargetCompression>(i) != TargetCompression::Default)
|
|
{
|
|
gui_skip_compression_values.insert(static_cast<TargetCompression>(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<VkSurfaceFormatKHR> 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<VkSurfaceFormatKHR> new_surface_priority_list;
|
|
for (auto const &surface_priority : get_surface_priority_list())
|
|
{
|
|
auto it = std::ranges::find_if(surface_formats_that_support_compression,
|
|
[&](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);
|
|
}
|
|
|
|
vkb::VulkanSampleC::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<vkb::RenderTarget> 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<VkFormat> 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_extension_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<VkImageCompressionControlEXT>(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<vkb::core::Image> 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<vkb::RenderTarget>(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<FixedRateCompressionLevel>(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<VkImageCompressionFixedRateFlagBitsEXT> &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<int>(compression_level));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT;
|
|
}
|
|
|
|
void ImageCompressionControlSample::render(vkb::core::CommandBufferC &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", static_cast<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 <typename T>
|
|
inline T generate_combo(T current_value, const char *combo_label, const std::unordered_map<T, std::string> &enum_to_string, float item_width, const std::set<T> *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::ranges::find(*skip_values, 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() >= static_cast<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<vkb::VulkanSampleC> create_image_compression_control()
|
|
{
|
|
return std::make_unique<ImageCompressionControlSample>();
|
|
}
|