From cb9dc4c1824b02851b6d4599bf1cdf27c3126142 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 11:10:50 +0100 Subject: [PATCH 01/12] vulkan: add legacy shaders --- build/vulkan.rs | 16 +++++++++---- src/gfx_apis/vulkan/renderer.rs | 24 ++++++++++++++----- src/gfx_apis/vulkan/shaders.rs | 6 +++++ .../vulkan/shaders/legacy/fill.common.glsl | 4 ++++ src/gfx_apis/vulkan/shaders/legacy/fill.frag | 10 ++++++++ src/gfx_apis/vulkan/shaders/legacy/fill.vert | 16 +++++++++++++ .../vulkan/shaders/legacy/tex.common.glsl | 5 ++++ src/gfx_apis/vulkan/shaders/legacy/tex.frag | 20 ++++++++++++++++ src/gfx_apis/vulkan/shaders/legacy/tex.vert | 18 ++++++++++++++ 9 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 src/gfx_apis/vulkan/shaders/legacy/fill.common.glsl create mode 100644 src/gfx_apis/vulkan/shaders/legacy/fill.frag create mode 100644 src/gfx_apis/vulkan/shaders/legacy/fill.vert create mode 100644 src/gfx_apis/vulkan/shaders/legacy/tex.common.glsl create mode 100644 src/gfx_apis/vulkan/shaders/legacy/tex.frag create mode 100644 src/gfx_apis/vulkan/shaders/legacy/tex.vert diff --git a/build/vulkan.rs b/build/vulkan.rs index fb649b57..996d3a85 100644 --- a/build/vulkan.rs +++ b/build/vulkan.rs @@ -15,16 +15,22 @@ pub fn main() -> anyhow::Result<()> { compile_simple("tex.frag")?; compile_simple("out.vert")?; compile_simple("out.frag")?; + compile_simple("legacy/fill.frag")?; + compile_simple("legacy/fill.vert")?; + compile_simple("legacy/tex.vert")?; + compile_simple("legacy/tex.frag")?; Ok(()) } fn compile_simple(name: &str) -> anyhow::Result<()> { - compile_shader(name, &format!("{name}.spv"), None).with_context(|| name.to_string()) + let out = format!("{name}.spv").replace("/", "_"); + compile_shader(name, &out).with_context(|| name.to_string()) } -fn compile_shader(name: &str, out: &str, options: Option) -> anyhow::Result<()> { - let read = |path: &str| std::fs::read_to_string(format!("{}/{}", ROOT, path)); - let mut options = options.unwrap_or_else(|| CompileOptions::new().unwrap()); +fn compile_shader(name: &str, out: &str) -> anyhow::Result<()> { + let root = Path::new(ROOT).join(Path::new(name).parent().unwrap()); + let read = |path: &str| std::fs::read_to_string(root.join(path)); + let mut options = CompileOptions::new().unwrap(); options.set_include_callback(|name, _, _, _| { Ok(ResolvedInclude { resolved_name: name.to_string(), @@ -40,7 +46,7 @@ fn compile_shader(name: &str, out: &str, options: Option) -> any "vert" => shaderc::ShaderKind::Vertex, n => bail!("Unknown shader stage {}", n), }; - let src = read(name)?; + let src = std::fs::read_to_string(format!("{}/{}", ROOT, name))?; let compiler = shaderc::Compiler::new().unwrap(); let binary = compiler .compile_into_spirv(&src, stage, name, "main", Some(&options)) diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 3a8b5725..8fbe8255 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -22,8 +22,9 @@ use { sampler::VulkanSampler, semaphore::VulkanSemaphore, shaders::{ - FILL_FRAG, FILL_VERT, FillPushConstants, OUT_FRAG, OUT_VERT, OutPushConstants, - TEX_FRAG, TEX_VERT, TexPushConstants, VulkanShader, + FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, + LEGACY_TEX_FRAG, LEGACY_TEX_VERT, OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, + TEX_VERT, TexPushConstants, VulkanShader, }, }, io_uring::IoUring, @@ -195,8 +196,21 @@ impl VulkanDevice { eng: &Rc, ring: &Rc, ) -> Result, VulkanError> { - let fill_vert_shader = self.create_shader(FILL_VERT)?; - let fill_frag_shader = self.create_shader(FILL_FRAG)?; + let fill_vert_shader; + let fill_frag_shader; + let tex_vert_shader; + let tex_frag_shader; + if self.descriptor_buffer.is_some() { + tex_vert_shader = self.create_shader(TEX_VERT)?; + tex_frag_shader = self.create_shader(TEX_FRAG)?; + fill_vert_shader = self.create_shader(FILL_VERT)?; + fill_frag_shader = self.create_shader(FILL_FRAG)?; + } else { + tex_vert_shader = self.create_shader(LEGACY_TEX_VERT)?; + tex_frag_shader = self.create_shader(LEGACY_TEX_FRAG)?; + fill_vert_shader = self.create_shader(LEGACY_FILL_VERT)?; + fill_frag_shader = self.create_shader(LEGACY_FILL_FRAG)?; + } let sampler = self.create_sampler()?; let tex_descriptor_set_layout = self.create_tex_descriptor_set_layout(&sampler)?; let out_descriptor_set_layout = self @@ -206,8 +220,6 @@ impl VulkanDevice { .transpose()?; let out_vert_shader = self.create_shader(OUT_VERT)?; let out_frag_shader = self.create_shader(OUT_FRAG)?; - let tex_vert_shader = self.create_shader(TEX_VERT)?; - let tex_frag_shader = self.create_shader(TEX_FRAG)?; let gfx_command_buffers = self.create_command_pool(self.graphics_queue_idx)?; let transfer_command_buffers = self .distinct_transfer_queue_family_idx diff --git a/src/gfx_apis/vulkan/shaders.rs b/src/gfx_apis/vulkan/shaders.rs index 4e019e37..07e49938 100644 --- a/src/gfx_apis/vulkan/shaders.rs +++ b/src/gfx_apis/vulkan/shaders.rs @@ -11,6 +11,12 @@ pub const TEX_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.vert.s pub const TEX_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.frag.spv")); pub const OUT_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/out.vert.spv")); pub const OUT_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/out.frag.spv")); +pub const LEGACY_FILL_VERT: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/legacy_fill.vert.spv")); +pub const LEGACY_FILL_FRAG: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/legacy_fill.frag.spv")); +pub const LEGACY_TEX_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/legacy_tex.vert.spv")); +pub const LEGACY_TEX_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/legacy_tex.frag.spv")); pub struct VulkanShader { pub(super) device: Rc, diff --git a/src/gfx_apis/vulkan/shaders/legacy/fill.common.glsl b/src/gfx_apis/vulkan/shaders/legacy/fill.common.glsl new file mode 100644 index 00000000..d49c75a8 --- /dev/null +++ b/src/gfx_apis/vulkan/shaders/legacy/fill.common.glsl @@ -0,0 +1,4 @@ +layout(push_constant, std430) uniform Data { + layout(offset = 0) vec2 pos[4]; + layout(offset = 32) vec4 color; +} data; diff --git a/src/gfx_apis/vulkan/shaders/legacy/fill.frag b/src/gfx_apis/vulkan/shaders/legacy/fill.frag new file mode 100644 index 00000000..d8a3eaf3 --- /dev/null +++ b/src/gfx_apis/vulkan/shaders/legacy/fill.frag @@ -0,0 +1,10 @@ +#version 450 + +#include "../frag_spec_const.glsl" +#include "fill.common.glsl" + +layout(location = 0) out vec4 out_color; + +void main() { + out_color = data.color; +} diff --git a/src/gfx_apis/vulkan/shaders/legacy/fill.vert b/src/gfx_apis/vulkan/shaders/legacy/fill.vert new file mode 100644 index 00000000..8b4a7aa7 --- /dev/null +++ b/src/gfx_apis/vulkan/shaders/legacy/fill.vert @@ -0,0 +1,16 @@ +#version 450 +//#extension GL_EXT_debug_printf : enable + +#include "fill.common.glsl" + +void main() { + vec2 pos; + switch (gl_VertexIndex) { + case 0: pos = data.pos[0]; break; + case 1: pos = data.pos[1]; break; + case 2: pos = data.pos[2]; break; + case 3: pos = data.pos[3]; break; + } + gl_Position = vec4(pos, 0.0, 1.0); +// debugPrintfEXT("gl_Position = %v4f", gl_Position); +} diff --git a/src/gfx_apis/vulkan/shaders/legacy/tex.common.glsl b/src/gfx_apis/vulkan/shaders/legacy/tex.common.glsl new file mode 100644 index 00000000..61f3ef3c --- /dev/null +++ b/src/gfx_apis/vulkan/shaders/legacy/tex.common.glsl @@ -0,0 +1,5 @@ +layout(push_constant, std430) uniform Data { + layout(offset = 0) vec2 pos[4]; + layout(offset = 32) vec2 tex_pos[4]; + layout(offset = 64) float mul; +} data; diff --git a/src/gfx_apis/vulkan/shaders/legacy/tex.frag b/src/gfx_apis/vulkan/shaders/legacy/tex.frag new file mode 100644 index 00000000..2896bc87 --- /dev/null +++ b/src/gfx_apis/vulkan/shaders/legacy/tex.frag @@ -0,0 +1,20 @@ +#version 450 + +#include "../frag_spec_const.glsl" +#include "tex.common.glsl" + +layout(set = 0, binding = 0) uniform sampler2D tex; +layout(location = 0) in vec2 tex_pos; +layout(location = 0) out vec4 out_color; + +void main() { + vec4 c = textureLod(tex, tex_pos, 0); + if (has_alpha_multiplier) { + if (src_has_alpha) { + c *= data.mul; + } else { + c = vec4(c.rgb * data.mul, data.mul); + } + } + out_color = c; +} diff --git a/src/gfx_apis/vulkan/shaders/legacy/tex.vert b/src/gfx_apis/vulkan/shaders/legacy/tex.vert new file mode 100644 index 00000000..7390b4a4 --- /dev/null +++ b/src/gfx_apis/vulkan/shaders/legacy/tex.vert @@ -0,0 +1,18 @@ +#version 450 +//#extension GL_EXT_debug_printf : enable + +#include "tex.common.glsl" + +layout(location = 0) out vec2 tex_pos; + +void main() { + vec2 pos; + switch (gl_VertexIndex) { + case 0: pos = data.pos[0]; tex_pos = data.tex_pos[0]; break; + case 1: pos = data.pos[1]; tex_pos = data.tex_pos[1]; break; + case 2: pos = data.pos[2]; tex_pos = data.tex_pos[2]; break; + case 3: pos = data.pos[3]; tex_pos = data.tex_pos[3]; break; + } + gl_Position = vec4(pos, 0.0, 1.0); +// debugPrintfEXT("gl_Position = %v4f, tex_pos = %v2f", gl_Position, tex_pos); +} From b7f93b37a64cbd765a9579d225798e7a0eb39758 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 25 Feb 2025 14:52:43 +0100 Subject: [PATCH 02/12] vulkan: rewrite shaders in terms of eotf and oetf --- src/gfx_apis/vulkan.rs | 1 + src/gfx_apis/vulkan/pipeline.rs | 10 +++++---- src/gfx_apis/vulkan/renderer.rs | 15 +++++++++---- .../vulkan/shaders/frag_spec_const.glsl | 3 ++- src/gfx_apis/vulkan/shaders/out.frag | 9 +++++--- src/gfx_apis/vulkan/shaders/tex.frag | 5 +++-- .../vulkan/shaders/transfer_functions.glsl | 21 +++++++++++++++++++ src/gfx_apis/vulkan/transfer_functions.rs | 2 ++ 8 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 src/gfx_apis/vulkan/transfer_functions.rs diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index 8a1e568d..befd77d2 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -17,6 +17,7 @@ mod shaders; mod shm_image; mod staging; mod transfer; +mod transfer_functions; use { crate::{ diff --git a/src/gfx_apis/vulkan/pipeline.rs b/src/gfx_apis/vulkan/pipeline.rs index f4a4e4f9..1679d47a 100644 --- a/src/gfx_apis/vulkan/pipeline.rs +++ b/src/gfx_apis/vulkan/pipeline.rs @@ -39,7 +39,8 @@ pub(super) struct PipelineCreateInfo { pub(super) blend: bool, pub(super) src_has_alpha: bool, pub(super) has_alpha_mult: bool, - pub(super) with_linear_output: bool, + pub(super) eotf: u32, + pub(super) oetf: u32, pub(super) frag_descriptor_set_layout: Option>, } @@ -77,8 +78,8 @@ impl VulkanDevice { }; let destroy_layout = OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) }); - let mut frag_spec_data = ArrayVec::<_, { 3 * 4 }>::new(); - let mut frag_spec_entries = ArrayVec::<_, 3>::new(); + let mut frag_spec_data = ArrayVec::<_, { 4 * 4 }>::new(); + let mut frag_spec_entries = ArrayVec::<_, 4>::new(); let mut frag_spec_entry = |data: &[u8]| { let entry = SpecializationMapEntry::default() .constant_id(frag_spec_entries.len() as _) @@ -89,7 +90,8 @@ impl VulkanDevice { }; frag_spec_entry(&(info.src_has_alpha as u32).to_ne_bytes()); frag_spec_entry(&(info.has_alpha_mult as u32).to_ne_bytes()); - frag_spec_entry(&(info.with_linear_output as u32).to_ne_bytes()); + frag_spec_entry(&info.eotf.to_ne_bytes()); + frag_spec_entry(&info.oetf.to_ne_bytes()); let frag_spec = SpecializationInfo::default() .map_entries(&frag_spec_entries) .data(&frag_spec_data); diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 8fbe8255..5eb50f1d 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -26,6 +26,7 @@ use { LEGACY_TEX_FRAG, LEGACY_TEX_VERT, OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, VulkanShader, }, + transfer_functions::{TF_LINEAR, TF_SRGB}, }, io_uring::IoUring, rect::{Rect, Region}, @@ -305,7 +306,10 @@ impl VulkanRenderer { format: vk::Format, pass: RenderPass, ) -> Result, VulkanError> { - let with_linear_output = pass == RenderPass::BlendBuffer; + let (eotf, oetf) = match pass { + RenderPass::BlendBuffer => (TF_SRGB, TF_LINEAR), + RenderPass::FrameBuffer => (TF_SRGB, TF_SRGB), + }; let pipelines = &self.pipelines[pass]; if let Some(pl) = pipelines.get(&format) { return Ok(pl); @@ -319,7 +323,8 @@ impl VulkanRenderer { blend: src_has_alpha, src_has_alpha, has_alpha_mult: false, - with_linear_output, + eotf, + oetf, frag_descriptor_set_layout: None, }) }; @@ -334,7 +339,8 @@ impl VulkanRenderer { blend: src_has_alpha || has_alpha_mult, src_has_alpha, has_alpha_mult, - with_linear_output, + eotf, + oetf, frag_descriptor_set_layout: Some(self.tex_descriptor_set_layout.clone()), }) }; @@ -847,7 +853,8 @@ impl VulkanRenderer { blend: false, src_has_alpha: true, has_alpha_mult: false, - with_linear_output: true, + eotf: TF_LINEAR, + oetf: TF_SRGB, frag_descriptor_set_layout: Some(layout.clone()), })?; e.insert(out.clone()); diff --git a/src/gfx_apis/vulkan/shaders/frag_spec_const.glsl b/src/gfx_apis/vulkan/shaders/frag_spec_const.glsl index 751b18bf..ada35281 100644 --- a/src/gfx_apis/vulkan/shaders/frag_spec_const.glsl +++ b/src/gfx_apis/vulkan/shaders/frag_spec_const.glsl @@ -3,6 +3,7 @@ layout(constant_id = 0) const bool src_has_alpha = false; layout(constant_id = 1) const bool has_alpha_multiplier = false; -layout(constant_id = 2) const bool color_management = false; +layout(constant_id = 2) const uint eotf = 0; +layout(constant_id = 3) const uint oetf = 0; #endif diff --git a/src/gfx_apis/vulkan/shaders/out.frag b/src/gfx_apis/vulkan/shaders/out.frag index 8401e5be..c832dee7 100644 --- a/src/gfx_apis/vulkan/shaders/out.frag +++ b/src/gfx_apis/vulkan/shaders/out.frag @@ -10,8 +10,11 @@ layout(location = 0) out vec4 out_color; void main() { vec4 c = texelFetch(in_color, ivec2(gl_FragCoord.xy), 0); - c.rgb /= mix(c.a, 1.0, c.a == 0.0); - c.rgb = oetf_srgb(c.rgb); - c.rgb *= c.a; + if (eotf != oetf) { + c.rgb /= mix(c.a, 1.0, c.a == 0.0); + c.rgb = apply_eotf(c.rgb); + c.rgb = apply_oetf(c.rgb); + c.rgb *= c.a; + } out_color = c; } diff --git a/src/gfx_apis/vulkan/shaders/tex.frag b/src/gfx_apis/vulkan/shaders/tex.frag index 9d9476fd..53f74e8c 100644 --- a/src/gfx_apis/vulkan/shaders/tex.frag +++ b/src/gfx_apis/vulkan/shaders/tex.frag @@ -10,11 +10,12 @@ layout(location = 0) out vec4 out_color; void main() { vec4 c = textureLod(tex, tex_pos, 0); - if (color_management) { + if (eotf != oetf) { if (src_has_alpha) { c.rgb /= mix(c.a, 1.0, c.a == 0.0); } - c.rgb = eotf_srgb(c.rgb); + c.rgb = apply_eotf(c.rgb); + c.rgb = apply_oetf(c.rgb); if (src_has_alpha) { c.rgb *= c.a; } diff --git a/src/gfx_apis/vulkan/shaders/transfer_functions.glsl b/src/gfx_apis/vulkan/shaders/transfer_functions.glsl index d72332a0..d051d13d 100644 --- a/src/gfx_apis/vulkan/shaders/transfer_functions.glsl +++ b/src/gfx_apis/vulkan/shaders/transfer_functions.glsl @@ -1,6 +1,11 @@ #ifndef TRANSFER_FUNCTIONS_GLSL #define TRANSFER_FUNCTIONS_GLSL +#include "frag_spec_const.glsl" + +#define SRGB 0 +#define LINEAR 1 + vec3 eotf_srgb(vec3 c) { return mix( c * vec3(1.0 / 12.92), @@ -18,4 +23,20 @@ vec3 oetf_srgb(vec3 c) { ); } +vec3 apply_eotf(vec3 c) { + switch (eotf) { + case SRGB: return eotf_srgb(c); + case LINEAR: return c; + default: return c; + } +} + +vec3 apply_oetf(vec3 c) { + switch (oetf) { + case SRGB: return oetf_srgb(c); + case LINEAR: return c; + default: return c; + } +} + #endif diff --git a/src/gfx_apis/vulkan/transfer_functions.rs b/src/gfx_apis/vulkan/transfer_functions.rs new file mode 100644 index 00000000..b267ce1b --- /dev/null +++ b/src/gfx_apis/vulkan/transfer_functions.rs @@ -0,0 +1,2 @@ +pub const TF_SRGB: u32 = 0; +pub const TF_LINEAR: u32 = 1; From 135f37dbcd7fa3c573987be4273b753136eaa637 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 25 Feb 2025 15:43:05 +0100 Subject: [PATCH 03/12] theme: store colors in linear space --- src/cli/damage_tracking.rs | 10 +- src/config/handler.rs | 6 +- src/damage.rs | 2 +- src/gfx_api.rs | 15 +- src/gfx_apis/gl.rs | 5 +- src/gfx_apis/gl/renderer/framebuffer.rs | 5 +- src/gfx_apis/vulkan/renderer.rs | 16 +- src/ifs/jay_damage_tracking.rs | 10 +- src/ifs/wl_buffer.rs | 5 +- src/it/test_gfx_api.rs | 6 +- src/it/test_ifs/test_shm_buffer.rs | 2 +- .../test_single_pixel_buffer_manager.rs | 11 +- src/it/test_utils/test_surface_ext.rs | 2 +- src/it/tests/t0007_subsurface.rs | 2 +- src/it/tests/t0020_surface_offset.rs | 2 +- src/it/tests/t0039_alpha_modifier.rs | 4 +- .../t0039_alpha_modifier/screenshot_2.qoi | Bin 8145 -> 8145 bytes .../t0042_toplevel_select/screenshot_1.qoi | Bin 10802 -> 10802 bytes .../t0042_toplevel_select/screenshot_2.qoi | Bin 10800 -> 10800 bytes .../t0042_toplevel_select/screenshot_3.qoi | Bin 10800 -> 10800 bytes .../ptl_remote_desktop/remote_desktop_gui.rs | 10 +- src/portal/ptl_screencast/screencast_gui.rs | 14 +- src/portal/ptl_text.rs | 6 +- src/portal/ptr_gui.rs | 12 +- src/renderer.rs | 13 +- src/text.rs | 6 +- src/theme.rs | 192 +++++++++++++----- 27 files changed, 221 insertions(+), 135 deletions(-) diff --git a/src/cli/damage_tracking.rs b/src/cli/damage_tracking.rs index cfc63464..726a0532 100644 --- a/src/cli/damage_tracking.rs +++ b/src/cli/damage_tracking.rs @@ -1,6 +1,7 @@ use { crate::{ cli::{GlobalArgs, color::parse_color, duration::parse_duration}, + theme::TransferFunction, tools::tool_client::{ToolClient, with_tool_client}, wire::jay_damage_tracking::{SetVisualizerColor, SetVisualizerDecay, SetVisualizerEnabled}, }, @@ -85,12 +86,13 @@ impl DamageTracking { } DamageTrackingCmd::SetColor(c) => { let color = parse_color(&c.color); + let [r, g, b, a] = color.to_array(TransferFunction::Srgb); tc.send(SetVisualizerColor { self_id: dt, - r: color.r, - g: color.g, - b: color.b, - a: color.a, + r, + g, + b, + a, }); } DamageTrackingCmd::SetDecay(c) => { diff --git a/src/config/handler.rs b/src/config/handler.rs index f18ccc79..fb4ee4e5 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -14,7 +14,7 @@ use { output_schedule::map_cursor_hz, scale::Scale, state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State}, - theme::{Color, ThemeSized}, + theme::{Color, ThemeSized, TransferFunction}, tree::{ ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode, TearingMode, VrrMode, WsMoveConfig, move_ws_to_output, @@ -1599,8 +1599,8 @@ impl ConfigProxyHandler { fn handle_get_color(&self, colorable: Colorable) -> Result<(), CphError> { let color = self.get_color(colorable)?.get(); - let color = - jay_config::theme::Color::new_f32_premultiplied(color.r, color.g, color.b, color.a); + let [r, g, b, a] = color.to_array(TransferFunction::Srgb); + let color = jay_config::theme::Color::new_f32_premultiplied(r, g, b, a); self.respond(Response::GetColor { color }); Ok(()) } diff --git a/src/damage.rs b/src/damage.rs index 24e4bef2..86b0f4a6 100644 --- a/src/damage.rs +++ b/src/damage.rs @@ -93,7 +93,7 @@ impl DamageVisualizer { entry_added: Default::default(), enabled: Default::default(), decay: Cell::new(Duration::from_secs(2)), - color: Cell::new(Color::from_rgba_straight(255, 0, 0, 128)), + color: Cell::new(Color::from_srgba_straight(255, 0, 0, 128)), } } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 67a06bc1..e832a8e1 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -352,25 +352,16 @@ impl dyn GfxFramebuffer { acquire_sync: AcquireSync, release_sync: ReleaseSync, ) -> Result, GfxError> { - self.clear_with(acquire_sync, release_sync, 0.0, 0.0, 0.0, 0.0) + self.clear_with(acquire_sync, release_sync, &Color::TRANSPARENT) } pub fn clear_with( self: &Rc, acquire_sync: AcquireSync, release_sync: ReleaseSync, - r: f32, - g: f32, - b: f32, - a: f32, + color: &Color, ) -> Result, GfxError> { - self.render( - acquire_sync, - release_sync, - &[], - Some(&Color { r, g, b, a }), - None, - ) + self.render(acquire_sync, release_sync, &[], Some(color), None) } pub fn logical_size(&self, transform: Transform) -> (i32, i32) { diff --git a/src/gfx_apis/gl.rs b/src/gfx_apis/gl.rs index f10acb32..ff86fc95 100644 --- a/src/gfx_apis/gl.rs +++ b/src/gfx_apis/gl.rs @@ -84,7 +84,7 @@ use { GL_TRIANGLE_STRIP, GL_TRIANGLES, }, }, - theme::Color, + theme::{Color, TransferFunction}, utils::{errorfmt::ErrorFmt, rc_eq::rc_eq, vecstorage::VecStorage}, video::{ dmabuf::DMA_BUF_SYNC_READ, @@ -305,10 +305,11 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option { } fn fill_boxes3(ctx: &GlRenderContext, boxes: &[[f32; 2]], color: &Color) { + let [r, g, b, a] = color.to_array(TransferFunction::Srgb); let gles = ctx.ctx.dpy.gles; unsafe { (gles.glUseProgram)(ctx.fill_prog.prog); - (gles.glUniform4f)(ctx.fill_prog_color, color.r, color.g, color.b, color.a); + (gles.glUniform4f)(ctx.fill_prog_color, r, g, b, a); (gles.glVertexAttribPointer)( ctx.fill_prog_pos as _, 2, diff --git a/src/gfx_apis/gl/renderer/framebuffer.rs b/src/gfx_apis/gl/renderer/framebuffer.rs index 7c0c968d..550fdd18 100644 --- a/src/gfx_apis/gl/renderer/framebuffer.rs +++ b/src/gfx_apis/gl/renderer/framebuffer.rs @@ -18,7 +18,7 @@ use { sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, }, rect::Region, - theme::Color, + theme::{Color, TransferFunction}, }, std::{ cell::Cell, @@ -78,7 +78,8 @@ impl Framebuffer { (gles.glBindFramebuffer)(GL_FRAMEBUFFER, self.gl.fbo); (gles.glViewport)(0, 0, self.gl.width, self.gl.height); if let Some(c) = clear { - (gles.glClearColor)(c.r, c.g, c.b, c.a); + let [r, g, b, a] = c.to_array(TransferFunction::Srgb); + (gles.glClearColor)(r, g, b, a); (gles.glClear)(GL_COLOR_BUFFER_BIT); } (gles.glBlendFunc)(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 5eb50f1d..bce73eb1 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -30,7 +30,7 @@ use { }, io_uring::IoUring, rect::{Rect, Region}, - theme::Color, + theme::{Color, TransferFunction}, utils::{ copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, once::Once, stack::Stack, @@ -589,8 +589,8 @@ impl VulkanRenderer { let clear_value = ClearValue { color: ClearColorValue { float32: match pass { - RenderPass::BlendBuffer => clear.to_array_linear(None), - RenderPass::FrameBuffer => clear.to_array_srgb(None), + RenderPass::BlendBuffer => clear.to_array(TransferFunction::Linear), + RenderPass::FrameBuffer => clear.to_array(TransferFunction::Srgb), }, }, }; @@ -704,8 +704,12 @@ impl VulkanRenderer { let push = FillPushConstants { pos: r.rect.to_points(), color: match pass { - RenderPass::BlendBuffer => r.color.to_array_linear(r.alpha), - RenderPass::FrameBuffer => r.color.to_array_srgb(r.alpha), + RenderPass::BlendBuffer => { + r.color.to_array2(TransferFunction::Linear, r.alpha) + } + RenderPass::FrameBuffer => { + r.color.to_array2(TransferFunction::Srgb, r.alpha) + } }, }; let source_type = match push.color[3] < 1.0 { @@ -1309,7 +1313,7 @@ impl VulkanRenderer { for opt in opts.iter().rev() { let (opaque, fb_rect) = match opt { GfxApiOpt::Sync => continue, - GfxApiOpt::FillRect(f) => (f.effective_color().a >= 1.0, f.rect), + GfxApiOpt::FillRect(f) => (f.effective_color().is_opaque(), f.rect), GfxApiOpt::CopyTexture(c) => { let opaque = 'opaque: { if let Some(a) = c.alpha { diff --git a/src/ifs/jay_damage_tracking.rs b/src/ifs/jay_damage_tracking.rs index 53f60429..021f512c 100644 --- a/src/ifs/jay_damage_tracking.rs +++ b/src/ifs/jay_damage_tracking.rs @@ -4,7 +4,7 @@ use { globals::{Global, GlobalName}, leaks::Tracker, object::{Object, Version}, - theme::Color, + theme::{Color, TransferFunction}, wire::{ JayCompositorId, jay_damage_tracking::{ @@ -96,12 +96,8 @@ impl JayDamageTrackingRequestHandler for JayDamageTracking { req: SetVisualizerColor, _slf: &Rc, ) -> Result<(), Self::Error> { - self.client.state.damage_visualizer.set_color(Color { - r: req.r, - g: req.g, - b: req.b, - a: req.a, - }); + let color = Color::new(TransferFunction::Srgb, req.r, req.g, req.b) * req.a; + self.client.state.damage_visualizer.set_color(color); Ok(()) } diff --git a/src/ifs/wl_buffer.rs b/src/ifs/wl_buffer.rs index d42aff1d..4c3f4835 100644 --- a/src/ifs/wl_buffer.rs +++ b/src/ifs/wl_buffer.rs @@ -8,7 +8,6 @@ use { leaks::Tracker, object::{Object, Version}, rect::{Rect, Region}, - theme::Color, utils::errorfmt::ErrorFmt, video::dmabuf::DmaBuf, wire::{WlBufferId, wl_buffer::*}, @@ -42,7 +41,7 @@ pub struct WlBuffer { render_ctx_version: Cell, pub storage: RefCell>, shm: bool, - pub color: Option, + pub color: Option<[u32; 4]>, width: i32, height: i32, pub tracker: Tracker, @@ -149,7 +148,7 @@ impl WlBuffer { width: 1, height: 1, tracker: Default::default(), - color: Some(Color::from_u32_rgba_premultiplied(r, g, b, a)), + color: Some([r, g, b, a]), } } diff --git a/src/it/test_gfx_api.rs b/src/it/test_gfx_api.rs index 6d5be8e7..1069fbd9 100644 --- a/src/it/test_gfx_api.rs +++ b/src/it/test_gfx_api.rs @@ -435,7 +435,7 @@ impl GfxFramebuffer for TestGfxFb { a = 255; } staging[(y * width + x) as usize] = - Color::from_rgba_premultiplied(r, g, b, a); + Color::from_srgba_premultiplied(r, g, b, a); } data = data.add(stride as usize); } @@ -446,7 +446,7 @@ impl GfxFramebuffer for TestGfxFb { for y in 0..height { for x in 0..width { let [r, g, b, a] = - staging[(y * width + x) as usize].to_rgba_premultiplied(); + staging[(y * width + x) as usize].to_srgba_premultiplied(); *data.add((x * 4) as usize).cast::<[u8; 4]>() = [b, g, r, a]; } data = data.add(stride as usize); @@ -499,7 +499,7 @@ impl GfxFramebuffer for TestGfxFb { if !t_format.has_alpha { a = 255; } - let mut color = Color::from_rgba_premultiplied(r, g, b, a); + let mut color = Color::from_srgba_premultiplied(r, g, b, a); if let Some(alpha) = c.alpha { color = color * alpha; } diff --git a/src/it/test_ifs/test_shm_buffer.rs b/src/it/test_ifs/test_shm_buffer.rs index ffe4bdf9..7a2c7fee 100644 --- a/src/it/test_ifs/test_shm_buffer.rs +++ b/src/it/test_ifs/test_shm_buffer.rs @@ -19,7 +19,7 @@ pub struct TestShmBuffer { impl TestShmBuffer { pub fn fill(&self, color: Color) { - let [cr, cg, cb, ca] = color.to_rgba_premultiplied(); + let [cr, cg, cb, ca] = color.to_srgba_premultiplied(); for [b, g, r, a] in self.deref().array_chunks_ext::<4>() { r.set(cr); g.set(cg); diff --git a/src/it/test_ifs/test_single_pixel_buffer_manager.rs b/src/it/test_ifs/test_single_pixel_buffer_manager.rs index ed097aef..9fc989bb 100644 --- a/src/it/test_ifs/test_single_pixel_buffer_manager.rs +++ b/src/it/test_ifs/test_single_pixel_buffer_manager.rs @@ -4,7 +4,7 @@ use { test_error::TestResult, test_ifs::test_buffer::TestBuffer, test_object::TestObject, test_transport::TestTransport, }, - theme::Color, + theme::{Color, TransferFunction}, wire::{WpSinglePixelBufferManagerV1Id, wp_single_pixel_buffer_manager_v1::*}, }, std::{cell::Cell, rc::Rc}, @@ -31,13 +31,14 @@ impl TestSinglePixelBufferManager { destroyed: Cell::new(false), }); let map = |c: f32| (c as f64 * u32::MAX as f64) as u32; + let [r, g, b, a] = color.to_array(TransferFunction::Srgb); self.tran.send(CreateU32RgbaBuffer { self_id: self.id, id: obj.id, - r: map(color.r), - g: map(color.g), - b: map(color.b), - a: map(color.a), + r: map(r), + g: map(g), + b: map(b), + a: map(a), })?; self.tran.add_obj(obj.clone())?; Ok(obj) diff --git a/src/it/test_utils/test_surface_ext.rs b/src/it/test_utils/test_surface_ext.rs index 4bafe68d..85b1d660 100644 --- a/src/it/test_utils/test_surface_ext.rs +++ b/src/it/test_utils/test_surface_ext.rs @@ -39,6 +39,6 @@ impl TestSurfaceExt { } pub fn set_color(&self, r: u8, g: u8, b: u8, a: u8) { - self.color.set(Color::from_rgba_straight(r, g, b, a)); + self.color.set(Color::from_srgba_straight(r, g, b, a)); } } diff --git a/src/it/tests/t0007_subsurface.rs b/src/it/tests/t0007_subsurface.rs index 087b564d..ea75622f 100644 --- a/src/it/tests/t0007_subsurface.rs +++ b/src/it/tests/t0007_subsurface.rs @@ -34,7 +34,7 @@ async fn test(run: Rc) -> Result<(), TestError> { let buffer = client .spbm - .create_buffer(Color::from_rgba_straight(255, 255, 255, 255))?; + .create_buffer(Color::from_srgba_straight(255, 255, 255, 255))?; child.attach(buffer.id)?; child_viewport.set_source(0, 0, 1, 1)?; diff --git a/src/it/tests/t0020_surface_offset.rs b/src/it/tests/t0020_surface_offset.rs index 882c2f85..b7cc9d4a 100644 --- a/src/it/tests/t0020_surface_offset.rs +++ b/src/it/tests/t0020_surface_offset.rs @@ -25,7 +25,7 @@ async fn test(run: Rc) -> Result<(), TestError> { let win1 = client.create_window().await?; win1.map2().await?; - let buffer = client.spbm.create_buffer(Color::from_rgb(255, 0, 0))?; + let buffer = client.spbm.create_buffer(Color::from_srgb(255, 0, 0))?; let surface = client.comp.create_surface().await?; let vp = client.viewporter.get_viewport(&surface)?; vp.set_destination(100, 100)?; diff --git a/src/it/tests/t0039_alpha_modifier.rs b/src/it/tests/t0039_alpha_modifier.rs index eb7f85f6..0a90bc60 100644 --- a/src/it/tests/t0039_alpha_modifier.rs +++ b/src/it/tests/t0039_alpha_modifier.rs @@ -36,11 +36,11 @@ async fn test(run: Rc) -> TestResult { }}; } - let buf1 = client.spbm.create_buffer(Color::from_rgb(0, 255, 0))?; + let buf1 = client.spbm.create_buffer(Color::from_srgb(0, 255, 0))?; let (ss1, alpha1) = create_surface!(&buf1, 0, 0); let buf2 = client.shm.create_buffer(1, 1)?; - buf2.fill(Color::from_rgb(0, 255, 0)); + buf2.fill(Color::from_srgb(0, 255, 0)); let (ss2, alpha2) = create_surface!(&buf2.buffer, 100, 0); client.compare_screenshot("1", false).await?; diff --git a/src/it/tests/t0039_alpha_modifier/screenshot_2.qoi b/src/it/tests/t0039_alpha_modifier/screenshot_2.qoi index bf8d403628cb9901d00551b17f041232335f521c..5c908f15a8d75762b28029fd4578a816ac251b23 100644 GIT binary patch literal 8145 zcmXTS&rD-rU{+vYV2WU7_@@zCe*PZ=1H)e=@KpS~DH8YZh~xh=Ha12MfN$c+ykW+^ zVub9WZ~u1f1to?v|NbK-iWee(|NcFLP&mpT4Uf_EFq$7=CC6xaFj^iUmj|$PF-nhy zz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By N2#^#4?-(F}5daK}Z<_!B literal 8145 zcmXTS&rD-rU{+vYV2WU7_@@zCe*PZ=1H)e=@KpS~DH8YZh~xh=Ha12MfN$c+ykW+^ zVub9WZ~tnWK#AeZzyC;y;)U7Yzkkml6pr#o!(%i(jOGVe$uU|UjFtz;|>mU^Ewu=7P~&Fq#WSbHQjX7|jKvxnMLGjOK#T PTripoNXZ2Z5WolkOSVgE literal 10802 zcmXTS&rD-rU{+vYV2WU7_@@zCe*PZ=1H)e=@KpS~DH8YZh~xh=Ha12MfN$au-i?3u z?p}xfDJdyI`7m+mLs0cMMn(SKK!k=7G@PUl!C3$NM3Uix&{THAVpQaY^r6wv_&b^s z{(`d4Xi6AO34cdZ0<=~btsO?o1aMLqtsO?o1aLwatsO>dhtW3DXf7Dd1*5rOG#8BK zg3(+snhQpA!DucR%>|>mU^Ewu=7P~&Fq#WSbHQjX7|jKvxnMLGjOK#TTripoMsvYv PE*Q-Pq~rny2w(&NMn=7F diff --git a/src/it/tests/t0042_toplevel_select/screenshot_2.qoi b/src/it/tests/t0042_toplevel_select/screenshot_2.qoi index 61f8ac6211141e6067f1e3ac6096a13f037baaef..e25c77abf865aee9c4a16797b2c067560f91ef25 100644 GIT binary patch literal 10800 zcmXTS&rD-rU{+vYV2WU7_@@zCe*PZ=1H)e=@KpS~DH8YZh~xh=Ha12MfN%XB5blkC z_U>MX#GxFhnj6xiB7gthzzhu|XgEn9g0Y~<>W1{8e??Lgq0$IJmQj%#qoMKlhV-G) zl<;>nCHx&t3E%>8G$o9t1aLwaEffBZri9UyFj^jt=7P~&Fq#WSbHQjX7|jKvxnMLG zjOK#TTripoMsvYvE*Q-Pqq$%-7mVhD(OfW^3r2InXf7Dd1*5rOG#8BKg3(+snhQpA L0SUQ)0Rk8SH@8b3 literal 10800 zcmXTS&rD-rU{+vYV2WU7_@@zCe*PZ=1H)e=@KpS~DH8YZh~xh=Ha12MfN%XB5blkC z_U>MX#GxFhnj6xiB7gthzzhu|XgEn9g0Y~<>W1{8e|{p#P-%pq#i+=Q(a`vNL;BEY zO87gP68?^+1aN^kni57+0yrUzmI;4HQ^IIU7%dM+bHQjX7|jKvxnMLGjOK#TTripo zMsvYvE*Q-Pqq$%-7mVhD(OfW^3r2InXf7Dd1*5rOG#8BKg3(+snhQpA!DucR%>|>m LfP`GY00E2uz0|!T diff --git a/src/it/tests/t0042_toplevel_select/screenshot_3.qoi b/src/it/tests/t0042_toplevel_select/screenshot_3.qoi index 61f8ac6211141e6067f1e3ac6096a13f037baaef..e25c77abf865aee9c4a16797b2c067560f91ef25 100644 GIT binary patch literal 10800 zcmXTS&rD-rU{+vYV2WU7_@@zCe*PZ=1H)e=@KpS~DH8YZh~xh=Ha12MfN%XB5blkC z_U>MX#GxFhnj6xiB7gthzzhu|XgEn9g0Y~<>W1{8e??Lgq0$IJmQj%#qoMKlhV-G) zl<;>nCHx&t3E%>8G$o9t1aLwaEffBZri9UyFj^jt=7P~&Fq#WSbHQjX7|jKvxnMLG zjOK#TTripoMsvYvE*Q-Pqq$%-7mVhD(OfW^3r2InXf7Dd1*5rOG#8BKg3(+snhQpA L0SUQ)0Rk8SH@8b3 literal 10800 zcmXTS&rD-rU{+vYV2WU7_@@zCe*PZ=1H)e=@KpS~DH8YZh~xh=Ha12MfN%XB5blkC z_U>MX#GxFhnj6xiB7gthzzhu|XgEn9g0Y~<>W1{8e|{p#P-%pq#i+=Q(a`vNL;BEY zO87gP68?^+1aN^kni57+0yrUzmI;4HQ^IIU7%dM+bHQjX7|jKvxnMLGjOK#TTripo zMsvYvE*Q-Pqq$%-7mVhD(OfW^3r2InXf7Dd1*5rOG#8BKg3(+snhQpA!DucR%>|>m LfP`GY00E2uz0|!T diff --git a/src/portal/ptl_remote_desktop/remote_desktop_gui.rs b/src/portal/ptl_remote_desktop/remote_desktop_gui.rs index c616cb6b..0da1db0e 100644 --- a/src/portal/ptl_remote_desktop/remote_desktop_gui.rs +++ b/src/portal/ptl_remote_desktop/remote_desktop_gui.rs @@ -67,18 +67,18 @@ fn create_accept_gui(surface: &Rc) -> Rc { let accept_button = static_button(surface, ButtonRole::Accept, "Allow"); let reject_button = static_button(surface, ButtonRole::Reject, "Reject"); for button in [&accept_button, &reject_button] { - button.border_color.set(Color::from_gray(100)); + button.border_color.set(Color::from_gray_srgb(100)); button.border.set(2.0); button.padding.set(5.0); } - accept_button.bg_color.set(Color::from_rgb(170, 200, 170)); + accept_button.bg_color.set(Color::from_srgb(170, 200, 170)); accept_button .bg_hover_color - .set(Color::from_rgb(170, 255, 170)); - reject_button.bg_color.set(Color::from_rgb(200, 170, 170)); + .set(Color::from_srgb(170, 255, 170)); + reject_button.bg_color.set(Color::from_srgb(200, 170, 170)); reject_button .bg_hover_color - .set(Color::from_rgb(255, 170, 170)); + .set(Color::from_srgb(255, 170, 170)); let flow = Rc::new(Flow::default()); flow.orientation.set(Orientation::Vertical); flow.cross_align.set(Align::Center); diff --git a/src/portal/ptl_screencast/screencast_gui.rs b/src/portal/ptl_screencast/screencast_gui.rs index a4276be1..20ae0dff 100644 --- a/src/portal/ptl_screencast/screencast_gui.rs +++ b/src/portal/ptl_screencast/screencast_gui.rs @@ -87,22 +87,22 @@ fn create_accept_gui(surface: &Rc, for_restore: bool) -> Rc &window_button, &reject_button, ] { - button.border_color.set(Color::from_gray(100)); + button.border_color.set(Color::from_gray_srgb(100)); button.border.set(2.0); button.padding.set(5.0); } - restore_button.bg_color.set(Color::from_rgb(170, 170, 200)); + restore_button.bg_color.set(Color::from_srgb(170, 170, 200)); restore_button .bg_hover_color - .set(Color::from_rgb(170, 170, 255)); + .set(Color::from_srgb(170, 170, 255)); for button in [&accept_button, &workspace_button, &window_button] { - button.bg_color.set(Color::from_rgb(170, 200, 170)); - button.bg_hover_color.set(Color::from_rgb(170, 255, 170)); + button.bg_color.set(Color::from_srgb(170, 200, 170)); + button.bg_hover_color.set(Color::from_srgb(170, 255, 170)); } - reject_button.bg_color.set(Color::from_rgb(200, 170, 170)); + reject_button.bg_color.set(Color::from_srgb(200, 170, 170)); reject_button .bg_hover_color - .set(Color::from_rgb(255, 170, 170)); + .set(Color::from_srgb(255, 170, 170)); let flow = Rc::new(Flow::default()); flow.orientation.set(Orientation::Vertical); flow.cross_align.set(Align::Center); diff --git a/src/portal/ptl_text.rs b/src/portal/ptl_text.rs index 3804c101..32ecc0ed 100644 --- a/src/portal/ptl_text.rs +++ b/src/portal/ptl_text.rs @@ -7,7 +7,7 @@ use { consts::{CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE}, }, rect::Rect, - theme::Color, + theme::{Color, TransferFunction}, }, std::{ops::Neg, rc::Rc, sync::Arc}, }; @@ -78,9 +78,9 @@ pub fn render( let data = create_data(font, width, height, scale)?; data.layout.set_text(text); let font_height = data.layout.pixel_size().1; + let [r, g, b, a] = color.to_array(TransferFunction::Srgb); data.cctx.set_operator(CAIRO_OPERATOR_SOURCE); - data.cctx - .set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _); + data.cctx.set_source_rgba(r as _, g as _, b as _, a as _); let y = y.unwrap_or((height - font_height) / 2); data.cctx.move_to(x as f64, y as f64); data.layout.show_layout(); diff --git a/src/portal/ptr_gui.rs b/src/portal/ptr_gui.rs index 8d3630d9..458634c0 100644 --- a/src/portal/ptr_gui.rs +++ b/src/portal/ptr_gui.rs @@ -139,9 +139,9 @@ impl Default for Button { hover: Default::default(), padding: Default::default(), border: Default::default(), - border_color: Cell::new(Color::from_gray(0)), - bg_color: Cell::new(Color::from_gray(255)), - bg_hover_color: Cell::new(Color::from_gray(255)), + border_color: Cell::new(Color::from_gray_srgb(0)), + bg_color: Cell::new(Color::from_gray_srgb(255)), + bg_hover_color: Cell::new(Color::from_gray_srgb(255)), text: Default::default(), font: Arc::new(DEFAULT_FONT.to_string()), tex: Default::default(), @@ -172,7 +172,7 @@ impl GuiElement for Button { None, &self.font, &text, - Color::from_gray(0), + Color::from_gray_srgb(0), Some(scale as _), true, ); @@ -296,7 +296,7 @@ impl GuiElement for Label { None, &self.font, &text, - Color::from_gray(255), + Color::from_gray_srgb(255), Some(scale as _), false, ); @@ -635,7 +635,7 @@ impl WindowData { AcquireSync::Implicit, ReleaseSync::Implicit, self.scale.get(), - Some(&Color::from_gray(0)), + Some(&Color::from_gray_srgb(0)), None, &mut |r| { if let Some(content) = self.content.get() { diff --git a/src/renderer.rs b/src/renderer.rs index fa8a0458..9c48ba11 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -11,7 +11,7 @@ use { renderer::renderer_base::RendererBase, scale::Scale, state::State, - theme::Color, + theme::{Color, TransferFunction}, tree::{ ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData, ToplevelNodeBase, WorkspaceNode, @@ -202,7 +202,7 @@ impl Renderer<'_> { let pos = placeholder.tl_data().pos.get(); self.base.fill_boxes( std::slice::from_ref(&pos.at_point(x, y)), - &Color::from_rgba_straight(20, 20, 20, 255), + &Color::from_srgba_straight(20, 20, 20, 255), ); if let Some(tex) = placeholder.textures.borrow().get(&self.base.scale) { if let Some(texture) = tex.texture() { @@ -448,8 +448,15 @@ impl Renderer<'_> { Some(bounds) => rect.intersect(*bounds), }; if !rect.is_empty() { + let color = Color::from_u32_premultiplied( + TransferFunction::Srgb, + color[0], + color[1], + color[2], + color[3], + ); self.base.ops.push(GfxApiOpt::Sync); - self.base.fill_scaled_boxes(&[rect], color, alpha); + self.base.fill_scaled_boxes(&[rect], &color, alpha); } } } else { diff --git a/src/text.rs b/src/text.rs index 6a0c972e..c8b2ab0c 100644 --- a/src/text.rs +++ b/src/text.rs @@ -14,7 +14,7 @@ use { }, }, rect::{Rect, Region}, - theme::Color, + theme::{Color, TransferFunction}, utils::{ clonecell::CloneCell, double_buffered::DoubleBuffered, on_drop_event::OnDropEvent, }, @@ -187,9 +187,9 @@ fn render( data.layout.set_text(text); } let font_height = data.layout.pixel_size().1; + let [r, g, b, a] = color.to_array(TransferFunction::Srgb); data.cctx.set_operator(CAIRO_OPERATOR_SOURCE); - data.cctx - .set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _); + data.cctx.set_source_rgba(r as _, g as _, b as _, a as _); let y = y.unwrap_or((height - font_height) / 2); data.cctx.move_to(x as f64, y as f64); data.layout.show_layout(); diff --git a/src/theme.rs b/src/theme.rs index 16dd235c..72e594b7 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -3,12 +3,18 @@ use { std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc}, }; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TransferFunction { + Srgb, + Linear, +} + #[derive(Copy, Clone, Debug, PartialEq)] pub struct Color { - pub r: f32, - pub g: f32, - pub b: f32, - pub a: f32, + r: f32, + g: f32, + b: f32, + a: f32, } impl Eq for Color {} @@ -65,77 +71,155 @@ impl Color { a: 1.0, }; - pub fn from_gray(g: u8) -> Self { - Self::from_rgb(g, g, g) + pub fn new(transfer_function: TransferFunction, mut r: f32, mut g: f32, mut b: f32) -> Self { + fn srgb(c: f32) -> f32 { + if c <= 0.04045 { + c / 12.92 + } else { + ((c + 0.055) / 1.055).powf(2.4) + } + } + #[inline(always)] + fn linear(c: f32) -> f32 { + c + } + macro_rules! convert { + ($tf:ident) => {{ + r = $tf(r); + g = $tf(g); + b = $tf(b); + }}; + } + match transfer_function { + TransferFunction::Srgb => convert!(srgb), + TransferFunction::Linear => convert!(linear), + } + Self { r, g, b, a: 1.0 } } - pub fn from_rgb(r: u8, g: u8, b: u8) -> Self { - Self { - r: to_f32(r), - g: to_f32(g), - b: to_f32(b), - a: 1.0, + pub fn new_premultiplied( + transfer_function: TransferFunction, + mut r: f32, + mut g: f32, + mut b: f32, + a: f32, + ) -> Self { + if transfer_function == TransferFunction::Linear { + return Self { r, g, b, a }; } + if a < 1.0 && a > 0.0 { + for c in [&mut r, &mut g, &mut b] { + *c /= a; + } + } + let mut c = Self::new(transfer_function, r, g, b); + if a < 1.0 { + c = c * a; + } + c + } + + pub fn is_opaque(&self) -> bool { + self.a >= 1.0 + } + + pub fn from_gray_srgb(g: u8) -> Self { + Self::from_srgb(g, g, g) + } + + pub fn from_srgb(r: u8, g: u8, b: u8) -> Self { + Self::new(TransferFunction::Srgb, to_f32(r), to_f32(g), to_f32(b)) } #[cfg_attr(not(feature = "it"), expect(dead_code))] - pub fn from_rgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self { - Self { - r: to_f32(r), - g: to_f32(g), - b: to_f32(b), - a: to_f32(a), - } + pub fn from_srgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self { + Self::new_premultiplied( + TransferFunction::Srgb, + to_f32(r), + to_f32(g), + to_f32(b), + to_f32(a), + ) } - pub fn from_u32_rgba_premultiplied(r: u32, g: u32, b: u32, a: u32) -> Self { + pub fn from_u32_premultiplied( + transfer_function: TransferFunction, + r: u32, + g: u32, + b: u32, + a: u32, + ) -> Self { fn to_f32(c: u32) -> f32 { ((c as f64) / (u32::MAX as f64)) as f32 } - Self { - r: to_f32(r), - g: to_f32(g), - b: to_f32(b), - a: to_f32(a), - } + Self::new_premultiplied( + transfer_function, + to_f32(r), + to_f32(g), + to_f32(b), + to_f32(a), + ) } - pub fn from_rgba_straight(r: u8, g: u8, b: u8, a: u8) -> Self { - let alpha = to_f32(a); - Self { - r: to_f32(r) * alpha, - g: to_f32(g) * alpha, - b: to_f32(b) * alpha, - a: alpha, + pub fn from_srgba_straight(r: u8, g: u8, b: u8, a: u8) -> Self { + let mut c = Self::new(TransferFunction::Srgb, to_f32(r), to_f32(g), to_f32(b)); + if a < 255 { + c = c * to_f32(a); } + c } #[cfg_attr(not(feature = "it"), expect(dead_code))] - pub fn to_rgba_premultiplied(self) -> [u8; 4] { - [to_u8(self.r), to_u8(self.g), to_u8(self.b), to_u8(self.a)] + pub fn to_srgba_premultiplied(self) -> [u8; 4] { + let [r, g, b, a] = self.to_array(TransferFunction::Srgb); + [to_u8(r), to_u8(g), to_u8(b), to_u8(a)] } - pub fn to_array_srgb(self, alpha: Option) -> [f32; 4] { - let a = alpha.unwrap_or(1.0); - [self.r * a, self.g * a, self.b * a, self.a * a] + pub fn to_array(self, transfer_function: TransferFunction) -> [f32; 4] { + self.to_array2(transfer_function, None) } - pub fn to_array_linear(self, alpha: Option) -> [f32; 4] { - fn to_linear(srgb: f32) -> f32 { - if srgb <= 0.04045 { - srgb / 12.92 + pub fn to_array2(self, transfer_function: TransferFunction, alpha: Option) -> [f32; 4] { + let mut res = [self.r, self.g, self.b, self.a]; + fn srgb(c: f32) -> f32 { + if c <= 0.0031308 { + c * 12.92 } else { - ((srgb + 0.055) / 1.055).powf(2.4) + 1.055 * c.powf(1.0 / 2.4) - 0.055 } } - let a1 = if self.a == 0.0 { 1.0 } else { self.a }; - let a2 = self.a * alpha.unwrap_or(1.0); - [ - to_linear(self.r / a1) * a2, - to_linear(self.g / a1) * a2, - to_linear(self.b / a1) * a2, - a2, - ] + fn linear(c: f32) -> f32 { + c + } + macro_rules! convert { + ($tf:ident) => {{ + for c in &mut res[..3] { + *c = $tf(*c); + } + }}; + } + if transfer_function != TransferFunction::Linear { + if self.a < 1.0 && self.a > 0.0 { + for c in &mut res[..3] { + *c /= self.a; + } + } + match transfer_function { + TransferFunction::Srgb => convert!(srgb), + TransferFunction::Linear => convert!(linear), + } + if self.a < 1.0 { + for c in &mut res[..3] { + *c *= self.a; + } + } + } + if let Some(a) = alpha { + for c in &mut res { + *c *= a; + } + } + res } #[cfg_attr(not(feature = "it"), expect(dead_code))] @@ -152,7 +236,7 @@ impl Color { impl From for Color { fn from(f: jay_config::theme::Color) -> Self { let [r, g, b, a] = f.to_f32_premultiplied(); - Self { r, g, b, a } + Self::new_premultiplied(TransferFunction::Srgb, r, g, b, a) } } @@ -184,10 +268,10 @@ macro_rules! colors { } }; (@colors ($r:expr, $g:expr, $b:expr)) => { - Color::from_rgb($r, $g, $b) + Color::from_srgb($r, $g, $b) }; (@colors ($r:expr, $g:expr, $b:expr, $a:expr)) => { - Color::from_rgba_straight($r, $g, $b, $a) + Color::from_srgba_straight($r, $g, $b, $a) }; } From db4ff003307119749c3d29257f725a669bf3fcc8 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 13:45:07 +0100 Subject: [PATCH 04/12] vulkan: pre-processes GfxApiOpt --- src/gfx_apis/vulkan/renderer.rs | 354 ++++++++++++++++++++------------ 1 file changed, 219 insertions(+), 135 deletions(-) diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index bce73eb1..f5f6e927 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -31,10 +31,7 @@ use { io_uring::IoUring, rect::{Rect, Region}, theme::{Color, TransferFunction}, - utils::{ - copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, once::Once, - stack::Stack, - }, + utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack}, video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file}, }, ahash::AHashMap, @@ -55,13 +52,15 @@ use { }, isnt::std_1::{collections::IsntHashMapExt, primitive::IsntSliceExt}, jay_algorithms::rect::Tag, - linearize::{Linearize, StaticMap, static_map}, + linearize::{Linearize, LinearizeExt, StaticMap, static_map}, std::{ borrow::Cow, cell::{Cell, RefCell}, collections::hash_map::Entry, fmt::{Debug, Formatter}, - mem, ptr, + mem, + ops::Range, + ptr, rc::{Rc, Weak}, slice, }, @@ -128,13 +127,13 @@ pub(super) struct UsedTexture { release_sync: ReleaseSync, } -#[derive(Linearize)] +#[derive(Copy, Clone, Linearize)] pub(super) enum TexCopyType { Identity, Multiply, } -#[derive(Linearize)] +#[derive(Copy, Clone, Linearize)] pub(super) enum TexSourceType { Opaque, HasAlpha, @@ -158,6 +157,33 @@ pub(super) struct Memory { resource_descriptor_buffer_writer: VulkanDescriptorBufferWriter, regions_1: Vec, regions_2: Vec>, + ops: StaticMap>, + fill_targets: Vec, + tex_targets: Vec<[Point; 2]>, +} + +type Point = [[f32; 2]; 4]; + +enum VulkanOp { + Fill(VulkanFillOp), + Tex(VulkanTexOp), +} + +struct VulkanTexOp { + tex: Rc, + range: Range, + buffer_resv: Option>, + acquire_sync: AcquireSync, + release_sync: ReleaseSync, + alpha: f32, + source_type: TexSourceType, + copy_type: TexCopyType, +} + +struct VulkanFillOp { + range: Range, + color: [f32; 4], + source_type: TexSourceType, } #[derive(Copy, Clone, Debug, Linearize, Eq, PartialEq)] @@ -375,7 +401,6 @@ impl VulkanRenderer { fn create_descriptor_buffers( &self, buf: CommandBuffer, - opts: &[GfxApiOpt], bb: Option<&VulkanImage>, ) -> Result<(), VulkanError> { let Some(db) = &self.device.descriptor_buffer else { @@ -396,21 +421,23 @@ impl VulkanRenderer { let mut writer = resource_writer.add_set(layout); writer.write(layout.offsets[0], &bb.sampled_image_descriptor); } - for cmd in opts { - let GfxApiOpt::CopyTexture(c) = cmd else { - continue; - }; - let tex = c.tex.clone().into_vk(&self.device.device); - if tex.descriptor_buffer_version.replace(version) == version { - continue; + for pass in RenderPass::variants() { + for cmd in &memory.ops[pass] { + let VulkanOp::Tex(c) = cmd else { + continue; + }; + let tex = &c.tex; + if tex.descriptor_buffer_version.replace(version) == version { + continue; + } + let offset = sampler_writer.next_offset(); + tex.descriptor_buffer_offset.set(offset); + let mut writer = sampler_writer.add_set(&self.tex_descriptor_set_layout); + writer.write( + self.tex_descriptor_set_layout.offsets[0], + &tex.shader_read_only_optimal_descriptor, + ); } - let offset = sampler_writer.next_offset(); - tex.descriptor_buffer_offset.set(offset); - let mut writer = sampler_writer.add_set(&self.tex_descriptor_set_layout); - writer.write( - self.tex_descriptor_set_layout.offsets[0], - &tex.shader_read_only_optimal_descriptor, - ); } let mut infos = ArrayVec::<_, 2>::new(); for (writer, cache) in [ @@ -433,35 +460,131 @@ impl VulkanRenderer { Ok(()) } - fn collect_memory(&self, opts: &[GfxApiOpt]) { + fn convert_ops(&self, opts: &[GfxApiOpt]) { + zone!("convert_ops"); + let memory = &mut *self.memory.borrow_mut(); + for ops in memory.ops.values_mut() { + ops.clear(); + } + memory.tex_targets.clear(); + memory.fill_targets.clear(); + for op in opts { + match op { + GfxApiOpt::Sync => { + // nothing + } + GfxApiOpt::FillRect(fr) => { + let target = fr.rect.to_points(); + for pass in RenderPass::variants() { + let tf = match pass { + RenderPass::BlendBuffer => TransferFunction::Linear, + RenderPass::FrameBuffer => TransferFunction::Srgb, + }; + let ops = &mut memory.ops[pass]; + let lo = memory.fill_targets.len(); + for region in &memory.paint_regions[pass] { + let mut target = target; + if !region.constrain(&mut target, None) { + continue; + } + memory.fill_targets.push(target); + } + let hi = memory.fill_targets.len(); + if lo == hi { + continue; + } + let color = fr.color.to_array2(tf, fr.alpha); + let source_type = match color[3] < 1.0 { + false => TexSourceType::Opaque, + true => TexSourceType::HasAlpha, + }; + ops.push(VulkanOp::Fill(VulkanFillOp { + range: lo..hi, + color, + source_type, + })); + } + } + GfxApiOpt::CopyTexture(ct) => { + let tex = ct.tex.clone().into_vk(&self.device.device); + if tex.contents_are_undefined.get() { + log::warn!("Ignoring undefined texture"); + continue; + } + if tex.queue_state.get().acquire(QueueFamily::Gfx) == QueueTransfer::Impossible + { + log::warn!("Ignoring texture owned by different queue"); + continue; + } + let target = ct.target.to_points(); + let source = ct.source.to_points(); + for pass in RenderPass::variants() { + let ops = &mut memory.ops[pass]; + let lo = memory.tex_targets.len(); + for region in &memory.paint_regions[pass] { + let mut target = target; + let mut source = source; + if !region.constrain(&mut target, Some(&mut source)) { + continue; + } + memory.tex_targets.push([target, source]); + } + let hi = memory.tex_targets.len(); + if lo == hi { + continue; + } + let copy_type = match ct.alpha.is_some() { + true => TexCopyType::Multiply, + false => TexCopyType::Identity, + }; + let source_type = match tex.format.has_alpha && !ct.opaque { + true => TexSourceType::HasAlpha, + false => TexSourceType::Opaque, + }; + ops.push(VulkanOp::Tex(VulkanTexOp { + tex: tex.clone(), + range: lo..hi, + buffer_resv: ct.buffer_resv.clone(), + acquire_sync: ct.acquire_sync.clone(), + release_sync: ct.release_sync, + alpha: ct.alpha.unwrap_or_default(), + source_type, + copy_type, + })); + } + } + } + } + } + + fn collect_memory(&self) { zone!("collect_memory"); - let mut memory = self.memory.borrow_mut(); + let memory = &mut *self.memory.borrow_mut(); memory.dmabuf_sample.clear(); memory.queue_transfer.clear(); let execution = self.allocate_point(); - for cmd in opts { - if let GfxApiOpt::CopyTexture(c) = cmd { - let tex = c.tex.clone().into_vk(&self.device.device); - if tex.contents_are_undefined.get() { - continue; + for pass in RenderPass::variants() { + for cmd in &memory.ops[pass] { + if let VulkanOp::Tex(c) = cmd { + let tex = &c.tex; + if tex.execution_version.replace(execution) == execution { + continue; + } + match tex.queue_state.get().acquire(QueueFamily::Gfx) { + QueueTransfer::Unnecessary => {} + QueueTransfer::Possible => memory.queue_transfer.push(tex.clone()), + QueueTransfer::Impossible => continue, + } + if let VulkanImageMemory::DmaBuf(_) = &tex.ty { + memory.dmabuf_sample.push(tex.clone()) + } + memory.textures.push(UsedTexture { + tex: tex.clone(), + resv: c.buffer_resv.clone(), + acquire_sync: c.acquire_sync.clone(), + release_sync: c.release_sync, + }); } - if tex.execution_version.replace(execution) == execution { - continue; - } - match tex.queue_state.get().acquire(QueueFamily::Gfx) { - QueueTransfer::Unnecessary => {} - QueueTransfer::Possible => memory.queue_transfer.push(tex.clone()), - QueueTransfer::Impossible => continue, - } - if let VulkanImageMemory::DmaBuf(_) = &tex.ty { - memory.dmabuf_sample.push(tex.clone()) - } - memory.textures.push(UsedTexture { - tex, - resv: c.buffer_resv.clone(), - acquire_sync: c.acquire_sync.clone(), - release_sync: c.release_sync, - }); } } } @@ -680,12 +803,10 @@ impl VulkanRenderer { &self, buf: CommandBuffer, target: &VulkanImage, - opts: &[GfxApiOpt], pass: RenderPass, ) -> Result<(), VulkanError> { zone!("record_draws"); let memory = &*self.memory.borrow(); - let paint_regions = &memory.paint_regions[pass]; let pipelines = self.get_or_create_pipelines(target.format.vk_format, pass)?; let dev = &self.device.device; let mut current_pipeline = None; @@ -697,33 +818,16 @@ impl VulkanRenderer { } } }; - for opt in opts { + for opt in &memory.ops[pass] { match opt { - GfxApiOpt::Sync => {} - GfxApiOpt::FillRect(r) => { - let push = FillPushConstants { - pos: r.rect.to_points(), - color: match pass { - RenderPass::BlendBuffer => { - r.color.to_array2(TransferFunction::Linear, r.alpha) - } - RenderPass::FrameBuffer => { - r.color.to_array2(TransferFunction::Srgb, r.alpha) - } - }, - }; - let source_type = match push.color[3] < 1.0 { - true => TexSourceType::HasAlpha, - false => TexSourceType::Opaque, - }; - let pipeline = &pipelines.fill[source_type]; - for region in paint_regions { - let mut push = push; - let draw = region.constrain(&mut push.pos, None); - if !draw { - continue; - } - bind(pipeline); + VulkanOp::Fill(r) => { + let pipeline = &pipelines.fill[r.source_type]; + bind(pipeline); + for &pos in &memory.fill_targets[r.range.clone()] { + let push = FillPushConstants { + pos, + color: r.color, + }; unsafe { dev.cmd_push_constants( buf, @@ -736,65 +840,44 @@ impl VulkanRenderer { } } } - GfxApiOpt::CopyTexture(c) => { - let tex = c.tex.as_vk(&self.device.device); - if tex.contents_are_undefined.get() { - log::warn!("Ignoring undefined texture"); - continue; - } - if tex.queue_state.get().acquire(QueueFamily::Gfx) == QueueTransfer::Impossible - { - log::warn!("Ignoring texture owned by different queue"); - continue; - } - let copy_type = match c.alpha.is_some() { - true => TexCopyType::Multiply, - false => TexCopyType::Identity, - }; - let source_type = match tex.format.has_alpha && !c.opaque { - true => TexSourceType::HasAlpha, - false => TexSourceType::Opaque, - }; - let pipeline = &pipelines.tex[copy_type][source_type]; - let push = TexPushConstants { - pos: c.target.to_points(), - tex_pos: c.source.to_points(), - alpha: c.alpha.unwrap_or_default(), - }; + VulkanOp::Tex(c) => { + let tex = &c.tex; + let pipeline = &pipelines.tex[c.copy_type][c.source_type]; + bind(pipeline); let image_info = DescriptorImageInfo::default() .image_view(tex.texture_view) .image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL); - let init = Once::default(); - for region in paint_regions { - let mut push = push; - let draw = region.constrain(&mut push.pos, Some(&mut push.tex_pos)); - if !draw { - continue; + if let Some(db) = &self.device.descriptor_buffer { + unsafe { + db.cmd_set_descriptor_buffer_offsets( + buf, + PipelineBindPoint::GRAPHICS, + pipeline.pipeline_layout, + 0, + &[0], + &[tex.descriptor_buffer_offset.get()], + ); } - init.exec(|| unsafe { - bind(pipeline); - if let Some(db) = &self.device.descriptor_buffer { - db.cmd_set_descriptor_buffer_offsets( - buf, - PipelineBindPoint::GRAPHICS, - pipeline.pipeline_layout, - 0, - &[0], - &[tex.descriptor_buffer_offset.get()], - ); - } else { - let write_descriptor_set = WriteDescriptorSet::default() - .descriptor_type(DescriptorType::COMBINED_IMAGE_SAMPLER) - .image_info(slice::from_ref(&image_info)); - self.device.push_descriptor.cmd_push_descriptor_set( - buf, - PipelineBindPoint::GRAPHICS, - pipeline.pipeline_layout, - 0, - slice::from_ref(&write_descriptor_set), - ); - } - }); + } else { + let write_descriptor_set = WriteDescriptorSet::default() + .descriptor_type(DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(slice::from_ref(&image_info)); + unsafe { + self.device.push_descriptor.cmd_push_descriptor_set( + buf, + PipelineBindPoint::GRAPHICS, + pipeline.pipeline_layout, + 0, + slice::from_ref(&write_descriptor_set), + ); + } + } + for &[pos, tex_pos] in &memory.tex_targets[c.range.clone()] { + let push = TexPushConstants { + pos, + tex_pos, + alpha: c.alpha, + }; unsafe { dev.cmd_push_constants( buf, @@ -1430,23 +1513,24 @@ impl VulkanRenderer { self.elide_blend_buffer(&mut blend_buffer); let bb = blend_buffer.as_deref(); let buf = self.gfx_command_buffers.allocate()?; - self.collect_memory(opts); + self.convert_ops(opts); + self.collect_memory(); self.begin_command_buffer(buf.buffer)?; - self.create_descriptor_buffers(buf.buffer, opts, bb)?; + self.create_descriptor_buffers(buf.buffer, bb)?; self.initial_barriers(buf.buffer, fb)?; self.set_viewport(buf.buffer, fb); if let Some(bb) = bb { zone!("blend buffer pass"); self.blend_buffer_initial_barrier(buf.buffer, bb); self.begin_rendering(buf.buffer, bb, clear, RenderPass::BlendBuffer); - self.record_draws(buf.buffer, bb, opts, RenderPass::BlendBuffer)?; + self.record_draws(buf.buffer, bb, RenderPass::BlendBuffer)?; self.end_rendering(buf.buffer); self.blend_buffer_final_barrier(buf.buffer, bb); } { zone!("frame buffer pass"); self.begin_rendering(buf.buffer, fb, clear, RenderPass::FrameBuffer); - self.record_draws(buf.buffer, fb, opts, RenderPass::FrameBuffer)?; + self.record_draws(buf.buffer, fb, RenderPass::FrameBuffer)?; if let Some(bb) = bb { self.blend_buffer_copy(buf.buffer, fb, bb)?; } @@ -1581,7 +1665,7 @@ async fn await_release( } impl PaintRegion { - fn constrain(&self, pos: &mut [[f32; 2]; 4], tex_pos: Option<&mut [[f32; 2]; 4]>) -> bool { + fn constrain(&self, pos: &mut Point, tex_pos: Option<&mut Point>) -> bool { zone!("constrain"); let mut npos = *pos; for [x, y] in &mut npos { From c83e3ffa4d6afb06b441780b96d64fb24e3a4ed0 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 14:07:24 +0100 Subject: [PATCH 05/12] vulkan: sort ops by type --- src/gfx_apis/vulkan/renderer.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index f5f6e927..22b6162b 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -158,6 +158,7 @@ pub(super) struct Memory { regions_1: Vec, regions_2: Vec>, ops: StaticMap>, + ops_tmp: StaticMap>, fill_targets: Vec, tex_targets: Vec<[Point; 2]>, } @@ -466,12 +467,34 @@ impl VulkanRenderer { for ops in memory.ops.values_mut() { ops.clear(); } + for ops in memory.ops_tmp.values_mut() { + ops.clear(); + } memory.tex_targets.clear(); memory.fill_targets.clear(); + let sync = |memory: &mut Memory| { + for pass in RenderPass::variants() { + let ops = &mut memory.ops_tmp[pass]; + ops.sort_unstable_by_key(|o| { + #[derive(Eq, PartialEq, PartialOrd, Ord)] + enum Key { + Fill { color: [u32; 4] }, + Tex, + } + match o { + VulkanOp::Fill(f) => Key::Fill { + color: f.color.map(|c| c.to_bits()), + }, + VulkanOp::Tex(_) => Key::Tex, + } + }); + memory.ops[pass].append(ops); + } + }; for op in opts { match op { GfxApiOpt::Sync => { - // nothing + sync(memory); } GfxApiOpt::FillRect(fr) => { let target = fr.rect.to_points(); @@ -480,7 +503,7 @@ impl VulkanRenderer { RenderPass::BlendBuffer => TransferFunction::Linear, RenderPass::FrameBuffer => TransferFunction::Srgb, }; - let ops = &mut memory.ops[pass]; + let ops = &mut memory.ops_tmp[pass]; let lo = memory.fill_targets.len(); for region in &memory.paint_regions[pass] { let mut target = target; @@ -519,7 +542,7 @@ impl VulkanRenderer { let target = ct.target.to_points(); let source = ct.source.to_points(); for pass in RenderPass::variants() { - let ops = &mut memory.ops[pass]; + let ops = &mut memory.ops_tmp[pass]; let lo = memory.tex_targets.len(); for region in &memory.paint_regions[pass] { let mut target = target; @@ -555,6 +578,7 @@ impl VulkanRenderer { } } } + sync(memory); } fn collect_memory(&self) { From c16ea9325e1d189bb6b83281539cda4bcb007035 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 16:17:16 +0100 Subject: [PATCH 06/12] vulkan: add early-out for ops outside of damage region --- src/gfx_apis/vulkan/renderer.rs | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 22b6162b..31be1a6b 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -150,6 +150,7 @@ pub(super) struct Memory { release_fence: Option>, release_sync_file: Option, descriptor_buffers: ArrayVec, + paint_bounds: StaticMap>, paint_regions: StaticMap>, clear_rects: StaticMap>, image_copy_regions: Vec>, @@ -193,6 +194,7 @@ pub(super) enum RenderPass { FrameBuffer, } +#[derive(Copy, Clone)] struct PaintRegion { x1: f32, y1: f32, @@ -499,6 +501,12 @@ impl VulkanRenderer { GfxApiOpt::FillRect(fr) => { let target = fr.rect.to_points(); for pass in RenderPass::variants() { + let Some(bounds) = memory.paint_bounds[pass] else { + continue; + }; + if !bounds.intersects(&target) { + continue; + } let tf = match pass { RenderPass::BlendBuffer => TransferFunction::Linear, RenderPass::FrameBuffer => TransferFunction::Srgb, @@ -542,6 +550,12 @@ impl VulkanRenderer { let target = ct.target.to_points(); let source = ct.source.to_points(); for pass in RenderPass::variants() { + let Some(bounds) = memory.paint_bounds[pass] else { + continue; + }; + if !bounds.intersects(&target) { + continue; + } let ops = &mut memory.ops_tmp[pass]; let lo = memory.tex_targets.len(); for region in &memory.paint_regions[pass] { @@ -1479,6 +1493,18 @@ impl VulkanRenderer { y2: to_fb(y2, fb.height), }); } + for pass in RenderPass::variants() { + let regions = &memory.paint_regions[pass]; + if regions.is_empty() { + memory.paint_bounds[pass] = None; + } else { + let mut union = regions[0]; + for region in ®ions[1..] { + union = union.union(region); + } + memory.paint_bounds[pass] = Some(union); + } + } let blend_clear = clear_region.intersect(&Region::from_rects2(&memory.regions_1)); let opaque_clear = clear_region.subtract_cow(&blend_clear); // if bb.is_none() { @@ -1689,6 +1715,30 @@ async fn await_release( } impl PaintRegion { + fn intersects(&self, pos: &Point) -> bool { + let mut p = *pos; + for [x, y] in &mut p { + *x = x.clamp(self.x1, self.x2); + *y = y.clamp(self.y1, self.y2); + } + if p[0] == p[1] && p[2] == p[3] { + return false; + } + if p[0] == p[2] && p[1] == p[3] { + return false; + } + true + } + + fn union(&self, other: &Self) -> Self { + Self { + x1: self.x1.min(other.x1), + y1: self.y1.min(other.y1), + x2: self.x2.max(other.x2), + y2: self.y2.max(other.y2), + } + } + fn constrain(&self, pos: &mut Point, tex_pos: Option<&mut Point>) -> bool { zone!("constrain"); let mut npos = *pos; From f84934bd9a88c451e0b64a4862b3ad1412740548 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 14:48:10 +0100 Subject: [PATCH 07/12] vulkan: replace DescriptorBufferCache by generic cache --- src/gfx_apis/vulkan.rs | 1 + src/gfx_apis/vulkan/buffer_cache.rs | 158 +++++++++++++++++++++++ src/gfx_apis/vulkan/descriptor_buffer.rs | 155 +--------------------- src/gfx_apis/vulkan/renderer.rs | 17 ++- 4 files changed, 169 insertions(+), 162 deletions(-) create mode 100644 src/gfx_apis/vulkan/buffer_cache.rs diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index befd77d2..4a177b79 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -1,6 +1,7 @@ mod allocator; mod blend_buffer; mod bo_allocator; +mod buffer_cache; mod command; mod descriptor; mod descriptor_buffer; diff --git a/src/gfx_apis/vulkan/buffer_cache.rs b/src/gfx_apis/vulkan/buffer_cache.rs new file mode 100644 index 00000000..d6654e73 --- /dev/null +++ b/src/gfx_apis/vulkan/buffer_cache.rs @@ -0,0 +1,158 @@ +use { + crate::{ + gfx_apis::vulkan::{ + VulkanError, + allocator::{VulkanAllocation, VulkanAllocator}, + device::VulkanDevice, + }, + utils::on_drop::OnDrop, + }, + ash::vk::{ + Buffer, BufferCreateInfo, BufferDeviceAddressInfo, BufferUsageFlags, DeviceAddress, + DeviceSize, + }, + gpu_alloc::UsageFlags, + std::{cell::RefCell, mem::ManuallyDrop, rc::Rc}, +}; + +pub struct VulkanBufferCache { + device: Rc, + allocator: Rc, + buffers: RefCell>, + usage: BufferUsageFlags, +} + +pub struct VulkanBuffer { + cache: Rc, + pub buffer: ManuallyDrop, +} + +pub struct VulkanBufferUnused { + device: Rc, + pub size: DeviceSize, + pub buffer: Buffer, + pub allocation: VulkanAllocation, + pub address: DeviceAddress, +} + +impl VulkanBufferCache { + pub fn new( + device: &Rc, + allocator: &Rc, + usage: BufferUsageFlags, + ) -> Rc { + Rc::new(Self { + device: device.clone(), + allocator: allocator.clone(), + buffers: Default::default(), + usage, + }) + } + + pub fn for_descriptor_buffer( + device: &Rc, + allocator: &Rc, + has_sampler: bool, + ) -> Rc { + let mut usage = BufferUsageFlags::RESOURCE_DESCRIPTOR_BUFFER_EXT + | BufferUsageFlags::SHADER_DEVICE_ADDRESS; + if has_sampler { + usage |= BufferUsageFlags::SAMPLER_DESCRIPTOR_BUFFER_EXT; + } + Self::new(device, allocator, usage) + } + + pub fn usage(&self) -> BufferUsageFlags { + self.usage + } + + pub fn allocate(self: &Rc, capacity: DeviceSize) -> Result { + const MIN_ALLOCATION: DeviceSize = 1024; + let capacity = capacity.max(MIN_ALLOCATION); + let mut smallest = None; + let mut smallest_size = DeviceSize::MAX; + let mut fitting = None; + let mut fitting_size = DeviceSize::MAX; + let buffers = &mut *self.buffers.borrow_mut(); + for (idx, buffer) in buffers.iter().enumerate() { + if buffer.size >= capacity { + if buffer.size < fitting_size { + fitting = Some(idx); + fitting_size = buffer.size; + } + } else { + if buffer.size < smallest_size { + smallest = Some(idx); + smallest_size = buffer.size; + } + } + } + if let Some(idx) = fitting { + return Ok(VulkanBuffer { + cache: self.clone(), + buffer: ManuallyDrop::new(buffers.swap_remove(idx)), + }); + } + if let Some(idx) = smallest { + log::debug!("discarding size {}", smallest_size); + buffers.swap_remove(idx); + } + let size = capacity.checked_next_power_of_two().unwrap(); + log::debug!("allocating size {}", size); + let buffer = { + let info = BufferCreateInfo::default().size(size).usage(self.usage); + unsafe { + self.device + .device + .create_buffer(&info, None) + .map_err(VulkanError::CreateBuffer)? + } + }; + let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) }); + let memory_requirements = + unsafe { self.device.device.get_buffer_memory_requirements(buffer) }; + let allocation = { + let flags = UsageFlags::UPLOAD + | UsageFlags::FAST_DEVICE_ACCESS + | UsageFlags::HOST_ACCESS + | UsageFlags::DEVICE_ADDRESS; + self.allocator.alloc(&memory_requirements, flags, true)? + }; + unsafe { + self.device + .device + .bind_buffer_memory(buffer, allocation.memory, allocation.offset) + .map_err(VulkanError::BindBufferMemory)?; + } + destroy_buffer.forget(); + let address = { + let info = BufferDeviceAddressInfo::default().buffer(buffer); + unsafe { self.device.device.get_buffer_device_address(&info) } + }; + Ok(VulkanBuffer { + cache: self.clone(), + buffer: ManuallyDrop::new(VulkanBufferUnused { + device: self.device.clone(), + size, + buffer, + allocation, + address, + }), + }) + } +} + +impl Drop for VulkanBuffer { + fn drop(&mut self) { + let buffer = unsafe { ManuallyDrop::take(&mut self.buffer) }; + self.cache.buffers.borrow_mut().push(buffer); + } +} + +impl Drop for VulkanBufferUnused { + fn drop(&mut self) { + unsafe { + self.device.device.destroy_buffer(self.buffer, None); + } + } +} diff --git a/src/gfx_apis/vulkan/descriptor_buffer.rs b/src/gfx_apis/vulkan/descriptor_buffer.rs index 5f72d7a0..4ee4ddcd 100644 --- a/src/gfx_apis/vulkan/descriptor_buffer.rs +++ b/src/gfx_apis/vulkan/descriptor_buffer.rs @@ -1,41 +1,8 @@ use { - crate::{ - gfx_apis::vulkan::{ - VulkanError, - allocator::{VulkanAllocation, VulkanAllocator}, - descriptor::VulkanDescriptorSetLayout, - device::VulkanDevice, - }, - utils::on_drop::OnDrop, - }, - ash::vk::{ - Buffer, BufferCreateInfo, BufferDeviceAddressInfo, BufferUsageFlags, DeviceAddress, - DeviceSize, - }, - gpu_alloc::UsageFlags, - std::{cell::RefCell, mem::ManuallyDrop, ops::Deref, rc::Rc}, + crate::gfx_apis::vulkan::descriptor::VulkanDescriptorSetLayout, ash::vk::DeviceSize, + std::ops::Deref, }; -pub struct VulkanDescriptorBufferCache { - device: Rc, - allocator: Rc, - buffers: RefCell>, - has_sampler: bool, -} - -pub struct VulkanDescriptorBuffer { - cache: Rc, - pub buffer: ManuallyDrop, -} - -pub struct VulkanDescriptorBufferUnused { - device: Rc, - pub size: DeviceSize, - pub buffer: Buffer, - pub allocation: VulkanAllocation, - pub address: DeviceAddress, -} - #[derive(Default)] pub struct VulkanDescriptorBufferWriter { buffer: Vec, @@ -45,124 +12,6 @@ pub struct VulkanDescriptorBufferSetWriter<'a> { set: &'a mut [u8], } -impl VulkanDescriptorBufferCache { - pub fn new( - device: &Rc, - allocator: &Rc, - has_sampler: bool, - ) -> Self { - Self { - device: device.clone(), - allocator: allocator.clone(), - buffers: Default::default(), - has_sampler, - } - } - - pub fn allocate( - self: &Rc, - capacity: DeviceSize, - ) -> Result { - const MIN_ALLOCATION: DeviceSize = 1024; - let capacity = capacity.max(MIN_ALLOCATION); - let mut smallest = None; - let mut smallest_size = DeviceSize::MAX; - let mut fitting = None; - let mut fitting_size = DeviceSize::MAX; - let buffers = &mut *self.buffers.borrow_mut(); - for (idx, buffer) in buffers.iter().enumerate() { - if buffer.size >= capacity { - if buffer.size < fitting_size { - fitting = Some(idx); - fitting_size = buffer.size; - } - } else { - if buffer.size < smallest_size { - smallest = Some(idx); - smallest_size = buffer.size; - } - } - } - if let Some(idx) = fitting { - return Ok(VulkanDescriptorBuffer { - cache: self.clone(), - buffer: ManuallyDrop::new(buffers.swap_remove(idx)), - }); - } - if let Some(idx) = smallest { - log::debug!("discarding size {}", smallest_size); - buffers.swap_remove(idx); - } - let size = capacity.checked_next_power_of_two().unwrap(); - log::debug!("allocating size {}", size); - let buffer = { - let usage = self.usage(); - let info = BufferCreateInfo::default().size(size).usage(usage); - unsafe { - self.device - .device - .create_buffer(&info, None) - .map_err(VulkanError::CreateBuffer)? - } - }; - let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) }); - let memory_requirements = - unsafe { self.device.device.get_buffer_memory_requirements(buffer) }; - let allocation = { - let flags = UsageFlags::UPLOAD - | UsageFlags::FAST_DEVICE_ACCESS - | UsageFlags::HOST_ACCESS - | UsageFlags::DEVICE_ADDRESS; - self.allocator.alloc(&memory_requirements, flags, true)? - }; - unsafe { - self.device - .device - .bind_buffer_memory(buffer, allocation.memory, allocation.offset) - .map_err(VulkanError::BindBufferMemory)?; - } - destroy_buffer.forget(); - let address = { - let info = BufferDeviceAddressInfo::default().buffer(buffer); - unsafe { self.device.device.get_buffer_device_address(&info) } - }; - Ok(VulkanDescriptorBuffer { - cache: self.clone(), - buffer: ManuallyDrop::new(VulkanDescriptorBufferUnused { - device: self.device.clone(), - size, - buffer, - allocation, - address, - }), - }) - } - - pub fn usage(&self) -> BufferUsageFlags { - let mut usage = BufferUsageFlags::RESOURCE_DESCRIPTOR_BUFFER_EXT - | BufferUsageFlags::SHADER_DEVICE_ADDRESS; - if self.has_sampler { - usage |= BufferUsageFlags::SAMPLER_DESCRIPTOR_BUFFER_EXT; - } - usage - } -} - -impl Drop for VulkanDescriptorBuffer { - fn drop(&mut self) { - let buffer = unsafe { ManuallyDrop::take(&mut self.buffer) }; - self.cache.buffers.borrow_mut().push(buffer); - } -} - -impl Drop for VulkanDescriptorBufferUnused { - fn drop(&mut self) { - unsafe { - self.device.device.destroy_buffer(self.buffer, None); - } - } -} - impl VulkanDescriptorBufferWriter { pub fn clear(&mut self) { self.buffer.clear(); diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 31be1a6b..6272e025 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -10,11 +10,10 @@ use { gfx_apis::vulkan::{ VulkanError, allocator::{VulkanAllocator, VulkanThreadedAllocator}, + buffer_cache::{VulkanBuffer, VulkanBufferCache}, command::{VulkanCommandBuffer, VulkanCommandPool}, descriptor::VulkanDescriptorSetLayout, - descriptor_buffer::{ - VulkanDescriptorBuffer, VulkanDescriptorBufferCache, VulkanDescriptorBufferWriter, - }, + descriptor_buffer::VulkanDescriptorBufferWriter, device::VulkanDevice, fence::VulkanFence, image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory}, @@ -95,8 +94,8 @@ pub struct VulkanRenderer { pub(super) pending_cpu_jobs: CopyHashMap, pub(super) shm_allocator: Rc, pub(super) sampler: Rc, - pub(super) sampler_descriptor_buffer_cache: Rc, - pub(super) resource_descriptor_buffer_cache: Rc, + pub(super) sampler_descriptor_buffer_cache: Rc, + pub(super) resource_descriptor_buffer_cache: Rc, pub(super) blend_buffers: RefCell>>, } @@ -149,7 +148,7 @@ pub(super) struct Memory { wait_semaphore_infos: Vec>, release_fence: Option>, release_sync_file: Option, - descriptor_buffers: ArrayVec, + descriptor_buffers: ArrayVec, paint_bounds: StaticMap>, paint_regions: StaticMap>, clear_rects: StaticMap>, @@ -212,7 +211,7 @@ pub(super) struct PendingFrame { wait_semaphores: Cell>>, waiter: Cell>>, _release_fence: Option>, - _descriptor_buffers: ArrayVec, + _descriptor_buffers: ArrayVec, } pub(super) struct VulkanFormatPipelines { @@ -289,9 +288,9 @@ impl VulkanDevice { let allocator = self.create_allocator()?; let shm_allocator = self.create_threaded_allocator()?; let sampler_descriptor_buffer_cache = - Rc::new(VulkanDescriptorBufferCache::new(self, &allocator, true)); + VulkanBufferCache::for_descriptor_buffer(self, &allocator, true); let resource_descriptor_buffer_cache = - Rc::new(VulkanDescriptorBufferCache::new(self, &allocator, false)); + VulkanBufferCache::for_descriptor_buffer(self, &allocator, false); let render = Rc::new(VulkanRenderer { formats: Rc::new(formats), device: self.clone(), From 10be846e4c0cd6334a60a2119f5949c3421184dd Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 15:37:28 +0100 Subject: [PATCH 08/12] vulkan: store fill vertices in buffer --- src/gfx_apis/vulkan/buffer_cache.rs | 9 +- src/gfx_apis/vulkan/pipeline.rs | 8 +- src/gfx_apis/vulkan/renderer.rs | 159 +++++++++++++++---- src/gfx_apis/vulkan/shaders.rs | 15 +- src/gfx_apis/vulkan/shaders/fill.common.glsl | 12 +- src/gfx_apis/vulkan/shaders/fill.frag | 1 - src/gfx_apis/vulkan/shaders/fill.vert | 10 +- 7 files changed, 160 insertions(+), 54 deletions(-) diff --git a/src/gfx_apis/vulkan/buffer_cache.rs b/src/gfx_apis/vulkan/buffer_cache.rs index d6654e73..a95d426a 100644 --- a/src/gfx_apis/vulkan/buffer_cache.rs +++ b/src/gfx_apis/vulkan/buffer_cache.rs @@ -66,7 +66,11 @@ impl VulkanBufferCache { self.usage } - pub fn allocate(self: &Rc, capacity: DeviceSize) -> Result { + pub fn allocate( + self: &Rc, + capacity: DeviceSize, + align: DeviceSize, + ) -> Result { const MIN_ALLOCATION: DeviceSize = 1024; let capacity = capacity.max(MIN_ALLOCATION); let mut smallest = None; @@ -109,8 +113,9 @@ impl VulkanBufferCache { } }; let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) }); - let memory_requirements = + let mut memory_requirements = unsafe { self.device.device.get_buffer_memory_requirements(buffer) }; + memory_requirements.alignment = memory_requirements.alignment.max(align); let allocation = { let flags = UsageFlags::UPLOAD | UsageFlags::FAST_DEVICE_ACCESS diff --git a/src/gfx_apis/vulkan/pipeline.rs b/src/gfx_apis/vulkan/pipeline.rs index 1679d47a..b6701cf1 100644 --- a/src/gfx_apis/vulkan/pipeline.rs +++ b/src/gfx_apis/vulkan/pipeline.rs @@ -49,13 +49,13 @@ impl VulkanDevice { &self, info: PipelineCreateInfo, ) -> Result, VulkanError> { - self.create_pipeline_(info, size_of::

() as _) + self.create_pipeline2(info, size_of::

()) } - fn create_pipeline_( + pub(super) fn create_pipeline2( &self, info: PipelineCreateInfo, - push_size: u32, + push_size: usize, ) -> Result, VulkanError> { let pipeline_layout = { let mut push_constant_ranges = ArrayVec::<_, 1>::new(); @@ -64,7 +64,7 @@ impl VulkanDevice { PushConstantRange::default() .stage_flags(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT) .offset(0) - .size(push_size), + .size(push_size as u32), ); } let mut descriptor_set_layouts = ArrayVec::<_, 1>::new(); diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 6272e025..58a3f2b0 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -22,8 +22,8 @@ use { semaphore::VulkanSemaphore, shaders::{ FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, - LEGACY_TEX_FRAG, LEGACY_TEX_VERT, OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, - TEX_VERT, TexPushConstants, VulkanShader, + LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, OUT_FRAG, OUT_VERT, + OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, VulkanShader, }, transfer_functions::{TF_LINEAR, TF_SRGB}, }, @@ -38,15 +38,16 @@ use { ash::{ Device, vk::{ - self, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, ClearAttachment, - ClearColorValue, ClearRect, ClearValue, CommandBuffer, CommandBufferBeginInfo, - CommandBufferSubmitInfo, CommandBufferUsageFlags, CopyImageInfo2, DependencyInfoKHR, - DescriptorBufferBindingInfoEXT, DescriptorImageInfo, DescriptorType, DeviceSize, - Extent2D, Extent3D, ImageAspectFlags, ImageCopy2, ImageLayout, ImageMemoryBarrier2, - ImageSubresourceLayers, ImageSubresourceRange, Offset2D, Offset3D, PipelineBindPoint, - PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, Rect2D, RenderingAttachmentInfo, - RenderingInfo, SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, - SubmitInfo2, Viewport, WriteDescriptorSet, + self, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferUsageFlags, + ClearAttachment, ClearColorValue, ClearRect, ClearValue, CommandBuffer, + CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags, + CopyImageInfo2, DependencyInfoKHR, DescriptorBufferBindingInfoEXT, DescriptorImageInfo, + DescriptorType, DeviceAddress, DeviceSize, Extent2D, Extent3D, ImageAspectFlags, + ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageSubresourceLayers, + ImageSubresourceRange, Offset2D, Offset3D, PipelineBindPoint, PipelineStageFlags2, + QUEUE_FAMILY_FOREIGN_EXT, Rect2D, RenderingAttachmentInfo, RenderingInfo, + SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport, + WriteDescriptorSet, }, }, isnt::std_1::{collections::IsntHashMapExt, primitive::IsntSliceExt}, @@ -97,6 +98,7 @@ pub struct VulkanRenderer { pub(super) sampler_descriptor_buffer_cache: Rc, pub(super) resource_descriptor_buffer_cache: Rc, pub(super) blend_buffers: RefCell>>, + pub(super) shader_buffer_cache: Rc, } pub(super) struct CachedCommandBuffers { @@ -148,7 +150,7 @@ pub(super) struct Memory { wait_semaphore_infos: Vec>, release_fence: Option>, release_sync_file: Option, - descriptor_buffers: ArrayVec, + used_buffers: ArrayVec, paint_bounds: StaticMap>, paint_regions: StaticMap>, clear_rects: StaticMap>, @@ -161,6 +163,7 @@ pub(super) struct Memory { ops_tmp: StaticMap>, fill_targets: Vec, tex_targets: Vec<[Point; 2]>, + data_buffer: Vec, } type Point = [[f32; 2]; 4]; @@ -185,6 +188,8 @@ struct VulkanFillOp { range: Range, color: [f32; 4], source_type: TexSourceType, + range_address: DeviceAddress, + instances: u32, } #[derive(Copy, Clone, Debug, Linearize, Eq, PartialEq)] @@ -211,7 +216,7 @@ pub(super) struct PendingFrame { wait_semaphores: Cell>>, waiter: Cell>>, _release_fence: Option>, - _descriptor_buffers: ArrayVec, + _used_buffers: ArrayVec, } pub(super) struct VulkanFormatPipelines { @@ -291,6 +296,11 @@ impl VulkanDevice { VulkanBufferCache::for_descriptor_buffer(self, &allocator, true); let resource_descriptor_buffer_cache = VulkanBufferCache::for_descriptor_buffer(self, &allocator, false); + let shader_buffer_cache = { + // TODO: https://github.com/KhronosGroup/Vulkan-Samples/issues/1286 + let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::STORAGE_BUFFER; + VulkanBufferCache::new(self, &allocator, usage) + }; let render = Rc::new(VulkanRenderer { formats: Rc::new(formats), device: self.clone(), @@ -322,6 +332,7 @@ impl VulkanDevice { sampler_descriptor_buffer_cache, resource_descriptor_buffer_cache, blend_buffers: Default::default(), + shader_buffer_cache, }); render.get_or_create_pipelines(XRGB8888.vk_format, RenderPass::FrameBuffer)?; Ok(render) @@ -343,18 +354,23 @@ impl VulkanRenderer { return Ok(pl); } let create_fill_pipeline = |src_has_alpha| { - self.device - .create_pipeline::(PipelineCreateInfo { - format, - vert: self.fill_vert_shader.clone(), - frag: self.fill_frag_shader.clone(), - blend: src_has_alpha, - src_has_alpha, - has_alpha_mult: false, - eotf, - oetf, - frag_descriptor_set_layout: None, - }) + let push_size = if self.device.descriptor_buffer.is_some() { + size_of::() + } else { + size_of::() + }; + let info = PipelineCreateInfo { + format, + vert: self.fill_vert_shader.clone(), + frag: self.fill_frag_shader.clone(), + blend: src_has_alpha, + src_has_alpha, + has_alpha_mult: false, + eotf, + oetf, + frag_descriptor_set_layout: None, + }; + self.device.create_pipeline2(info, push_size) }; let fill_opaque = create_fill_pipeline(false)?; let fill_alpha = create_fill_pipeline(true)?; @@ -411,7 +427,6 @@ impl VulkanRenderer { zone!("create_descriptor_buffers"); let version = self.allocate_point(); let memory = &mut *self.memory.borrow_mut(); - memory.descriptor_buffers.clear(); let sampler_writer = &mut memory.sampler_descriptor_buffer_writer; sampler_writer.clear(); let resource_writer = &mut memory.resource_descriptor_buffer_writer; @@ -446,7 +461,7 @@ impl VulkanRenderer { (&sampler_writer, &self.sampler_descriptor_buffer_cache), (&resource_writer, &self.resource_descriptor_buffer_cache), ] { - let buffer = cache.allocate(writer.len() as DeviceSize)?; + let buffer = cache.allocate(writer.len() as DeviceSize, 1)?; buffer.buffer.allocation.upload(|ptr, _| unsafe { ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len()) })?; @@ -454,7 +469,7 @@ impl VulkanRenderer { .usage(cache.usage()) .address(buffer.buffer.address); infos.push(info); - memory.descriptor_buffers.push(buffer); + memory.used_buffers.push(buffer); } unsafe { db.cmd_bind_descriptor_buffers(buf, &infos); @@ -473,6 +488,7 @@ impl VulkanRenderer { } memory.tex_targets.clear(); memory.fill_targets.clear(); + memory.data_buffer.clear(); let sync = |memory: &mut Memory| { for pass in RenderPass::variants() { let ops = &mut memory.ops_tmp[pass]; @@ -489,7 +505,32 @@ impl VulkanRenderer { VulkanOp::Tex(_) => Key::Tex, } }); - memory.ops[pass].append(ops); + let mops = &mut memory.ops[pass]; + if self.device.descriptor_buffer.is_none() { + mops.append(ops); + continue; + } + for (idx, op) in ops.drain(..).enumerate() { + match op { + VulkanOp::Fill(mut f) => { + f.range_address = memory.data_buffer.len() as DeviceAddress; + f.instances = f.range.len() as u32; + for pos in &memory.fill_targets[f.range.clone()] { + memory.data_buffer.extend_from_slice(uapi::as_bytes(pos)); + } + if let Some(VulkanOp::Fill(p)) = mops.last_mut() { + if p.color == f.color && idx > 0 { + p.instances += f.instances; + continue; + } + } + mops.push(VulkanOp::Fill(f)); + } + VulkanOp::Tex(_) => { + mops.push(op); + } + } + } } }; for op in opts { @@ -532,6 +573,8 @@ impl VulkanRenderer { range: lo..hi, color, source_type, + range_address: 0, + instances: 0, })); } } @@ -594,6 +637,34 @@ impl VulkanRenderer { sync(memory); } + fn create_data_buffer(&self) -> Result<(), VulkanError> { + if self.device.descriptor_buffer.is_none() { + return Ok(()); + } + zone!("create_data_buffer"); + let memory = &mut *self.memory.borrow_mut(); + let buf = &mut memory.data_buffer; + if buf.is_empty() { + return Ok(()); + } + let buffer = self.shader_buffer_cache.allocate(buf.len() as _, 8)?; + 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 { + match op { + VulkanOp::Fill(f) => { + f.range_address += buffer.buffer.address; + } + VulkanOp::Tex(_) => {} + } + } + } + memory.used_buffers.push(buffer); + Ok(()) + } + fn collect_memory(&self) { zone!("collect_memory"); let memory = &mut *self.memory.borrow_mut(); @@ -860,10 +931,12 @@ impl VulkanRenderer { VulkanOp::Fill(r) => { let pipeline = &pipelines.fill[r.source_type]; bind(pipeline); - for &pos in &memory.fill_targets[r.range.clone()] { + if self.device.descriptor_buffer.is_some() { let push = FillPushConstants { - pos, color: r.color, + vertices: r.range_address, + _padding1: 0, + _padding2: 0, }; unsafe { dev.cmd_push_constants( @@ -873,7 +946,24 @@ impl VulkanRenderer { 0, uapi::as_bytes(&push), ); - dev.cmd_draw(buf, 4, 1, 0, 0); + dev.cmd_draw(buf, 4, r.instances, 0, 0); + } + } else { + for &pos in &memory.fill_targets[r.range.clone()] { + let push = LegacyFillPushConstants { + pos, + color: r.color, + }; + unsafe { + dev.cmd_push_constants( + buf, + pipeline.pipeline_layout, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, + 0, + uapi::as_bytes(&push), + ); + dev.cmd_draw(buf, 4, 1, 0, 0); + } } } } @@ -1358,7 +1448,7 @@ impl VulkanRenderer { wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)), waiter: Cell::new(None), _release_fence: memory.release_fence.take(), - _descriptor_buffers: mem::take(&mut memory.descriptor_buffers), + _used_buffers: mem::take(&mut memory.used_buffers), }); self.pending_frames.set(frame.point, frame.clone()); let future = self.eng.spawn( @@ -1400,7 +1490,7 @@ impl VulkanRenderer { memory.queue_transfer.clear(); memory.wait_semaphores.clear(); memory.release_fence.take(); - memory.descriptor_buffers.clear(); + memory.used_buffers.clear(); memory.release_sync_file.take() }; res.map(|_| sync_file) @@ -1563,6 +1653,7 @@ impl VulkanRenderer { let bb = blend_buffer.as_deref(); let buf = self.gfx_command_buffers.allocate()?; self.convert_ops(opts); + self.create_data_buffer()?; self.collect_memory(); self.begin_command_buffer(buf.buffer)?; self.create_descriptor_buffers(buf.buffer, bb)?; diff --git a/src/gfx_apis/vulkan/shaders.rs b/src/gfx_apis/vulkan/shaders.rs index 07e49938..7e09af5d 100644 --- a/src/gfx_apis/vulkan/shaders.rs +++ b/src/gfx_apis/vulkan/shaders.rs @@ -1,6 +1,6 @@ use { crate::gfx_apis::vulkan::{VulkanError, device::VulkanDevice}, - ash::vk::{ShaderModule, ShaderModuleCreateInfo}, + ash::vk::{DeviceAddress, ShaderModule, ShaderModuleCreateInfo}, std::rc::Rc, uapi::Packed, }; @@ -26,11 +26,22 @@ pub struct VulkanShader { #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct FillPushConstants { + pub color: [f32; 4], + pub vertices: DeviceAddress, + pub _padding1: u32, + pub _padding2: u32, +} + +unsafe impl Packed for FillPushConstants {} + +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct LegacyFillPushConstants { pub pos: [[f32; 2]; 4], pub color: [f32; 4], } -unsafe impl Packed for FillPushConstants {} +unsafe impl Packed for LegacyFillPushConstants {} #[derive(Copy, Clone, Debug)] #[repr(C)] diff --git a/src/gfx_apis/vulkan/shaders/fill.common.glsl b/src/gfx_apis/vulkan/shaders/fill.common.glsl index d49c75a8..70ca4554 100644 --- a/src/gfx_apis/vulkan/shaders/fill.common.glsl +++ b/src/gfx_apis/vulkan/shaders/fill.common.glsl @@ -1,4 +1,12 @@ +#extension GL_EXT_buffer_reference : require + +layout(buffer_reference, buffer_reference_align = 8, std430) buffer Vertices { + vec2 pos[][4]; +}; + layout(push_constant, std430) uniform Data { - layout(offset = 0) vec2 pos[4]; - layout(offset = 32) vec4 color; + vec4 color; + Vertices vertices; + uint padding1; + uint padding2; } data; diff --git a/src/gfx_apis/vulkan/shaders/fill.frag b/src/gfx_apis/vulkan/shaders/fill.frag index c6424d9d..d4718db3 100644 --- a/src/gfx_apis/vulkan/shaders/fill.frag +++ b/src/gfx_apis/vulkan/shaders/fill.frag @@ -1,6 +1,5 @@ #version 450 -#include "frag_spec_const.glsl" #include "fill.common.glsl" layout(location = 0) out vec4 out_color; diff --git a/src/gfx_apis/vulkan/shaders/fill.vert b/src/gfx_apis/vulkan/shaders/fill.vert index 8b4a7aa7..a530d0ca 100644 --- a/src/gfx_apis/vulkan/shaders/fill.vert +++ b/src/gfx_apis/vulkan/shaders/fill.vert @@ -1,16 +1,8 @@ #version 450 -//#extension GL_EXT_debug_printf : enable #include "fill.common.glsl" void main() { - vec2 pos; - switch (gl_VertexIndex) { - case 0: pos = data.pos[0]; break; - case 1: pos = data.pos[1]; break; - case 2: pos = data.pos[2]; break; - case 3: pos = data.pos[3]; break; - } + vec2 pos = data.vertices.pos[gl_InstanceIndex][gl_VertexIndex]; gl_Position = vec4(pos, 0.0, 1.0); -// debugPrintfEXT("gl_Position = %v4f", gl_Position); } From 73611fd28780af9594c688728c5debb78431142a Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 16:14:22 +0100 Subject: [PATCH 09/12] vulkan: store out vertices in buffer --- src/gfx_apis/vulkan/renderer.rs | 45 ++++++++++++--------- src/gfx_apis/vulkan/shaders.rs | 2 +- src/gfx_apis/vulkan/shaders/out.common.glsl | 8 +++- src/gfx_apis/vulkan/shaders/out.vert | 10 +---- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 58a3f2b0..5637527e 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -164,6 +164,7 @@ pub(super) struct Memory { fill_targets: Vec, tex_targets: Vec<[Point; 2]>, data_buffer: Vec, + out_address: DeviceAddress, } type Point = [[f32; 2]; 4]; @@ -644,6 +645,17 @@ impl VulkanRenderer { zone!("create_data_buffer"); let memory = &mut *self.memory.borrow_mut(); let buf = &mut memory.data_buffer; + { + memory.out_address = buf.len() as _; + for region in &memory.paint_regions[RenderPass::BlendBuffer] { + buf.extend_from_slice(uapi::as_bytes(&[ + [region.x2, region.y1], + [region.x1, region.y1], + [region.x2, region.y2], + [region.x1, region.y2], + ])); + } + } if buf.is_empty() { return Ok(()); } @@ -661,6 +673,7 @@ impl VulkanRenderer { } } } + memory.out_address += buffer.buffer.address; memory.used_buffers.push(buffer); Ok(()) } @@ -1075,6 +1088,10 @@ impl VulkanRenderer { out } }; + let push = OutPushConstants { + vertices: memory.out_address, + }; + let instances = memory.paint_regions[RenderPass::BlendBuffer].len() as u32; let dev = &self.device.device; unsafe { dev.cmd_bind_pipeline(buf, PipelineBindPoint::GRAPHICS, pipeline.pipeline); @@ -1086,26 +1103,14 @@ impl VulkanRenderer { &[1], &[bb.descriptor_buffer_offset.get()], ); - } - for region in &memory.paint_regions[RenderPass::BlendBuffer] { - let push = OutPushConstants { - pos: [ - [region.x2, region.y1], - [region.x1, region.y1], - [region.x2, region.y2], - [region.x1, region.y2], - ], - }; - unsafe { - dev.cmd_push_constants( - buf, - pipeline.pipeline_layout, - ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, - 0, - uapi::as_bytes(&push), - ); - dev.cmd_draw(buf, 4, 1, 0, 0); - } + dev.cmd_push_constants( + buf, + pipeline.pipeline_layout, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, + 0, + uapi::as_bytes(&push), + ); + dev.cmd_draw(buf, 4, instances, 0, 0); } Ok(()) } diff --git a/src/gfx_apis/vulkan/shaders.rs b/src/gfx_apis/vulkan/shaders.rs index 7e09af5d..4c77a00a 100644 --- a/src/gfx_apis/vulkan/shaders.rs +++ b/src/gfx_apis/vulkan/shaders.rs @@ -56,7 +56,7 @@ unsafe impl Packed for TexPushConstants {} #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct OutPushConstants { - pub pos: [[f32; 2]; 4], + pub vertices: DeviceAddress, } unsafe impl Packed for OutPushConstants {} diff --git a/src/gfx_apis/vulkan/shaders/out.common.glsl b/src/gfx_apis/vulkan/shaders/out.common.glsl index 23a3579d..4f709e30 100644 --- a/src/gfx_apis/vulkan/shaders/out.common.glsl +++ b/src/gfx_apis/vulkan/shaders/out.common.glsl @@ -1,3 +1,9 @@ +#extension GL_EXT_buffer_reference : require + +layout(buffer_reference, buffer_reference_align = 8, std430) buffer Vertices { + vec2 pos[][4]; +}; + layout(push_constant, std430) uniform Data { - layout(offset = 0) vec2 pos[4]; + Vertices vertices; } data; diff --git a/src/gfx_apis/vulkan/shaders/out.vert b/src/gfx_apis/vulkan/shaders/out.vert index 4bad8d2f..cfefa4a8 100644 --- a/src/gfx_apis/vulkan/shaders/out.vert +++ b/src/gfx_apis/vulkan/shaders/out.vert @@ -1,16 +1,8 @@ #version 450 -//#extension GL_EXT_debug_printf : enable #include "out.common.glsl" void main() { - vec2 pos; - switch (gl_VertexIndex) { - case 0: pos = data.pos[0]; break; - case 1: pos = data.pos[1]; break; - case 2: pos = data.pos[2]; break; - case 3: pos = data.pos[3]; break; - } + vec2 pos = data.vertices.pos[gl_InstanceIndex][gl_VertexIndex]; gl_Position = vec4(pos, 0.0, 1.0); -// debugPrintfEXT("X gl_Position = %v4f, pos = %v2f", gl_Position, pos); } From 9534da89a226433278c3e8984df46633ff3c9aec Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 16:41:18 +0100 Subject: [PATCH 10/12] vulkan: store tex vertices in buffer --- src/gfx_apis/vulkan/renderer.rs | 98 ++++++++++++++------- src/gfx_apis/vulkan/shaders.rs | 20 ++++- src/gfx_apis/vulkan/shaders/tex.common.glsl | 16 +++- src/gfx_apis/vulkan/shaders/tex.vert | 13 +-- 4 files changed, 100 insertions(+), 47 deletions(-) diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 5637527e..4fda1a63 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -22,8 +22,9 @@ use { semaphore::VulkanSemaphore, shaders::{ FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, - LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, OUT_FRAG, OUT_VERT, - OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, VulkanShader, + LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, LegacyTexPushConstants, + OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, + TexVertex, VulkanShader, }, transfer_functions::{TF_LINEAR, TF_SRGB}, }, @@ -183,6 +184,8 @@ struct VulkanTexOp { alpha: f32, source_type: TexSourceType, copy_type: TexCopyType, + range_address: DeviceAddress, + instances: u32, } struct VulkanFillOp { @@ -376,18 +379,23 @@ impl VulkanRenderer { let fill_opaque = create_fill_pipeline(false)?; let fill_alpha = create_fill_pipeline(true)?; let create_tex_pipeline = |src_has_alpha, has_alpha_mult| { - self.device - .create_pipeline::(PipelineCreateInfo { - format, - vert: self.tex_vert_shader.clone(), - frag: self.tex_frag_shader.clone(), - blend: src_has_alpha || has_alpha_mult, - src_has_alpha, - has_alpha_mult, - eotf, - oetf, - frag_descriptor_set_layout: Some(self.tex_descriptor_set_layout.clone()), - }) + let push_size = if self.device.descriptor_buffer.is_some() { + size_of::() + } else { + size_of::() + }; + let info = PipelineCreateInfo { + format, + vert: self.tex_vert_shader.clone(), + frag: self.tex_frag_shader.clone(), + blend: src_has_alpha || has_alpha_mult, + src_has_alpha, + has_alpha_mult, + eotf, + oetf, + frag_descriptor_set_layout: Some(self.tex_descriptor_set_layout.clone()), + }; + self.device.create_pipeline2(info, push_size) }; let tex_opaque = create_tex_pipeline(false, false)?; let tex_alpha = create_tex_pipeline(true, false)?; @@ -527,8 +535,16 @@ impl VulkanRenderer { } mops.push(VulkanOp::Fill(f)); } - VulkanOp::Tex(_) => { - mops.push(op); + VulkanOp::Tex(mut c) => { + c.range_address = memory.data_buffer.len() as DeviceAddress; + c.instances = c.range.len() as u32; + for &[pos, tex_pos] in &memory.tex_targets[c.range.clone()] { + let vertex = TexVertex { pos, tex_pos }; + memory + .data_buffer + .extend_from_slice(uapi::as_bytes(&vertex)); + } + mops.push(VulkanOp::Tex(c)); } } } @@ -630,6 +646,8 @@ impl VulkanRenderer { alpha: ct.alpha.unwrap_or_default(), source_type, copy_type, + range_address: 0, + instances: 0, })); } } @@ -669,7 +687,9 @@ impl VulkanRenderer { VulkanOp::Fill(f) => { f.range_address += buffer.buffer.address; } - VulkanOp::Tex(_) => {} + VulkanOp::Tex(c) => { + c.range_address += buffer.buffer.address; + } } } } @@ -988,6 +1008,10 @@ impl VulkanRenderer { .image_view(tex.texture_view) .image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL); if let Some(db) = &self.device.descriptor_buffer { + let push = TexPushConstants { + vertices: c.range_address, + alpha: c.alpha, + }; unsafe { db.cmd_set_descriptor_buffer_offsets( buf, @@ -997,6 +1021,14 @@ impl VulkanRenderer { &[0], &[tex.descriptor_buffer_offset.get()], ); + dev.cmd_push_constants( + buf, + pipeline.pipeline_layout, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, + 0, + uapi::as_bytes(&push), + ); + dev.cmd_draw(buf, 4, c.instances, 0, 0); } } else { let write_descriptor_set = WriteDescriptorSet::default() @@ -1011,22 +1043,22 @@ impl VulkanRenderer { slice::from_ref(&write_descriptor_set), ); } - } - for &[pos, tex_pos] in &memory.tex_targets[c.range.clone()] { - let push = TexPushConstants { - pos, - tex_pos, - alpha: c.alpha, - }; - unsafe { - dev.cmd_push_constants( - buf, - pipeline.pipeline_layout, - ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, - 0, - uapi::as_bytes(&push), - ); - dev.cmd_draw(buf, 4, 1, 0, 0); + for &[pos, tex_pos] in &memory.tex_targets[c.range.clone()] { + let push = LegacyTexPushConstants { + pos, + tex_pos, + alpha: c.alpha, + }; + unsafe { + dev.cmd_push_constants( + buf, + pipeline.pipeline_layout, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, + 0, + uapi::as_bytes(&push), + ); + dev.cmd_draw(buf, 4, 1, 0, 0); + } } } } diff --git a/src/gfx_apis/vulkan/shaders.rs b/src/gfx_apis/vulkan/shaders.rs index 4c77a00a..5fa21821 100644 --- a/src/gfx_apis/vulkan/shaders.rs +++ b/src/gfx_apis/vulkan/shaders.rs @@ -43,15 +43,33 @@ pub struct LegacyFillPushConstants { unsafe impl Packed for LegacyFillPushConstants {} +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct TexVertex { + pub pos: [[f32; 2]; 4], + pub tex_pos: [[f32; 2]; 4], +} + +unsafe impl Packed for TexVertex {} + #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct TexPushConstants { + pub vertices: DeviceAddress, + pub alpha: f32, +} + +unsafe impl Packed for TexPushConstants {} + +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct LegacyTexPushConstants { pub pos: [[f32; 2]; 4], pub tex_pos: [[f32; 2]; 4], pub alpha: f32, } -unsafe impl Packed for TexPushConstants {} +unsafe impl Packed for LegacyTexPushConstants {} #[derive(Copy, Clone, Debug)] #[repr(C)] diff --git a/src/gfx_apis/vulkan/shaders/tex.common.glsl b/src/gfx_apis/vulkan/shaders/tex.common.glsl index 61f3ef3c..9f501e27 100644 --- a/src/gfx_apis/vulkan/shaders/tex.common.glsl +++ b/src/gfx_apis/vulkan/shaders/tex.common.glsl @@ -1,5 +1,15 @@ +#extension GL_EXT_buffer_reference : require + +struct Vertex { + vec2 pos[4]; + vec2 tex_pos[4]; +}; + +layout(buffer_reference, buffer_reference_align = 8, std430) buffer Vertices { + Vertex vertices[]; +}; + layout(push_constant, std430) uniform Data { - layout(offset = 0) vec2 pos[4]; - layout(offset = 32) vec2 tex_pos[4]; - layout(offset = 64) float mul; + Vertices vertices; + float mul; } data; diff --git a/src/gfx_apis/vulkan/shaders/tex.vert b/src/gfx_apis/vulkan/shaders/tex.vert index 7390b4a4..f4f3e13c 100644 --- a/src/gfx_apis/vulkan/shaders/tex.vert +++ b/src/gfx_apis/vulkan/shaders/tex.vert @@ -1,18 +1,11 @@ #version 450 -//#extension GL_EXT_debug_printf : enable #include "tex.common.glsl" layout(location = 0) out vec2 tex_pos; void main() { - vec2 pos; - switch (gl_VertexIndex) { - case 0: pos = data.pos[0]; tex_pos = data.tex_pos[0]; break; - case 1: pos = data.pos[1]; tex_pos = data.tex_pos[1]; break; - case 2: pos = data.pos[2]; tex_pos = data.tex_pos[2]; break; - case 3: pos = data.pos[3]; tex_pos = data.tex_pos[3]; break; - } - gl_Position = vec4(pos, 0.0, 1.0); -// debugPrintfEXT("gl_Position = %v4f, tex_pos = %v2f", gl_Position, tex_pos); + Vertex vertex = data.vertices.vertices[gl_InstanceIndex]; + gl_Position = vec4(vertex.pos[gl_VertexIndex], 0.0, 1.0); + tex_pos = vertex.tex_pos[gl_VertexIndex]; } From d7d0cbf9e2bc78b711815f78de691faa6ae60d10 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 18:22:21 +0100 Subject: [PATCH 11/12] vulkan: skip async transfer if damage is empty --- src/gfx_apis/vulkan/transfer.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gfx_apis/vulkan/transfer.rs b/src/gfx_apis/vulkan/transfer.rs index 3fbac290..4fd49d13 100644 --- a/src/gfx_apis/vulkan/transfer.rs +++ b/src/gfx_apis/vulkan/transfer.rs @@ -77,6 +77,9 @@ impl VulkanShmImage { callback: Rc, tt: TransferType, ) -> Result, VulkanError> { + if damage.is_empty() { + return Ok(None); + } let data = self.async_data.as_ref().unwrap(); let res = self.try_async_transfer(img, staging, data, client_mem, damage, tt); match res { From dd332be995bd7a5b252bf4ec442f4d62f3fd751a Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 27 Feb 2025 21:04:31 +0100 Subject: [PATCH 12/12] damage: add sync point before visualizing damage --- src/damage.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/damage.rs b/src/damage.rs index 86b0f4a6..88b0aadc 100644 --- a/src/damage.rs +++ b/src/damage.rs @@ -2,6 +2,7 @@ use { crate::{ async_engine::AsyncEngine, fixed::Fixed, + gfx_api::GfxApiOpt, ifs::wl_output::WlOutputGlobal, rect::{Rect, Region}, renderer::renderer_base::RendererBase, @@ -160,6 +161,7 @@ impl DamageVisualizer { let dx = -cursor_rect.x1(); let dy = -cursor_rect.y1(); let decay_millis = decay.as_millis() as u64 as f32; + renderer.ops.push(GfxApiOpt::Sync); for entry in entries.iter().rev() { let region = Region::new(entry.rect); let region = region.subtract(&used);