1
0
Fork 0
forked from wry/wry

vulkan: take transfer functions from color descriptions

This commit is contained in:
Julian Orth 2025-03-01 14:59:50 +01:00
parent a174881138
commit b5044d7fe7
5 changed files with 199 additions and 109 deletions

View file

@ -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,

View file

@ -219,7 +219,6 @@ pub struct CopyTexture {
pub release_sync: ReleaseSync,
pub alpha: Option<f32>,
pub opaque: bool,
#[expect(dead_code)]
pub cd: Rc<ColorDescription>,
}

View file

@ -553,13 +553,13 @@ impl GfxFramebuffer for VulkanImage {
self: Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
_cd: &Rc<ColorDescription>,
cd: &Rc<ColorDescription>,
ops: &[GfxApiOpt],
clear: Option<&Color>,
_clear_cd: &Rc<LinearColorDescription>,
region: &Region,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
_blend_cd: &Rc<ColorDescription>,
blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, 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())
}

View file

@ -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<AHashMap<u32, GfxFormat>>,
pub(super) device: Rc<VulkanDevice>,
pub(super) pipelines: StaticMap<RenderPass, CopyHashMap<vk::Format, Rc<VulkanFormatPipelines>>>,
pub(super) out_pipelines: RefCell<AHashMap<vk::Format, Rc<VulkanPipeline>>>,
pub(super) fill_pipelines: CopyHashMap<vk::Format, FillPipelines>,
pub(super) tex_pipelines:
StaticMap<TransferFunction, CopyHashMap<vk::Format, Rc<TexPipelines>>>,
pub(super) out_pipelines:
StaticMap<TransferFunction, CopyHashMap<OutPipelineKey, Rc<VulkanPipeline>>>,
pub(super) gfx_command_buffers: CachedCommandBuffers,
pub(super) transfer_command_buffers: Option<CachedCommandBuffers>,
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
@ -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<ColorDescription>,
}
struct VulkanFillOp {
@ -225,9 +227,25 @@ pub(super) struct PendingFrame {
_used_buffers: ArrayVec<VulkanBuffer, 3>,
}
pub(super) struct VulkanFormatPipelines {
pub(super) fill: StaticMap<TexSourceType, Rc<VulkanPipeline>>,
pub(super) tex: StaticMap<TexCopyType, StaticMap<TexSourceType, Rc<VulkanPipeline>>>,
type FillPipelines = Rc<StaticMap<TexSourceType, Rc<VulkanPipeline>>>;
#[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<TexPipelineKey, Rc<VulkanPipeline>>,
}
#[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<Rc<VulkanFormatPipelines>, 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<FillPipelines, VulkanError> {
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::<TexPushConstants>()
} else {
size_of::<LegacyTexPushConstants>()
};
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<TexPipelines> {
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<Rc<VulkanPipeline>, 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::<TexPushConstants>()
} else {
size_of::<LegacyTexPushConstants>()
};
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<Rc<VulkanPipeline>, 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::<OutPushConstants>(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::<OutPushConstants>(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<VulkanImage>,
fb_acquire_sync: AcquireSync,
fb_release_sync: ReleaseSync,
fb_cd: &Rc<ColorDescription>,
opts: &[GfxApiOpt],
clear: Option<&Color>,
region: &Region,
blend_buffer: Option<Rc<VulkanImage>>,
blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, 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<VulkanImage>,
fb_acquire_sync: AcquireSync,
fb_release_sync: ReleaseSync,
fb_cd: &Rc<ColorDescription>,
opts: &[GfxApiOpt],
clear: Option<&Color>,
region: &Region,
mut blend_buffer: Option<Rc<VulkanImage>>,
bb_cd: &Rc<ColorDescription>,
) -> 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);
}

View file

@ -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,
}
}
}