From b5044d7fe7e2a34a15ea4ee69d1d6722ed052dfb Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 1 Mar 2025 14:59:50 +0100 Subject: [PATCH] vulkan: take transfer functions from color descriptions --- src/cmm/cmm_transfer_function.rs | 4 +- src/gfx_api.rs | 1 - src/gfx_apis/vulkan/image.rs | 6 +- src/gfx_apis/vulkan/renderer.rs | 282 ++++++++++++++-------- src/gfx_apis/vulkan/transfer_functions.rs | 15 ++ 5 files changed, 199 insertions(+), 109 deletions(-) diff --git a/src/cmm/cmm_transfer_function.rs b/src/cmm/cmm_transfer_function.rs index 22fc9920..50613a64 100644 --- a/src/cmm/cmm_transfer_function.rs +++ b/src/cmm/cmm_transfer_function.rs @@ -1,4 +1,6 @@ -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +use linearize::Linearize; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Linearize)] pub enum TransferFunction { Srgb, Linear, diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 302f63af..3e436f77 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -219,7 +219,6 @@ pub struct CopyTexture { pub release_sync: ReleaseSync, pub alpha: Option, pub opaque: bool, - #[expect(dead_code)] pub cd: Rc, } diff --git a/src/gfx_apis/vulkan/image.rs b/src/gfx_apis/vulkan/image.rs index b1ddcdcd..b74ce224 100644 --- a/src/gfx_apis/vulkan/image.rs +++ b/src/gfx_apis/vulkan/image.rs @@ -553,13 +553,13 @@ impl GfxFramebuffer for VulkanImage { self: Rc, acquire_sync: AcquireSync, release_sync: ReleaseSync, - _cd: &Rc, + cd: &Rc, ops: &[GfxApiOpt], clear: Option<&Color>, _clear_cd: &Rc, region: &Region, blend_buffer: Option<&Rc>, - _blend_cd: &Rc, + blend_cd: &Rc, ) -> Result, GfxError> { let mut blend_buffer = blend_buffer.map(|b| b.clone().into_vk(&self.renderer.device.device)); @@ -578,10 +578,12 @@ impl GfxFramebuffer for VulkanImage { &self, acquire_sync, release_sync, + cd, ops, clear, region, blend_buffer, + blend_cd, ) .map_err(|e| e.into()) } diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index b46e5af7..df3ae761 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -1,9 +1,8 @@ use { crate::{ async_engine::{AsyncEngine, SpawnedFuture}, - cmm::cmm_transfer_function::TransferFunction, + cmm::{cmm_description::ColorDescription, cmm_transfer_function::TransferFunction}, cpu_worker::PendingJob, - format::XRGB8888, gfx_api::{ AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, GfxFormat, GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile, @@ -27,7 +26,7 @@ use { OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, TexVertex, VulkanShader, }, - transfer_functions::{TF_LINEAR, TF_SRGB}, + transfer_functions::{TF_LINEAR, TransferFunctionExt}, }, io_uring::IoUring, rect::{Rect, Region}, @@ -58,7 +57,6 @@ use { std::{ borrow::Cow, cell::{Cell, RefCell}, - collections::hash_map::Entry, fmt::{Debug, Formatter}, mem, ops::Range, @@ -72,8 +70,11 @@ use { pub struct VulkanRenderer { pub(super) formats: Rc>, pub(super) device: Rc, - pub(super) pipelines: StaticMap>>, - pub(super) out_pipelines: RefCell>>, + pub(super) fill_pipelines: CopyHashMap, + pub(super) tex_pipelines: + StaticMap>>, + pub(super) out_pipelines: + StaticMap>>, pub(super) gfx_command_buffers: CachedCommandBuffers, pub(super) transfer_command_buffers: Option, pub(super) wait_semaphores: Stack>, @@ -131,13 +132,13 @@ pub(super) struct UsedTexture { release_sync: ReleaseSync, } -#[derive(Copy, Clone, Linearize)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Linearize)] pub(super) enum TexCopyType { Identity, Multiply, } -#[derive(Copy, Clone, Linearize)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Linearize)] pub(super) enum TexSourceType { Opaque, HasAlpha, @@ -188,6 +189,7 @@ struct VulkanTexOp { copy_type: TexCopyType, range_address: DeviceAddress, instances: u32, + tex_cd: Rc, } struct VulkanFillOp { @@ -225,9 +227,25 @@ pub(super) struct PendingFrame { _used_buffers: ArrayVec, } -pub(super) struct VulkanFormatPipelines { - pub(super) fill: StaticMap>, - pub(super) tex: StaticMap>>, +type FillPipelines = Rc>>; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +struct TexPipelineKey { + tex_copy_type: TexCopyType, + tex_source_type: TexSourceType, + eotf: TransferFunction, +} + +pub(super) struct TexPipelines { + format: vk::Format, + oetf: TransferFunction, + pipelines: CopyHashMap>, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub(super) struct OutPipelineKey { + format: vk::Format, + eotf: TransferFunction, } impl VulkanDevice { @@ -319,7 +337,8 @@ impl VulkanDevice { let render = Rc::new(VulkanRenderer { formats: Rc::new(formats), device: self.clone(), - pipelines: Default::default(), + fill_pipelines: Default::default(), + tex_pipelines: Default::default(), out_pipelines: Default::default(), gfx_command_buffers, transfer_command_buffers, @@ -350,23 +369,16 @@ impl VulkanDevice { blend_buffers: Default::default(), shader_buffer_cache, }); - render.get_or_create_pipelines(XRGB8888.vk_format, RenderPass::FrameBuffer)?; Ok(render) } } impl VulkanRenderer { - fn get_or_create_pipelines( + fn get_or_create_fill_pipelines( &self, format: vk::Format, - pass: RenderPass, - ) -> Result, VulkanError> { - let (eotf, oetf) = match pass { - RenderPass::BlendBuffer => (TF_SRGB, TF_LINEAR), - RenderPass::FrameBuffer => (TF_SRGB, TF_SRGB), - }; - let pipelines = &self.pipelines[pass]; - if let Some(pl) = pipelines.get(&format) { + ) -> Result { + if let Some(pl) = self.fill_pipelines.get(&format) { return Ok(pl); } let create_fill_pipeline = |src_has_alpha| { @@ -382,55 +394,116 @@ impl VulkanRenderer { blend: src_has_alpha, src_has_alpha, has_alpha_mult: false, - eotf, - oetf, + // all transformations are applied in the compositor + eotf: TF_LINEAR, + oetf: TF_LINEAR, descriptor_set_layouts: Default::default(), }; self.device.create_pipeline2(info, push_size) }; - let fill_opaque = create_fill_pipeline(false)?; - let fill_alpha = create_fill_pipeline(true)?; - let create_tex_pipeline = |src_has_alpha, has_alpha_mult| { - let push_size = if self.device.descriptor_buffer.is_some() { - size_of::() - } else { - size_of::() - }; - let info = PipelineCreateInfo { - format, - vert: self.tex_vert_shader.clone(), - frag: self.tex_frag_shader.clone(), - blend: src_has_alpha || has_alpha_mult, - src_has_alpha, - has_alpha_mult, - eotf, - oetf, - descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(), - }; - self.device.create_pipeline2(info, push_size) - }; - let tex_opaque = create_tex_pipeline(false, false)?; - let tex_alpha = create_tex_pipeline(true, false)?; - let tex_mult_opaque = create_tex_pipeline(false, true)?; - let tex_mult_alpha = create_tex_pipeline(true, true)?; - let format_pipelines = Rc::new(VulkanFormatPipelines { - fill: static_map! { - TexSourceType::HasAlpha => fill_alpha.clone(), - TexSourceType::Opaque => fill_opaque.clone(), - }, - tex: static_map! { - TexCopyType::Identity => static_map! { - TexSourceType::HasAlpha => tex_alpha.clone(), - TexSourceType::Opaque => tex_opaque.clone(), - }, - TexCopyType::Multiply => static_map! { - TexSourceType::HasAlpha => tex_mult_alpha.clone(), - TexSourceType::Opaque => tex_mult_opaque.clone(), - }, - }, + let fill_pipelines = Rc::new(static_map! { + TexSourceType::HasAlpha => create_fill_pipeline(true)?, + TexSourceType::Opaque => create_fill_pipeline(false)?, }); - pipelines.set(format, format_pipelines.clone()); - Ok(format_pipelines) + self.fill_pipelines.set(format, fill_pipelines.clone()); + Ok(fill_pipelines) + } + + fn get_or_create_tex_pipelines( + &self, + format: vk::Format, + target_cd: &ColorDescription, + ) -> Rc { + let pipelines = &self.tex_pipelines[target_cd.transfer_function]; + match pipelines.get(&format) { + Some(pl) => pl, + _ => { + let pl = Rc::new(TexPipelines { + format, + oetf: target_cd.transfer_function, + pipelines: Default::default(), + }); + pipelines.set(format, pl.clone()); + pl + } + } + } + + fn get_or_create_tex_pipeline( + &self, + pipelines: &TexPipelines, + tex_cd: &ColorDescription, + tex_copy_type: TexCopyType, + tex_source_type: TexSourceType, + ) -> Result, VulkanError> { + let key = TexPipelineKey { + tex_copy_type, + tex_source_type, + eotf: tex_cd.transfer_function, + }; + if let Some(pl) = pipelines.pipelines.get(&key) { + return Ok(pl); + } + let src_has_alpha = match tex_source_type { + TexSourceType::Opaque => false, + TexSourceType::HasAlpha => true, + }; + let has_alpha_mult = match tex_copy_type { + TexCopyType::Identity => false, + TexCopyType::Multiply => true, + }; + let push_size = if self.device.descriptor_buffer.is_some() { + size_of::() + } else { + size_of::() + }; + let info = PipelineCreateInfo { + format: pipelines.format, + vert: self.tex_vert_shader.clone(), + frag: self.tex_frag_shader.clone(), + blend: src_has_alpha || has_alpha_mult, + src_has_alpha, + has_alpha_mult, + eotf: key.eotf.to_vulkan(), + oetf: pipelines.oetf.to_vulkan(), + descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(), + }; + let pl = self.device.create_pipeline2(info, push_size)?; + pipelines.pipelines.set(key, pl.clone()); + Ok(pl) + } + + fn get_or_create_out_pipeline( + &self, + format: vk::Format, + bb_cd: &ColorDescription, + fb_cd: &ColorDescription, + ) -> Result, VulkanError> { + let key = OutPipelineKey { + format, + eotf: bb_cd.transfer_function, + }; + let pipelines = &self.out_pipelines[fb_cd.transfer_function]; + if let Some(pl) = pipelines.get(&key) { + return Ok(pl); + } + let mut descriptor_set_layouts = ArrayVec::new(); + descriptor_set_layouts.push(self.out_descriptor_set_layout.clone().unwrap()); + let out = self + .device + .create_pipeline::(PipelineCreateInfo { + format: key.format, + vert: self.out_vert_shader.clone().unwrap(), + frag: self.out_frag_shader.clone().unwrap(), + blend: false, + src_has_alpha: true, + has_alpha_mult: false, + eotf: key.eotf.to_vulkan(), + oetf: fb_cd.transfer_function.to_vulkan(), + descriptor_set_layouts, + })?; + pipelines.set(key, out.clone()); + Ok(out) } pub(super) fn allocate_point(&self) -> u64 { @@ -511,7 +584,12 @@ impl VulkanRenderer { Ok(()) } - fn convert_ops(&self, opts: &[GfxApiOpt]) { + fn convert_ops( + &self, + opts: &[GfxApiOpt], + blend_cd: &ColorDescription, + fb_cd: &ColorDescription, + ) { zone!("convert_ops"); let memory = &mut *self.memory.borrow_mut(); for ops in memory.ops.values_mut() { @@ -590,8 +668,8 @@ impl VulkanRenderer { continue; } let tf = match pass { - RenderPass::BlendBuffer => TransferFunction::Linear, - RenderPass::FrameBuffer => TransferFunction::Srgb, + RenderPass::BlendBuffer => blend_cd.transfer_function, + RenderPass::FrameBuffer => fb_cd.transfer_function, }; let ops = &mut memory.ops_tmp[pass]; let lo = memory.fill_targets.len(); @@ -673,6 +751,7 @@ impl VulkanRenderer { copy_type, range_address: 0, instances: 0, + tex_cd: ct.cd.clone(), })); } } @@ -867,6 +946,7 @@ impl VulkanRenderer { target: &VulkanImage, clear: Option<&Color>, pass: RenderPass, + target_cd: &ColorDescription, ) { zone!("begin_rendering"); let memory = &mut *self.memory.borrow_mut(); @@ -877,10 +957,7 @@ impl VulkanRenderer { if clear_rects.is_not_empty() { let clear_value = ClearValue { color: ClearColorValue { - float32: match pass { - RenderPass::BlendBuffer => clear.to_array(TransferFunction::Linear), - RenderPass::FrameBuffer => clear.to_array(TransferFunction::Srgb), - }, + float32: clear.to_array(target_cd.transfer_function), }, }; let use_load_clear = clear_rects.len() == 1 && { @@ -970,10 +1047,12 @@ impl VulkanRenderer { buf: CommandBuffer, target: &VulkanImage, pass: RenderPass, + target_cd: &ColorDescription, ) -> Result<(), VulkanError> { zone!("record_draws"); let memory = &*self.memory.borrow(); - let pipelines = self.get_or_create_pipelines(target.format.vk_format, pass)?; + let fill_pl = self.get_or_create_fill_pipelines(target.format.vk_format)?; + let tex_pl = self.get_or_create_tex_pipelines(target.format.vk_format, target_cd); let dev = &self.device.device; let mut current_pipeline = None; let mut bind = |pipeline: &VulkanPipeline| { @@ -987,7 +1066,7 @@ impl VulkanRenderer { for opt in &memory.ops[pass] { match opt { VulkanOp::Fill(r) => { - let pipeline = &pipelines.fill[r.source_type]; + let pipeline = &fill_pl[r.source_type]; bind(pipeline); if self.device.descriptor_buffer.is_some() { let push = FillPushConstants { @@ -1027,8 +1106,13 @@ impl VulkanRenderer { } VulkanOp::Tex(c) => { let tex = &c.tex; - let pipeline = &pipelines.tex[c.copy_type][c.source_type]; - bind(pipeline); + let pipeline = self.get_or_create_tex_pipeline( + &tex_pl, + &c.tex_cd, + c.copy_type, + c.source_type, + )?; + bind(&pipeline); let image_info = DescriptorImageInfo::default() .image_view(tex.texture_view) .image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL); @@ -1119,33 +1203,14 @@ impl VulkanRenderer { &self, buf: CommandBuffer, fb: &VulkanImage, + fb_cd: &ColorDescription, bb: &VulkanImage, + bb_cd: &ColorDescription, ) -> Result<(), VulkanError> { zone!("blend_buffer_copy"); let memory = &*self.memory.borrow(); let db = self.device.descriptor_buffer.as_ref().unwrap(); - let pipeline = match self.out_pipelines.borrow_mut().entry(fb.format.vk_format) { - Entry::Occupied(pipeline) => pipeline.get().clone(), - Entry::Vacant(e) => { - let mut descriptor_set_layouts = ArrayVec::new(); - descriptor_set_layouts.push(self.out_descriptor_set_layout.clone().unwrap()); - let out = self - .device - .create_pipeline::(PipelineCreateInfo { - format: fb.format.vk_format, - vert: self.out_vert_shader.clone().unwrap(), - frag: self.out_frag_shader.clone().unwrap(), - blend: false, - src_has_alpha: true, - has_alpha_mult: false, - eotf: TF_LINEAR, - oetf: TF_SRGB, - descriptor_set_layouts, - })?; - e.insert(out.clone()); - out - } - }; + let pipeline = self.get_or_create_out_pipeline(fb.format.vk_format, bb_cd, fb_cd)?; let push = OutPushConstants { vertices: memory.out_address, }; @@ -1531,20 +1596,24 @@ impl VulkanRenderer { fb: &Rc, fb_acquire_sync: AcquireSync, fb_release_sync: ReleaseSync, + fb_cd: &Rc, opts: &[GfxApiOpt], clear: Option<&Color>, region: &Region, blend_buffer: Option>, + blend_cd: &Rc, ) -> Result, VulkanError> { zone!("execute"); let res = self.try_execute( fb, fb_acquire_sync, fb_release_sync, + fb_cd, opts, clear, region, blend_buffer, + blend_cd, ); let sync_file = { let mut memory = self.memory.borrow_mut(); @@ -1707,17 +1776,19 @@ impl VulkanRenderer { fb: &Rc, fb_acquire_sync: AcquireSync, fb_release_sync: ReleaseSync, + fb_cd: &Rc, opts: &[GfxApiOpt], clear: Option<&Color>, region: &Region, mut blend_buffer: Option>, + bb_cd: &Rc, ) -> Result<(), VulkanError> { self.check_defunct()?; self.create_regions(fb, opts, clear, region, blend_buffer.as_deref()); self.elide_blend_buffer(&mut blend_buffer); let bb = blend_buffer.as_deref(); let buf = self.gfx_command_buffers.allocate()?; - self.convert_ops(opts); + self.convert_ops(opts, bb_cd, fb_cd); self.create_data_buffer()?; self.collect_memory(); self.begin_command_buffer(buf.buffer)?; @@ -1726,18 +1797,19 @@ impl VulkanRenderer { self.set_viewport(buf.buffer, fb); if let Some(bb) = bb { zone!("blend buffer pass"); + let rp = RenderPass::BlendBuffer; self.blend_buffer_initial_barrier(buf.buffer, bb); - self.begin_rendering(buf.buffer, bb, clear, RenderPass::BlendBuffer); - self.record_draws(buf.buffer, bb, RenderPass::BlendBuffer)?; + self.begin_rendering(buf.buffer, bb, clear, rp, bb_cd); + self.record_draws(buf.buffer, bb, rp, bb_cd)?; self.end_rendering(buf.buffer); self.blend_buffer_final_barrier(buf.buffer, bb); } { zone!("frame buffer pass"); - self.begin_rendering(buf.buffer, fb, clear, RenderPass::FrameBuffer); - self.record_draws(buf.buffer, fb, RenderPass::FrameBuffer)?; + self.begin_rendering(buf.buffer, fb, clear, RenderPass::FrameBuffer, fb_cd); + self.record_draws(buf.buffer, fb, RenderPass::FrameBuffer, fb_cd)?; if let Some(bb) = bb { - self.blend_buffer_copy(buf.buffer, fb, bb)?; + self.blend_buffer_copy(buf.buffer, fb, fb_cd, bb, bb_cd)?; } self.end_rendering(buf.buffer); } diff --git a/src/gfx_apis/vulkan/transfer_functions.rs b/src/gfx_apis/vulkan/transfer_functions.rs index b267ce1b..b5bc2acc 100644 --- a/src/gfx_apis/vulkan/transfer_functions.rs +++ b/src/gfx_apis/vulkan/transfer_functions.rs @@ -1,2 +1,17 @@ +use crate::cmm::cmm_transfer_function::TransferFunction; + pub const TF_SRGB: u32 = 0; pub const TF_LINEAR: u32 = 1; + +pub trait TransferFunctionExt: Sized { + fn to_vulkan(self) -> u32; +} + +impl TransferFunctionExt for TransferFunction { + fn to_vulkan(self) -> u32 { + match self { + TransferFunction::Srgb => TF_SRGB, + TransferFunction::Linear => TF_LINEAR, + } + } +}