Merge pull request #598 from mahkoh/jorth/tf-power
Various color management enhancements
This commit is contained in:
commit
6ba8b71b3e
14 changed files with 571 additions and 185 deletions
|
|
@ -1,14 +1,59 @@
|
||||||
use linearize::Linearize;
|
use crate::utils::ordered_float::F32;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Linearize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Eotf {
|
pub enum Eotf {
|
||||||
Linear,
|
Linear,
|
||||||
St2084Pq,
|
St2084Pq,
|
||||||
Bt1886,
|
Bt1886(F32),
|
||||||
Gamma22,
|
Gamma22,
|
||||||
|
Gamma24,
|
||||||
Gamma28,
|
Gamma28,
|
||||||
St240,
|
St240,
|
||||||
Log100,
|
Log100,
|
||||||
Log316,
|
Log316,
|
||||||
St428,
|
St428,
|
||||||
|
Pow(EotfPow),
|
||||||
|
}
|
||||||
|
|
||||||
|
const MUL: u32 = 10_000;
|
||||||
|
const MUL_F32: f32 = MUL as f32;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct EotfPow(pub u32);
|
||||||
|
|
||||||
|
impl EotfPow {
|
||||||
|
pub const MIN: Self = Self(10_000);
|
||||||
|
pub const LINEAR: Self = Self(10_000);
|
||||||
|
pub const GAMMA22: Self = Self(22_000);
|
||||||
|
pub const GAMMA24: Self = Self(24_000);
|
||||||
|
pub const GAMMA28: Self = Self(28_000);
|
||||||
|
pub const MAX: Self = Self(100_000);
|
||||||
|
|
||||||
|
pub fn eotf_f32(self) -> f32 {
|
||||||
|
self.0 as f32 / MUL_F32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inv_eotf_f32(self) -> f32 {
|
||||||
|
MUL_F32 / self.0 as f32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bt1886_eotf_args(c: F32) -> [f32; 4] {
|
||||||
|
let c = c.0;
|
||||||
|
let gamma = 1.0 / 2.4;
|
||||||
|
let a1 = 1.0 / (1.0 - c);
|
||||||
|
let a2 = 1.0 - c.powf(gamma);
|
||||||
|
let a3 = c.powf(gamma);
|
||||||
|
let a4 = c;
|
||||||
|
[a1, a2, a3, a4]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bt1886_inv_eotf_args(c: F32) -> [f32; 4] {
|
||||||
|
let c = c.0;
|
||||||
|
let gamma = 1.0 / 2.4;
|
||||||
|
let a1 = 1.0 / (1.0 - c.powf(gamma));
|
||||||
|
let a2 = 1.0 - c;
|
||||||
|
let a3 = c;
|
||||||
|
let a4 = c.powf(gamma);
|
||||||
|
[a1, a2, a3, a4]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub(super) struct VulkanDescriptorSetLayout {
|
||||||
pub(super) device: Rc<VulkanDevice>,
|
pub(super) device: Rc<VulkanDevice>,
|
||||||
pub(super) layout: DescriptorSetLayout,
|
pub(super) layout: DescriptorSetLayout,
|
||||||
pub(super) size: DeviceSize,
|
pub(super) size: DeviceSize,
|
||||||
pub(super) offsets: ArrayVec<DeviceSize, 2>,
|
pub(super) offsets: ArrayVec<DeviceSize, 4>,
|
||||||
pub(super) _sampler: Option<Rc<VulkanSampler>>,
|
pub(super) _sampler: Option<Rc<VulkanSampler>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,6 +98,16 @@ impl VulkanDevice {
|
||||||
.stage_flags(ShaderStageFlags::FRAGMENT)
|
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||||
.descriptor_count(1)
|
.descriptor_count(1)
|
||||||
.descriptor_type(DescriptorType::UNIFORM_BUFFER),
|
.descriptor_type(DescriptorType::UNIFORM_BUFFER),
|
||||||
|
DescriptorSetLayoutBinding::default()
|
||||||
|
.binding(2)
|
||||||
|
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||||
|
.descriptor_count(1)
|
||||||
|
.descriptor_type(DescriptorType::UNIFORM_BUFFER),
|
||||||
|
DescriptorSetLayoutBinding::default()
|
||||||
|
.binding(3)
|
||||||
|
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||||
|
.descriptor_count(1)
|
||||||
|
.descriptor_type(DescriptorType::UNIFORM_BUFFER),
|
||||||
];
|
];
|
||||||
let create_info = DescriptorSetLayoutCreateInfo::default()
|
let create_info = DescriptorSetLayoutCreateInfo::default()
|
||||||
.bindings(&bindings)
|
.bindings(&bindings)
|
||||||
|
|
@ -110,6 +120,8 @@ impl VulkanDevice {
|
||||||
unsafe {
|
unsafe {
|
||||||
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 0));
|
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 0));
|
||||||
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 1));
|
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 1));
|
||||||
|
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 2));
|
||||||
|
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 3));
|
||||||
}
|
}
|
||||||
Ok(Rc::new(VulkanDescriptorSetLayout {
|
Ok(Rc::new(VulkanDescriptorSetLayout {
|
||||||
device: self.clone(),
|
device: self.clone(),
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,77 @@
|
||||||
use crate::cmm::cmm_eotf::Eotf;
|
use {crate::cmm::cmm_eotf::Eotf, linearize::Linearize};
|
||||||
|
|
||||||
pub const EOTF_LINEAR: u32 = 1;
|
pub const EOTF_LINEAR: u32 = 1;
|
||||||
pub const EOTF_ST2084_PQ: u32 = 2;
|
pub const EOTF_ST2084_PQ: u32 = 2;
|
||||||
pub const EOTF_GAMMA24: u32 = 3;
|
pub const EOTF_BT1886: u32 = 3;
|
||||||
pub const EOTF_GAMMA22: u32 = 4;
|
pub const EOTF_GAMMA22: u32 = 4;
|
||||||
pub const EOTF_GAMMA28: u32 = 5;
|
pub const EOTF_GAMMA28: u32 = 5;
|
||||||
pub const EOTF_ST240: u32 = 6;
|
pub const EOTF_ST240: u32 = 6;
|
||||||
pub const EOTF_LOG100: u32 = 8;
|
pub const EOTF_LOG100: u32 = 8;
|
||||||
pub const EOTF_LOG316: u32 = 9;
|
pub const EOTF_LOG316: u32 = 9;
|
||||||
pub const EOTF_ST428: u32 = 10;
|
pub const EOTF_ST428: u32 = 10;
|
||||||
|
pub const EOTF_POW: u32 = 11;
|
||||||
|
pub const EOTF_GAMMA24: u32 = 12;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Linearize)]
|
||||||
|
pub enum VulkanEotf {
|
||||||
|
Linear,
|
||||||
|
St2084Pq,
|
||||||
|
Bt1886,
|
||||||
|
Gamma22,
|
||||||
|
Gamma24,
|
||||||
|
Gamma28,
|
||||||
|
St240,
|
||||||
|
Log100,
|
||||||
|
Log316,
|
||||||
|
St428,
|
||||||
|
Pow,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait EotfExt: Sized {
|
pub trait EotfExt: Sized {
|
||||||
fn to_vulkan(self) -> u32;
|
fn to_vulkan(self) -> VulkanEotf;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EotfExt for Eotf {
|
impl EotfExt for Eotf {
|
||||||
fn to_vulkan(self) -> u32 {
|
fn to_vulkan(self) -> VulkanEotf {
|
||||||
match self {
|
macro_rules! map {
|
||||||
Eotf::Linear => EOTF_LINEAR,
|
($($name:ident,)*) => {
|
||||||
Eotf::St2084Pq => EOTF_ST2084_PQ,
|
match self {
|
||||||
Eotf::Bt1886 => EOTF_GAMMA24,
|
$(
|
||||||
Eotf::Gamma22 => EOTF_GAMMA22,
|
Self::$name { .. } => VulkanEotf::$name,
|
||||||
Eotf::Gamma28 => EOTF_GAMMA28,
|
)*
|
||||||
Eotf::St240 => EOTF_ST240,
|
}
|
||||||
Eotf::Log100 => EOTF_LOG100,
|
};
|
||||||
Eotf::Log316 => EOTF_LOG316,
|
}
|
||||||
Eotf::St428 => EOTF_ST428,
|
map! {
|
||||||
|
Linear,
|
||||||
|
St2084Pq,
|
||||||
|
Bt1886,
|
||||||
|
Gamma22,
|
||||||
|
Gamma24,
|
||||||
|
Gamma28,
|
||||||
|
St240,
|
||||||
|
Log100,
|
||||||
|
Log316,
|
||||||
|
St428,
|
||||||
|
Pow,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VulkanEotf {
|
||||||
|
pub fn to_vulkan(self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Self::Linear => EOTF_LINEAR,
|
||||||
|
Self::St2084Pq => EOTF_ST2084_PQ,
|
||||||
|
Self::Bt1886 => EOTF_BT1886,
|
||||||
|
Self::Gamma22 => EOTF_GAMMA22,
|
||||||
|
Self::Gamma24 => EOTF_GAMMA24,
|
||||||
|
Self::Gamma28 => EOTF_GAMMA28,
|
||||||
|
Self::St240 => EOTF_ST240,
|
||||||
|
Self::Log100 => EOTF_LOG100,
|
||||||
|
Self::Log316 => EOTF_LOG316,
|
||||||
|
Self::St428 => EOTF_ST428,
|
||||||
|
Self::Pow => EOTF_POW,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use {
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
cmm::{
|
cmm::{
|
||||||
cmm_description::{ColorDescription, LinearColorDescription, LinearColorDescriptionId},
|
cmm_description::{ColorDescription, LinearColorDescription, LinearColorDescriptionId},
|
||||||
cmm_eotf::Eotf,
|
cmm_eotf::{Eotf, EotfPow, bt1886_eotf_args, bt1886_inv_eotf_args},
|
||||||
cmm_transform::ColorMatrix,
|
cmm_transform::ColorMatrix,
|
||||||
},
|
},
|
||||||
cpu_worker::PendingJob,
|
cpu_worker::PendingJob,
|
||||||
|
|
@ -19,23 +19,26 @@ use {
|
||||||
descriptor::VulkanDescriptorSetLayout,
|
descriptor::VulkanDescriptorSetLayout,
|
||||||
descriptor_buffer::VulkanDescriptorBufferWriter,
|
descriptor_buffer::VulkanDescriptorBufferWriter,
|
||||||
device::VulkanDevice,
|
device::VulkanDevice,
|
||||||
eotfs::{EOTF_LINEAR, EotfExt},
|
eotfs::{EOTF_LINEAR, EotfExt, VulkanEotf},
|
||||||
fence::VulkanFence,
|
fence::VulkanFence,
|
||||||
image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory},
|
image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory},
|
||||||
pipeline::{PipelineCreateInfo, VulkanPipeline},
|
pipeline::{PipelineCreateInfo, VulkanPipeline},
|
||||||
sampler::VulkanSampler,
|
sampler::VulkanSampler,
|
||||||
semaphore::VulkanSemaphore,
|
semaphore::VulkanSemaphore,
|
||||||
shaders::{
|
shaders::{
|
||||||
FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT,
|
ColorManagementData, EotfArgs, FILL_FRAG, FILL_VERT, FillPushConstants,
|
||||||
LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, LegacyTexPushConstants,
|
InvEotfArgs, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, LEGACY_TEX_FRAG, LEGACY_TEX_VERT,
|
||||||
OUT_FRAG, OUT_VERT, OutColorManagementData, OutPushConstants, TEX_FRAG, TEX_VERT,
|
LegacyFillPushConstants, LegacyTexPushConstants, OUT_FRAG, OUT_VERT,
|
||||||
TexColorManagementData, TexPushConstants, TexVertex, VulkanShader,
|
OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, TexVertex, VulkanShader,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
rect::{Rect, Region},
|
rect::{Rect, Region},
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack},
|
utils::{
|
||||||
|
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, ordered_float::F32,
|
||||||
|
stack::Stack,
|
||||||
|
},
|
||||||
video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file},
|
video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -78,8 +81,9 @@ pub struct VulkanRenderer {
|
||||||
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
|
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
|
||||||
pub(super) device: Rc<VulkanDevice>,
|
pub(super) device: Rc<VulkanDevice>,
|
||||||
pub(super) fill_pipelines: CopyHashMap<vk::Format, FillPipelines>,
|
pub(super) fill_pipelines: CopyHashMap<vk::Format, FillPipelines>,
|
||||||
pub(super) tex_pipelines: StaticMap<Eotf, CopyHashMap<vk::Format, Rc<TexPipelines>>>,
|
pub(super) tex_pipelines: StaticMap<VulkanEotf, CopyHashMap<vk::Format, Rc<TexPipelines>>>,
|
||||||
pub(super) out_pipelines: StaticMap<Eotf, CopyHashMap<OutPipelineKey, Rc<VulkanPipeline>>>,
|
pub(super) out_pipelines:
|
||||||
|
StaticMap<VulkanEotf, CopyHashMap<OutPipelineKey, Rc<VulkanPipeline>>>,
|
||||||
pub(super) gfx_command_buffers: CachedCommandBuffers,
|
pub(super) gfx_command_buffers: CachedCommandBuffers,
|
||||||
pub(super) transfer_command_buffers: Option<CachedCommandBuffers>,
|
pub(super) transfer_command_buffers: Option<CachedCommandBuffers>,
|
||||||
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
|
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
|
||||||
|
|
@ -176,10 +180,16 @@ pub(super) struct Memory {
|
||||||
data_buffer: Vec<u8>,
|
data_buffer: Vec<u8>,
|
||||||
out_address: DeviceAddress,
|
out_address: DeviceAddress,
|
||||||
color_transforms: ColorTransforms,
|
color_transforms: ColorTransforms,
|
||||||
|
eotf_args_cache: EotfArgsCache,
|
||||||
uniform_buffer_writer: GenericBufferWriter,
|
uniform_buffer_writer: GenericBufferWriter,
|
||||||
uniform_buffer_descriptor_cache: Option<Box<[u8]>>,
|
uniform_buffer_descriptor_cache: Option<Box<[u8]>>,
|
||||||
|
blend_buffer_inv_eotf_args_descriptor: Option<Box<[u8]>>,
|
||||||
|
fb_inv_eotf_args_descriptor: Option<Box<[u8]>>,
|
||||||
blend_buffer_descriptor_buffer_offset: DeviceAddress,
|
blend_buffer_descriptor_buffer_offset: DeviceAddress,
|
||||||
blend_buffer_color_management_data_address: Option<DeviceSize>,
|
blend_buffer_color_management_data_address: Option<DeviceSize>,
|
||||||
|
blend_buffer_eotf_args_address: Option<DeviceSize>,
|
||||||
|
blend_buffer_inv_eotf_args_address: Option<DeviceSize>,
|
||||||
|
fb_inv_eotf_args_address: Option<DeviceSize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Point = [[f32; 2]; 4];
|
type Point = [[f32; 2]; 4];
|
||||||
|
|
@ -202,6 +212,7 @@ struct VulkanTexOp {
|
||||||
instances: u32,
|
instances: u32,
|
||||||
tex_cd: Rc<ColorDescription>,
|
tex_cd: Rc<ColorDescription>,
|
||||||
color_management_data_address: Option<DeviceAddress>,
|
color_management_data_address: Option<DeviceAddress>,
|
||||||
|
eotf_args_address: Option<DeviceAddress>,
|
||||||
resource_descriptor_buffer_offset: DeviceAddress,
|
resource_descriptor_buffer_offset: DeviceAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,20 +257,21 @@ type FillPipelines = Rc<StaticMap<TexSourceType, Rc<VulkanPipeline>>>;
|
||||||
struct TexPipelineKey {
|
struct TexPipelineKey {
|
||||||
tex_copy_type: TexCopyType,
|
tex_copy_type: TexCopyType,
|
||||||
tex_source_type: TexSourceType,
|
tex_source_type: TexSourceType,
|
||||||
eotf: Eotf,
|
eotf: VulkanEotf,
|
||||||
has_color_management_data: bool,
|
has_color_management_data: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct TexPipelines {
|
pub(super) struct TexPipelines {
|
||||||
format: vk::Format,
|
format: vk::Format,
|
||||||
eotf: Eotf,
|
eotf: VulkanEotf,
|
||||||
pipelines: CopyHashMap<TexPipelineKey, Rc<VulkanPipeline>>,
|
pipelines: CopyHashMap<TexPipelineKey, Rc<VulkanPipeline>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub(super) struct OutPipelineKey {
|
pub(super) struct OutPipelineKey {
|
||||||
format: vk::Format,
|
format: vk::Format,
|
||||||
eotf: Eotf,
|
eotf: VulkanEotf,
|
||||||
|
has_color_management_data: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanDevice {
|
impl VulkanDevice {
|
||||||
|
|
@ -351,7 +363,9 @@ impl VulkanDevice {
|
||||||
};
|
};
|
||||||
let uniform_buffer_cache = {
|
let uniform_buffer_cache = {
|
||||||
let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::UNIFORM_BUFFER;
|
let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::UNIFORM_BUFFER;
|
||||||
let align = align_of::<TexColorManagementData>() as DeviceSize;
|
let align = align_of::<ColorManagementData>()
|
||||||
|
.max(align_of::<EotfArgs>())
|
||||||
|
.max(align_of::<InvEotfArgs>()) as DeviceSize;
|
||||||
VulkanBufferCache::new(self, &allocator, usage, align)
|
VulkanBufferCache::new(self, &allocator, usage, align)
|
||||||
};
|
};
|
||||||
let render = Rc::new(VulkanRenderer {
|
let render = Rc::new(VulkanRenderer {
|
||||||
|
|
@ -436,13 +450,14 @@ impl VulkanRenderer {
|
||||||
format: vk::Format,
|
format: vk::Format,
|
||||||
target_cd: &ColorDescription,
|
target_cd: &ColorDescription,
|
||||||
) -> Rc<TexPipelines> {
|
) -> Rc<TexPipelines> {
|
||||||
let pipelines = &self.tex_pipelines[target_cd.eotf];
|
let eotf = target_cd.eotf.to_vulkan();
|
||||||
|
let pipelines = &self.tex_pipelines[eotf];
|
||||||
match pipelines.get(&format) {
|
match pipelines.get(&format) {
|
||||||
Some(pl) => pl,
|
Some(pl) => pl,
|
||||||
_ => {
|
_ => {
|
||||||
let pl = Rc::new(TexPipelines {
|
let pl = Rc::new(TexPipelines {
|
||||||
format,
|
format,
|
||||||
eotf: target_cd.eotf,
|
eotf,
|
||||||
pipelines: Default::default(),
|
pipelines: Default::default(),
|
||||||
});
|
});
|
||||||
pipelines.set(format, pl.clone());
|
pipelines.set(format, pl.clone());
|
||||||
|
|
@ -462,7 +477,7 @@ impl VulkanRenderer {
|
||||||
let key = TexPipelineKey {
|
let key = TexPipelineKey {
|
||||||
tex_copy_type,
|
tex_copy_type,
|
||||||
tex_source_type,
|
tex_source_type,
|
||||||
eotf: tex_cd.eotf,
|
eotf: tex_cd.eotf.to_vulkan(),
|
||||||
has_color_management_data,
|
has_color_management_data,
|
||||||
};
|
};
|
||||||
if let Some(pl) = pipelines.pipelines.get(&key) {
|
if let Some(pl) = pipelines.pipelines.get(&key) {
|
||||||
|
|
@ -507,9 +522,11 @@ impl VulkanRenderer {
|
||||||
) -> Result<Rc<VulkanPipeline>, VulkanError> {
|
) -> Result<Rc<VulkanPipeline>, VulkanError> {
|
||||||
let key = OutPipelineKey {
|
let key = OutPipelineKey {
|
||||||
format,
|
format,
|
||||||
eotf: bb_cd.eotf,
|
eotf: bb_cd.eotf.to_vulkan(),
|
||||||
|
has_color_management_data,
|
||||||
};
|
};
|
||||||
let pipelines = &self.out_pipelines[fb_cd.eotf];
|
let fb_eotf = fb_cd.eotf.to_vulkan();
|
||||||
|
let pipelines = &self.out_pipelines[fb_eotf];
|
||||||
if let Some(pl) = pipelines.get(&key) {
|
if let Some(pl) = pipelines.get(&key) {
|
||||||
return Ok(pl);
|
return Ok(pl);
|
||||||
}
|
}
|
||||||
|
|
@ -525,7 +542,7 @@ impl VulkanRenderer {
|
||||||
src_has_alpha: true,
|
src_has_alpha: true,
|
||||||
has_alpha_mult: false,
|
has_alpha_mult: false,
|
||||||
eotf: key.eotf.to_vulkan(),
|
eotf: key.eotf.to_vulkan(),
|
||||||
inv_eotf: fb_cd.eotf.to_vulkan(),
|
inv_eotf: fb_eotf.to_vulkan(),
|
||||||
descriptor_set_layouts,
|
descriptor_set_layouts,
|
||||||
has_color_management_data,
|
has_color_management_data,
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -558,33 +575,73 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
let resource_writer = &mut memory.resource_descriptor_buffer_writer;
|
let resource_writer = &mut memory.resource_descriptor_buffer_writer;
|
||||||
resource_writer.clear();
|
resource_writer.clear();
|
||||||
let uniform_buffer_descriptor_cache = memory
|
macro_rules! ub_descriptor_cache {
|
||||||
.uniform_buffer_descriptor_cache
|
($field:ident) => {
|
||||||
.get_or_insert_with(|| {
|
memory.$field.get_or_insert_with(|| {
|
||||||
vec![0u8; self.device.uniform_buffer_descriptor_size].into_boxed_slice()
|
vec![0u8; self.device.uniform_buffer_descriptor_size].into_boxed_slice()
|
||||||
});
|
})
|
||||||
if let Some(bb) = bb {
|
};
|
||||||
let layout = self.out_descriptor_set_layout.as_ref().unwrap();
|
}
|
||||||
memory.blend_buffer_descriptor_buffer_offset = resource_writer.next_offset();
|
let uniform_buffer_descriptor_cache = ub_descriptor_cache!(uniform_buffer_descriptor_cache);
|
||||||
let mut writer = resource_writer.add_set(layout);
|
macro_rules! get_ub_descriptor {
|
||||||
writer.write(layout.offsets[0], &bb.sampled_image_descriptor);
|
($addr:expr, $ty:ty, $descriptor:expr $(,)?) => {{
|
||||||
if let Some(addr) = memory.blend_buffer_color_management_data_address {
|
|
||||||
let uniform_buffer = DescriptorAddressInfoEXT::default()
|
let uniform_buffer = DescriptorAddressInfoEXT::default()
|
||||||
.address(addr)
|
.address($addr)
|
||||||
.range(size_of::<OutColorManagementData>() as _);
|
.range(size_of::<$ty>() as _);
|
||||||
let info = DescriptorGetInfoEXT::default()
|
let info = DescriptorGetInfoEXT::default()
|
||||||
.ty(DescriptorType::UNIFORM_BUFFER)
|
.ty(DescriptorType::UNIFORM_BUFFER)
|
||||||
.data(DescriptorDataEXT {
|
.data(DescriptorDataEXT {
|
||||||
p_uniform_buffer: &uniform_buffer,
|
p_uniform_buffer: &uniform_buffer,
|
||||||
});
|
});
|
||||||
unsafe {
|
unsafe {
|
||||||
db.get_descriptor(&info, uniform_buffer_descriptor_cache);
|
db.get_descriptor(&info, $descriptor);
|
||||||
}
|
}
|
||||||
writer.write(layout.offsets[1], uniform_buffer_descriptor_cache);
|
&*$descriptor
|
||||||
|
}};
|
||||||
|
($addr:expr, $ty:ty $(,)?) => {
|
||||||
|
get_ub_descriptor!($addr, $ty, uniform_buffer_descriptor_cache)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let mut blend_buffer_inv_eotf_args_descriptor = None;
|
||||||
|
if let Some(addr) = memory.blend_buffer_inv_eotf_args_address {
|
||||||
|
blend_buffer_inv_eotf_args_descriptor = Some(get_ub_descriptor!(
|
||||||
|
addr,
|
||||||
|
InvEotfArgs,
|
||||||
|
ub_descriptor_cache!(blend_buffer_inv_eotf_args_descriptor),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut fb_inv_eotf_args_descriptor = None;
|
||||||
|
if let Some(addr) = memory.fb_inv_eotf_args_address {
|
||||||
|
fb_inv_eotf_args_descriptor = Some(get_ub_descriptor!(
|
||||||
|
addr,
|
||||||
|
InvEotfArgs,
|
||||||
|
ub_descriptor_cache!(fb_inv_eotf_args_descriptor),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(bb) = bb {
|
||||||
|
let layout = self.out_descriptor_set_layout.as_ref().unwrap();
|
||||||
|
memory.blend_buffer_descriptor_buffer_offset = resource_writer.next_offset();
|
||||||
|
let mut writer = resource_writer.add_set(layout);
|
||||||
|
writer.write(layout.offsets[0], &bb.sampled_image_descriptor);
|
||||||
|
if let Some(addr) = memory.blend_buffer_color_management_data_address {
|
||||||
|
writer.write(
|
||||||
|
layout.offsets[1],
|
||||||
|
get_ub_descriptor!(addr, ColorManagementData),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(addr) = memory.blend_buffer_eotf_args_address {
|
||||||
|
writer.write(layout.offsets[2], get_ub_descriptor!(addr, EotfArgs));
|
||||||
|
}
|
||||||
|
if let Some(desc) = fb_inv_eotf_args_descriptor {
|
||||||
|
writer.write(layout.offsets[3], desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let tex_descriptor_set_layout = &self.tex_descriptor_set_layouts[1];
|
let tex_descriptor_set_layout = &self.tex_descriptor_set_layouts[1];
|
||||||
for pass in RenderPass::variants() {
|
for pass in RenderPass::variants() {
|
||||||
|
let inv_eotf_desc = match pass {
|
||||||
|
RenderPass::BlendBuffer => blend_buffer_inv_eotf_args_descriptor,
|
||||||
|
RenderPass::FrameBuffer => fb_inv_eotf_args_descriptor,
|
||||||
|
};
|
||||||
for cmd in &mut memory.ops[pass] {
|
for cmd in &mut memory.ops[pass] {
|
||||||
let VulkanOp::Tex(c) = cmd else {
|
let VulkanOp::Tex(c) = cmd else {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -597,22 +654,20 @@ impl VulkanRenderer {
|
||||||
&tex.sampled_image_descriptor,
|
&tex.sampled_image_descriptor,
|
||||||
);
|
);
|
||||||
if let Some(addr) = c.color_management_data_address {
|
if let Some(addr) = c.color_management_data_address {
|
||||||
let uniform_buffer = DescriptorAddressInfoEXT::default()
|
|
||||||
.address(addr)
|
|
||||||
.range(size_of::<TexColorManagementData>() as _);
|
|
||||||
let info = DescriptorGetInfoEXT::default()
|
|
||||||
.ty(DescriptorType::UNIFORM_BUFFER)
|
|
||||||
.data(DescriptorDataEXT {
|
|
||||||
p_uniform_buffer: &uniform_buffer,
|
|
||||||
});
|
|
||||||
unsafe {
|
|
||||||
db.get_descriptor(&info, uniform_buffer_descriptor_cache);
|
|
||||||
}
|
|
||||||
writer.write(
|
writer.write(
|
||||||
tex_descriptor_set_layout.offsets[1],
|
tex_descriptor_set_layout.offsets[1],
|
||||||
uniform_buffer_descriptor_cache,
|
get_ub_descriptor!(addr, ColorManagementData),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if let Some(addr) = c.eotf_args_address {
|
||||||
|
writer.write(
|
||||||
|
tex_descriptor_set_layout.offsets[2],
|
||||||
|
get_ub_descriptor!(addr, EotfArgs),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(desc) = inv_eotf_desc {
|
||||||
|
writer.write(tex_descriptor_set_layout.offsets[3], desc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut infos = ArrayVec::<_, 2>::new();
|
let mut infos = ArrayVec::<_, 2>::new();
|
||||||
|
|
@ -655,6 +710,7 @@ impl VulkanRenderer {
|
||||||
memory.data_buffer.clear();
|
memory.data_buffer.clear();
|
||||||
memory.uniform_buffer_writer.clear();
|
memory.uniform_buffer_writer.clear();
|
||||||
memory.color_transforms.map.clear();
|
memory.color_transforms.map.clear();
|
||||||
|
memory.eotf_args_cache.map.clear();
|
||||||
let sync = |memory: &mut Memory| {
|
let sync = |memory: &mut Memory| {
|
||||||
for pass in RenderPass::variants() {
|
for pass in RenderPass::variants() {
|
||||||
let ops = &mut memory.ops_tmp[pass];
|
let ops = &mut memory.ops_tmp[pass];
|
||||||
|
|
@ -809,6 +865,12 @@ impl VulkanRenderer {
|
||||||
self.device.uniform_buffer_offset_mask,
|
self.device.uniform_buffer_offset_mask,
|
||||||
&mut memory.uniform_buffer_writer,
|
&mut memory.uniform_buffer_writer,
|
||||||
);
|
);
|
||||||
|
let eotf_args_address = memory.eotf_args_cache.get_offset(
|
||||||
|
&ct.cd,
|
||||||
|
false,
|
||||||
|
self.device.uniform_buffer_offset_mask,
|
||||||
|
&mut memory.uniform_buffer_writer,
|
||||||
|
);
|
||||||
ops.push(VulkanOp::Tex(VulkanTexOp {
|
ops.push(VulkanOp::Tex(VulkanTexOp {
|
||||||
tex: tex.clone(),
|
tex: tex.clone(),
|
||||||
range: lo..hi,
|
range: lo..hi,
|
||||||
|
|
@ -822,6 +884,7 @@ impl VulkanRenderer {
|
||||||
instances: 0,
|
instances: 0,
|
||||||
tex_cd: ct.cd.clone(),
|
tex_cd: ct.cd.clone(),
|
||||||
color_management_data_address,
|
color_management_data_address,
|
||||||
|
eotf_args_address,
|
||||||
resource_descriptor_buffer_offset: 0,
|
resource_descriptor_buffer_offset: 0,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -832,21 +895,41 @@ impl VulkanRenderer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_blend_cm_data(
|
fn create_fixed_cm_data(
|
||||||
&self,
|
&self,
|
||||||
bb: Option<&VulkanImage>,
|
bb: Option<&VulkanImage>,
|
||||||
bb_cd: &ColorDescription,
|
bb_cd: &ColorDescription,
|
||||||
fb_cd: &ColorDescription,
|
fb_cd: &ColorDescription,
|
||||||
) {
|
) {
|
||||||
zone!("create_blend_cm_data");
|
zone!("create_fixed_cm_data");
|
||||||
let memory = &mut *self.memory.borrow_mut();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
memory.blend_buffer_color_management_data_address = None;
|
memory.blend_buffer_color_management_data_address = None;
|
||||||
if bb.is_none() {
|
memory.blend_buffer_eotf_args_address = None;
|
||||||
return;
|
memory.blend_buffer_inv_eotf_args_address = None;
|
||||||
|
memory.fb_inv_eotf_args_address = None;
|
||||||
|
if bb.is_some() {
|
||||||
|
memory.blend_buffer_color_management_data_address = memory.color_transforms.get_offset(
|
||||||
|
&bb_cd.linear,
|
||||||
|
fb_cd,
|
||||||
|
self.device.uniform_buffer_offset_mask,
|
||||||
|
&mut memory.uniform_buffer_writer,
|
||||||
|
);
|
||||||
|
memory.blend_buffer_eotf_args_address = memory.eotf_args_cache.get_offset(
|
||||||
|
bb_cd,
|
||||||
|
false,
|
||||||
|
self.device.uniform_buffer_offset_mask,
|
||||||
|
&mut memory.uniform_buffer_writer,
|
||||||
|
);
|
||||||
|
memory.blend_buffer_inv_eotf_args_address = memory.eotf_args_cache.get_offset(
|
||||||
|
bb_cd,
|
||||||
|
true,
|
||||||
|
self.device.uniform_buffer_offset_mask,
|
||||||
|
&mut memory.uniform_buffer_writer,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
memory.blend_buffer_color_management_data_address = memory.color_transforms.get_offset(
|
memory.fb_inv_eotf_args_address = memory.eotf_args_cache.get_offset(
|
||||||
&bb_cd.linear,
|
|
||||||
fb_cd,
|
fb_cd,
|
||||||
|
true,
|
||||||
self.device.uniform_buffer_offset_mask,
|
self.device.uniform_buffer_offset_mask,
|
||||||
&mut memory.uniform_buffer_writer,
|
&mut memory.uniform_buffer_writer,
|
||||||
);
|
);
|
||||||
|
|
@ -908,18 +991,25 @@ impl VulkanRenderer {
|
||||||
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||||
ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());
|
ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());
|
||||||
})?;
|
})?;
|
||||||
|
macro_rules! adj {
|
||||||
|
($expr:expr) => {
|
||||||
|
if let Some(addr) = $expr {
|
||||||
|
*addr += buffer.buffer.address;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
for ops in memory.ops.values_mut() {
|
for ops in memory.ops.values_mut() {
|
||||||
for op in ops {
|
for op in ops {
|
||||||
if let VulkanOp::Tex(c) = op
|
if let VulkanOp::Tex(c) = op {
|
||||||
&& let Some(addr) = &mut c.color_management_data_address
|
adj!(&mut c.color_management_data_address);
|
||||||
{
|
adj!(&mut c.eotf_args_address);
|
||||||
*addr += buffer.buffer.address;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(addr) = &mut memory.blend_buffer_color_management_data_address {
|
adj!(&mut memory.blend_buffer_color_management_data_address);
|
||||||
*addr += buffer.buffer.address;
|
adj!(&mut memory.blend_buffer_eotf_args_address);
|
||||||
}
|
adj!(&mut memory.blend_buffer_inv_eotf_args_address);
|
||||||
|
adj!(&mut memory.fb_inv_eotf_args_address);
|
||||||
memory.used_buffers.push(buffer);
|
memory.used_buffers.push(buffer);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -1938,7 +2028,7 @@ impl VulkanRenderer {
|
||||||
let bb = blend_buffer.as_deref();
|
let bb = blend_buffer.as_deref();
|
||||||
let buf = self.gfx_command_buffers.allocate()?;
|
let buf = self.gfx_command_buffers.allocate()?;
|
||||||
self.convert_ops(opts, bb_cd, fb_cd)?;
|
self.convert_ops(opts, bb_cd, fb_cd)?;
|
||||||
self.create_blend_cm_data(bb, bb_cd, fb_cd);
|
self.create_fixed_cm_data(bb, bb_cd, fb_cd);
|
||||||
self.create_data_buffer()?;
|
self.create_data_buffer()?;
|
||||||
self.create_uniform_buffer()?;
|
self.create_uniform_buffer()?;
|
||||||
self.collect_memory();
|
self.collect_memory();
|
||||||
|
|
@ -2229,7 +2319,7 @@ impl ColorTransforms {
|
||||||
) -> Option<DeviceSize> {
|
) -> Option<DeviceSize> {
|
||||||
let ct = self.get_or_create(src, dst)?;
|
let ct = self.get_or_create(src, dst)?;
|
||||||
if ct.offset.is_none() {
|
if ct.offset.is_none() {
|
||||||
let data = TexColorManagementData {
|
let data = ColorManagementData {
|
||||||
matrix: ct.matrix.to_f32(),
|
matrix: ct.matrix.to_f32(),
|
||||||
};
|
};
|
||||||
let offset = writer.write(uniform_buffer_offset_mask, &data);
|
let offset = writer.write(uniform_buffer_offset_mask, &data);
|
||||||
|
|
@ -2238,3 +2328,73 @@ impl ColorTransforms {
|
||||||
ct.offset
|
ct.offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct EotfArgsCache {
|
||||||
|
map: AHashMap<(EotfCacheKey, bool), EotfArg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
|
enum EotfCacheKey {
|
||||||
|
Pow(EotfPow),
|
||||||
|
Bt1886(F32),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EotfArg {
|
||||||
|
offset: DeviceSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EotfArgsCache {
|
||||||
|
fn get_offset(
|
||||||
|
&mut self,
|
||||||
|
desc: &ColorDescription,
|
||||||
|
inv: bool,
|
||||||
|
uniform_buffer_offset_mask: DeviceSize,
|
||||||
|
writer: &mut GenericBufferWriter,
|
||||||
|
) -> Option<DeviceSize> {
|
||||||
|
let key = match desc.eotf {
|
||||||
|
Eotf::Bt1886(c) => EotfCacheKey::Bt1886(c),
|
||||||
|
Eotf::Pow(pow) => EotfCacheKey::Pow(pow),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let ct = match self.map.entry((key, inv)) {
|
||||||
|
Entry::Occupied(o) => o.into_mut(),
|
||||||
|
Entry::Vacant(e) => {
|
||||||
|
#[expect(unused_assignments)]
|
||||||
|
let [mut arg1, mut arg2, mut arg3, mut arg4] = [0.0; 4];
|
||||||
|
if inv {
|
||||||
|
match key {
|
||||||
|
EotfCacheKey::Pow(pow) => arg1 = pow.inv_eotf_f32(),
|
||||||
|
EotfCacheKey::Bt1886(c) => {
|
||||||
|
[arg1, arg2, arg3, arg4] = bt1886_inv_eotf_args(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let data = InvEotfArgs {
|
||||||
|
arg1,
|
||||||
|
arg2,
|
||||||
|
arg3,
|
||||||
|
arg4,
|
||||||
|
};
|
||||||
|
let offset = writer.write(uniform_buffer_offset_mask, &data);
|
||||||
|
e.insert(EotfArg { offset })
|
||||||
|
} else {
|
||||||
|
match key {
|
||||||
|
EotfCacheKey::Pow(pow) => arg1 = pow.eotf_f32(),
|
||||||
|
EotfCacheKey::Bt1886(c) => {
|
||||||
|
[arg1, arg2, arg3, arg4] = bt1886_eotf_args(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let data = EotfArgs {
|
||||||
|
arg1,
|
||||||
|
arg2,
|
||||||
|
arg3,
|
||||||
|
arg4,
|
||||||
|
};
|
||||||
|
let offset = writer.write(uniform_buffer_offset_mask, &data);
|
||||||
|
e.insert(EotfArg { offset })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some(ct.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,19 +63,33 @@ unsafe impl Packed for TexPushConstants {}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, align(16))]
|
#[repr(C, align(16))]
|
||||||
pub struct TexColorManagementData {
|
pub struct ColorManagementData {
|
||||||
pub matrix: [[f32; 4]; 4],
|
pub matrix: [[f32; 4]; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Packed for TexColorManagementData {}
|
unsafe impl Packed for ColorManagementData {}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C, align(16))]
|
#[repr(C, align(16))]
|
||||||
pub struct OutColorManagementData {
|
pub struct EotfArgs {
|
||||||
pub matrix: [[f32; 4]; 4],
|
pub arg1: f32,
|
||||||
|
pub arg2: f32,
|
||||||
|
pub arg3: f32,
|
||||||
|
pub arg4: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Packed for OutColorManagementData {}
|
unsafe impl Packed for EotfArgs {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[repr(C, align(16))]
|
||||||
|
pub struct InvEotfArgs {
|
||||||
|
pub arg1: f32,
|
||||||
|
pub arg2: f32,
|
||||||
|
pub arg3: f32,
|
||||||
|
pub arg4: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Packed for InvEotfArgs {}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,37 @@
|
||||||
#define EOTFS_GLSL
|
#define EOTFS_GLSL
|
||||||
|
|
||||||
#include "frag_spec_const.glsl"
|
#include "frag_spec_const.glsl"
|
||||||
|
#include "tex_set.glsl"
|
||||||
|
|
||||||
#define TF_LINEAR 1
|
#define TF_LINEAR 1
|
||||||
#define TF_ST2084_PQ 2
|
#define TF_ST2084_PQ 2
|
||||||
#define TF_GAMMA24 3
|
#define TF_BT1886 3
|
||||||
#define TF_GAMMA22 4
|
#define TF_GAMMA22 4
|
||||||
#define TF_GAMMA28 5
|
#define TF_GAMMA28 5
|
||||||
#define TF_ST240 6
|
#define TF_ST240 6
|
||||||
#define TF_LOG100 8
|
#define TF_LOG100 8
|
||||||
#define TF_LOG316 9
|
#define TF_LOG316 9
|
||||||
#define TF_ST428 10
|
#define TF_ST428 10
|
||||||
|
#define TF_POW 11
|
||||||
|
#define TF_GAMMA24 12
|
||||||
|
|
||||||
|
vec3 eotf_bt1886(vec3 c) {
|
||||||
|
c = clamp(c, 0.0, 1.0);
|
||||||
|
float a1 = cm_eotf_args.arg1;
|
||||||
|
float a2 = cm_eotf_args.arg2;
|
||||||
|
float a3 = cm_eotf_args.arg3;
|
||||||
|
float a4 = cm_eotf_args.arg4;
|
||||||
|
return a1 * (pow(a2 * c + a3, vec3(2.4)) - a4);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 inv_eotf_bt1886(vec3 c) {
|
||||||
|
c = clamp(c, 0.0, 1.0);
|
||||||
|
float a1 = cm_inv_eotf_args.arg1;
|
||||||
|
float a2 = cm_inv_eotf_args.arg2;
|
||||||
|
float a3 = cm_inv_eotf_args.arg3;
|
||||||
|
float a4 = cm_inv_eotf_args.arg4;
|
||||||
|
return a1 * (pow(a2 * c + a3, vec3(1.0 / 2.4)) - a4);
|
||||||
|
}
|
||||||
|
|
||||||
vec3 eotf_st2084_pq(vec3 c) {
|
vec3 eotf_st2084_pq(vec3 c) {
|
||||||
c = clamp(c, 0.0, 1.0);
|
c = clamp(c, 0.0, 1.0);
|
||||||
|
|
@ -84,13 +105,15 @@ vec3 apply_eotf(vec3 c) {
|
||||||
switch (eotf) {
|
switch (eotf) {
|
||||||
case TF_LINEAR: return c;
|
case TF_LINEAR: return c;
|
||||||
case TF_ST2084_PQ: return eotf_st2084_pq(c);
|
case TF_ST2084_PQ: return eotf_st2084_pq(c);
|
||||||
case TF_GAMMA24: return sign(c) * pow(abs(c), vec3(2.4));
|
case TF_BT1886: return eotf_bt1886(c);
|
||||||
case TF_GAMMA22: return sign(c) * pow(abs(c), vec3(2.2));
|
case TF_GAMMA22: return sign(c) * pow(abs(c), vec3(2.2));
|
||||||
|
case TF_GAMMA24: return sign(c) * pow(abs(c), vec3(2.4));
|
||||||
case TF_GAMMA28: return sign(c) * pow(abs(c), vec3(2.8));
|
case TF_GAMMA28: return sign(c) * pow(abs(c), vec3(2.8));
|
||||||
case TF_ST240: return eotf_st240(c);
|
case TF_ST240: return eotf_st240(c);
|
||||||
case TF_LOG100: return eotf_log100(c);
|
case TF_LOG100: return eotf_log100(c);
|
||||||
case TF_LOG316: return eotf_log316(c);
|
case TF_LOG316: return eotf_log316(c);
|
||||||
case TF_ST428: return eotf_st428(c);
|
case TF_ST428: return eotf_st428(c);
|
||||||
|
case TF_POW: return sign(c) * pow(abs(c), vec3(cm_eotf_args.arg1));
|
||||||
default: return c;
|
default: return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,13 +122,15 @@ vec3 apply_inv_eotf(vec3 c) {
|
||||||
switch (inv_eotf) {
|
switch (inv_eotf) {
|
||||||
case TF_LINEAR: return c;
|
case TF_LINEAR: return c;
|
||||||
case TF_ST2084_PQ: return inv_eotf_st2084_pq(c);
|
case TF_ST2084_PQ: return inv_eotf_st2084_pq(c);
|
||||||
case TF_GAMMA24: return sign(c) * pow(abs(c), vec3(1.0 / 2.4));
|
case TF_BT1886: return inv_eotf_bt1886(c);
|
||||||
case TF_GAMMA22: return sign(c) * pow(abs(c), vec3(1.0 / 2.2));
|
case TF_GAMMA22: return sign(c) * pow(abs(c), vec3(1.0 / 2.2));
|
||||||
|
case TF_GAMMA24: return sign(c) * pow(abs(c), vec3(1.0 / 2.4));
|
||||||
case TF_GAMMA28: return sign(c) * pow(abs(c), vec3(1.0 / 2.8));
|
case TF_GAMMA28: return sign(c) * pow(abs(c), vec3(1.0 / 2.8));
|
||||||
case TF_ST240: return inv_eotf_st240(c);
|
case TF_ST240: return inv_eotf_st240(c);
|
||||||
case TF_LOG100: return inv_eotf_log100(c);
|
case TF_LOG100: return inv_eotf_log100(c);
|
||||||
case TF_LOG316: return inv_eotf_log316(c);
|
case TF_LOG316: return inv_eotf_log316(c);
|
||||||
case TF_ST428: return inv_eotf_st428(c);
|
case TF_ST428: return inv_eotf_st428(c);
|
||||||
|
case TF_POW: return sign(c) * pow(abs(c), vec3(cm_inv_eotf_args.arg1));
|
||||||
default: return c;
|
default: return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,17 @@
|
||||||
#extension GL_EXT_samplerless_texture_functions : require
|
#extension GL_EXT_samplerless_texture_functions : require
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
#extension GL_EXT_scalar_block_layout : require
|
||||||
|
|
||||||
#include "frag_spec_const.glsl"
|
#define TEX_SET 0
|
||||||
#include "eotfs.glsl"
|
|
||||||
#include "out.common.glsl"
|
#include "frag_spec_const.glsl"
|
||||||
|
#include "out.common.glsl"
|
||||||
|
#include "tex_set.glsl"
|
||||||
|
#include "eotfs.glsl"
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform texture2D in_color;
|
|
||||||
layout(set = 0, binding = 1, row_major, std430) uniform ColorManagementData {
|
|
||||||
mat4x4 matrix;
|
|
||||||
} cm_data;
|
|
||||||
layout(location = 0) out vec4 out_color;
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 c = texelFetch(in_color, ivec2(gl_FragCoord.xy), 0);
|
vec4 c = texelFetch(tex, ivec2(gl_FragCoord.xy), 0);
|
||||||
if (eotf != inv_eotf || has_matrix) {
|
if (eotf != inv_eotf || has_matrix) {
|
||||||
vec3 rgb = c.rgb;
|
vec3 rgb = c.rgb;
|
||||||
rgb /= mix(c.a, 1.0, c.a == 0.0);
|
rgb /= mix(c.a, 1.0, c.a == 0.0);
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@
|
||||||
|
|
||||||
#extension GL_EXT_scalar_block_layout : require
|
#extension GL_EXT_scalar_block_layout : require
|
||||||
|
|
||||||
|
#define TEX_SET 1
|
||||||
|
|
||||||
#include "frag_spec_const.glsl"
|
#include "frag_spec_const.glsl"
|
||||||
#include "eotfs.glsl"
|
|
||||||
#include "tex.common.glsl"
|
#include "tex.common.glsl"
|
||||||
|
#include "tex_set.glsl"
|
||||||
|
#include "eotfs.glsl"
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform sampler sam;
|
layout(set = 0, binding = 0) uniform sampler sam;
|
||||||
layout(set = 1, binding = 0) uniform texture2D tex;
|
|
||||||
layout(set = 1, binding = 1, row_major, std430) uniform ColorManagementData {
|
|
||||||
mat4x4 matrix;
|
|
||||||
} cm_data;
|
|
||||||
layout(location = 0) in vec2 tex_pos;
|
layout(location = 0) in vec2 tex_pos;
|
||||||
layout(location = 0) out vec4 out_color;
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
|
|
|
||||||
21
src/gfx_apis/vulkan/shaders/tex_set.glsl
Normal file
21
src/gfx_apis/vulkan/shaders/tex_set.glsl
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef TEX_SET_GLSL
|
||||||
|
#define TEX_SET_GLSL
|
||||||
|
|
||||||
|
layout(set = TEX_SET, binding = 0) uniform texture2D tex;
|
||||||
|
layout(set = TEX_SET, binding = 1, row_major, std430) uniform ColorManagementData {
|
||||||
|
mat4x4 matrix;
|
||||||
|
} cm_data;
|
||||||
|
layout(set = TEX_SET, binding = 2, row_major, std430) uniform EotfArgs {
|
||||||
|
float arg1;
|
||||||
|
float arg2;
|
||||||
|
float arg3;
|
||||||
|
float arg4;
|
||||||
|
} cm_eotf_args;
|
||||||
|
layout(set = TEX_SET, binding = 3, row_major, std430) uniform InvEotfArgs {
|
||||||
|
float arg1;
|
||||||
|
float arg2;
|
||||||
|
float arg3;
|
||||||
|
float arg4;
|
||||||
|
} cm_inv_eotf_args;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -5,6 +5,7 @@ use {
|
||||||
ifs::{
|
ifs::{
|
||||||
color_management::{
|
color_management::{
|
||||||
FEATURE_EXTENDED_TARGET_VOLUME, FEATURE_SET_MASTERING_DISPLAY_PRIMARIES,
|
FEATURE_EXTENDED_TARGET_VOLUME, FEATURE_SET_MASTERING_DISPLAY_PRIMARIES,
|
||||||
|
FEATURE_SET_TF_POWER,
|
||||||
consts::{
|
consts::{
|
||||||
FEATURE_PARAMETRIC, FEATURE_SET_LUMINANCES, FEATURE_SET_PRIMARIES,
|
FEATURE_PARAMETRIC, FEATURE_SET_LUMINANCES, FEATURE_SET_PRIMARIES,
|
||||||
FEATURE_WINDOWS_SCRGB, PRIMARIES_ADOBE_RGB, PRIMARIES_BT2020,
|
FEATURE_WINDOWS_SCRGB, PRIMARIES_ADOBE_RGB, PRIMARIES_BT2020,
|
||||||
|
|
@ -79,6 +80,7 @@ impl WpColorManagerV1 {
|
||||||
self.send_supported_feature(FEATURE_SET_PRIMARIES);
|
self.send_supported_feature(FEATURE_SET_PRIMARIES);
|
||||||
self.send_supported_feature(FEATURE_SET_LUMINANCES);
|
self.send_supported_feature(FEATURE_SET_LUMINANCES);
|
||||||
self.send_supported_feature(FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
|
self.send_supported_feature(FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
|
||||||
|
self.send_supported_feature(FEATURE_SET_TF_POWER);
|
||||||
self.send_supported_feature(FEATURE_EXTENDED_TARGET_VOLUME);
|
self.send_supported_feature(FEATURE_EXTENDED_TARGET_VOLUME);
|
||||||
self.send_supported_feature(FEATURE_WINDOWS_SCRGB);
|
self.send_supported_feature(FEATURE_WINDOWS_SCRGB);
|
||||||
self.send_supported_tf_named(TRANSFER_FUNCTION_BT1886);
|
self.send_supported_tf_named(TRANSFER_FUNCTION_BT1886);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
cmm::{
|
cmm::{
|
||||||
cmm_eotf::Eotf,
|
cmm_eotf::{Eotf, EotfPow},
|
||||||
cmm_luminance::{Luminance, TargetLuminance},
|
cmm_luminance::{Luminance, TargetLuminance},
|
||||||
cmm_primaries::{NamedPrimaries, Primaries},
|
cmm_primaries::{NamedPrimaries, Primaries},
|
||||||
},
|
},
|
||||||
|
|
@ -21,7 +21,7 @@ use {
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
utils::ordered_float::F64,
|
utils::ordered_float::{F32, F64},
|
||||||
wire::{
|
wire::{
|
||||||
WpImageDescriptionCreatorParamsV1Id,
|
WpImageDescriptionCreatorParamsV1Id,
|
||||||
wp_image_description_creator_params_v1::{
|
wp_image_description_creator_params_v1::{
|
||||||
|
|
@ -53,14 +53,14 @@ impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreat
|
||||||
type Error = WpImageDescriptionCreatorParamsV1Error;
|
type Error = WpImageDescriptionCreatorParamsV1Error;
|
||||||
|
|
||||||
fn create(&self, req: Create, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn create(&self, req: Create, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
let Some(eotf) = self.tf.get() else {
|
let Some(mut eotf) = self.tf.get() else {
|
||||||
return Err(WpImageDescriptionCreatorParamsV1Error::TfNotSet);
|
return Err(WpImageDescriptionCreatorParamsV1Error::TfNotSet);
|
||||||
};
|
};
|
||||||
let Some((named_primaries, primaries)) = self.primaries.get() else {
|
let Some((named_primaries, primaries)) = self.primaries.get() else {
|
||||||
return Err(WpImageDescriptionCreatorParamsV1Error::PrimariesNotSet);
|
return Err(WpImageDescriptionCreatorParamsV1Error::PrimariesNotSet);
|
||||||
};
|
};
|
||||||
let default_luminance = match eotf {
|
let default_luminance = match eotf {
|
||||||
Eotf::Bt1886 => Luminance::BT1886,
|
Eotf::Bt1886 { .. } => Luminance::BT1886,
|
||||||
Eotf::St2084Pq => Luminance::ST2084_PQ,
|
Eotf::St2084Pq => Luminance::ST2084_PQ,
|
||||||
_ => Luminance::SRGB,
|
_ => Luminance::SRGB,
|
||||||
};
|
};
|
||||||
|
|
@ -71,6 +71,13 @@ impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreat
|
||||||
if luminance.max.0 <= luminance.min.0 || luminance.white.0 <= luminance.min.0 {
|
if luminance.max.0 <= luminance.min.0 || luminance.white.0 <= luminance.min.0 {
|
||||||
return Err(WpImageDescriptionCreatorParamsV1Error::MinLuminanceTooLow);
|
return Err(WpImageDescriptionCreatorParamsV1Error::MinLuminanceTooLow);
|
||||||
}
|
}
|
||||||
|
if let Eotf::Bt1886(c) = &mut eotf {
|
||||||
|
if luminance.min.0 == 0.0 {
|
||||||
|
eotf = Eotf::Gamma24;
|
||||||
|
} else {
|
||||||
|
c.0 = (luminance.min.0 / luminance.max.0) as f32;
|
||||||
|
}
|
||||||
|
}
|
||||||
let target_primaries = self.mastering_primaries.get().unwrap_or(primaries);
|
let target_primaries = self.mastering_primaries.get().unwrap_or(primaries);
|
||||||
let target_luminance = self
|
let target_luminance = self
|
||||||
.mastering_luminance
|
.mastering_luminance
|
||||||
|
|
@ -102,7 +109,7 @@ impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreat
|
||||||
|
|
||||||
fn set_tf_named(&self, req: SetTfNamed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_tf_named(&self, req: SetTfNamed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
let tf = match req.tf {
|
let tf = match req.tf {
|
||||||
TRANSFER_FUNCTION_BT1886 => Eotf::Bt1886,
|
TRANSFER_FUNCTION_BT1886 => Eotf::Bt1886(F32(0.0)),
|
||||||
TRANSFER_FUNCTION_GAMMA22 => Eotf::Gamma22,
|
TRANSFER_FUNCTION_GAMMA22 => Eotf::Gamma22,
|
||||||
TRANSFER_FUNCTION_GAMMA28 => Eotf::Gamma28,
|
TRANSFER_FUNCTION_GAMMA28 => Eotf::Gamma28,
|
||||||
TRANSFER_FUNCTION_ST240 => Eotf::St240,
|
TRANSFER_FUNCTION_ST240 => Eotf::St240,
|
||||||
|
|
@ -125,8 +132,21 @@ impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreat
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tf_power(&self, _req: SetTfPower, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_tf_power(&self, req: SetTfPower, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
Err(WpImageDescriptionCreatorParamsV1Error::SetTfPowerNotSupported)
|
let pow = EotfPow(req.eexp);
|
||||||
|
if pow < EotfPow::MIN || pow > EotfPow::MAX {
|
||||||
|
return Err(WpImageDescriptionCreatorParamsV1Error::SetTfPowerOutOfBounds);
|
||||||
|
}
|
||||||
|
let tf = match pow {
|
||||||
|
EotfPow::LINEAR => Eotf::Linear,
|
||||||
|
EotfPow::GAMMA22 => Eotf::Gamma22,
|
||||||
|
EotfPow::GAMMA28 => Eotf::Gamma28,
|
||||||
|
_ => Eotf::Pow(pow),
|
||||||
|
};
|
||||||
|
if self.tf.replace(Some(tf)).is_some() {
|
||||||
|
return Err(WpImageDescriptionCreatorParamsV1Error::TfAlreadySet);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_primaries_named(
|
fn set_primaries_named(
|
||||||
|
|
@ -259,8 +279,8 @@ pub enum WpImageDescriptionCreatorParamsV1Error {
|
||||||
ClientError(Box<ClientError>),
|
ClientError(Box<ClientError>),
|
||||||
#[error("{} is not a supported named primary", .0)]
|
#[error("{} is not a supported named primary", .0)]
|
||||||
UnsupportedPrimaries(u32),
|
UnsupportedPrimaries(u32),
|
||||||
#[error("set_tf_power is not supported")]
|
#[error("The exponent is out of bounds")]
|
||||||
SetTfPowerNotSupported,
|
SetTfPowerOutOfBounds,
|
||||||
#[error("{} is not a supported named EOTF", .0)]
|
#[error("{} is not a supported named EOTF", .0)]
|
||||||
UnsupportedTf(u32),
|
UnsupportedTf(u32),
|
||||||
#[error("The EOTF has already been set")]
|
#[error("The EOTF has already been set")]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::Client,
|
client::Client,
|
||||||
cmm::{cmm_description::ColorDescription, cmm_eotf::Eotf, cmm_primaries::NamedPrimaries},
|
cmm::{
|
||||||
|
cmm_description::ColorDescription,
|
||||||
|
cmm_eotf::{Eotf, EotfPow},
|
||||||
|
cmm_primaries::NamedPrimaries,
|
||||||
|
},
|
||||||
ifs::color_management::{
|
ifs::color_management::{
|
||||||
MIN_LUM_MUL, PRIMARIES_ADOBE_RGB, PRIMARIES_BT2020, PRIMARIES_CIE1931_XYZ,
|
MIN_LUM_MUL, PRIMARIES_ADOBE_RGB, PRIMARIES_BT2020, PRIMARIES_CIE1931_XYZ,
|
||||||
PRIMARIES_DCI_P3, PRIMARIES_DISPLAY_P3, PRIMARIES_GENERIC_FILM, PRIMARIES_MUL,
|
PRIMARIES_DCI_P3, PRIMARIES_DISPLAY_P3, PRIMARIES_GENERIC_FILM, PRIMARIES_MUL,
|
||||||
|
|
@ -28,17 +32,28 @@ pub struct WpImageDescriptionInfoV1 {
|
||||||
|
|
||||||
impl WpImageDescriptionInfoV1 {
|
impl WpImageDescriptionInfoV1 {
|
||||||
pub fn send_description(&self, d: &ColorDescription) {
|
pub fn send_description(&self, d: &ColorDescription) {
|
||||||
let tf = match d.eotf {
|
'tf: {
|
||||||
Eotf::Linear => TRANSFER_FUNCTION_EXT_LINEAR,
|
let tf = match d.eotf {
|
||||||
Eotf::St2084Pq => TRANSFER_FUNCTION_ST2084_PQ,
|
Eotf::Linear => TRANSFER_FUNCTION_EXT_LINEAR,
|
||||||
Eotf::Bt1886 => TRANSFER_FUNCTION_BT1886,
|
Eotf::St2084Pq => TRANSFER_FUNCTION_ST2084_PQ,
|
||||||
Eotf::Gamma22 => TRANSFER_FUNCTION_GAMMA22,
|
Eotf::Bt1886 { .. } => TRANSFER_FUNCTION_BT1886,
|
||||||
Eotf::Gamma28 => TRANSFER_FUNCTION_GAMMA28,
|
Eotf::Gamma22 => TRANSFER_FUNCTION_GAMMA22,
|
||||||
Eotf::St240 => TRANSFER_FUNCTION_ST240,
|
Eotf::Gamma24 => {
|
||||||
Eotf::Log100 => TRANSFER_FUNCTION_LOG_100,
|
self.send_tf_power(EotfPow::GAMMA24);
|
||||||
Eotf::Log316 => TRANSFER_FUNCTION_LOG_316,
|
break 'tf;
|
||||||
Eotf::St428 => TRANSFER_FUNCTION_ST428,
|
}
|
||||||
};
|
Eotf::Gamma28 => TRANSFER_FUNCTION_GAMMA28,
|
||||||
|
Eotf::St240 => TRANSFER_FUNCTION_ST240,
|
||||||
|
Eotf::Log100 => TRANSFER_FUNCTION_LOG_100,
|
||||||
|
Eotf::Log316 => TRANSFER_FUNCTION_LOG_316,
|
||||||
|
Eotf::St428 => TRANSFER_FUNCTION_ST428,
|
||||||
|
Eotf::Pow(e) => {
|
||||||
|
self.send_tf_power(e);
|
||||||
|
break 'tf;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.send_tf_named(tf);
|
||||||
|
}
|
||||||
self.send_primaries(&d.linear.primaries);
|
self.send_primaries(&d.linear.primaries);
|
||||||
if let Some(n) = d.named_primaries {
|
if let Some(n) = d.named_primaries {
|
||||||
let n = match n {
|
let n = match n {
|
||||||
|
|
@ -55,7 +70,6 @@ impl WpImageDescriptionInfoV1 {
|
||||||
};
|
};
|
||||||
self.send_primaries_named(n);
|
self.send_primaries_named(n);
|
||||||
}
|
}
|
||||||
self.send_tf_named(tf);
|
|
||||||
self.send_luminances(&d.linear.luminance);
|
self.send_luminances(&d.linear.luminance);
|
||||||
self.send_target_primaries(&d.linear.target_primaries);
|
self.send_target_primaries(&d.linear.target_primaries);
|
||||||
self.send_target_luminances(&d.linear.target_luminance);
|
self.send_target_luminances(&d.linear.target_luminance);
|
||||||
|
|
@ -103,11 +117,10 @@ impl WpImageDescriptionInfoV1 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
pub fn send_tf_power(&self, e: EotfPow) {
|
||||||
pub fn send_tf_power(&self, eexp: f64) {
|
|
||||||
self.client.event(TfPower {
|
self.client.event(TfPower {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
eexp: (eexp * 10_000.0) as u32,
|
eexp: e.0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
29
src/theme.rs
29
src/theme.rs
|
|
@ -1,7 +1,10 @@
|
||||||
#![expect(clippy::excessive_precision)]
|
#![expect(clippy::excessive_precision)]
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{cmm::cmm_eotf::Eotf, utils::clonecell::CloneCell},
|
crate::{
|
||||||
|
cmm::cmm_eotf::{Eotf, bt1886_eotf_args, bt1886_inv_eotf_args},
|
||||||
|
utils::clonecell::CloneCell,
|
||||||
|
},
|
||||||
num_traits::Float,
|
num_traits::Float,
|
||||||
std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc},
|
std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc},
|
||||||
};
|
};
|
||||||
|
|
@ -114,13 +117,23 @@ impl Color {
|
||||||
match eotf {
|
match eotf {
|
||||||
Eotf::Linear => convert!(linear),
|
Eotf::Linear => convert!(linear),
|
||||||
Eotf::St2084Pq => convert!(st2084_pq),
|
Eotf::St2084Pq => convert!(st2084_pq),
|
||||||
Eotf::Bt1886 => convert!(gamma24),
|
Eotf::Bt1886(c) => {
|
||||||
|
let [a1, a2, a3, a4] = bt1886_eotf_args(c);
|
||||||
|
let bt1886 = |c: f32| -> f32 { a1 * ((a2 * c + a3).powf(2.4) - a4) };
|
||||||
|
convert!(bt1886)
|
||||||
|
}
|
||||||
Eotf::Gamma22 => convert!(gamma22),
|
Eotf::Gamma22 => convert!(gamma22),
|
||||||
|
Eotf::Gamma24 => convert!(gamma24),
|
||||||
Eotf::Gamma28 => convert!(gamma28),
|
Eotf::Gamma28 => convert!(gamma28),
|
||||||
Eotf::St240 => convert!(st240),
|
Eotf::St240 => convert!(st240),
|
||||||
Eotf::Log100 => convert!(log100),
|
Eotf::Log100 => convert!(log100),
|
||||||
Eotf::Log316 => convert!(log316),
|
Eotf::Log316 => convert!(log316),
|
||||||
Eotf::St428 => convert!(st428),
|
Eotf::St428 => convert!(st428),
|
||||||
|
Eotf::Pow(n) => {
|
||||||
|
let e = n.eotf_f32();
|
||||||
|
let pow = |c: f32| -> f32 { c.signum() * c.abs().powf(e) };
|
||||||
|
convert!(pow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Self { r, g, b, a: 1.0 }
|
Self { r, g, b, a: 1.0 }
|
||||||
}
|
}
|
||||||
|
|
@ -239,13 +252,23 @@ impl Color {
|
||||||
match eotf {
|
match eotf {
|
||||||
Eotf::Linear => convert!(linear),
|
Eotf::Linear => convert!(linear),
|
||||||
Eotf::St2084Pq => convert!(st2084_pq),
|
Eotf::St2084Pq => convert!(st2084_pq),
|
||||||
Eotf::Bt1886 => convert!(gamma24),
|
Eotf::Bt1886(c) => {
|
||||||
|
let [a1, a2, a3, a4] = bt1886_inv_eotf_args(c);
|
||||||
|
let bt1886 = |c: f32| -> f32 { a1 * ((a2 * c + a3).powf(1.0 / 2.4) - a4) };
|
||||||
|
convert!(bt1886)
|
||||||
|
}
|
||||||
Eotf::Gamma22 => convert!(gamma22),
|
Eotf::Gamma22 => convert!(gamma22),
|
||||||
|
Eotf::Gamma24 => convert!(gamma24),
|
||||||
Eotf::Gamma28 => convert!(gamma28),
|
Eotf::Gamma28 => convert!(gamma28),
|
||||||
Eotf::St240 => convert!(st240),
|
Eotf::St240 => convert!(st240),
|
||||||
Eotf::Log100 => convert!(log100),
|
Eotf::Log100 => convert!(log100),
|
||||||
Eotf::Log316 => convert!(log316),
|
Eotf::Log316 => convert!(log316),
|
||||||
Eotf::St428 => convert!(st428),
|
Eotf::St428 => convert!(st428),
|
||||||
|
Eotf::Pow(n) => {
|
||||||
|
let e = n.inv_eotf_f32();
|
||||||
|
let pow = |c: f32| -> f32 { c.signum() * c.abs().powf(e) };
|
||||||
|
convert!(pow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if self.a < 1.0 {
|
if self.a < 1.0 {
|
||||||
for c in &mut res[..3] {
|
for c in &mut res[..3] {
|
||||||
|
|
|
||||||
|
|
@ -4,64 +4,71 @@ use std::{
|
||||||
ops::{Add, Div, Mul, Sub},
|
ops::{Add, Div, Mul, Sub},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
macro_rules! define {
|
||||||
#[repr(transparent)]
|
($big:ident, $little:ty) => {
|
||||||
pub struct F64(pub f64);
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct $big(pub $little);
|
||||||
|
|
||||||
impl Eq for F64 {}
|
impl Eq for $big {}
|
||||||
|
|
||||||
impl PartialEq for F64 {
|
impl PartialEq for $big {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.0.to_bits() == other.0.to_bits()
|
self.0.to_bits() == other.0.to_bits()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for $big {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.to_bits().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Self> for $big {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: $big) -> Self::Output {
|
||||||
|
Self(self.0 + rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Self> for $big {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 - rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Self> for $big {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 * rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Self> for $big {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 / rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for $big {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for $big {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Debug::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for F64 {
|
define!(F64, f64);
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
define!(F32, f32);
|
||||||
self.0.to_bits().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<F64> for F64 {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn add(self, rhs: F64) -> Self::Output {
|
|
||||||
Self(self.0 + rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<F64> for F64 {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn sub(self, rhs: F64) -> Self::Output {
|
|
||||||
Self(self.0 - rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<F64> for F64 {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn mul(self, rhs: F64) -> Self::Output {
|
|
||||||
Self(self.0 * rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<F64> for F64 {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn div(self, rhs: F64) -> Self::Output {
|
|
||||||
Self(self.0 / rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for F64 {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
Display::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for F64 {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
Debug::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue