Skip to content

Commit

Permalink
add HDR display support
Browse files Browse the repository at this point in the history
  • Loading branch information
adrien-ben committed May 1, 2024
1 parent d0a0654 commit f7ad076
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 25 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ using [Ash][2]. It runs on Window, Linux and MacOS.
- [x] Animation controller
- [x] Camera details
- [x] Renderer settings
- [] HDR display
- [x] Allow enabling HDR (if supported by the device)
- [] Add HDR specific tonemapping

## Requirements

Expand Down
6 changes: 6 additions & 0 deletions crates/libs/vulkan/src/context/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod shared;

pub use self::shared::HDR_SURFACE_FORMAT;

use self::shared::*;
use crate::MsaaSamples;
use ash::{
Expand Down Expand Up @@ -114,6 +116,10 @@ impl Context {
self.shared_context.synchronization2()
}

pub fn has_hdr_support(&self) -> bool {
self.shared_context.has_hdr_support()
}

pub fn general_command_pool(&self) -> vk::CommandPool {
self.general_command_pool
}
Expand Down
32 changes: 32 additions & 0 deletions crates/libs/vulkan/src/context/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ use std::{
};
use winit::window::Window;

pub const HDR_SURFACE_FORMAT: vk::SurfaceFormatKHR = vk::SurfaceFormatKHR {
format: vk::Format::R16G16B16A16_SFLOAT,
color_space: vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT,
};

pub struct SharedContext {
_entry: Entry,
instance: Instance,
Expand All @@ -26,6 +31,7 @@ pub struct SharedContext {
present_queue: vk::Queue,
dynamic_rendering: DynamicRendering,
synchronization2: Synchronization2,
has_hdr_support: bool,
}

impl SharedContext {
Expand Down Expand Up @@ -64,6 +70,13 @@ impl SharedContext {
let dynamic_rendering = DynamicRendering::new(&instance, &device);
let synchronization2 = Synchronization2::new(&instance, &device);

let has_hdr_support = unsafe {
surface
.get_physical_device_surface_formats(physical_device, surface_khr)
.expect("failed to list physical device surface formats")
.contains(&HDR_SURFACE_FORMAT)
};

Self {
_entry: entry,
instance,
Expand All @@ -77,6 +90,7 @@ impl SharedContext {
present_queue,
dynamic_rendering,
synchronization2,
has_hdr_support,
}
}
}
Expand All @@ -99,6 +113,9 @@ fn create_instance(entry: &Entry, window: &Window, enable_debug: bool) -> Instan
if enable_debug {
extension_names.push(DebugUtils::name().as_ptr());
}
if has_ext_colorspace_support(entry) {
extension_names.push(vk::ExtSwapchainColorspaceFn::name().as_ptr());
}

let instance_create_info = vk::InstanceCreateInfo::builder()
.application_info(&app_info)
Expand Down Expand Up @@ -180,6 +197,17 @@ fn is_device_suitable(
&& features.sampler_anisotropy == vk::TRUE
}

fn has_ext_colorspace_support(entry: &Entry) -> bool {
let extension_props = entry
.enumerate_instance_extension_properties(None)
.expect("Failed to enumerate instance extention properties");

extension_props.iter().any(|ext| {
let name = unsafe { CStr::from_ptr(ext.extension_name.as_ptr()) };
vk::ExtSwapchainColorspaceFn::name() == name
})
}

fn check_device_extension_support(instance: &Instance, device: vk::PhysicalDevice) -> bool {
let required_extentions = get_required_device_extensions();

Expand Down Expand Up @@ -366,6 +394,10 @@ impl SharedContext {
pub fn synchronization2(&self) -> &Synchronization2 {
&self.synchronization2
}

pub fn has_hdr_support(&self) -> bool {
self.has_hdr_support
}
}

impl SharedContext {
Expand Down
24 changes: 18 additions & 6 deletions crates/libs/vulkan/src/swapchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@ impl Swapchain {
context: Arc<Context>,
swapchain_support_details: SwapchainSupportDetails,
dimensions: [u32; 2],
preferred_format: Option<vk::SurfaceFormatKHR>,
preferred_vsync: bool,
) -> Self {
log::debug!("Creating swapchain.");

let properties =
swapchain_support_details.get_ideal_swapchain_properties(dimensions, preferred_vsync);
let properties = swapchain_support_details.get_ideal_swapchain_properties(
preferred_format,
dimensions,
preferred_vsync,
);

let format = properties.format;
let present_mode = properties.present_mode;
Expand Down Expand Up @@ -234,12 +238,13 @@ impl SwapchainSupportDetails {
}
}

pub fn get_ideal_swapchain_properties(
fn get_ideal_swapchain_properties(
&self,
preferred_format: Option<vk::SurfaceFormatKHR>,
preferred_dimensions: [u32; 2],
preferred_vsync: bool,
) -> SwapchainProperties {
let format = Self::choose_swapchain_surface_format(&self.formats);
let format = Self::choose_swapchain_surface_format(&self.formats, preferred_format);
let present_mode =
Self::choose_swapchain_surface_present_mode(&self.present_modes, preferred_vsync);
let extent = Self::choose_swapchain_extent(self.capabilities, preferred_dimensions);
Expand All @@ -254,11 +259,18 @@ impl SwapchainSupportDetails {

/// Choose the swapchain surface format.
///
/// Will choose R8G8B8A8_SRGB/SRGB_NONLINEAR if possible or
/// the first available otherwise.
/// Will choose the preferred format or R8G8B8A8_SRGB/SRGB_NONLINEAR or
/// the first available.
fn choose_swapchain_surface_format(
available_formats: &[vk::SurfaceFormatKHR],
preferred_format: Option<vk::SurfaceFormatKHR>,
) -> vk::SurfaceFormatKHR {
if let Some(format) = preferred_format {
if available_formats.contains(&format) {
return format;
}
}

*available_formats
.iter()
.find(|format| {
Expand Down
14 changes: 13 additions & 1 deletion crates/viewer/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl Gui {
pub fn get_new_renderer_settings(&self) -> Option<RendererSettings> {
if self.state.renderer_settings_changed {
Some(RendererSettings {
hdr_enabled: self.state.hdr_enabled,
emissive_intensity: self.state.emissive_intensity,
ssao_enabled: self.state.ssao_enabled,
ssao_kernel_size: SSAO_KERNEL_SIZES[self.state.ssao_kernel_size_index],
Expand Down Expand Up @@ -248,6 +249,12 @@ fn build_renderer_settings_window(ui: &mut Ui, state: &mut State) {
ui.heading("Settings");
ui.separator();

ui.add_enabled_ui(state.hdr_enabled.is_some(), |ui| {
if let Some(hdr_enabled) = state.hdr_enabled.as_mut() {
ui.checkbox(hdr_enabled, "Enable HDR");
}
});

ui.add(
egui::Slider::new(&mut state.emissive_intensity, 1.0..=200.0)
.text("Emissive intensity")
Expand Down Expand Up @@ -316,6 +323,7 @@ struct State {

reset_camera: bool,

hdr_enabled: Option<bool>,
selected_output_mode: usize,
selected_tone_map_mode: usize,
emissive_intensity: f32,
Expand All @@ -332,6 +340,7 @@ struct State {
impl State {
fn new(renderer_settings: RendererSettings) -> Self {
Self {
hdr_enabled: renderer_settings.hdr_enabled,
selected_output_mode: renderer_settings.output_mode as _,
selected_tone_map_mode: renderer_settings.tone_map_mode as _,
emissive_intensity: renderer_settings.emissive_intensity,
Expand All @@ -345,6 +354,7 @@ impl State {

fn reset(&self) -> Self {
Self {
hdr_enabled: self.hdr_enabled,
selected_output_mode: self.selected_output_mode,
selected_tone_map_mode: self.selected_tone_map_mode,
emissive_intensity: self.emissive_intensity,
Expand All @@ -357,7 +367,8 @@ impl State {
}

fn check_renderer_settings_changed(&mut self, other: &Self) {
self.renderer_settings_changed = self.selected_output_mode != other.selected_output_mode
self.renderer_settings_changed = self.hdr_enabled != other.hdr_enabled
|| self.selected_output_mode != other.selected_output_mode
|| self.selected_tone_map_mode != other.selected_tone_map_mode
|| self.emissive_intensity != other.emissive_intensity
|| self.ssao_enabled != other.ssao_enabled
Expand All @@ -380,6 +391,7 @@ impl Default for State {

reset_camera: false,

hdr_enabled: None,
selected_output_mode: 0,
selected_tone_map_mode: 0,
emissive_intensity: 1.0,
Expand Down
27 changes: 22 additions & 5 deletions crates/viewer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn run(config: Config, enable_debug: bool, path: Option<PathBuf>) {

let context = Arc::new(Context::new(&window, enable_debug));

let renderer_settings = RendererSettings::default();
let mut renderer_settings = RendererSettings::new(&context);

let environment = Environment::new(&context, config.env().path(), config.env().resolution());
let mut gui = Gui::new(&window, renderer_settings);
Expand Down Expand Up @@ -141,16 +141,33 @@ fn run(config: Config, enable_debug: bool, path: Option<PathBuf>) {
}
}

// Update renderer settings
if let Some(renderer_settings) = gui.get_new_renderer_settings() {
renderer.update_settings(renderer_settings);
// Check if settings changed
if let Some(new_renderer_settings) = gui.get_new_renderer_settings() {
// recreate swapchain if hdr was toggled
if renderer_settings.hdr_enabled != new_renderer_settings.hdr_enabled {
renderer.recreate_swapchain(
window.inner_size().into(),
config.vsync(),
new_renderer_settings.hdr_enabled.unwrap_or_default(),
);
dirty_swapchain = false;
}

// Update renderer
renderer.update_settings(new_renderer_settings);

renderer_settings = new_renderer_settings;
}

// If swapchain must be recreated wait for windows to not be minimized anymore
if dirty_swapchain {
let PhysicalSize { width, height } = window.inner_size();
if width > 0 && height > 0 {
renderer.recreate_swapchain(window.inner_size().into(), config.vsync());
renderer.recreate_swapchain(
window.inner_size().into(),
config.vsync(),
renderer_settings.hdr_enabled.unwrap_or_default(),
);
} else {
return;
}
Expand Down
31 changes: 23 additions & 8 deletions crates/viewer/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub enum RenderError {

#[derive(Clone, Copy, Debug)]
pub struct RendererSettings {
pub hdr_enabled: Option<bool>,
pub emissive_intensity: f32,
pub ssao_enabled: bool,
pub ssao_kernel_size: u32,
Expand All @@ -56,9 +57,10 @@ pub struct RendererSettings {
pub bloom_strength: f32,
}

impl Default for RendererSettings {
fn default() -> Self {
impl RendererSettings {
pub fn new(context: &Context) -> Self {
Self {
hdr_enabled: context.has_hdr_support().then_some(false),
emissive_intensity: DEFAULT_EMISSIVE_INTENSITY,
ssao_enabled: true,
ssao_kernel_size: DEFAULT_SSAO_KERNEL_SIZE,
Expand Down Expand Up @@ -106,8 +108,6 @@ impl Renderer {
);

let resolution = [config.resolution().width(), config.resolution().height()];
let swapchain_properties =
swapchain_support_details.get_ideal_swapchain_properties(resolution, config.vsync());
let depth_format = find_depth_format(&context);
let msaa_samples = context.get_max_usable_sample_count(config.msaa());
log::debug!(
Expand All @@ -120,6 +120,10 @@ impl Renderer {
Arc::clone(&context),
swapchain_support_details,
resolution,
settings
.hdr_enabled
.unwrap_or_default()
.then_some(HDR_SURFACE_FORMAT),
config.vsync(),
);

Expand All @@ -132,7 +136,7 @@ impl Renderer {

let attachments = Attachments::new(
&context,
swapchain_properties.extent,
swapchain.properties().extent,
depth_format,
msaa_samples,
);
Expand Down Expand Up @@ -161,7 +165,7 @@ impl Renderer {

let final_pass = FinalPass::create(
Arc::clone(&context),
swapchain_properties.format.format,
swapchain.properties().format.format,
&attachments,
settings,
);
Expand All @@ -171,7 +175,7 @@ impl Renderer {
context.physical_device(),
context.device().clone(),
DynamicRendering {
color_attachment_format: swapchain_properties.format.format,
color_attachment_format: swapchain.properties().format.format,
depth_attachment_format: None,
},
Options {
Expand Down Expand Up @@ -848,7 +852,7 @@ impl Renderer {
}
}

pub fn recreate_swapchain(&mut self, dimensions: [u32; 2], vsync: bool) {
pub fn recreate_swapchain(&mut self, dimensions: [u32; 2], vsync: bool, hdr: bool) {
log::debug!("Recreating swapchain.");

self.wait_idle_gpu();
Expand All @@ -865,6 +869,7 @@ impl Renderer {
Arc::clone(&self.context),
swapchain_support_details,
dimensions,
hdr.then_some(HDR_SURFACE_FORMAT),
vsync,
);

Expand Down Expand Up @@ -920,7 +925,17 @@ impl Renderer {
self.bloom_pass.set_attachments(&self.attachments);

// Final
self.final_pass
.set_output_format(swapchain_properties.format.format);
self.final_pass.set_attachments(&self.attachments);

// UI Renderer
self.gui_renderer
.set_dynamic_rendering(DynamicRendering {
color_attachment_format: dbg!(swapchain_properties.format.format),
depth_attachment_format: None,
})
.expect("update gui renderer");
}

pub fn update_settings(&mut self, settings: RendererSettings) {
Expand Down
Loading

0 comments on commit f7ad076

Please sign in to comment.