1
0
Fork 0
forked from wry/wry

all: remove traditional i3 titlebars, add corner rounding

This commit is contained in:
kossLAN 2026-04-09 23:04:33 -04:00
parent e1928863d9
commit a41dbae899
No known key found for this signature in database
52 changed files with 1866 additions and 1047 deletions

View file

@ -27,9 +27,14 @@ use {
semaphore::VulkanSemaphore,
shaders::{
ColorManagementData, EotfArgs, FILL_FRAG, FILL_VERT, FillPushConstants,
InvEotfArgs, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, LEGACY_TEX_FRAG, LEGACY_TEX_VERT,
LegacyFillPushConstants, LegacyTexPushConstants, OUT_FRAG, OUT_VERT,
OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, TexVertex, VulkanShader,
InvEotfArgs, LEGACY_FILL_FRAG, LEGACY_FILL_VERT, LEGACY_ROUNDED_FILL_FRAG,
LEGACY_ROUNDED_FILL_VERT, LEGACY_ROUNDED_TEX_FRAG, LEGACY_ROUNDED_TEX_VERT,
LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants,
LegacyRoundedFillPushConstants, LegacyRoundedTexPushConstants,
LegacyTexPushConstants, OUT_FRAG, OUT_VERT, OutPushConstants, ROUNDED_FILL_FRAG,
ROUNDED_FILL_VERT, ROUNDED_TEX_FRAG, ROUNDED_TEX_VERT, RoundedFillPushConstants,
RoundedTexPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants, TexVertex,
VulkanShader,
},
},
io_uring::IoUring,
@ -104,6 +109,13 @@ pub struct VulkanRenderer {
pub(super) tex_frag_shader: Rc<VulkanShader>,
pub(super) out_vert_shader: Option<Rc<VulkanShader>>,
pub(super) out_frag_shader: Option<Rc<VulkanShader>>,
pub(super) rounded_fill_vert_shader: Rc<VulkanShader>,
pub(super) rounded_fill_frag_shader: Rc<VulkanShader>,
pub(super) rounded_tex_vert_shader: Rc<VulkanShader>,
pub(super) rounded_tex_frag_shader: Rc<VulkanShader>,
pub(super) rounded_fill_pipelines: CopyHashMap<vk::Format, FillPipelines>,
pub(super) rounded_tex_pipelines:
StaticMap<VulkanEotf, CopyHashMap<vk::Format, Rc<TexPipelines>>>,
pub(super) tex_descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
pub(super) out_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
pub(super) defunct: Cell<bool>,
@ -202,6 +214,8 @@ type Point = [[f32; 2]; 4];
enum VulkanOp {
Fill(VulkanFillOp),
Tex(VulkanTexOp),
RoundedFill(VulkanRoundedFillOp),
RoundedTex(VulkanRoundedTexOp),
}
struct VulkanTexOp {
@ -231,6 +245,39 @@ struct VulkanFillOp {
instances: u32,
}
struct VulkanRoundedFillOp {
target: Point,
color: [f32; 4],
source_type: TexSourceType,
size: [f32; 2],
corner_radius: [f32; 4],
border_width: f32,
scale: f32,
range_address: DeviceAddress,
}
struct VulkanRoundedTexOp {
tex: Rc<VulkanImage>,
index: usize,
target: Point,
source: Point,
buffer_resv: Option<Rc<dyn BufferResv>>,
acquire_sync: Option<AcquireSync>,
release_sync: ReleaseSync,
alpha: f32,
source_type: TexSourceType,
copy_type: TexCopyType,
alpha_mode: AlphaMode,
tex_cd: Rc<ColorDescription>,
color_management_data_address: Option<DeviceAddress>,
eotf_args_address: Option<DeviceAddress>,
resource_descriptor_buffer_offset: DeviceAddress,
size: [f32; 2],
corner_radius: [f32; 4],
scale: f32,
range_address: DeviceAddress,
}
#[derive(Copy, Clone, Debug, Linearize, Eq, PartialEq)]
pub(super) enum RenderPass {
BlendBuffer,
@ -295,12 +342,20 @@ impl VulkanDevice {
let tex_frag_shader;
let out_vert_shader;
let out_frag_shader;
let rounded_fill_vert_shader;
let rounded_fill_frag_shader;
let rounded_tex_vert_shader;
let rounded_tex_frag_shader;
let mut tex_descriptor_set_layouts = ArrayVec::new();
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)?;
rounded_fill_vert_shader = self.create_shader(ROUNDED_FILL_VERT)?;
rounded_fill_frag_shader = self.create_shader(ROUNDED_FILL_FRAG)?;
rounded_tex_vert_shader = self.create_shader(ROUNDED_TEX_VERT)?;
rounded_tex_frag_shader = self.create_shader(ROUNDED_TEX_FRAG)?;
out_vert_shader = Some(self.create_shader(OUT_VERT)?);
out_frag_shader = Some(self.create_shader(OUT_FRAG)?);
tex_descriptor_set_layouts
@ -311,6 +366,10 @@ impl VulkanDevice {
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)?;
rounded_fill_vert_shader = self.create_shader(LEGACY_ROUNDED_FILL_VERT)?;
rounded_fill_frag_shader = self.create_shader(LEGACY_ROUNDED_FILL_FRAG)?;
rounded_tex_vert_shader = self.create_shader(LEGACY_ROUNDED_TEX_VERT)?;
rounded_tex_frag_shader = self.create_shader(LEGACY_ROUNDED_TEX_FRAG)?;
out_vert_shader = None;
out_frag_shader = None;
tex_descriptor_set_layouts
@ -400,6 +459,12 @@ impl VulkanDevice {
tex_frag_shader,
out_vert_shader,
out_frag_shader,
rounded_fill_vert_shader,
rounded_fill_frag_shader,
rounded_tex_vert_shader,
rounded_tex_frag_shader,
rounded_fill_pipelines: Default::default(),
rounded_tex_pipelines: Default::default(),
tex_descriptor_set_layouts,
out_descriptor_set_layout,
defunct: Cell::new(false),
@ -457,6 +522,112 @@ impl VulkanRenderer {
Ok(fill_pipelines)
}
fn get_or_create_rounded_fill_pipelines(
&self,
format: vk::Format,
) -> Result<FillPipelines, VulkanError> {
if let Some(pl) = self.rounded_fill_pipelines.get(&format) {
return Ok(pl);
}
let create_pipeline = |src_has_alpha| {
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<RoundedFillPushConstants>()
} else {
size_of::<LegacyRoundedFillPushConstants>()
};
let info = PipelineCreateInfo {
format,
vert: self.rounded_fill_vert_shader.clone(),
frag: self.rounded_fill_frag_shader.clone(),
blend: src_has_alpha,
src_has_alpha,
has_alpha_mult: false,
alpha_mode: AlphaMode::PremultipliedOptical,
eotf: EOTF_LINEAR,
inv_eotf: EOTF_LINEAR,
descriptor_set_layouts: Default::default(),
has_color_management_data: false,
};
self.device.create_pipeline2(info, push_size)
};
let pipelines = Rc::new(static_map! {
TexSourceType::HasAlpha => create_pipeline(true)?,
TexSourceType::Opaque => create_pipeline(false)?,
});
self.rounded_fill_pipelines.set(format, pipelines.clone());
Ok(pipelines)
}
fn get_or_create_rounded_tex_pipelines(
&self,
format: vk::Format,
target_cd: &ColorDescription,
) -> Rc<TexPipelines> {
let eotf = target_cd.eotf.to_vulkan();
let pipelines = &self.rounded_tex_pipelines[eotf];
match pipelines.get(&format) {
Some(pl) => pl,
_ => {
let pl = Rc::new(TexPipelines {
format,
eotf,
pipelines: Default::default(),
});
pipelines.set(format, pl.clone());
pl
}
}
}
fn get_or_create_rounded_tex_pipeline(
&self,
pipelines: &TexPipelines,
tex_cd: &ColorDescription,
tex_copy_type: TexCopyType,
tex_source_type: TexSourceType,
mut tex_alpha_mode: AlphaMode,
has_color_management_data: bool,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
if tex_source_type == TexSourceType::Opaque {
tex_alpha_mode = AlphaMode::PremultipliedElectrical;
}
let key = TexPipelineKey {
tex_copy_type,
tex_source_type,
tex_alpha_mode,
eotf: tex_cd.eotf.to_vulkan(),
has_color_management_data,
};
if let Some(pl) = pipelines.pipelines.get(&key) {
return Ok(pl);
}
let has_alpha_mult = match tex_copy_type {
TexCopyType::Identity => false,
TexCopyType::Multiply => true,
};
let push_size = if self.device.descriptor_buffer.is_some() {
size_of::<RoundedTexPushConstants>()
} else {
size_of::<LegacyRoundedTexPushConstants>()
};
let info = PipelineCreateInfo {
format: pipelines.format,
vert: self.rounded_tex_vert_shader.clone(),
frag: self.rounded_tex_frag_shader.clone(),
blend: true, // always blend since corners are transparent
src_has_alpha: true, // rounding makes everything have alpha
has_alpha_mult,
alpha_mode: key.tex_alpha_mode,
eotf: key.eotf.to_vulkan(),
inv_eotf: pipelines.eotf.to_vulkan(),
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
has_color_management_data,
};
let pl = self.device.create_pipeline2(info, push_size)?;
pipelines.pipelines.set(key, pl.clone());
Ok(pl)
}
fn get_or_create_tex_pipelines(
&self,
format: vk::Format,
@ -665,23 +836,34 @@ impl VulkanRenderer {
RenderPass::FrameBuffer => fb_inv_eotf_args_descriptor,
};
for cmd in &mut memory.ops[pass] {
let VulkanOp::Tex(c) = cmd else {
continue;
let (tex, resource_offset, cm_addr, eotf_addr) = match cmd {
VulkanOp::Tex(c) => (
&c.tex,
&mut c.resource_descriptor_buffer_offset,
&c.color_management_data_address,
&c.eotf_args_address,
),
VulkanOp::RoundedTex(c) => (
&c.tex,
&mut c.resource_descriptor_buffer_offset,
&c.color_management_data_address,
&c.eotf_args_address,
),
_ => continue,
};
let tex = &c.tex;
c.resource_descriptor_buffer_offset = resource_writer.next_offset();
*resource_offset = resource_writer.next_offset();
let mut writer = resource_writer.add_set(tex_descriptor_set_layout);
writer.write(
tex_descriptor_set_layout.offsets[0],
tex.sampled_image_descriptor.as_ref().unwrap(),
);
if let Some(addr) = c.color_management_data_address {
if let Some(addr) = *cm_addr {
writer.write(
tex_descriptor_set_layout.offsets[1],
get_ub_descriptor!(addr, ColorManagementData),
);
}
if let Some(addr) = c.eotf_args_address {
if let Some(addr) = *eotf_addr {
writer.write(
tex_descriptor_set_layout.offsets[2],
get_ub_descriptor!(addr, EotfArgs),
@ -741,12 +923,18 @@ impl VulkanRenderer {
enum Key {
Fill { color: [u32; 4] },
Tex(usize),
RoundedFill { color: [u32; 4] },
RoundedTex(usize),
}
match o {
VulkanOp::Fill(f) => Key::Fill {
color: f.color.map(|c| c.to_bits()),
},
VulkanOp::Tex(t) => Key::Tex(t.index),
VulkanOp::RoundedFill(f) => Key::RoundedFill {
color: f.color.map(|c| c.to_bits()),
},
VulkanOp::RoundedTex(t) => Key::RoundedTex(t.index),
}
});
let mops = &mut memory.ops[pass];
@ -782,6 +970,22 @@ impl VulkanRenderer {
}
mops.push(VulkanOp::Tex(c));
}
VulkanOp::RoundedFill(mut f) => {
f.range_address = memory.data_buffer.len() as DeviceAddress;
memory.data_buffer.extend_from_slice(uapi::as_bytes(&f.target));
mops.push(VulkanOp::RoundedFill(f));
}
VulkanOp::RoundedTex(mut c) => {
c.range_address = memory.data_buffer.len() as DeviceAddress;
let vertex = TexVertex {
pos: c.target,
tex_pos: c.source,
};
memory
.data_buffer
.extend_from_slice(uapi::as_bytes(&vertex));
mops.push(VulkanOp::RoundedTex(c));
}
}
}
}
@ -917,6 +1121,105 @@ impl VulkanRenderer {
}));
}
}
GfxApiOpt::RoundedFillRect(rf) => {
let target = rf.rect.to_points();
for pass in RenderPass::variants() {
let Some(bounds) = memory.paint_bounds[pass] else {
continue;
};
if !bounds.intersects(&target) {
continue;
}
let target_cd = match pass {
RenderPass::BlendBuffer => blend_cd,
RenderPass::FrameBuffer => fb_cd,
};
let tf = target_cd.eotf;
let color = memory.color_transforms.apply_to_color(
&rf.cd,
target_cd,
rf.render_intent,
rf.color,
);
let color = color.to_array2(tf, rf.alpha);
let source_type = TexSourceType::HasAlpha;
memory.ops_tmp[pass].push(VulkanOp::RoundedFill(VulkanRoundedFillOp {
target,
color,
source_type,
size: rf.size,
corner_radius: rf.corner_radius,
border_width: rf.border_width,
scale: rf.scale,
range_address: 0,
}));
}
}
GfxApiOpt::RoundedCopyTexture(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 Some(bounds) = memory.paint_bounds[pass] else {
continue;
};
if !bounds.intersects(&target) {
continue;
}
let copy_type = match ct.alpha.is_some() {
true => TexCopyType::Multiply,
false => TexCopyType::Identity,
};
let source_type = TexSourceType::HasAlpha;
let target_cd = match pass {
RenderPass::BlendBuffer => blend_cd,
RenderPass::FrameBuffer => fb_cd,
};
let color_management_data_address = memory.color_transforms.get_offset(
&ct.cd.linear,
target_cd,
ct.render_intent,
self.device.uniform_buffer_offset_mask,
&mut memory.uniform_buffer_writer,
);
let eotf_args_address = memory.eotf_args_cache.get_offset(
&ct.cd,
false,
self.device.uniform_buffer_offset_mask,
&mut memory.uniform_buffer_writer,
);
memory.ops_tmp[pass].push(VulkanOp::RoundedTex(VulkanRoundedTexOp {
tex: tex.clone(),
index,
target,
source,
buffer_resv: ct.buffer_resv.clone(),
acquire_sync: Some(ct.acquire_sync.clone()),
release_sync: ct.release_sync,
alpha: ct.alpha.unwrap_or_default(),
source_type,
copy_type,
alpha_mode: ct.alpha_mode,
tex_cd: ct.cd.clone(),
color_management_data_address,
eotf_args_address,
resource_descriptor_buffer_offset: 0,
size: ct.size,
corner_radius: ct.corner_radius,
scale: ct.scale,
range_address: 0,
}));
}
}
}
}
sync(memory);
@ -998,6 +1301,12 @@ impl VulkanRenderer {
VulkanOp::Tex(c) => {
c.range_address += buffer.buffer.address;
}
VulkanOp::RoundedFill(f) => {
f.range_address += buffer.buffer.address;
}
VulkanOp::RoundedTex(c) => {
c.range_address += buffer.buffer.address;
}
}
}
}
@ -1029,9 +1338,16 @@ impl VulkanRenderer {
}
for ops in memory.ops.values_mut() {
for op in ops {
if let VulkanOp::Tex(c) = op {
adj!(&mut c.color_management_data_address);
adj!(&mut c.eotf_args_address);
match op {
VulkanOp::Tex(c) => {
adj!(&mut c.color_management_data_address);
adj!(&mut c.eotf_args_address);
}
VulkanOp::RoundedTex(c) => {
adj!(&mut c.color_management_data_address);
adj!(&mut c.eotf_args_address);
}
_ => {}
}
}
}
@ -1051,8 +1367,12 @@ impl VulkanRenderer {
let execution = self.allocate_point();
for pass in RenderPass::variants() {
for cmd in &mut memory.ops[pass] {
if let VulkanOp::Tex(c) = cmd {
let tex = &c.tex;
let tex_data = match cmd {
VulkanOp::Tex(c) => Some((&c.tex, &mut c.buffer_resv, &mut c.acquire_sync, c.release_sync)),
VulkanOp::RoundedTex(c) => Some((&c.tex, &mut c.buffer_resv, &mut c.acquire_sync, c.release_sync)),
_ => None,
};
if let Some((tex, buffer_resv, acquire_sync, release_sync)) = tex_data {
if tex.execution_version.replace(execution) == execution {
continue;
}
@ -1066,9 +1386,9 @@ impl VulkanRenderer {
}
memory.textures.push(UsedTexture {
tex: tex.clone(),
resv: c.buffer_resv.take(),
acquire_sync: c.acquire_sync.take().unwrap(),
release_sync: c.release_sync,
resv: buffer_resv.take(),
acquire_sync: acquire_sync.take().unwrap(),
release_sync,
});
}
}
@ -1301,6 +1621,10 @@ impl VulkanRenderer {
let memory = &*self.memory.borrow();
let fill_pl = self.get_or_create_fill_pipelines(target.format.vk_format)?;
let tex_pl = self.get_or_create_tex_pipelines(target.format.vk_format, target_cd);
let rounded_fill_pl =
self.get_or_create_rounded_fill_pipelines(target.format.vk_format)?;
let rounded_tex_pl =
self.get_or_create_rounded_tex_pipelines(target.format.vk_format, target_cd);
let dev = &self.device.device;
let mut current_pipeline = None;
let mut bind = |pipeline: &VulkanPipeline| {
@ -1421,6 +1745,134 @@ impl VulkanRenderer {
}
}
}
VulkanOp::RoundedFill(r) => {
let pipeline = &rounded_fill_pl[r.source_type];
bind(pipeline);
if self.device.descriptor_buffer.is_some() {
let push = RoundedFillPushConstants {
color: r.color,
vertices: r.range_address,
size: r.size,
corner_radius: r.corner_radius,
border_width: r.border_width,
scale: r.scale,
};
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);
}
} else {
let push = LegacyRoundedFillPushConstants {
pos: r.target,
color: r.color,
size_x: r.size[0],
size_y: r.size[1],
corner_radius_tl: r.corner_radius[0],
corner_radius_tr: r.corner_radius[1],
corner_radius_br: r.corner_radius[2],
corner_radius_bl: r.corner_radius[3],
border_width: r.border_width,
scale: r.scale,
};
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);
}
}
}
VulkanOp::RoundedTex(c) => {
let tex = &c.tex;
let pipeline = self.get_or_create_rounded_tex_pipeline(
&rounded_tex_pl,
&c.tex_cd,
c.copy_type,
c.source_type,
c.alpha_mode,
c.color_management_data_address.is_some(),
)?;
bind(&pipeline);
let image_info = DescriptorImageInfo::default()
.image_view(tex.texture_view)
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
if let Some(db) = &self.device.descriptor_buffer {
let push = RoundedTexPushConstants {
vertices: c.range_address,
alpha: c.alpha,
size_x: c.size[0],
size_y: c.size[1],
corner_radius_tl: c.corner_radius[0],
corner_radius_tr: c.corner_radius[1],
corner_radius_br: c.corner_radius[2],
corner_radius_bl: c.corner_radius[3],
scale: c.scale,
};
unsafe {
db.cmd_set_descriptor_buffer_offsets(
buf,
PipelineBindPoint::GRAPHICS,
pipeline.pipeline_layout,
0,
&[0, 1],
&[0, c.resource_descriptor_buffer_offset],
);
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);
}
} 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),
);
}
let push = LegacyRoundedTexPushConstants {
pos: c.target,
tex_pos: c.source,
alpha: c.alpha,
size_x: c.size[0],
size_y: c.size[1],
corner_radius_tl: c.corner_radius[0],
corner_radius_tr: c.corner_radius[1],
corner_radius_br: c.corner_radius[2],
corner_radius_bl: c.corner_radius[3],
scale: c.scale,
};
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);
}
}
}
}
}
Ok(())
@ -1933,6 +2385,13 @@ impl VulkanRenderer {
};
(opaque, c.target)
}
GfxApiOpt::RoundedFillRect(_) => {
// Rounded rects are never fully opaque due to AA at corners
continue;
}
GfxApiOpt::RoundedCopyTexture(_) => {
continue;
}
};
if opaque || bb.is_none() {
tag |= 1;