1
0
Fork 0
forked from wry/wry

vulkan: apply color space transforms to textures

This commit is contained in:
Julian Orth 2025-03-01 19:19:27 +01:00
parent c4d0fdd4bb
commit 8e65de91f9
12 changed files with 233 additions and 66 deletions

View file

@ -157,7 +157,6 @@ impl<T, U> ColorMatrix<T, U> {
Self(m, PhantomData) Self(m, PhantomData)
} }
#[expect(dead_code)]
pub const fn to_f32(&self) -> [[f32; 4]; 4] { pub const fn to_f32(&self) -> [[f32; 4]; 4] {
let m = &self.0; let m = &self.0;
macro_rules! map { macro_rules! map {

View file

@ -106,8 +106,6 @@ impl VulkanRenderer {
ty: VulkanImageMemory::Blend(allocation), ty: VulkanImageMemory::Blend(allocation),
bridge: None, bridge: None,
sampled_image_descriptor: self.sampled_image_descriptor(view), sampled_image_descriptor: self.sampled_image_descriptor(view),
descriptor_buffer_version: Default::default(),
descriptor_buffer_offset: Default::default(),
execution_version: Default::default(), execution_version: Default::default(),
}); });
cached.insert_entry(Rc::downgrade(&img)); cached.insert_entry(Rc::downgrade(&img));

View file

@ -12,7 +12,8 @@ use {
DeviceSize, DeviceSize,
}, },
gpu_alloc::UsageFlags, gpu_alloc::UsageFlags,
std::{cell::RefCell, mem::ManuallyDrop, rc::Rc}, std::{cell::RefCell, mem::ManuallyDrop, ops::Deref, rc::Rc},
uapi::Packed,
}; };
pub struct VulkanBufferCache { pub struct VulkanBufferCache {
@ -20,6 +21,7 @@ pub struct VulkanBufferCache {
allocator: Rc<VulkanAllocator>, allocator: Rc<VulkanAllocator>,
buffers: RefCell<Vec<VulkanBufferUnused>>, buffers: RefCell<Vec<VulkanBufferUnused>>,
usage: BufferUsageFlags, usage: BufferUsageFlags,
min_alignment: DeviceSize,
} }
pub struct VulkanBuffer { pub struct VulkanBuffer {
@ -40,12 +42,14 @@ impl VulkanBufferCache {
device: &Rc<VulkanDevice>, device: &Rc<VulkanDevice>,
allocator: &Rc<VulkanAllocator>, allocator: &Rc<VulkanAllocator>,
usage: BufferUsageFlags, usage: BufferUsageFlags,
min_alignment: DeviceSize,
) -> Rc<Self> { ) -> Rc<Self> {
Rc::new(Self { Rc::new(Self {
device: device.clone(), device: device.clone(),
allocator: allocator.clone(), allocator: allocator.clone(),
buffers: Default::default(), buffers: Default::default(),
usage, usage,
min_alignment,
}) })
} }
@ -55,25 +59,27 @@ impl VulkanBufferCache {
for_sampler: bool, for_sampler: bool,
) -> Rc<Self> { ) -> Rc<Self> {
let mut usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS; let mut usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS;
let mut min_alignment = 1;
if for_sampler { if for_sampler {
usage |= BufferUsageFlags::SAMPLER_DESCRIPTOR_BUFFER_EXT; usage |= BufferUsageFlags::SAMPLER_DESCRIPTOR_BUFFER_EXT;
} else { } else {
usage |= BufferUsageFlags::RESOURCE_DESCRIPTOR_BUFFER_EXT; usage |= BufferUsageFlags::RESOURCE_DESCRIPTOR_BUFFER_EXT;
if device.is_anv {
// https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33903
min_alignment = 4096;
}
} }
Self::new(device, allocator, usage) Self::new(device, allocator, usage, min_alignment)
} }
pub fn usage(&self) -> BufferUsageFlags { pub fn usage(&self) -> BufferUsageFlags {
self.usage self.usage
} }
pub fn allocate( pub fn allocate(self: &Rc<Self>, capacity: DeviceSize) -> Result<VulkanBuffer, VulkanError> {
self: &Rc<Self>,
capacity: DeviceSize,
align: DeviceSize,
) -> Result<VulkanBuffer, VulkanError> {
const MIN_ALLOCATION: DeviceSize = 1024; const MIN_ALLOCATION: DeviceSize = 1024;
let capacity = (capacity.max(MIN_ALLOCATION) + align - 1) & !(align - 1); let align_mask = self.min_alignment - 1;
let capacity = (capacity.max(MIN_ALLOCATION) + align_mask) & !align_mask;
let mut smallest = None; let mut smallest = None;
let mut smallest_size = DeviceSize::MAX; let mut smallest_size = DeviceSize::MAX;
let mut fitting = None; let mut fitting = None;
@ -116,7 +122,7 @@ impl VulkanBufferCache {
let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) }); let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) });
let mut memory_requirements = let mut memory_requirements =
unsafe { self.device.device.get_buffer_memory_requirements(buffer) }; unsafe { self.device.device.get_buffer_memory_requirements(buffer) };
memory_requirements.alignment = memory_requirements.alignment.max(align); memory_requirements.alignment = memory_requirements.alignment.max(self.min_alignment);
let allocation = { let allocation = {
let flags = UsageFlags::UPLOAD let flags = UsageFlags::UPLOAD
| UsageFlags::FAST_DEVICE_ACCESS | UsageFlags::FAST_DEVICE_ACCESS
@ -162,3 +168,31 @@ impl Drop for VulkanBufferUnused {
} }
} }
} }
#[derive(Default)]
pub struct GenericBufferWriter {
buf: Vec<u8>,
}
impl GenericBufferWriter {
pub fn clear(&mut self) {
self.buf.clear();
}
pub fn write(&mut self, offset_mask: DeviceSize, data: &(impl Packed + ?Sized)) -> DeviceSize {
let mut offset = self.buf.len() as DeviceSize;
let mask = offset_mask | (align_of_val(data) as DeviceSize - 1);
offset = (offset + mask) & !mask;
self.buf.resize(offset as usize, 0);
self.buf.extend_from_slice(uapi::as_bytes(data));
offset
}
}
impl Deref for GenericBufferWriter {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.buf
}
}

View file

@ -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, 1>, pub(super) offsets: ArrayVec<DeviceSize, 2>,
pub(super) _sampler: Option<Rc<VulkanSampler>>, pub(super) _sampler: Option<Rc<VulkanSampler>>,
} }
@ -87,12 +87,20 @@ impl VulkanDevice {
pub(super) fn create_tex_resource_descriptor_set_layout( pub(super) fn create_tex_resource_descriptor_set_layout(
self: &Rc<Self>, self: &Rc<Self>,
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> { ) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
let binding = DescriptorSetLayoutBinding::default() let bindings = [
.stage_flags(ShaderStageFlags::FRAGMENT) DescriptorSetLayoutBinding::default()
.descriptor_count(1) .binding(0)
.descriptor_type(DescriptorType::SAMPLED_IMAGE); .stage_flags(ShaderStageFlags::FRAGMENT)
.descriptor_count(1)
.descriptor_type(DescriptorType::SAMPLED_IMAGE),
DescriptorSetLayoutBinding::default()
.binding(1)
.stage_flags(ShaderStageFlags::FRAGMENT)
.descriptor_count(1)
.descriptor_type(DescriptorType::UNIFORM_BUFFER),
];
let create_info = DescriptorSetLayoutCreateInfo::default() let create_info = DescriptorSetLayoutCreateInfo::default()
.bindings(slice::from_ref(&binding)) .bindings(&bindings)
.flags(DescriptorSetLayoutCreateFlags::DESCRIPTOR_BUFFER_EXT); .flags(DescriptorSetLayoutCreateFlags::DESCRIPTOR_BUFFER_EXT);
let layout = unsafe { self.device.create_descriptor_set_layout(&create_info, None) }; let layout = unsafe { self.device.create_descriptor_set_layout(&create_info, None) };
let layout = layout.map_err(VulkanError::CreateDescriptorSetLayout)?; let layout = layout.map_err(VulkanError::CreateDescriptorSetLayout)?;
@ -101,6 +109,7 @@ impl VulkanDevice {
let mut offsets = ArrayVec::new(); let mut offsets = ArrayVec::new();
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));
} }
Ok(Rc::new(VulkanDescriptorSetLayout { Ok(Rc::new(VulkanDescriptorSetLayout {
device: self.clone(), device: self.clone(),

View file

@ -37,8 +37,9 @@ use {
PhysicalDeviceDrmPropertiesEXT, PhysicalDeviceDynamicRenderingFeatures, PhysicalDeviceDrmPropertiesEXT, PhysicalDeviceDynamicRenderingFeatures,
PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceProperties, PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceProperties,
PhysicalDeviceProperties2, PhysicalDeviceSynchronization2Features, PhysicalDeviceProperties2, PhysicalDeviceSynchronization2Features,
PhysicalDeviceTimelineSemaphoreFeatures, PhysicalDeviceVulkan12Properties, Queue, PhysicalDeviceTimelineSemaphoreFeatures,
QueueFlags, PhysicalDeviceUniformBufferStandardLayoutFeatures, PhysicalDeviceVulkan12Properties,
Queue, QueueFlags,
}, },
}, },
isnt::std_1::collections::IsntHashMap2Ext, isnt::std_1::collections::IsntHashMap2Ext,
@ -75,6 +76,8 @@ pub struct VulkanDevice {
pub(super) sampler_descriptor_size: usize, pub(super) sampler_descriptor_size: usize,
pub(super) sampled_image_descriptor_size: usize, pub(super) sampled_image_descriptor_size: usize,
pub(super) is_anv: bool, pub(super) is_anv: bool,
pub(super) uniform_buffer_offset_mask: DeviceSize,
pub(super) uniform_buffer_descriptor_size: usize,
} }
impl Drop for VulkanDevice { impl Drop for VulkanDevice {
@ -308,6 +311,9 @@ impl VulkanInstance {
PhysicalDeviceDescriptorBufferFeaturesEXT::default().descriptor_buffer(true); PhysicalDeviceDescriptorBufferFeaturesEXT::default().descriptor_buffer(true);
let mut buffer_device_address_features = let mut buffer_device_address_features =
PhysicalDeviceBufferDeviceAddressFeatures::default().buffer_device_address(true); PhysicalDeviceBufferDeviceAddressFeatures::default().buffer_device_address(true);
let mut uniform_buffer_standard_layout_features =
PhysicalDeviceUniformBufferStandardLayoutFeatures::default()
.uniform_buffer_standard_layout(true);
let mut queue_create_infos = ArrayVec::<_, 2>::new(); let mut queue_create_infos = ArrayVec::<_, 2>::new();
queue_create_infos.push( queue_create_infos.push(
DeviceQueueCreateInfo::default() DeviceQueueCreateInfo::default()
@ -325,6 +331,7 @@ impl VulkanInstance {
.push_next(&mut semaphore_features) .push_next(&mut semaphore_features)
.push_next(&mut synchronization2_features) .push_next(&mut synchronization2_features)
.push_next(&mut dynamic_rendering_features) .push_next(&mut dynamic_rendering_features)
.push_next(&mut uniform_buffer_standard_layout_features)
.queue_create_infos(&queue_create_infos) .queue_create_infos(&queue_create_infos)
.enabled_extension_names(&enabled_extensions); .enabled_extension_names(&enabled_extensions);
if supports_descriptor_buffer { if supports_descriptor_buffer {
@ -382,6 +389,14 @@ impl VulkanInstance {
let mut descriptor_buffer_offset_mask = 0; let mut descriptor_buffer_offset_mask = 0;
let mut sampler_descriptor_size = 0; let mut sampler_descriptor_size = 0;
let mut sampled_image_descriptor_size = 0; let mut sampled_image_descriptor_size = 0;
let mut uniform_buffer_descriptor_size = 0;
let uniform_buffer_offset_mask = physical_device_properties2
.properties
.limits
.min_uniform_buffer_offset_alignment
.checked_next_power_of_two()
.unwrap()
- 1;
if supports_descriptor_buffer { if supports_descriptor_buffer {
descriptor_buffer_offset_mask = descriptor_buffer_props descriptor_buffer_offset_mask = descriptor_buffer_props
.descriptor_buffer_offset_alignment .descriptor_buffer_offset_alignment
@ -390,6 +405,7 @@ impl VulkanInstance {
- 1; - 1;
sampler_descriptor_size = descriptor_buffer_props.sampler_descriptor_size; sampler_descriptor_size = descriptor_buffer_props.sampler_descriptor_size;
sampled_image_descriptor_size = descriptor_buffer_props.sampled_image_descriptor_size; sampled_image_descriptor_size = descriptor_buffer_props.sampled_image_descriptor_size;
uniform_buffer_descriptor_size = descriptor_buffer_props.uniform_buffer_descriptor_size;
} }
let memory_properties = let memory_properties =
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) }; unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
@ -432,6 +448,8 @@ impl VulkanInstance {
blend_limits, blend_limits,
is_anv: physical_device_vulkan12_properties.driver_id is_anv: physical_device_vulkan12_properties.driver_id
== DriverId::INTEL_OPEN_SOURCE_MESA, == DriverId::INTEL_OPEN_SOURCE_MESA,
uniform_buffer_offset_mask,
uniform_buffer_descriptor_size,
})) }))
} }
} }

View file

@ -21,8 +21,8 @@ use {
ash::vk::{ ash::vk::{
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle, BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
DescriptorDataEXT, DescriptorGetInfoEXT, DescriptorImageInfo, DescriptorType, DeviceMemory, DescriptorDataEXT, DescriptorGetInfoEXT, DescriptorImageInfo, DescriptorType, DeviceMemory,
DeviceSize, Extent3D, ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo, Extent3D, ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo, FormatFeatureFlags,
FormatFeatureFlags, Image, ImageAspectFlags, ImageCreateFlags, ImageCreateInfo, Image, ImageAspectFlags, ImageCreateFlags, ImageCreateInfo,
ImageDrmFormatModifierExplicitCreateInfoEXT, ImageLayout, ImageMemoryRequirementsInfo2, ImageDrmFormatModifierExplicitCreateInfoEXT, ImageLayout, ImageMemoryRequirementsInfo2,
ImagePlaneMemoryRequirementsInfo, ImageSubresourceRange, ImageTiling, ImageType, ImagePlaneMemoryRequirementsInfo, ImageSubresourceRange, ImageTiling, ImageType,
ImageUsageFlags, ImageView, ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR, ImageUsageFlags, ImageView, ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR,
@ -66,8 +66,6 @@ pub struct VulkanImage {
pub(super) ty: VulkanImageMemory, pub(super) ty: VulkanImageMemory,
pub(super) bridge: Option<VulkanFramebufferBridge>, pub(super) bridge: Option<VulkanFramebufferBridge>,
pub(super) sampled_image_descriptor: Box<[u8]>, pub(super) sampled_image_descriptor: Box<[u8]>,
pub(super) descriptor_buffer_version: Cell<u64>,
pub(super) descriptor_buffer_offset: Cell<DeviceSize>,
pub(super) execution_version: Cell<u64>, pub(super) execution_version: Cell<u64>,
} }
@ -468,8 +466,6 @@ impl VulkanDmaBufImageTemplate {
}), }),
bridge, bridge,
sampled_image_descriptor: self.renderer.sampled_image_descriptor(texture_view), sampled_image_descriptor: self.renderer.sampled_image_descriptor(texture_view),
descriptor_buffer_version: Cell::new(0),
descriptor_buffer_offset: Cell::new(0),
execution_version: Cell::new(0), execution_version: Cell::new(0),
})) }))
} }

View file

@ -42,6 +42,7 @@ pub(super) struct PipelineCreateInfo {
pub(super) eotf: u32, pub(super) eotf: u32,
pub(super) oetf: u32, pub(super) oetf: u32,
pub(super) descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>, pub(super) descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
pub(super) has_color_management_data: bool,
} }
impl VulkanDevice { impl VulkanDevice {
@ -77,8 +78,8 @@ impl VulkanDevice {
}; };
let destroy_layout = let destroy_layout =
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) }); OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
let mut frag_spec_data = ArrayVec::<_, { 4 * 4 }>::new(); let mut frag_spec_data = ArrayVec::<_, { 5 * 4 }>::new();
let mut frag_spec_entries = ArrayVec::<_, 4>::new(); let mut frag_spec_entries = ArrayVec::<_, 5>::new();
let mut frag_spec_entry = |data: &[u8]| { let mut frag_spec_entry = |data: &[u8]| {
let entry = SpecializationMapEntry::default() let entry = SpecializationMapEntry::default()
.constant_id(frag_spec_entries.len() as _) .constant_id(frag_spec_entries.len() as _)
@ -91,6 +92,7 @@ impl VulkanDevice {
frag_spec_entry(&(info.has_alpha_mult as u32).to_ne_bytes()); frag_spec_entry(&(info.has_alpha_mult as u32).to_ne_bytes());
frag_spec_entry(&info.eotf.to_ne_bytes()); frag_spec_entry(&info.eotf.to_ne_bytes());
frag_spec_entry(&info.oetf.to_ne_bytes()); frag_spec_entry(&info.oetf.to_ne_bytes());
frag_spec_entry(&(info.has_color_management_data as u32).to_ne_bytes());
let frag_spec = SpecializationInfo::default() let frag_spec = SpecializationInfo::default()
.map_entries(&frag_spec_entries) .map_entries(&frag_spec_entries)
.data(&frag_spec_data); .data(&frag_spec_data);

View file

@ -14,7 +14,7 @@ use {
gfx_apis::vulkan::{ gfx_apis::vulkan::{
VulkanError, VulkanError,
allocator::{VulkanAllocator, VulkanThreadedAllocator}, allocator::{VulkanAllocator, VulkanThreadedAllocator},
buffer_cache::{VulkanBuffer, VulkanBufferCache}, buffer_cache::{GenericBufferWriter, VulkanBuffer, VulkanBufferCache},
command::{VulkanCommandBuffer, VulkanCommandPool}, command::{VulkanCommandBuffer, VulkanCommandPool},
descriptor::VulkanDescriptorSetLayout, descriptor::VulkanDescriptorSetLayout,
descriptor_buffer::VulkanDescriptorBufferWriter, descriptor_buffer::VulkanDescriptorBufferWriter,
@ -27,8 +27,8 @@ use {
shaders::{ shaders::{
FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT,
LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, LegacyTexPushConstants, LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, LegacyTexPushConstants,
OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexColorManagementData,
TexVertex, VulkanShader, TexPushConstants, TexVertex, VulkanShader,
}, },
transfer_functions::{TF_LINEAR, TransferFunctionExt}, transfer_functions::{TF_LINEAR, TransferFunctionExt},
}, },
@ -46,9 +46,10 @@ use {
self, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferUsageFlags, self, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferUsageFlags,
ClearAttachment, ClearColorValue, ClearRect, ClearValue, CommandBuffer, ClearAttachment, ClearColorValue, ClearRect, ClearValue, CommandBuffer,
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags, CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags,
CopyImageInfo2, DependencyInfoKHR, DescriptorBufferBindingInfoEXT, DescriptorImageInfo, CopyImageInfo2, DependencyInfoKHR, DescriptorAddressInfoEXT,
DescriptorType, DeviceAddress, DeviceSize, Extent2D, Extent3D, ImageAspectFlags, DescriptorBufferBindingInfoEXT, DescriptorDataEXT, DescriptorGetInfoEXT,
ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageSubresourceLayers, DescriptorImageInfo, DescriptorType, DeviceAddress, DeviceSize, Extent2D, Extent3D,
ImageAspectFlags, ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageSubresourceLayers,
ImageSubresourceRange, Offset2D, Offset3D, PipelineBindPoint, PipelineStageFlags2, ImageSubresourceRange, Offset2D, Offset3D, PipelineBindPoint, PipelineStageFlags2,
QUEUE_FAMILY_FOREIGN_EXT, Rect2D, RenderingAttachmentInfo, RenderingInfo, QUEUE_FAMILY_FOREIGN_EXT, Rect2D, RenderingAttachmentInfo, RenderingInfo,
SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport, SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport,
@ -108,6 +109,7 @@ pub struct VulkanRenderer {
pub(super) resource_descriptor_buffer_cache: Rc<VulkanBufferCache>, pub(super) resource_descriptor_buffer_cache: Rc<VulkanBufferCache>,
pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>, pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>,
pub(super) shader_buffer_cache: Rc<VulkanBufferCache>, pub(super) shader_buffer_cache: Rc<VulkanBufferCache>,
pub(super) uniform_buffer_cache: Rc<VulkanBufferCache>,
} }
pub(super) struct CachedCommandBuffers { pub(super) struct CachedCommandBuffers {
@ -159,7 +161,7 @@ pub(super) struct Memory {
wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>, wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>,
release_fence: Option<Rc<VulkanFence>>, release_fence: Option<Rc<VulkanFence>>,
release_sync_file: Option<SyncFile>, release_sync_file: Option<SyncFile>,
used_buffers: ArrayVec<VulkanBuffer, 3>, used_buffers: ArrayVec<VulkanBuffer, 4>,
paint_bounds: StaticMap<RenderPass, Option<PaintRegion>>, paint_bounds: StaticMap<RenderPass, Option<PaintRegion>>,
paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>, paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>,
clear_rects: StaticMap<RenderPass, Vec<ClearRect>>, clear_rects: StaticMap<RenderPass, Vec<ClearRect>>,
@ -175,6 +177,9 @@ pub(super) struct Memory {
data_buffer: Vec<u8>, data_buffer: Vec<u8>,
out_address: DeviceAddress, out_address: DeviceAddress,
color_transforms: ColorTransforms, color_transforms: ColorTransforms,
uniform_buffer_writer: GenericBufferWriter,
uniform_buffer_descriptor_cache: Option<Box<[u8]>>,
blend_buffer_descriptor_buffer_offset: DeviceAddress,
} }
type Point = [[f32; 2]; 4]; type Point = [[f32; 2]; 4];
@ -196,6 +201,8 @@ struct VulkanTexOp {
range_address: DeviceAddress, range_address: DeviceAddress,
instances: u32, instances: u32,
tex_cd: Rc<ColorDescription>, tex_cd: Rc<ColorDescription>,
color_management_data_address: Option<DeviceAddress>,
resource_descriptor_buffer_offset: DeviceAddress,
} }
struct VulkanFillOp { struct VulkanFillOp {
@ -230,7 +237,7 @@ pub(super) struct PendingFrame {
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>, wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
waiter: Cell<Option<SpawnedFuture<()>>>, waiter: Cell<Option<SpawnedFuture<()>>>,
_release_fence: Option<Rc<VulkanFence>>, _release_fence: Option<Rc<VulkanFence>>,
_used_buffers: ArrayVec<VulkanBuffer, 3>, _used_buffers: ArrayVec<VulkanBuffer, 4>,
} }
type FillPipelines = Rc<StaticMap<TexSourceType, Rc<VulkanPipeline>>>; type FillPipelines = Rc<StaticMap<TexSourceType, Rc<VulkanPipeline>>>;
@ -240,6 +247,7 @@ struct TexPipelineKey {
tex_copy_type: TexCopyType, tex_copy_type: TexCopyType,
tex_source_type: TexSourceType, tex_source_type: TexSourceType,
eotf: TransferFunction, eotf: TransferFunction,
has_color_management_data: bool,
} }
pub(super) struct TexPipelines { pub(super) struct TexPipelines {
@ -338,7 +346,13 @@ impl VulkanDevice {
let shader_buffer_cache = { let shader_buffer_cache = {
// TODO: https://github.com/KhronosGroup/Vulkan-Samples/issues/1286 // TODO: https://github.com/KhronosGroup/Vulkan-Samples/issues/1286
let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::STORAGE_BUFFER; let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::STORAGE_BUFFER;
VulkanBufferCache::new(self, &allocator, usage) let align = 8;
VulkanBufferCache::new(self, &allocator, usage, align)
};
let uniform_buffer_cache = {
let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::UNIFORM_BUFFER;
let align = align_of::<TexColorManagementData>() as DeviceSize;
VulkanBufferCache::new(self, &allocator, usage, align)
}; };
let render = Rc::new(VulkanRenderer { let render = Rc::new(VulkanRenderer {
formats: Rc::new(formats), formats: Rc::new(formats),
@ -374,6 +388,7 @@ impl VulkanDevice {
resource_descriptor_buffer_cache, resource_descriptor_buffer_cache,
blend_buffers: Default::default(), blend_buffers: Default::default(),
shader_buffer_cache, shader_buffer_cache,
uniform_buffer_cache,
}); });
Ok(render) Ok(render)
} }
@ -404,6 +419,7 @@ impl VulkanRenderer {
eotf: TF_LINEAR, eotf: TF_LINEAR,
oetf: TF_LINEAR, oetf: TF_LINEAR,
descriptor_set_layouts: Default::default(), descriptor_set_layouts: Default::default(),
has_color_management_data: false,
}; };
self.device.create_pipeline2(info, push_size) self.device.create_pipeline2(info, push_size)
}; };
@ -441,11 +457,13 @@ impl VulkanRenderer {
tex_cd: &ColorDescription, tex_cd: &ColorDescription,
tex_copy_type: TexCopyType, tex_copy_type: TexCopyType,
tex_source_type: TexSourceType, tex_source_type: TexSourceType,
has_color_management_data: bool,
) -> Result<Rc<VulkanPipeline>, VulkanError> { ) -> Result<Rc<VulkanPipeline>, VulkanError> {
let key = TexPipelineKey { let key = TexPipelineKey {
tex_copy_type, tex_copy_type,
tex_source_type, tex_source_type,
eotf: tex_cd.transfer_function, eotf: tex_cd.transfer_function,
has_color_management_data,
}; };
if let Some(pl) = pipelines.pipelines.get(&key) { if let Some(pl) = pipelines.pipelines.get(&key) {
return Ok(pl); return Ok(pl);
@ -473,6 +491,7 @@ impl VulkanRenderer {
eotf: key.eotf.to_vulkan(), eotf: key.eotf.to_vulkan(),
oetf: pipelines.oetf.to_vulkan(), oetf: pipelines.oetf.to_vulkan(),
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(), descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
has_color_management_data,
}; };
let pl = self.device.create_pipeline2(info, push_size)?; let pl = self.device.create_pipeline2(info, push_size)?;
pipelines.pipelines.set(key, pl.clone()); pipelines.pipelines.set(key, pl.clone());
@ -507,6 +526,7 @@ impl VulkanRenderer {
eotf: key.eotf.to_vulkan(), eotf: key.eotf.to_vulkan(),
oetf: fb_cd.transfer_function.to_vulkan(), oetf: fb_cd.transfer_function.to_vulkan(),
descriptor_set_layouts, descriptor_set_layouts,
has_color_management_data: false,
})?; })?;
pipelines.set(key, out.clone()); pipelines.set(key, out.clone());
Ok(out) Ok(out)
@ -525,7 +545,6 @@ impl VulkanRenderer {
return Ok(()); return Ok(());
}; };
zone!("create_descriptor_buffers"); zone!("create_descriptor_buffers");
let version = self.allocate_point();
let memory = &mut *self.memory.borrow_mut(); let memory = &mut *self.memory.borrow_mut();
let sampler_writer = &mut memory.sampler_descriptor_buffer_writer; let sampler_writer = &mut memory.sampler_descriptor_buffer_writer;
sampler_writer.clear(); sampler_writer.clear();
@ -538,30 +557,47 @@ 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
.uniform_buffer_descriptor_cache
.get_or_insert_with(|| {
vec![0u8; self.device.uniform_buffer_descriptor_size].into_boxed_slice()
});
if let Some(bb) = bb { if let Some(bb) = bb {
let layout = self.out_descriptor_set_layout.as_ref().unwrap(); let layout = self.out_descriptor_set_layout.as_ref().unwrap();
let offset = resource_writer.next_offset(); memory.blend_buffer_descriptor_buffer_offset = resource_writer.next_offset();
bb.descriptor_buffer_offset.set(offset);
let mut writer = resource_writer.add_set(layout); let mut writer = resource_writer.add_set(layout);
writer.write(layout.offsets[0], &bb.sampled_image_descriptor); writer.write(layout.offsets[0], &bb.sampled_image_descriptor);
} }
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() {
for cmd in &memory.ops[pass] { for cmd in &mut memory.ops[pass] {
let VulkanOp::Tex(c) = cmd else { let VulkanOp::Tex(c) = cmd else {
continue; continue;
}; };
let tex = &c.tex; let tex = &c.tex;
if tex.descriptor_buffer_version.replace(version) == version { c.resource_descriptor_buffer_offset = resource_writer.next_offset();
continue;
}
let offset = resource_writer.next_offset();
tex.descriptor_buffer_offset.set(offset);
let mut writer = resource_writer.add_set(tex_descriptor_set_layout); let mut writer = resource_writer.add_set(tex_descriptor_set_layout);
writer.write( writer.write(
tex_descriptor_set_layout.offsets[0], tex_descriptor_set_layout.offsets[0],
&tex.sampled_image_descriptor, &tex.sampled_image_descriptor,
); );
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(
tex_descriptor_set_layout.offsets[1],
uniform_buffer_descriptor_cache,
);
}
} }
} }
let mut infos = ArrayVec::<_, 2>::new(); let mut infos = ArrayVec::<_, 2>::new();
@ -569,12 +605,7 @@ impl VulkanRenderer {
(&sampler_writer, &self.sampler_descriptor_buffer_cache), (&sampler_writer, &self.sampler_descriptor_buffer_cache),
(&resource_writer, &self.resource_descriptor_buffer_cache), (&resource_writer, &self.resource_descriptor_buffer_cache),
] { ] {
let mut min_alignment = 1; let buffer = cache.allocate(writer.len() as DeviceSize)?;
if self.device.is_anv {
// https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33903
min_alignment = 4096;
}
let buffer = cache.allocate(writer.len() as DeviceSize, min_alignment)?;
buffer.buffer.allocation.upload(|ptr, _| unsafe { buffer.buffer.allocation.upload(|ptr, _| unsafe {
ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len()) ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len())
})?; })?;
@ -607,6 +638,7 @@ impl VulkanRenderer {
memory.tex_targets.clear(); memory.tex_targets.clear();
memory.fill_targets.clear(); memory.fill_targets.clear();
memory.data_buffer.clear(); memory.data_buffer.clear();
memory.uniform_buffer_writer.clear();
memory.color_transforms.map.clear(); memory.color_transforms.map.clear();
let sync = |memory: &mut Memory| { let sync = |memory: &mut Memory| {
for pass in RenderPass::variants() { for pass in RenderPass::variants() {
@ -751,6 +783,16 @@ impl VulkanRenderer {
true => TexSourceType::HasAlpha, true => TexSourceType::HasAlpha,
false => TexSourceType::Opaque, false => TexSourceType::Opaque,
}; };
let target_cd = match pass {
RenderPass::BlendBuffer => blend_cd,
RenderPass::FrameBuffer => fb_cd,
};
let color_management_data_address = memory.color_transforms.get_offset(
&ct.cd.linear,
target_cd,
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,
@ -763,6 +805,8 @@ impl VulkanRenderer {
range_address: 0, range_address: 0,
instances: 0, instances: 0,
tex_cd: ct.cd.clone(), tex_cd: ct.cd.clone(),
color_management_data_address,
resource_descriptor_buffer_offset: 0,
})); }));
} }
} }
@ -792,7 +836,7 @@ impl VulkanRenderer {
if buf.is_empty() { if buf.is_empty() {
return Ok(()); return Ok(());
} }
let buffer = self.shader_buffer_cache.allocate(buf.len() as _, 8)?; let buffer = self.shader_buffer_cache.allocate(buf.len() as _)?;
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());
})?; })?;
@ -813,6 +857,33 @@ impl VulkanRenderer {
Ok(()) Ok(())
} }
fn create_uniform_buffer(&self) -> Result<(), VulkanError> {
if self.device.descriptor_buffer.is_none() {
return Ok(());
}
zone!("create_uniform_buffer");
let memory = &mut *self.memory.borrow_mut();
let buf = &memory.uniform_buffer_writer;
if buf.is_empty() {
return Ok(());
}
let buffer = self.uniform_buffer_cache.allocate(buf.len() as _)?;
buffer.buffer.allocation.upload(|ptr, _| unsafe {
ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());
})?;
for ops in memory.ops.values_mut() {
for op in ops {
if let VulkanOp::Tex(c) = op {
if let Some(addr) = &mut c.color_management_data_address {
*addr += buffer.buffer.address;
}
}
}
}
memory.used_buffers.push(buffer);
Ok(())
}
fn collect_memory(&self) { fn collect_memory(&self) {
zone!("collect_memory"); zone!("collect_memory");
let memory = &mut *self.memory.borrow_mut(); let memory = &mut *self.memory.borrow_mut();
@ -1126,6 +1197,7 @@ impl VulkanRenderer {
&c.tex_cd, &c.tex_cd,
c.copy_type, c.copy_type,
c.source_type, c.source_type,
c.color_management_data_address.is_some(),
)?; )?;
bind(&pipeline); bind(&pipeline);
let image_info = DescriptorImageInfo::default() let image_info = DescriptorImageInfo::default()
@ -1143,7 +1215,7 @@ impl VulkanRenderer {
pipeline.pipeline_layout, pipeline.pipeline_layout,
0, 0,
&[0, 1], &[0, 1],
&[0, tex.descriptor_buffer_offset.get()], &[0, c.resource_descriptor_buffer_offset],
); );
dev.cmd_push_constants( dev.cmd_push_constants(
buf, buf,
@ -1219,7 +1291,6 @@ impl VulkanRenderer {
buf: CommandBuffer, buf: CommandBuffer,
fb: &VulkanImage, fb: &VulkanImage,
fb_cd: &ColorDescription, fb_cd: &ColorDescription,
bb: &VulkanImage,
bb_cd: &ColorDescription, bb_cd: &ColorDescription,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
zone!("blend_buffer_copy"); zone!("blend_buffer_copy");
@ -1239,7 +1310,7 @@ impl VulkanRenderer {
pipeline.pipeline_layout, pipeline.pipeline_layout,
0, 0,
&[1], &[1],
&[bb.descriptor_buffer_offset.get()], &[memory.blend_buffer_descriptor_buffer_offset],
); );
dev.cmd_push_constants( dev.cmd_push_constants(
buf, buf,
@ -1808,6 +1879,7 @@ impl VulkanRenderer {
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_data_buffer()?; self.create_data_buffer()?;
self.create_uniform_buffer()?;
self.collect_memory(); self.collect_memory();
self.begin_command_buffer(buf.buffer)?; self.begin_command_buffer(buf.buffer)?;
self.create_descriptor_buffers(buf.buffer, bb)?; self.create_descriptor_buffers(buf.buffer, bb)?;
@ -1827,8 +1899,8 @@ impl VulkanRenderer {
let rp = RenderPass::FrameBuffer; let rp = RenderPass::FrameBuffer;
self.begin_rendering(buf.buffer, fb, clear, clear_cd, rp, fb_cd); self.begin_rendering(buf.buffer, fb, clear, clear_cd, rp, fb_cd);
self.record_draws(buf.buffer, fb, rp, fb_cd)?; self.record_draws(buf.buffer, fb, rp, fb_cd)?;
if let Some(bb) = bb { if bb.is_some() {
self.blend_buffer_copy(buf.buffer, fb, fb_cd, bb, bb_cd)?; self.blend_buffer_copy(buf.buffer, fb, fb_cd, bb_cd)?;
} }
self.end_rendering(buf.buffer); self.end_rendering(buf.buffer);
} }
@ -2055,6 +2127,7 @@ struct ColorTransforms {
struct ColorTransform { struct ColorTransform {
matrix: ColorMatrix, matrix: ColorMatrix,
offset: Option<DeviceSize>,
} }
impl ColorTransforms { impl ColorTransforms {
@ -2070,7 +2143,10 @@ impl ColorTransforms {
Entry::Occupied(o) => o.into_mut(), Entry::Occupied(o) => o.into_mut(),
Entry::Vacant(e) => { Entry::Vacant(e) => {
let matrix = src.color_transform(&dst.linear); let matrix = src.color_transform(&dst.linear);
let ct = ColorTransform { matrix }; let ct = ColorTransform {
matrix,
offset: None,
};
e.insert(ct) e.insert(ct)
} }
}; };
@ -2088,4 +2164,22 @@ impl ColorTransforms {
}; };
color color
} }
fn get_offset(
&mut self,
src: &LinearColorDescription,
dst: &ColorDescription,
uniform_buffer_offset_mask: DeviceSize,
writer: &mut GenericBufferWriter,
) -> Option<DeviceSize> {
let ct = self.get_or_create(src, dst)?;
if ct.offset.is_none() {
let data = TexColorManagementData {
matrix: ct.matrix.to_f32(),
};
let offset = writer.write(uniform_buffer_offset_mask, &data);
ct.offset = Some(offset);
}
ct.offset
}
} }

View file

@ -61,6 +61,14 @@ pub struct TexPushConstants {
unsafe impl Packed for TexPushConstants {} unsafe impl Packed for TexPushConstants {}
#[derive(Copy, Clone, Debug)]
#[repr(C, align(16))]
pub struct TexColorManagementData {
pub matrix: [[f32; 4]; 4],
}
unsafe impl Packed for TexColorManagementData {}
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[repr(C)] #[repr(C)]
pub struct LegacyTexPushConstants { pub struct LegacyTexPushConstants {

View file

@ -5,5 +5,6 @@ layout(constant_id = 0) const bool src_has_alpha = false;
layout(constant_id = 1) const bool has_alpha_multiplier = false; layout(constant_id = 1) const bool has_alpha_multiplier = false;
layout(constant_id = 2) const uint eotf = 0; layout(constant_id = 2) const uint eotf = 0;
layout(constant_id = 3) const uint oetf = 0; layout(constant_id = 3) const uint oetf = 0;
layout(constant_id = 4) const bool has_matrix = false;
#endif #endif

View file

@ -1,25 +1,35 @@
#version 450 #version 450
#extension GL_EXT_scalar_block_layout : require
#include "frag_spec_const.glsl" #include "frag_spec_const.glsl"
#include "transfer_functions.glsl" #include "transfer_functions.glsl"
#include "tex.common.glsl" #include "tex.common.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 = 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;
void main() { void main() {
vec4 c = textureLod(sampler2D(tex, sam), tex_pos, 0); vec4 c = textureLod(sampler2D(tex, sam), tex_pos, 0);
if (eotf != oetf) { if (eotf != oetf || has_matrix) {
vec3 rgb = c.rgb;
if (src_has_alpha) { if (src_has_alpha) {
c.rgb /= mix(c.a, 1.0, c.a == 0.0); rgb /= mix(c.a, 1.0, c.a == 0.0);
} }
c.rgb = apply_eotf(c.rgb); rgb = apply_eotf(rgb);
c.rgb = apply_oetf(c.rgb); if (has_matrix) {
rgb = (cm_data.matrix * vec4(rgb, 1.0)).rgb;
}
rgb = apply_oetf(rgb);
if (src_has_alpha) { if (src_has_alpha) {
c.rgb *= c.a; rgb *= c.a;
} }
c.rgb = rgb;
} }
if (has_alpha_multiplier) { if (has_alpha_multiplier) {
if (src_has_alpha) { if (src_has_alpha) {

View file

@ -460,8 +460,6 @@ impl VulkanRenderer {
ty: VulkanImageMemory::Internal(shm), ty: VulkanImageMemory::Internal(shm),
bridge: None, bridge: None,
sampled_image_descriptor: self.sampled_image_descriptor(view), sampled_image_descriptor: self.sampled_image_descriptor(view),
descriptor_buffer_version: Cell::new(0),
descriptor_buffer_offset: Cell::new(0),
execution_version: Cell::new(0), execution_version: Cell::new(0),
}); });
let shm = match &img.ty { let shm = match &img.ty {