Merge pull request #381 from mahkoh/jorth/blend-buffer-2
vulkan: add blend buffers
This commit is contained in:
commit
084006d64a
44 changed files with 2257 additions and 357 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::mem_replace_with_default,
|
clippy::mem_replace_with_default,
|
||||||
clippy::comparison_chain,
|
clippy::comparison_chain,
|
||||||
|
clippy::collapsible_else_if,
|
||||||
clippy::needless_lifetimes
|
clippy::needless_lifetimes
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,31 +5,72 @@ use {
|
||||||
std::fmt::{Debug, Formatter},
|
std::fmt::{Debug, Formatter},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub trait Tag: Copy + Eq + Ord + Debug + Default + Sized {
|
||||||
|
const IS_SIGNIFICANT: bool;
|
||||||
|
|
||||||
|
fn constrain(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Default)]
|
||||||
|
pub struct NoTag;
|
||||||
|
|
||||||
|
impl Tag for NoTag {
|
||||||
|
const IS_SIGNIFICANT: bool = false;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn constrain(self) -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tag for u32 {
|
||||||
|
const IS_SIGNIFICANT: bool = true;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn constrain(self) -> Self {
|
||||||
|
self & 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
||||||
pub struct RectRaw {
|
pub struct RectRaw<T = NoTag>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
pub x1: i32,
|
pub x1: i32,
|
||||||
pub y1: i32,
|
pub y1: i32,
|
||||||
pub x2: i32,
|
pub x2: i32,
|
||||||
pub y2: i32,
|
pub y2: i32,
|
||||||
|
pub tag: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for RectRaw {
|
impl<T> Debug for RectRaw<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Rect")
|
let mut debug = f.debug_struct("Rect");
|
||||||
|
debug
|
||||||
.field("x1", &self.x1)
|
.field("x1", &self.x1)
|
||||||
.field("y1", &self.y1)
|
.field("y1", &self.y1)
|
||||||
.field("x2", &self.x2)
|
.field("x2", &self.x2)
|
||||||
.field("y2", &self.y2)
|
.field("y2", &self.y2)
|
||||||
.field("width", &(self.x2 - self.x1))
|
.field("width", &(self.x2 - self.x1))
|
||||||
.field("height", &(self.y2 - self.y1))
|
.field("height", &(self.y2 - self.y1));
|
||||||
.finish()
|
if T::IS_SIGNIFICANT {
|
||||||
|
debug.field("tag", &self.tag);
|
||||||
|
}
|
||||||
|
debug.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RectRaw {
|
impl<T> RectRaw<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.x1 == self.x2 || self.y1 == self.y2
|
self.x1 == self.x2 || self.y1 == self.y2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Container = SmallVec<[RectRaw; 1]>;
|
type Container<T = NoTag> = SmallVec<[RectRaw<T>; 1]>;
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,64 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
rect::{Container, RectRaw},
|
rect::{Container, NoTag, RectRaw, Tag},
|
||||||
windows::WindowsExt,
|
windows::WindowsExt,
|
||||||
},
|
},
|
||||||
std::{cmp::Ordering, collections::BinaryHeap, mem, ops::Deref},
|
std::{cmp::Ordering, collections::BinaryHeap, marker::PhantomData, mem, ops::Deref},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn union(left: &Container, right: &Container) -> Container {
|
pub fn union(left: &Container, right: &Container) -> Container {
|
||||||
op::<Union>(left, right)
|
op::<_, _, _, Union>(left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subtract(left: &Container, right: &Container) -> Container {
|
pub fn subtract(left: &Container, right: &Container) -> Container {
|
||||||
op::<Subtract>(left, right)
|
op::<_, _, _, Subtract>(left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Bands<'a> {
|
pub fn intersect(left: &Container, right: &Container) -> Container {
|
||||||
rects: &'a [RectRaw],
|
op::<_, _, _, Intersect<NoTag>>(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect_tagged(left: &Container<u32>, right: &Container) -> Container<u32> {
|
||||||
|
op::<_, _, _, Intersect<u32>>(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bands<'a, T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
rects: &'a [RectRaw<T>],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Band<'a> {
|
struct Band<'a, T>
|
||||||
rects: &'a [RectRaw],
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
rects: &'a [RectRaw<T>],
|
||||||
y1: i32,
|
y1: i32,
|
||||||
y2: i32,
|
y2: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Band<'a> {
|
impl<'a, T> Band<'a, T>
|
||||||
fn can_merge_with(&self, next: &Band) -> bool {
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
fn can_merge_with(&self, next: &Band<'_, T>) -> bool {
|
||||||
next.rects.len() == self.rects.len()
|
next.rects.len() == self.rects.len()
|
||||||
&& next.y1 == self.y2
|
&& next.y1 == self.y2
|
||||||
&& next
|
&& next
|
||||||
.rects
|
.rects
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.rects.iter())
|
.zip(self.rects.iter())
|
||||||
.all(|(a, b)| (a.x1, a.x2) == (b.x1, b.x2))
|
.all(|(a, b)| (a.x1, a.x2, a.tag) == (b.x1, b.x2, b.tag))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Bands<'a> {
|
impl<'a, T> Iterator for Bands<'a, T>
|
||||||
type Item = Band<'a>;
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
type Item = Band<'a, T>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.rects.is_empty() {
|
if self.rects.is_empty() {
|
||||||
|
|
@ -62,7 +82,10 @@ impl<'a> Iterator for Bands<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn extents(a: &[RectRaw]) -> RectRaw {
|
pub fn extents<T>(a: &[RectRaw<T>]) -> RectRaw
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
let mut a = a.iter();
|
let mut a = a.iter();
|
||||||
let mut res = match a.next() {
|
let mut res = match a.next() {
|
||||||
Some(a) => *a,
|
Some(a) => *a,
|
||||||
|
|
@ -74,10 +97,21 @@ pub fn extents(a: &[RectRaw]) -> RectRaw {
|
||||||
res.x2 = res.x2.max(a.x2);
|
res.x2 = res.x2.max(a.x2);
|
||||||
res.y2 = res.y2.max(a.y2);
|
res.y2 = res.y2.max(a.y2);
|
||||||
}
|
}
|
||||||
res
|
RectRaw {
|
||||||
|
x1: res.x1,
|
||||||
|
y1: res.y1,
|
||||||
|
x2: res.x2,
|
||||||
|
y2: res.y2,
|
||||||
|
tag: NoTag,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
fn op<T1, T2, T3, O: Op<T1, T2, T3>>(a: &[RectRaw<T2>], b: &[RectRaw<T3>]) -> Container<T1>
|
||||||
|
where
|
||||||
|
T1: Tag,
|
||||||
|
T2: Tag,
|
||||||
|
T3: Tag,
|
||||||
|
{
|
||||||
let mut res = Container::new();
|
let mut res = Container::new();
|
||||||
|
|
||||||
let mut prev_band_y2 = 0;
|
let mut prev_band_y2 = 0;
|
||||||
|
|
@ -100,7 +134,7 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! append_nonoverlapping {
|
macro_rules! append_nonoverlapping {
|
||||||
($append_opt:expr, $a:expr, $a_opt:expr, $a_bands:expr, $b:expr) => {{
|
($append_opt:expr, $map:ident, $a:expr, $a_opt:expr, $a_bands:expr, $b:expr) => {{
|
||||||
if $append_opt {
|
if $append_opt {
|
||||||
let y2 = $a.y2.min($b.y1);
|
let y2 = $a.y2.min($b.y1);
|
||||||
cur_band_start = res.len();
|
cur_band_start = res.len();
|
||||||
|
|
@ -111,6 +145,7 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
||||||
y1: $a.y1,
|
y1: $a.y1,
|
||||||
x2: rect.x2,
|
x2: rect.x2,
|
||||||
y2,
|
y2,
|
||||||
|
tag: O::$map(rect.tag),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fixup_new_band!($a.y1, y2);
|
fixup_new_band!($a.y1, y2);
|
||||||
|
|
@ -125,9 +160,9 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
||||||
|
|
||||||
while let (Some(a), Some(b)) = (&mut a_opt, &mut b_opt) {
|
while let (Some(a), Some(b)) = (&mut a_opt, &mut b_opt) {
|
||||||
if a.y1 < b.y1 {
|
if a.y1 < b.y1 {
|
||||||
append_nonoverlapping!(O::APPEND_NON_A, a, a_opt, a_bands, b);
|
append_nonoverlapping!(O::APPEND_NON_A, map_t2_to_t1, a, a_opt, a_bands, b);
|
||||||
} else if b.y1 < a.y1 {
|
} else if b.y1 < a.y1 {
|
||||||
append_nonoverlapping!(O::APPEND_NON_B, b, b_opt, b_bands, a);
|
append_nonoverlapping!(O::APPEND_NON_B, map_t3_to_t1, b, b_opt, b_bands, a);
|
||||||
} else {
|
} else {
|
||||||
let y2 = a.y2.min(b.y2);
|
let y2 = a.y2.min(b.y2);
|
||||||
cur_band_start = res.len();
|
cur_band_start = res.len();
|
||||||
|
|
@ -149,7 +184,7 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! push_trailing {
|
macro_rules! push_trailing {
|
||||||
($a_opt:expr, $a_bands:expr) => {{
|
($a_opt:expr, $a_bands:expr, $map:ident) => {{
|
||||||
while let Some(a) = $a_opt {
|
while let Some(a) = $a_opt {
|
||||||
cur_band_start = res.len();
|
cur_band_start = res.len();
|
||||||
res.reserve(a.rects.len());
|
res.reserve(a.rects.len());
|
||||||
|
|
@ -159,6 +194,7 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
||||||
y1: a.y1,
|
y1: a.y1,
|
||||||
x2: rect.x2,
|
x2: rect.x2,
|
||||||
y2: a.y2,
|
y2: a.y2,
|
||||||
|
tag: O::$map(rect.tag),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fixup_new_band!(a.y1, a.y2);
|
fixup_new_band!(a.y1, a.y2);
|
||||||
|
|
@ -168,25 +204,28 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
if O::APPEND_NON_A {
|
if O::APPEND_NON_A {
|
||||||
push_trailing!(a_opt, a_bands);
|
push_trailing!(a_opt, a_bands, map_t2_to_t1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if O::APPEND_NON_B {
|
if O::APPEND_NON_B {
|
||||||
push_trailing!(b_opt, b_bands);
|
push_trailing!(b_opt, b_bands, map_t3_to_t1);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.shrink_to_fit();
|
res.shrink_to_fit();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coalesce(new: &mut Container, a: usize, b: usize, y2: i32) -> bool {
|
fn coalesce<T>(new: &mut Container<T>, a: usize, b: usize, y2: i32) -> bool
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
if new.len() - b != b - a {
|
if new.len() - b != b - a {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let slice_a = &new[a..b];
|
let slice_a = &new[a..b];
|
||||||
let slice_b = &new[b..];
|
let slice_b = &new[b..];
|
||||||
for (a, b) in slice_a.iter().zip(slice_b.iter()) {
|
for (a, b) in slice_a.iter().zip(slice_b.iter()) {
|
||||||
if (a.x1, a.x2) != (b.x1, b.x2) {
|
if (a.x1, a.x2, a.tag) != (b.x1, b.x2, b.tag) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,16 +236,25 @@ fn coalesce(new: &mut Container, a: usize, b: usize, y2: i32) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Op {
|
trait Op<T1, T2, T3>
|
||||||
|
where
|
||||||
|
T1: Tag,
|
||||||
|
T2: Tag,
|
||||||
|
T3: Tag,
|
||||||
|
{
|
||||||
const APPEND_NON_A: bool;
|
const APPEND_NON_A: bool;
|
||||||
const APPEND_NON_B: bool;
|
const APPEND_NON_B: bool;
|
||||||
|
|
||||||
fn handle_band(new: &mut Container, a: &[RectRaw], b: &[RectRaw], y1: i32, y2: i32);
|
fn handle_band(new: &mut Container<T1>, a: &[RectRaw<T2>], b: &[RectRaw<T3>], y1: i32, y2: i32);
|
||||||
|
|
||||||
|
fn map_t2_to_t1(tag: T2) -> T1;
|
||||||
|
|
||||||
|
fn map_t3_to_t1(tag: T3) -> T1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Union;
|
struct Union;
|
||||||
|
|
||||||
impl Op for Union {
|
impl Op<NoTag, NoTag, NoTag> for Union {
|
||||||
const APPEND_NON_A: bool = true;
|
const APPEND_NON_A: bool = true;
|
||||||
const APPEND_NON_B: bool = true;
|
const APPEND_NON_B: bool = true;
|
||||||
|
|
||||||
|
|
@ -216,7 +264,13 @@ impl Op for Union {
|
||||||
|
|
||||||
macro_rules! push {
|
macro_rules! push {
|
||||||
() => {
|
() => {
|
||||||
new.push(RectRaw { x1, y1, x2, y2 });
|
new.push(RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag: NoTag,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,11 +326,21 @@ impl Op for Union {
|
||||||
|
|
||||||
push!();
|
push!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn map_t2_to_t1(_tag: NoTag) -> NoTag {
|
||||||
|
NoTag
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn map_t3_to_t1(_tag: NoTag) -> NoTag {
|
||||||
|
NoTag
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Subtract;
|
struct Subtract;
|
||||||
|
|
||||||
impl Op for Subtract {
|
impl Op<NoTag, NoTag, NoTag> for Subtract {
|
||||||
const APPEND_NON_A: bool = true;
|
const APPEND_NON_A: bool = true;
|
||||||
const APPEND_NON_B: bool = false;
|
const APPEND_NON_B: bool = false;
|
||||||
|
|
||||||
|
|
@ -291,6 +355,7 @@ impl Op for Subtract {
|
||||||
y1,
|
y1,
|
||||||
x2: $x2,
|
x2: $x2,
|
||||||
y2,
|
y2,
|
||||||
|
tag: NoTag,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -337,33 +402,145 @@ impl Op for Subtract {
|
||||||
pull!();
|
pull!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn map_t2_to_t1(_tag: NoTag) -> NoTag {
|
||||||
|
NoTag
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn map_t3_to_t1(_tag: NoTag) -> NoTag {
|
||||||
|
NoTag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Intersect<T>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T> Op<T, T, NoTag> for Intersect<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
const APPEND_NON_A: bool = false;
|
||||||
|
const APPEND_NON_B: bool = false;
|
||||||
|
|
||||||
|
fn handle_band(new: &mut Container<T>, a: &[RectRaw<T>], b: &[RectRaw], y1: i32, y2: i32) {
|
||||||
|
let mut x1;
|
||||||
|
let mut x2;
|
||||||
|
let mut tag;
|
||||||
|
|
||||||
|
macro_rules! push {
|
||||||
|
($x2:expr) => {
|
||||||
|
new.push(RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2: $x2,
|
||||||
|
y2,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut a_iter = a.iter();
|
||||||
|
let mut b_iter = b.iter();
|
||||||
|
|
||||||
|
macro_rules! pull {
|
||||||
|
() => {
|
||||||
|
match a_iter.next() {
|
||||||
|
Some(n) => {
|
||||||
|
x1 = n.x1;
|
||||||
|
x2 = n.x2;
|
||||||
|
tag = n.tag;
|
||||||
|
}
|
||||||
|
_ => return,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pull!();
|
||||||
|
|
||||||
|
let mut b_opt = b_iter.next();
|
||||||
|
|
||||||
|
while let Some(b) = b_opt {
|
||||||
|
if b.x2 <= x1 {
|
||||||
|
b_opt = b_iter.next();
|
||||||
|
} else if b.x1 >= x2 {
|
||||||
|
pull!();
|
||||||
|
} else {
|
||||||
|
x1 = x1.max(b.x1);
|
||||||
|
if x2 <= b.x2 {
|
||||||
|
push!(x2);
|
||||||
|
pull!();
|
||||||
|
} else {
|
||||||
|
push!(b.x2);
|
||||||
|
b_opt = b_iter.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn map_t2_to_t1(_tag: T) -> T {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn map_t3_to_t1(_tag: NoTag) -> T {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rects_to_bands(rects_tmp: &[RectRaw]) -> Container {
|
pub fn rects_to_bands(rects_tmp: &[RectRaw]) -> Container {
|
||||||
|
rects_to_bands_(rects_tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rects_to_bands_tagged(rects_tmp: &[RectRaw<u32>]) -> Container<u32> {
|
||||||
|
rects_to_bands_(rects_tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn rects_to_bands_<T>(rects_tmp: &[RectRaw<T>]) -> Container<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct W(RectRaw);
|
struct W<T>(RectRaw<T>)
|
||||||
impl Eq for W {}
|
where
|
||||||
impl PartialEq<Self> for W {
|
T: Tag;
|
||||||
|
impl<T> Eq for W<T> where T: Tag {}
|
||||||
|
impl<T> PartialEq<Self> for W<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.0 == other.0
|
self.0 == other.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl PartialOrd<Self> for W {
|
impl<T> PartialOrd<Self> for W<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Ord for W {
|
impl<T> Ord for W<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
self.0
|
self.0
|
||||||
.y1
|
.y1
|
||||||
.cmp(&other.0.y1)
|
.cmp(&other.0.y1)
|
||||||
.then_with(|| self.0.x1.cmp(&other.0.x1))
|
.then_with(|| self.0.x1.cmp(&other.0.x1))
|
||||||
|
.then_with(|| self.0.tag.cmp(&other.0.tag))
|
||||||
.reverse()
|
.reverse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Deref for W {
|
impl<T> Deref for W<T>
|
||||||
type Target = RectRaw;
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
type Target = RectRaw<T>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
@ -411,17 +588,60 @@ pub fn rects_to_bands(rects_tmp: &[RectRaw]) -> Container {
|
||||||
check_rect!(rect);
|
check_rect!(rect);
|
||||||
let mut x1 = rect.x1;
|
let mut x1 = rect.x1;
|
||||||
let mut x2 = rect.x2;
|
let mut x2 = rect.x2;
|
||||||
|
let mut tag: T = rect.tag;
|
||||||
while let Some(mut rect) = rects.peek().copied() {
|
while let Some(mut rect) = rects.peek().copied() {
|
||||||
check_rect!(rect);
|
check_rect!(rect);
|
||||||
if rect.x1 > x2 {
|
if rect.x1 > x2 || (rect.tag != tag && rect.x1 == x2) {
|
||||||
res.push(RectRaw { x1, x2, y1, y2 });
|
res.push(RectRaw {
|
||||||
|
x1,
|
||||||
|
x2,
|
||||||
|
y1,
|
||||||
|
y2,
|
||||||
|
tag: tag.constrain(),
|
||||||
|
});
|
||||||
x1 = rect.x1;
|
x1 = rect.x1;
|
||||||
x2 = rect.x2;
|
x2 = rect.x2;
|
||||||
|
tag = rect.tag;
|
||||||
} else {
|
} else {
|
||||||
x2 = x2.max(rect.x2);
|
if rect.tag == tag {
|
||||||
|
x2 = x2.max(rect.x2);
|
||||||
|
} else if rect.tag > tag {
|
||||||
|
if rect.x2 > x2 {
|
||||||
|
rect.0.x1 = x2;
|
||||||
|
rect.0.y1 = y1;
|
||||||
|
rect.0.y2 = y2;
|
||||||
|
rects.push(rect);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if x2 > rect.x2 {
|
||||||
|
rects.push(W(RectRaw {
|
||||||
|
x1: rect.x2,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
res.push(RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2: rect.x1,
|
||||||
|
y2,
|
||||||
|
tag: tag.constrain(),
|
||||||
|
});
|
||||||
|
x1 = rect.x1;
|
||||||
|
x2 = rect.x2;
|
||||||
|
tag = rect.tag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.push(RectRaw { x1, x2, y1, y2 });
|
res.push(RectRaw {
|
||||||
|
x1,
|
||||||
|
x2,
|
||||||
|
y1,
|
||||||
|
y2,
|
||||||
|
tag: tag.constrain(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ pub fn main() -> anyhow::Result<()> {
|
||||||
compile_simple("fill.vert")?;
|
compile_simple("fill.vert")?;
|
||||||
compile_simple("tex.vert")?;
|
compile_simple("tex.vert")?;
|
||||||
compile_simple("tex.frag")?;
|
compile_simple("tex.frag")?;
|
||||||
|
compile_simple("out.vert")?;
|
||||||
|
compile_simple("out.frag")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
- Various bugfixes.
|
- Various bugfixes.
|
||||||
|
- The vulkan renderer now only renders in damaged areas. This has exposed several places
|
||||||
|
where the damage tracking was incorrect. There might be additional damage tracking bugs.
|
||||||
|
Such bugs manifest through flickering or through areas getting stuck with an old image.
|
||||||
|
If you encounter such an issue, please open a bug.
|
||||||
|
- The vulkan renderer now performs blending in linear space. A green window with 50%
|
||||||
|
opacity on top of a red window will produce a perfectly yellow image instead of a muddy
|
||||||
|
yellow. The blend buffer is only used for those areas of the screen where blending is
|
||||||
|
observable. This should have no impact on performance in the common case.
|
||||||
|
|
||||||
# 1.9.1 (2025-02-13)
|
# 1.9.1 (2025-02-13)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -570,7 +570,7 @@ impl MetalConnector {
|
||||||
match opt {
|
match opt {
|
||||||
GfxApiOpt::Sync => {}
|
GfxApiOpt::Sync => {}
|
||||||
GfxApiOpt::FillRect(fr) => {
|
GfxApiOpt::FillRect(fr) => {
|
||||||
if fr.color == Color::SOLID_BLACK {
|
if fr.effective_color() == Color::SOLID_BLACK {
|
||||||
// Black fills can be ignored because this is the CRTC background color.
|
// Black fills can be ignored because this is the CRTC background color.
|
||||||
if fr.rect.is_covering() {
|
if fr.rect.is_covering() {
|
||||||
// If fill covers the entire screen, we don't have to look further.
|
// If fill covers the entire screen, we don't have to look further.
|
||||||
|
|
@ -749,6 +749,7 @@ impl MetalConnector {
|
||||||
ReleaseSync::Explicit,
|
ReleaseSync::Explicit,
|
||||||
&latched.pass,
|
&latched.pass,
|
||||||
&latched.damage,
|
&latched.damage,
|
||||||
|
buffer.blend_buffer.as_ref(),
|
||||||
)
|
)
|
||||||
.map_err(MetalError::RenderFrame)?;
|
.map_err(MetalError::RenderFrame)?;
|
||||||
sync_file = buffer.copy_to_dev(sf)?;
|
sync_file = buffer.copy_to_dev(sf)?;
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ use {
|
||||||
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
||||||
format::{ARGB8888, Format, XRGB8888},
|
format::{ARGB8888, Format, XRGB8888},
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync, SyncFile,
|
AcquireSync, GfxBlendBuffer, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync,
|
||||||
needs_render_usage,
|
SyncFile, needs_render_usage,
|
||||||
},
|
},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_output::OutputId,
|
wl_output::OutputId,
|
||||||
|
|
@ -2607,6 +2607,15 @@ impl MetalBackend {
|
||||||
ctx: &MetalRenderContext,
|
ctx: &MetalRenderContext,
|
||||||
cursor: bool,
|
cursor: bool,
|
||||||
) -> Result<[RenderBuffer; N], MetalError> {
|
) -> Result<[RenderBuffer; N], MetalError> {
|
||||||
|
let mut blend_buffer = None;
|
||||||
|
if !cursor {
|
||||||
|
match ctx.gfx.acquire_blend_buffer(width, height) {
|
||||||
|
Ok(bb) => blend_buffer = Some(bb),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not create blend buffer: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut damage_queue = ArrayVec::from(DamageQueue::new::<N>());
|
let mut damage_queue = ArrayVec::from(DamageQueue::new::<N>());
|
||||||
let mut create = || {
|
let mut create = || {
|
||||||
self.create_scanout_buffer(
|
self.create_scanout_buffer(
|
||||||
|
|
@ -2618,6 +2627,7 @@ impl MetalBackend {
|
||||||
ctx,
|
ctx,
|
||||||
cursor,
|
cursor,
|
||||||
damage_queue.pop().unwrap(),
|
damage_queue.pop().unwrap(),
|
||||||
|
blend_buffer.clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let mut array = ArrayVec::<_, N>::new();
|
let mut array = ArrayVec::<_, N>::new();
|
||||||
|
|
@ -2640,6 +2650,7 @@ impl MetalBackend {
|
||||||
render_ctx: &MetalRenderContext,
|
render_ctx: &MetalRenderContext,
|
||||||
cursor: bool,
|
cursor: bool,
|
||||||
damage_queue: DamageQueue,
|
damage_queue: DamageQueue,
|
||||||
|
blend_buffer: Option<Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<RenderBuffer, MetalError> {
|
) -> Result<RenderBuffer, MetalError> {
|
||||||
let ctx = dev.ctx.get();
|
let ctx = dev.ctx.get();
|
||||||
let dev_gfx_formats = ctx.gfx.formats();
|
let dev_gfx_formats = ctx.gfx.formats();
|
||||||
|
|
@ -2771,6 +2782,7 @@ impl MetalBackend {
|
||||||
damage_queue,
|
damage_queue,
|
||||||
dev_bo,
|
dev_bo,
|
||||||
_render_bo: render_bo,
|
_render_bo: render_bo,
|
||||||
|
blend_buffer,
|
||||||
dev_fb,
|
dev_fb,
|
||||||
dev_tex,
|
dev_tex,
|
||||||
render_tex,
|
render_tex,
|
||||||
|
|
@ -2996,6 +3008,7 @@ pub struct RenderBuffer {
|
||||||
pub damage_queue: DamageQueue,
|
pub damage_queue: DamageQueue,
|
||||||
pub dev_bo: GbmBo,
|
pub dev_bo: GbmBo,
|
||||||
pub _render_bo: Option<GbmBo>,
|
pub _render_bo: Option<GbmBo>,
|
||||||
|
pub blend_buffer: Option<Rc<dyn GfxBlendBuffer>>,
|
||||||
// ctx = dev
|
// ctx = dev
|
||||||
// buffer location = dev
|
// buffer location = dev
|
||||||
pub dev_fb: Rc<dyn GfxFramebuffer>,
|
pub dev_fb: Rc<dyn GfxFramebuffer>,
|
||||||
|
|
|
||||||
|
|
@ -755,6 +755,7 @@ impl XBackend {
|
||||||
ReleaseSync::Implicit,
|
ReleaseSync::Implicit,
|
||||||
&image.tex.get(),
|
&image.tex.get(),
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
log::error!("Could not render screen: {}", ErrorFmt(e));
|
log::error!("Could not render screen: {}", ErrorFmt(e));
|
||||||
|
|
|
||||||
|
|
@ -391,6 +391,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -414,6 +415,7 @@ impl Cursor for StaticCursor {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -455,6 +457,7 @@ impl Cursor for AnimatedCursor {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -408,7 +408,7 @@ static XBGR16161616: &Format = &Format {
|
||||||
..default(ConfigFormat::XBGR16161616)
|
..default(ConfigFormat::XBGR16161616)
|
||||||
};
|
};
|
||||||
|
|
||||||
static ABGR16161616F: &Format = &Format {
|
pub static ABGR16161616F: &Format = &Format {
|
||||||
name: "abgr16161616f",
|
name: "abgr16161616f",
|
||||||
vk_format: vk::Format::R16G16B16A16_SFLOAT,
|
vk_format: vk::Format::R16G16B16A16_SFLOAT,
|
||||||
drm: fourcc_code('A', 'B', '4', 'H'),
|
drm: fourcc_code('A', 'B', '4', 'H'),
|
||||||
|
|
|
||||||
103
src/gfx_api.rs
103
src/gfx_api.rs
|
|
@ -116,7 +116,7 @@ impl SampleRect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct FramebufferRect {
|
pub struct FramebufferRect {
|
||||||
pub x1: f32,
|
pub x1: f32,
|
||||||
pub x2: f32,
|
pub x2: f32,
|
||||||
|
|
@ -165,12 +165,45 @@ impl FramebufferRect {
|
||||||
pub fn is_covering(&self) -> bool {
|
pub fn is_covering(&self) -> bool {
|
||||||
self.x1 == -1.0 && self.y1 == -1.0 && self.x2 == 1.0 && self.y2 == 1.0
|
self.x1 == -1.0 && self.y1 == -1.0 && self.x2 == 1.0 && self.y2 == 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_rect(&self, width: f32, height: f32) -> Rect {
|
||||||
|
let mut x1 = self.x1;
|
||||||
|
let mut x2 = self.x2;
|
||||||
|
let mut y1 = self.y1;
|
||||||
|
let mut y2 = self.y2;
|
||||||
|
(x1, y1, x2, y2) = match self.output_transform {
|
||||||
|
Transform::None => (x1, y1, x2, y2),
|
||||||
|
Transform::Rotate90 => (y1, -x2, y2, -x1),
|
||||||
|
Transform::Rotate180 => (-x2, -y2, -x1, -y1),
|
||||||
|
Transform::Rotate270 => (-y2, x1, -y1, x2),
|
||||||
|
Transform::Flip => (-x2, y1, -x1, y2),
|
||||||
|
Transform::FlipRotate90 => (y1, x1, y2, x2),
|
||||||
|
Transform::FlipRotate180 => (x1, -y2, x2, -y1),
|
||||||
|
Transform::FlipRotate270 => (-y2, -x2, -y1, -x1),
|
||||||
|
};
|
||||||
|
let x1 = ((x1 + 1.0) / 2.0 * width).round() as i32;
|
||||||
|
let x2 = ((x2 + 1.0) / 2.0 * width).round() as i32;
|
||||||
|
let y1 = ((y1 + 1.0) / 2.0 * height).round() as i32;
|
||||||
|
let y2 = ((y2 + 1.0) / 2.0 * height).round() as i32;
|
||||||
|
Rect::new(x1, y1, x2, y2).unwrap_or_default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FillRect {
|
pub struct FillRect {
|
||||||
pub rect: FramebufferRect,
|
pub rect: FramebufferRect,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
pub alpha: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FillRect {
|
||||||
|
pub fn effective_color(&self) -> Color {
|
||||||
|
let mut color = self.color;
|
||||||
|
if let Some(alpha) = self.alpha {
|
||||||
|
color = color * alpha;
|
||||||
|
}
|
||||||
|
color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CopyTexture {
|
pub struct CopyTexture {
|
||||||
|
|
@ -181,6 +214,7 @@ pub struct CopyTexture {
|
||||||
pub acquire_sync: AcquireSync,
|
pub acquire_sync: AcquireSync,
|
||||||
pub release_sync: ReleaseSync,
|
pub release_sync: ReleaseSync,
|
||||||
pub alpha: Option<f32>,
|
pub alpha: Option<f32>,
|
||||||
|
pub opaque: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -254,6 +288,10 @@ pub enum ResetStatus {
|
||||||
Other(u32),
|
Other(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait GfxBlendBuffer: Debug {
|
||||||
|
fn into_any(self: Rc<Self>) -> Rc<dyn Any>;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait GfxFramebuffer: Debug {
|
pub trait GfxFramebuffer: Debug {
|
||||||
fn physical_size(&self) -> (i32, i32);
|
fn physical_size(&self) -> (i32, i32);
|
||||||
|
|
||||||
|
|
@ -264,9 +302,15 @@ pub trait GfxFramebuffer: Debug {
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError>;
|
) -> Result<Option<SyncFile>, GfxError>;
|
||||||
|
|
||||||
fn format(&self) -> &'static Format;
|
fn format(&self) -> &'static Format;
|
||||||
|
|
||||||
|
fn full_region(&self) -> Region {
|
||||||
|
let (width, height) = self.physical_size();
|
||||||
|
Region::new2(Rect::new_sized_unchecked(0, 0, width, height))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GfxInternalFramebuffer: GfxFramebuffer {
|
pub trait GfxInternalFramebuffer: GfxFramebuffer {
|
||||||
|
|
@ -291,14 +335,16 @@ impl dyn GfxFramebuffer {
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.clone()
|
self.clone().render_with_region(
|
||||||
.render_with_region(acquire_sync, release_sync, ops, clear, &self.full_region())
|
acquire_sync,
|
||||||
}
|
release_sync,
|
||||||
|
ops,
|
||||||
fn full_region(&self) -> Region {
|
clear,
|
||||||
let (width, height) = self.physical_size();
|
&self.full_region(),
|
||||||
Region::new2(Rect::new_sized_unchecked(0, 0, width, height))
|
blend_buffer,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(
|
pub fn clear(
|
||||||
|
|
@ -318,7 +364,13 @@ impl dyn GfxFramebuffer {
|
||||||
b: f32,
|
b: f32,
|
||||||
a: f32,
|
a: f32,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.render(acquire_sync, release_sync, &[], Some(&Color { r, g, b, a }))
|
self.render(
|
||||||
|
acquire_sync,
|
||||||
|
release_sync,
|
||||||
|
&[],
|
||||||
|
Some(&Color { r, g, b, a }),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
|
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
|
||||||
|
|
@ -360,9 +412,10 @@ impl dyn GfxFramebuffer {
|
||||||
resv.cloned(),
|
resv.cloned(),
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
|
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
|
||||||
self.render(fb_acquire_sync, fb_release_sync, &ops, clear)
|
self.render(fb_acquire_sync, fb_release_sync, &ops, clear, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_custom(
|
pub fn render_custom(
|
||||||
|
|
@ -371,12 +424,13 @@ impl dyn GfxFramebuffer {
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
f: &mut dyn FnMut(&mut RendererBase),
|
f: &mut dyn FnMut(&mut RendererBase),
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let mut ops = vec![];
|
let mut ops = vec![];
|
||||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
||||||
f(&mut renderer);
|
f(&mut renderer);
|
||||||
self.render(acquire_sync, release_sync, &ops, clear)
|
self.render(acquire_sync, release_sync, &ops, clear, blend_buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_render_pass(
|
pub fn create_render_pass(
|
||||||
|
|
@ -413,6 +467,7 @@ impl dyn GfxFramebuffer {
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
pass: &GfxRenderPass,
|
pass: &GfxRenderPass,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.clone().render_with_region(
|
self.clone().render_with_region(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
|
|
@ -420,6 +475,7 @@ impl dyn GfxFramebuffer {
|
||||||
&pass.ops,
|
&pass.ops,
|
||||||
pass.clear.as_ref(),
|
pass.clear.as_ref(),
|
||||||
region,
|
region,
|
||||||
|
blend_buffer,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -433,6 +489,7 @@ impl dyn GfxFramebuffer {
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
fill_black_in_grace_period: bool,
|
fill_black_in_grace_period: bool,
|
||||||
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.render_node(
|
self.render_node(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
|
|
@ -446,6 +503,7 @@ impl dyn GfxFramebuffer {
|
||||||
node.has_fullscreen(),
|
node.has_fullscreen(),
|
||||||
fill_black_in_grace_period,
|
fill_black_in_grace_period,
|
||||||
node.global.persistent.transform.get(),
|
node.global.persistent.transform.get(),
|
||||||
|
blend_buffer,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -462,6 +520,7 @@ impl dyn GfxFramebuffer {
|
||||||
black_background: bool,
|
black_background: bool,
|
||||||
fill_black_in_grace_period: bool,
|
fill_black_in_grace_period: bool,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let pass = self.create_render_pass(
|
let pass = self.create_render_pass(
|
||||||
node,
|
node,
|
||||||
|
|
@ -475,7 +534,13 @@ impl dyn GfxFramebuffer {
|
||||||
transform,
|
transform,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
self.perform_render_pass(acquire_sync, release_sync, &pass, &self.full_region())
|
self.perform_render_pass(
|
||||||
|
acquire_sync,
|
||||||
|
release_sync,
|
||||||
|
&pass,
|
||||||
|
&self.full_region(),
|
||||||
|
blend_buffer,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_hardware_cursor(
|
pub fn render_hardware_cursor(
|
||||||
|
|
@ -498,7 +563,13 @@ impl dyn GfxFramebuffer {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
cursor.render_hardware_cursor(&mut renderer);
|
cursor.render_hardware_cursor(&mut renderer);
|
||||||
self.render(acquire_sync, release_sync, &ops, Some(&Color::TRANSPARENT))
|
self.render(
|
||||||
|
acquire_sync,
|
||||||
|
release_sync,
|
||||||
|
&ops,
|
||||||
|
Some(&Color::TRANSPARENT),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -665,6 +736,12 @@ pub trait GfxContext: Debug {
|
||||||
}
|
}
|
||||||
Rc::new(Dummy(size))
|
Rc::new(Dummy(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn acquire_blend_buffer(
|
||||||
|
&self,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
) -> Result<Rc<dyn GfxBlendBuffer>, GfxError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -197,12 +197,14 @@ enum RenderError {
|
||||||
UnsupportedShmFormat(&'static str),
|
UnsupportedShmFormat(&'static str),
|
||||||
#[error("Could not access the client memory")]
|
#[error("Could not access the client memory")]
|
||||||
AccessFailed(#[source] Box<dyn Error + Sync + Send>),
|
AccessFailed(#[source] Box<dyn Error + Sync + Send>),
|
||||||
|
#[error("OpenGL does not support blend buffers")]
|
||||||
|
NoBlendBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct GfxGlState {
|
struct GfxGlState {
|
||||||
triangles: RefCell<Vec<[f32; 2]>>,
|
triangles: RefCell<Vec<[f32; 2]>>,
|
||||||
fill_rect: VecStorage<&'static FillRect>,
|
fill_rect: VecStorage<FillRect>,
|
||||||
copy_tex: VecStorage<&'static CopyTexture>,
|
copy_tex: VecStorage<&'static CopyTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +235,11 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GfxApiOpt::FillRect(f) => {
|
GfxApiOpt::FillRect(f) => {
|
||||||
fill_rect.push(f);
|
fill_rect.push(FillRect {
|
||||||
|
rect: f.rect,
|
||||||
|
color: f.effective_color(),
|
||||||
|
alpha: None,
|
||||||
|
});
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
GfxApiOpt::CopyTexture(c) => {
|
GfxApiOpt::CopyTexture(c) => {
|
||||||
|
|
@ -249,7 +255,7 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
||||||
triangles.clear();
|
triangles.clear();
|
||||||
let mut color = None;
|
let mut color = None;
|
||||||
while i < fill_rect.len() {
|
while i < fill_rect.len() {
|
||||||
let fr = fill_rect[i];
|
let fr = &fill_rect[i];
|
||||||
match color {
|
match color {
|
||||||
None => color = Some(fr.color),
|
None => color = Some(fr.color),
|
||||||
Some(c) if c == fr.color => {}
|
Some(c) if c == fr.color => {}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use {
|
||||||
cpu_worker::CpuWorker,
|
cpu_worker::CpuWorker,
|
||||||
format::{Format, XRGB8888},
|
format::{Format, XRGB8888},
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AsyncShmGfxTexture, BufferResvUser, GfxContext, GfxError, GfxFormat, GfxFramebuffer,
|
AsyncShmGfxTexture, BufferResvUser, GfxBlendBuffer, GfxContext, GfxError, GfxFormat,
|
||||||
GfxImage, GfxInternalFramebuffer, ResetStatus, ShmGfxTexture,
|
GfxFramebuffer, GfxImage, GfxInternalFramebuffer, ResetStatus, ShmGfxTexture,
|
||||||
},
|
},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
GfxGlState, RenderError, Texture,
|
GfxGlState, RenderError, Texture,
|
||||||
|
|
@ -339,4 +339,12 @@ impl GfxContext for GlRenderContext {
|
||||||
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
||||||
Some(&self.sync_ctx)
|
Some(&self.sync_ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn acquire_blend_buffer(
|
||||||
|
&self,
|
||||||
|
_width: i32,
|
||||||
|
_height: i32,
|
||||||
|
) -> Result<Rc<dyn GfxBlendBuffer>, GfxError> {
|
||||||
|
Err(GfxError(Box::new(RenderError::NoBlendBuffer)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxError, GfxFramebuffer,
|
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxBlendBuffer, GfxError,
|
||||||
GfxInternalFramebuffer, GfxStagingBuffer, PendingShmTransfer, ReleaseSync, ShmMemory,
|
GfxFramebuffer, GfxInternalFramebuffer, GfxStagingBuffer, PendingShmTransfer,
|
||||||
SyncFile,
|
ReleaseSync, ShmMemory, SyncFile,
|
||||||
},
|
},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
RenderError,
|
RenderError,
|
||||||
|
|
@ -106,6 +106,7 @@ impl GfxFramebuffer for Framebuffer {
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
_region: &Region,
|
_region: &Region,
|
||||||
|
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
(*self)
|
(*self)
|
||||||
.render(acquire_sync, ops, clear)
|
.render(acquire_sync, ops, clear)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
mod allocator;
|
mod allocator;
|
||||||
|
mod blend_buffer;
|
||||||
mod bo_allocator;
|
mod bo_allocator;
|
||||||
mod command;
|
mod command;
|
||||||
mod descriptor;
|
mod descriptor;
|
||||||
|
|
@ -24,9 +25,9 @@ use {
|
||||||
cpu_worker::{CpuWorker, jobs::read_write::ReadWriteJobError},
|
cpu_worker::{CpuWorker, jobs::read_write::ReadWriteJobError},
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AsyncShmGfxTexture, GfxContext, GfxError, GfxFormat, GfxImage, GfxInternalFramebuffer,
|
AsyncShmGfxTexture, GfxBlendBuffer, GfxContext, GfxError, GfxFormat, GfxImage,
|
||||||
GfxStagingBuffer, ResetStatus, STAGING_DOWNLOAD, STAGING_UPLOAD, ShmGfxTexture,
|
GfxInternalFramebuffer, GfxStagingBuffer, ResetStatus, STAGING_DOWNLOAD,
|
||||||
StagingBufferUsecase,
|
STAGING_UPLOAD, ShmGfxTexture, StagingBufferUsecase,
|
||||||
},
|
},
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
|
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
|
||||||
|
|
@ -204,6 +205,8 @@ pub enum VulkanError {
|
||||||
UndefinedContents,
|
UndefinedContents,
|
||||||
#[error("The framebuffer is being used by the transfer queue")]
|
#[error("The framebuffer is being used by the transfer queue")]
|
||||||
BusyInTransfer,
|
BusyInTransfer,
|
||||||
|
#[error("Driver does not support descriptor buffers")]
|
||||||
|
NoDescriptorBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<VulkanError> for GfxError {
|
impl From<VulkanError> for GfxError {
|
||||||
|
|
@ -270,6 +273,7 @@ impl GfxContext for Context {
|
||||||
let old = old.into_texture().into_vk(&self.0.device.device);
|
let old = old.into_texture().into_vk(&self.0.device.device);
|
||||||
let shm = match &old.ty {
|
let shm = match &old.ty {
|
||||||
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
||||||
|
VulkanImageMemory::Blend(_) => unreachable!(),
|
||||||
VulkanImageMemory::Internal(shm) => shm,
|
VulkanImageMemory::Internal(shm) => shm,
|
||||||
};
|
};
|
||||||
if old.width as i32 == width
|
if old.width as i32 == width
|
||||||
|
|
@ -350,6 +354,15 @@ impl GfxContext for Context {
|
||||||
.device
|
.device
|
||||||
.create_staging_shell(size as u64, upload, download)
|
.create_staging_shell(size as u64, upload, download)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn acquire_blend_buffer(
|
||||||
|
&self,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
) -> Result<Rc<dyn GfxBlendBuffer>, GfxError> {
|
||||||
|
let buffer = self.0.acquire_blend_buffer(width, height)?;
|
||||||
|
Ok(buffer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Context {
|
impl Drop for Context {
|
||||||
|
|
|
||||||
139
src/gfx_apis/vulkan/blend_buffer.rs
Normal file
139
src/gfx_apis/vulkan/blend_buffer.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
gfx_api::GfxBlendBuffer,
|
||||||
|
gfx_apis::vulkan::{
|
||||||
|
VulkanError,
|
||||||
|
format::{BLEND_FORMAT, BLEND_USAGE},
|
||||||
|
image::{QueueFamily, QueueState, VulkanImage, VulkanImageMemory},
|
||||||
|
renderer::VulkanRenderer,
|
||||||
|
},
|
||||||
|
utils::on_drop::OnDrop,
|
||||||
|
},
|
||||||
|
ash::vk::{
|
||||||
|
DescriptorDataEXT, DescriptorGetInfoEXT, DescriptorImageInfo, DescriptorType, Extent3D,
|
||||||
|
ImageAspectFlags, ImageCreateInfo, ImageLayout, ImageSubresourceRange, ImageTiling,
|
||||||
|
ImageType, ImageViewCreateInfo, ImageViewType, SampleCountFlags, SharingMode,
|
||||||
|
},
|
||||||
|
gpu_alloc::UsageFlags,
|
||||||
|
std::{any::Any, cell::Cell, collections::hash_map::Entry, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl VulkanRenderer {
|
||||||
|
pub fn acquire_blend_buffer(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
) -> Result<Rc<VulkanImage>, VulkanError> {
|
||||||
|
let Some(db) = &self.device.descriptor_buffer else {
|
||||||
|
return Err(VulkanError::NoDescriptorBuffer);
|
||||||
|
};
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
return Err(VulkanError::NonPositiveImageSize);
|
||||||
|
}
|
||||||
|
let width = width as u32;
|
||||||
|
let height = height as u32;
|
||||||
|
let cached = &mut *self.blend_buffers.borrow_mut();
|
||||||
|
let cached = cached.entry((width, height));
|
||||||
|
if let Entry::Occupied(entry) = &cached {
|
||||||
|
if let Some(buffer) = entry.get().upgrade() {
|
||||||
|
return Ok(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let limits = self.device.blend_limits;
|
||||||
|
if width > limits.max_width || height > limits.max_height {
|
||||||
|
return Err(VulkanError::ImageTooLarge);
|
||||||
|
}
|
||||||
|
let create_info = ImageCreateInfo::default()
|
||||||
|
.image_type(ImageType::TYPE_2D)
|
||||||
|
.format(BLEND_FORMAT.vk_format)
|
||||||
|
.mip_levels(1)
|
||||||
|
.array_layers(1)
|
||||||
|
.tiling(ImageTiling::OPTIMAL)
|
||||||
|
.samples(SampleCountFlags::TYPE_1)
|
||||||
|
.sharing_mode(SharingMode::EXCLUSIVE)
|
||||||
|
.initial_layout(ImageLayout::UNDEFINED)
|
||||||
|
.extent(Extent3D {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth: 1,
|
||||||
|
})
|
||||||
|
.usage(BLEND_USAGE);
|
||||||
|
let image = unsafe { self.device.device.create_image(&create_info, None) };
|
||||||
|
let image = image.map_err(VulkanError::CreateImage)?;
|
||||||
|
let destroy_image = OnDrop(|| unsafe { self.device.device.destroy_image(image, None) });
|
||||||
|
let memory_requirements =
|
||||||
|
unsafe { self.device.device.get_image_memory_requirements(image) };
|
||||||
|
let allocation =
|
||||||
|
self.allocator
|
||||||
|
.alloc(&memory_requirements, UsageFlags::FAST_DEVICE_ACCESS, false)?;
|
||||||
|
let res = unsafe {
|
||||||
|
self.device
|
||||||
|
.device
|
||||||
|
.bind_image_memory(image, allocation.memory, allocation.offset)
|
||||||
|
};
|
||||||
|
res.map_err(VulkanError::BindImageMemory)?;
|
||||||
|
let image_view_create_info = ImageViewCreateInfo::default()
|
||||||
|
.image(image)
|
||||||
|
.format(BLEND_FORMAT.vk_format)
|
||||||
|
.view_type(ImageViewType::TYPE_2D)
|
||||||
|
.subresource_range(ImageSubresourceRange {
|
||||||
|
aspect_mask: ImageAspectFlags::COLOR,
|
||||||
|
base_mip_level: 0,
|
||||||
|
level_count: 1,
|
||||||
|
base_array_layer: 0,
|
||||||
|
layer_count: 1,
|
||||||
|
});
|
||||||
|
let view = unsafe {
|
||||||
|
self.device
|
||||||
|
.device
|
||||||
|
.create_image_view(&image_view_create_info, None)
|
||||||
|
};
|
||||||
|
let view = view.map_err(VulkanError::CreateImageView)?;
|
||||||
|
destroy_image.forget();
|
||||||
|
let sampled_image_descriptor = {
|
||||||
|
let mut buf = vec![0; self.device.sampled_image_descriptor_size].into_boxed_slice();
|
||||||
|
let image_info = DescriptorImageInfo::default()
|
||||||
|
.image_view(view)
|
||||||
|
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
let info = DescriptorGetInfoEXT::default()
|
||||||
|
.ty(DescriptorType::SAMPLED_IMAGE)
|
||||||
|
.data(DescriptorDataEXT {
|
||||||
|
p_sampled_image: &image_info,
|
||||||
|
});
|
||||||
|
unsafe {
|
||||||
|
db.get_descriptor(&info, &mut buf);
|
||||||
|
}
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
let img = Rc::new(VulkanImage {
|
||||||
|
renderer: self.clone(),
|
||||||
|
format: BLEND_FORMAT,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
stride: 0,
|
||||||
|
texture_view: view,
|
||||||
|
render_view: None,
|
||||||
|
image,
|
||||||
|
is_undefined: Cell::new(true),
|
||||||
|
contents_are_undefined: Cell::new(true),
|
||||||
|
queue_state: Cell::new(QueueState::Acquired {
|
||||||
|
family: QueueFamily::Gfx,
|
||||||
|
}),
|
||||||
|
ty: VulkanImageMemory::Blend(allocation),
|
||||||
|
bridge: None,
|
||||||
|
shader_read_only_optimal_descriptor: Box::new([]),
|
||||||
|
sampled_image_descriptor,
|
||||||
|
descriptor_buffer_version: Default::default(),
|
||||||
|
descriptor_buffer_offset: Default::default(),
|
||||||
|
execution_version: Default::default(),
|
||||||
|
});
|
||||||
|
cached.insert_entry(Rc::downgrade(&img));
|
||||||
|
Ok(img)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GfxBlendBuffer for VulkanImage {
|
||||||
|
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
use {
|
use {
|
||||||
crate::gfx_apis::vulkan::{VulkanError, device::VulkanDevice, sampler::VulkanSampler},
|
crate::gfx_apis::vulkan::{VulkanError, device::VulkanDevice, sampler::VulkanSampler},
|
||||||
arrayvec::ArrayVec,
|
arrayvec::ArrayVec,
|
||||||
ash::vk::{
|
ash::{
|
||||||
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateFlags,
|
ext::descriptor_buffer,
|
||||||
DescriptorSetLayoutCreateInfo, DescriptorType, DeviceSize, ShaderStageFlags,
|
vk::{
|
||||||
|
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateFlags,
|
||||||
|
DescriptorSetLayoutCreateInfo, DescriptorType, DeviceSize, ShaderStageFlags,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
std::{rc::Rc, slice},
|
std::{rc::Rc, slice},
|
||||||
};
|
};
|
||||||
|
|
@ -14,7 +17,6 @@ pub(super) struct VulkanDescriptorSetLayout {
|
||||||
pub(super) size: DeviceSize,
|
pub(super) size: DeviceSize,
|
||||||
pub(super) offsets: ArrayVec<DeviceSize, 1>,
|
pub(super) offsets: ArrayVec<DeviceSize, 1>,
|
||||||
pub(super) _sampler: Option<Rc<VulkanSampler>>,
|
pub(super) _sampler: Option<Rc<VulkanSampler>>,
|
||||||
pub(super) has_sampler: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for VulkanDescriptorSetLayout {
|
impl Drop for VulkanDescriptorSetLayout {
|
||||||
|
|
@ -28,7 +30,7 @@ impl Drop for VulkanDescriptorSetLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanDevice {
|
impl VulkanDevice {
|
||||||
pub(super) fn create_descriptor_set_layout(
|
pub(super) fn create_tex_descriptor_set_layout(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
sampler: &Rc<VulkanSampler>,
|
sampler: &Rc<VulkanSampler>,
|
||||||
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
|
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
|
||||||
|
|
@ -52,9 +54,7 @@ impl VulkanDevice {
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
let mut offsets = ArrayVec::new();
|
let mut offsets = ArrayVec::new();
|
||||||
if let Some(db) = &self.descriptor_buffer {
|
if let Some(db) = &self.descriptor_buffer {
|
||||||
size = unsafe { db.get_descriptor_set_layout_size(layout) };
|
size = self.get_descriptor_set_size(db, layout);
|
||||||
size =
|
|
||||||
(size + self.descriptor_buffer_offset_mask) & !self.descriptor_buffer_offset_mask;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 0));
|
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 0));
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +65,43 @@ impl VulkanDevice {
|
||||||
size,
|
size,
|
||||||
offsets,
|
offsets,
|
||||||
_sampler: Some(sampler.clone()),
|
_sampler: Some(sampler.clone()),
|
||||||
has_sampler: true,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn create_out_descriptor_set_layout(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
db: &descriptor_buffer::Device,
|
||||||
|
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
|
||||||
|
let binding = DescriptorSetLayoutBinding::default()
|
||||||
|
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||||
|
.descriptor_count(1)
|
||||||
|
.descriptor_type(DescriptorType::SAMPLED_IMAGE);
|
||||||
|
let create_info = DescriptorSetLayoutCreateInfo::default()
|
||||||
|
.bindings(slice::from_ref(&binding))
|
||||||
|
.flags(DescriptorSetLayoutCreateFlags::DESCRIPTOR_BUFFER_EXT);
|
||||||
|
let layout = unsafe { self.device.create_descriptor_set_layout(&create_info, None) };
|
||||||
|
let layout = layout.map_err(VulkanError::CreateDescriptorSetLayout)?;
|
||||||
|
let size = self.get_descriptor_set_size(db, layout);
|
||||||
|
let mut offsets = ArrayVec::new();
|
||||||
|
unsafe {
|
||||||
|
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 0));
|
||||||
|
}
|
||||||
|
Ok(Rc::new(VulkanDescriptorSetLayout {
|
||||||
|
device: self.clone(),
|
||||||
|
layout,
|
||||||
|
size,
|
||||||
|
offsets,
|
||||||
|
_sampler: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_descriptor_set_size(
|
||||||
|
&self,
|
||||||
|
db: &descriptor_buffer::Device,
|
||||||
|
layout: DescriptorSetLayout,
|
||||||
|
) -> DeviceSize {
|
||||||
|
let mut size = unsafe { db.get_descriptor_set_layout_size(layout) };
|
||||||
|
size = (size + self.descriptor_buffer_offset_mask) & !self.descriptor_buffer_offset_mask;
|
||||||
|
size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ pub struct VulkanDescriptorBufferUnused {
|
||||||
pub address: DeviceAddress,
|
pub address: DeviceAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct VulkanDescriptorBufferWriter {
|
pub struct VulkanDescriptorBufferWriter {
|
||||||
set_size: usize,
|
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,13 +49,13 @@ impl VulkanDescriptorBufferCache {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device: &Rc<VulkanDevice>,
|
device: &Rc<VulkanDevice>,
|
||||||
allocator: &Rc<VulkanAllocator>,
|
allocator: &Rc<VulkanAllocator>,
|
||||||
layout: &VulkanDescriptorSetLayout,
|
has_sampler: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
device: device.clone(),
|
device: device.clone(),
|
||||||
allocator: allocator.clone(),
|
allocator: allocator.clone(),
|
||||||
buffers: Default::default(),
|
buffers: Default::default(),
|
||||||
has_sampler: layout.has_sampler,
|
has_sampler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,13 +164,6 @@ impl Drop for VulkanDescriptorBufferUnused {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanDescriptorBufferWriter {
|
impl VulkanDescriptorBufferWriter {
|
||||||
pub fn new(layout: &VulkanDescriptorSetLayout) -> Self {
|
|
||||||
Self {
|
|
||||||
set_size: layout.size as usize,
|
|
||||||
buffer: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.buffer.clear();
|
self.buffer.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -179,10 +172,13 @@ impl VulkanDescriptorBufferWriter {
|
||||||
self.buffer.len() as DeviceSize
|
self.buffer.len() as DeviceSize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_set(&mut self) -> VulkanDescriptorBufferSetWriter<'_> {
|
pub fn add_set(
|
||||||
|
&mut self,
|
||||||
|
layout: &VulkanDescriptorSetLayout,
|
||||||
|
) -> VulkanDescriptorBufferSetWriter<'_> {
|
||||||
let buffer = &mut self.buffer;
|
let buffer = &mut self.buffer;
|
||||||
let lo = buffer.len();
|
let lo = buffer.len();
|
||||||
buffer.resize(lo + self.set_size, 0);
|
buffer.resize(lo + layout.size as usize, 0);
|
||||||
VulkanDescriptorBufferSetWriter {
|
VulkanDescriptorBufferSetWriter {
|
||||||
set: &mut buffer[lo..],
|
set: &mut buffer[lo..],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use {
|
||||||
format::XRGB8888,
|
format::XRGB8888,
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
VulkanError,
|
VulkanError,
|
||||||
format::VulkanFormat,
|
format::{VulkanBlendBufferLimits, VulkanFormat},
|
||||||
instance::{
|
instance::{
|
||||||
API_VERSION, ApiVersionDisplay, Extensions, VulkanInstance,
|
API_VERSION, ApiVersionDisplay, Extensions, VulkanInstance,
|
||||||
map_extension_properties,
|
map_extension_properties,
|
||||||
|
|
@ -63,6 +63,7 @@ pub struct VulkanDevice {
|
||||||
pub(super) image_drm_format_modifier: image_drm_format_modifier::Device,
|
pub(super) image_drm_format_modifier: image_drm_format_modifier::Device,
|
||||||
pub(super) descriptor_buffer: Option<descriptor_buffer::Device>,
|
pub(super) descriptor_buffer: Option<descriptor_buffer::Device>,
|
||||||
pub(super) formats: AHashMap<u32, VulkanFormat>,
|
pub(super) formats: AHashMap<u32, VulkanFormat>,
|
||||||
|
pub(super) blend_limits: VulkanBlendBufferLimits,
|
||||||
pub(super) memory_types: ArrayVec<MemoryType, MAX_MEMORY_TYPES>,
|
pub(super) memory_types: ArrayVec<MemoryType, MAX_MEMORY_TYPES>,
|
||||||
pub(super) graphics_queue: Queue,
|
pub(super) graphics_queue: Queue,
|
||||||
pub(super) graphics_queue_idx: u32,
|
pub(super) graphics_queue_idx: u32,
|
||||||
|
|
@ -71,6 +72,7 @@ pub struct VulkanDevice {
|
||||||
pub(super) transfer_granularity_mask: (u32, u32),
|
pub(super) transfer_granularity_mask: (u32, u32),
|
||||||
pub(super) descriptor_buffer_offset_mask: DeviceSize,
|
pub(super) descriptor_buffer_offset_mask: DeviceSize,
|
||||||
pub(super) combined_image_sampler_descriptor_size: usize,
|
pub(super) combined_image_sampler_descriptor_size: usize,
|
||||||
|
pub(super) sampled_image_descriptor_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for VulkanDevice {
|
impl Drop for VulkanDevice {
|
||||||
|
|
@ -272,6 +274,9 @@ impl VulkanInstance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let supports_descriptor_buffer = extensions.contains_key(descriptor_buffer::NAME);
|
let supports_descriptor_buffer = extensions.contains_key(descriptor_buffer::NAME);
|
||||||
|
if !supports_descriptor_buffer {
|
||||||
|
log::warn!("Vulkan device does not support descriptor buffers");
|
||||||
|
}
|
||||||
let (graphics_queue_family_idx, transfer_queue_family) = self.find_queues(phy_dev)?;
|
let (graphics_queue_family_idx, transfer_queue_family) = self.find_queues(phy_dev)?;
|
||||||
let mut distinct_transfer_queue_family_idx = None;
|
let mut distinct_transfer_queue_family_idx = None;
|
||||||
let mut transfer_granularity_mask = (0, 0);
|
let mut transfer_granularity_mask = (0, 0);
|
||||||
|
|
@ -334,6 +339,7 @@ impl VulkanInstance {
|
||||||
Err(e) => return Err(VulkanError::CreateDevice(e)),
|
Err(e) => return Err(VulkanError::CreateDevice(e)),
|
||||||
};
|
};
|
||||||
let destroy_device = OnDrop(|| unsafe { device.destroy_device(None) });
|
let destroy_device = OnDrop(|| unsafe { device.destroy_device(None) });
|
||||||
|
let blend_limits = self.load_blend_format_limits(phy_dev)?;
|
||||||
let formats = self.load_formats(phy_dev)?;
|
let formats = self.load_formats(phy_dev)?;
|
||||||
let supports_xrgb8888 = formats
|
let supports_xrgb8888 = formats
|
||||||
.get(&XRGB8888.drm)
|
.get(&XRGB8888.drm)
|
||||||
|
|
@ -361,6 +367,7 @@ impl VulkanInstance {
|
||||||
.then(|| descriptor_buffer::Device::new(&self.instance, &device));
|
.then(|| descriptor_buffer::Device::new(&self.instance, &device));
|
||||||
let mut descriptor_buffer_offset_mask = 0;
|
let mut descriptor_buffer_offset_mask = 0;
|
||||||
let mut combined_image_sampler_descriptor_size = 0;
|
let mut combined_image_sampler_descriptor_size = 0;
|
||||||
|
let mut sampled_image_descriptor_size = 0;
|
||||||
if supports_descriptor_buffer {
|
if supports_descriptor_buffer {
|
||||||
let mut descriptor_buffer_props =
|
let mut descriptor_buffer_props =
|
||||||
PhysicalDeviceDescriptorBufferPropertiesEXT::default();
|
PhysicalDeviceDescriptorBufferPropertiesEXT::default();
|
||||||
|
|
@ -377,6 +384,7 @@ impl VulkanInstance {
|
||||||
- 1;
|
- 1;
|
||||||
combined_image_sampler_descriptor_size =
|
combined_image_sampler_descriptor_size =
|
||||||
descriptor_buffer_props.combined_image_sampler_descriptor_size;
|
descriptor_buffer_props.combined_image_sampler_descriptor_size;
|
||||||
|
sampled_image_descriptor_size = descriptor_buffer_props.sampled_image_descriptor_size;
|
||||||
}
|
}
|
||||||
let memory_properties =
|
let memory_properties =
|
||||||
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
|
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
|
||||||
|
|
@ -415,6 +423,8 @@ impl VulkanInstance {
|
||||||
transfer_granularity_mask,
|
transfer_granularity_mask,
|
||||||
descriptor_buffer_offset_mask,
|
descriptor_buffer_offset_mask,
|
||||||
combined_image_sampler_descriptor_size,
|
combined_image_sampler_descriptor_size,
|
||||||
|
sampled_image_descriptor_size,
|
||||||
|
blend_limits,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::{FORMATS, Format},
|
format::{ABGR16161616F, FORMATS, Format},
|
||||||
gfx_apis::vulkan::{VulkanError, instance::VulkanInstance},
|
gfx_apis::vulkan::{VulkanError, instance::VulkanInstance},
|
||||||
video::{LINEAR_MODIFIER, Modifier},
|
video::{LINEAR_MODIFIER, Modifier},
|
||||||
},
|
},
|
||||||
|
|
@ -38,18 +38,24 @@ pub struct VulkanModifier {
|
||||||
pub render_needs_bridge: bool,
|
pub render_needs_bridge: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct VulkanModifierLimits {
|
pub struct VulkanModifierLimits {
|
||||||
pub max_width: u32,
|
pub max_width: u32,
|
||||||
pub max_height: u32,
|
pub max_height: u32,
|
||||||
pub exportable: bool,
|
pub exportable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct VulkanInternalFormat {
|
pub struct VulkanInternalFormat {
|
||||||
pub limits: VulkanModifierLimits,
|
pub limits: VulkanModifierLimits,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct VulkanBlendBufferLimits {
|
||||||
|
pub max_width: u32,
|
||||||
|
pub max_height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
const FRAMEBUFFER_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
|
const FRAMEBUFFER_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
|
||||||
0 | FormatFeatureFlags::COLOR_ATTACHMENT.as_raw()
|
0 | FormatFeatureFlags::COLOR_ATTACHMENT.as_raw()
|
||||||
| FormatFeatureFlags::COLOR_ATTACHMENT_BLEND.as_raw(),
|
| FormatFeatureFlags::COLOR_ATTACHMENT_BLEND.as_raw(),
|
||||||
|
|
@ -79,6 +85,16 @@ const TRANSFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
|
||||||
const SHM_USAGE: ImageUsageFlags =
|
const SHM_USAGE: ImageUsageFlags =
|
||||||
ImageUsageFlags::from_raw(TRANSFER_USAGE.as_raw() | TEX_USAGE.as_raw());
|
ImageUsageFlags::from_raw(TRANSFER_USAGE.as_raw() | TEX_USAGE.as_raw());
|
||||||
|
|
||||||
|
pub const BLEND_FORMAT: &Format = ABGR16161616F;
|
||||||
|
const BLEND_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
|
||||||
|
0 | FormatFeatureFlags::COLOR_ATTACHMENT.as_raw()
|
||||||
|
| FormatFeatureFlags::COLOR_ATTACHMENT_BLEND.as_raw()
|
||||||
|
| FormatFeatureFlags::SAMPLED_IMAGE.as_raw(),
|
||||||
|
);
|
||||||
|
pub const BLEND_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
|
||||||
|
ImageUsageFlags::COLOR_ATTACHMENT.as_raw() | ImageUsageFlags::SAMPLED.as_raw(),
|
||||||
|
);
|
||||||
|
|
||||||
impl VulkanInstance {
|
impl VulkanInstance {
|
||||||
pub(super) fn load_formats(
|
pub(super) fn load_formats(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -126,6 +142,29 @@ impl VulkanInstance {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_blend_format_limits(
|
||||||
|
&self,
|
||||||
|
phy_dev: PhysicalDevice,
|
||||||
|
) -> Result<VulkanBlendBufferLimits, VulkanError> {
|
||||||
|
let format_properties = unsafe {
|
||||||
|
self.instance
|
||||||
|
.get_physical_device_format_properties(phy_dev, BLEND_FORMAT.vk_format)
|
||||||
|
};
|
||||||
|
let l = self
|
||||||
|
.load_internal_format(
|
||||||
|
phy_dev,
|
||||||
|
BLEND_FORMAT,
|
||||||
|
&format_properties,
|
||||||
|
BLEND_FEATURES,
|
||||||
|
BLEND_USAGE,
|
||||||
|
)?
|
||||||
|
.unwrap_or_default();
|
||||||
|
Ok(VulkanBlendBufferLimits {
|
||||||
|
max_width: l.limits.max_width,
|
||||||
|
max_height: l.limits.max_height,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn load_shm_format(
|
fn load_shm_format(
|
||||||
&self,
|
&self,
|
||||||
phy_dev: PhysicalDevice,
|
phy_dev: PhysicalDevice,
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ use {
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
|
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
|
||||||
AsyncShmGfxTextureTransferCancellable, GfxApiOpt, GfxError, GfxFramebuffer, GfxImage,
|
AsyncShmGfxTextureTransferCancellable, GfxApiOpt, GfxBlendBuffer, GfxError,
|
||||||
GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, PendingShmTransfer, ReleaseSync,
|
GfxFramebuffer, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
|
||||||
ShmGfxTexture, ShmMemory, SyncFile,
|
PendingShmTransfer, ReleaseSync, ShmGfxTexture, ShmMemory, SyncFile,
|
||||||
},
|
},
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
VulkanError, allocator::VulkanAllocation, device::VulkanDevice,
|
VulkanError, allocator::VulkanAllocation, device::VulkanDevice,
|
||||||
|
|
@ -64,6 +64,7 @@ pub struct VulkanImage {
|
||||||
pub(super) ty: VulkanImageMemory,
|
pub(super) ty: VulkanImageMemory,
|
||||||
pub(super) bridge: Option<VulkanFramebufferBridge>,
|
pub(super) bridge: Option<VulkanFramebufferBridge>,
|
||||||
pub(super) shader_read_only_optimal_descriptor: Box<[u8]>,
|
pub(super) shader_read_only_optimal_descriptor: Box<[u8]>,
|
||||||
|
pub(super) sampled_image_descriptor: Box<[u8]>,
|
||||||
pub(super) descriptor_buffer_version: Cell<u64>,
|
pub(super) descriptor_buffer_version: Cell<u64>,
|
||||||
pub(super) descriptor_buffer_offset: Cell<DeviceSize>,
|
pub(super) descriptor_buffer_offset: Cell<DeviceSize>,
|
||||||
pub(super) execution_version: Cell<u64>,
|
pub(super) execution_version: Cell<u64>,
|
||||||
|
|
@ -102,6 +103,7 @@ pub enum QueueTransfer {
|
||||||
pub enum VulkanImageMemory {
|
pub enum VulkanImageMemory {
|
||||||
DmaBuf(VulkanDmaBufImage),
|
DmaBuf(VulkanDmaBufImage),
|
||||||
Internal(VulkanShmImage),
|
Internal(VulkanShmImage),
|
||||||
|
Blend(#[expect(dead_code)] VulkanAllocation),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VulkanDmaBufImage {
|
pub struct VulkanDmaBufImage {
|
||||||
|
|
@ -451,6 +453,7 @@ impl VulkanDmaBufImageTemplate {
|
||||||
shader_read_only_optimal_descriptor: self
|
shader_read_only_optimal_descriptor: self
|
||||||
.renderer
|
.renderer
|
||||||
.sampler_read_only_descriptor(texture_view),
|
.sampler_read_only_descriptor(texture_view),
|
||||||
|
sampled_image_descriptor: Box::new([]),
|
||||||
descriptor_buffer_version: Cell::new(0),
|
descriptor_buffer_version: Cell::new(0),
|
||||||
descriptor_buffer_offset: Cell::new(0),
|
descriptor_buffer_offset: Cell::new(0),
|
||||||
execution_version: Cell::new(0),
|
execution_version: Cell::new(0),
|
||||||
|
|
@ -539,9 +542,30 @@ impl GfxFramebuffer for VulkanImage {
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
|
let mut blend_buffer =
|
||||||
|
blend_buffer.map(|b| b.clone().into_vk(&self.renderer.device.device));
|
||||||
|
if let Some(bb) = &blend_buffer {
|
||||||
|
if bb.size() != self.size() {
|
||||||
|
log::error!(
|
||||||
|
"Blend buffer has invalid size: {:?} != {:?}",
|
||||||
|
bb.size(),
|
||||||
|
self.size()
|
||||||
|
);
|
||||||
|
blend_buffer = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
self.renderer
|
self.renderer
|
||||||
.execute(&self, acquire_sync, release_sync, ops, clear, region)
|
.execute(
|
||||||
|
&self,
|
||||||
|
acquire_sync,
|
||||||
|
release_sync,
|
||||||
|
ops,
|
||||||
|
clear,
|
||||||
|
region,
|
||||||
|
blend_buffer,
|
||||||
|
)
|
||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -609,6 +633,7 @@ impl GfxTexture for VulkanImage {
|
||||||
match &self.ty {
|
match &self.ty {
|
||||||
VulkanImageMemory::DmaBuf(b) => Some(&b.template.dmabuf),
|
VulkanImageMemory::DmaBuf(b) => Some(&b.template.dmabuf),
|
||||||
VulkanImageMemory::Internal(_) => None,
|
VulkanImageMemory::Internal(_) => None,
|
||||||
|
VulkanImageMemory::Blend(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ pub(super) struct PipelineCreateInfo {
|
||||||
pub(super) blend: bool,
|
pub(super) blend: bool,
|
||||||
pub(super) src_has_alpha: bool,
|
pub(super) src_has_alpha: bool,
|
||||||
pub(super) has_alpha_mult: bool,
|
pub(super) has_alpha_mult: bool,
|
||||||
|
pub(super) with_linear_output: bool,
|
||||||
pub(super) frag_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
pub(super) frag_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,8 +77,8 @@ impl VulkanDevice {
|
||||||
};
|
};
|
||||||
let destroy_layout =
|
let destroy_layout =
|
||||||
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
|
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
|
||||||
let mut frag_spec_data = ArrayVec::<_, 8>::new();
|
let mut frag_spec_data = ArrayVec::<_, { 3 * 4 }>::new();
|
||||||
let mut frag_spec_entries = ArrayVec::<_, 2>::new();
|
let mut frag_spec_entries = ArrayVec::<_, 3>::new();
|
||||||
let mut frag_spec_entry = |data: &[u8]| {
|
let mut frag_spec_entry = |data: &[u8]| {
|
||||||
let entry = SpecializationMapEntry::default()
|
let entry = SpecializationMapEntry::default()
|
||||||
.constant_id(frag_spec_entries.len() as _)
|
.constant_id(frag_spec_entries.len() as _)
|
||||||
|
|
@ -88,6 +89,7 @@ impl VulkanDevice {
|
||||||
};
|
};
|
||||||
frag_spec_entry(&(info.src_has_alpha as u32).to_ne_bytes());
|
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.has_alpha_mult as u32).to_ne_bytes());
|
||||||
|
frag_spec_entry(&(info.with_linear_output as u32).to_ne_bytes());
|
||||||
let frag_spec = SpecializationInfo::default()
|
let frag_spec = SpecializationInfo::default()
|
||||||
.map_entries(&frag_spec_entries)
|
.map_entries(&frag_spec_entries)
|
||||||
.data(&frag_spec_data);
|
.data(&frag_spec_data);
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use {
|
||||||
cpu_worker::PendingJob,
|
cpu_worker::PendingJob,
|
||||||
format::XRGB8888,
|
format::XRGB8888,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxTexture,
|
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, GfxFormat,
|
||||||
GfxWriteModifier, ReleaseSync, SyncFile,
|
GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile,
|
||||||
},
|
},
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
VulkanError,
|
VulkanError,
|
||||||
|
|
@ -22,12 +22,12 @@ use {
|
||||||
sampler::VulkanSampler,
|
sampler::VulkanSampler,
|
||||||
semaphore::VulkanSemaphore,
|
semaphore::VulkanSemaphore,
|
||||||
shaders::{
|
shaders::{
|
||||||
FILL_FRAG, FILL_VERT, FillPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants,
|
FILL_FRAG, FILL_VERT, FillPushConstants, OUT_FRAG, OUT_VERT, OutPushConstants,
|
||||||
VulkanShader,
|
TEX_FRAG, TEX_VERT, TexPushConstants, VulkanShader,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
rect::Region,
|
rect::{Rect, Region},
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::{
|
utils::{
|
||||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, once::Once,
|
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, once::Once,
|
||||||
|
|
@ -36,6 +36,7 @@ use {
|
||||||
video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file},
|
video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
arrayvec::ArrayVec,
|
||||||
ash::{
|
ash::{
|
||||||
Device,
|
Device,
|
||||||
vk::{
|
vk::{
|
||||||
|
|
@ -51,12 +52,15 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isnt::std_1::{collections::IsntHashMapExt, primitive::IsntSliceExt},
|
isnt::std_1::{collections::IsntHashMapExt, primitive::IsntSliceExt},
|
||||||
|
jay_algorithms::rect::Tag,
|
||||||
linearize::{Linearize, StaticMap, static_map},
|
linearize::{Linearize, StaticMap, static_map},
|
||||||
std::{
|
std::{
|
||||||
|
borrow::Cow,
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
collections::hash_map::Entry,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
mem, ptr,
|
mem, ptr,
|
||||||
rc::Rc,
|
rc::{Rc, Weak},
|
||||||
slice,
|
slice,
|
||||||
},
|
},
|
||||||
uapi::OwnedFd,
|
uapi::OwnedFd,
|
||||||
|
|
@ -65,7 +69,8 @@ use {
|
||||||
pub struct VulkanRenderer {
|
pub struct VulkanRenderer {
|
||||||
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
|
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
|
||||||
pub(super) device: Rc<VulkanDevice>,
|
pub(super) device: Rc<VulkanDevice>,
|
||||||
pub(super) pipelines: CopyHashMap<vk::Format, Rc<VulkanFormatPipelines>>,
|
pub(super) pipelines: StaticMap<RenderPass, CopyHashMap<vk::Format, Rc<VulkanFormatPipelines>>>,
|
||||||
|
pub(super) out_pipelines: RefCell<AHashMap<vk::Format, Rc<VulkanPipeline>>>,
|
||||||
pub(super) gfx_command_buffers: CachedCommandBuffers,
|
pub(super) gfx_command_buffers: CachedCommandBuffers,
|
||||||
pub(super) transfer_command_buffers: Option<CachedCommandBuffers>,
|
pub(super) transfer_command_buffers: Option<CachedCommandBuffers>,
|
||||||
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
|
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
|
||||||
|
|
@ -81,13 +86,17 @@ pub struct VulkanRenderer {
|
||||||
pub(super) fill_frag_shader: Rc<VulkanShader>,
|
pub(super) fill_frag_shader: Rc<VulkanShader>,
|
||||||
pub(super) tex_vert_shader: Rc<VulkanShader>,
|
pub(super) tex_vert_shader: Rc<VulkanShader>,
|
||||||
pub(super) tex_frag_shader: Rc<VulkanShader>,
|
pub(super) tex_frag_shader: Rc<VulkanShader>,
|
||||||
|
pub(super) out_vert_shader: Rc<VulkanShader>,
|
||||||
|
pub(super) out_frag_shader: Rc<VulkanShader>,
|
||||||
pub(super) tex_descriptor_set_layout: Rc<VulkanDescriptorSetLayout>,
|
pub(super) tex_descriptor_set_layout: Rc<VulkanDescriptorSetLayout>,
|
||||||
|
pub(super) out_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
||||||
pub(super) defunct: Cell<bool>,
|
pub(super) defunct: Cell<bool>,
|
||||||
pub(super) pending_cpu_jobs: CopyHashMap<u64, PendingJob>,
|
pub(super) pending_cpu_jobs: CopyHashMap<u64, PendingJob>,
|
||||||
pub(super) shm_allocator: Rc<VulkanThreadedAllocator>,
|
pub(super) shm_allocator: Rc<VulkanThreadedAllocator>,
|
||||||
pub(super) sampler: Rc<VulkanSampler>,
|
pub(super) sampler: Rc<VulkanSampler>,
|
||||||
pub(super) tex_sampler_descriptor_buffer_cache: Rc<VulkanDescriptorBufferCache>,
|
pub(super) sampler_descriptor_buffer_cache: Rc<VulkanDescriptorBufferCache>,
|
||||||
pub(super) tex_descriptor_buffer_writer: RefCell<VulkanDescriptorBufferWriter>,
|
pub(super) resource_descriptor_buffer_cache: Rc<VulkanDescriptorBufferCache>,
|
||||||
|
pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct CachedCommandBuffers {
|
pub(super) struct CachedCommandBuffers {
|
||||||
|
|
@ -139,14 +148,23 @@ pub(super) struct Memory {
|
||||||
wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>,
|
wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>,
|
||||||
release_fence: Option<Rc<VulkanFence>>,
|
release_fence: Option<Rc<VulkanFence>>,
|
||||||
release_sync_file: Option<SyncFile>,
|
release_sync_file: Option<SyncFile>,
|
||||||
descriptor_buffer: Option<VulkanDescriptorBuffer>,
|
descriptor_buffers: ArrayVec<VulkanDescriptorBuffer, 2>,
|
||||||
paint_regions: Vec<PaintRegion>,
|
paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>,
|
||||||
clear_rects: Vec<ClearRect>,
|
clear_rects: StaticMap<RenderPass, Vec<ClearRect>>,
|
||||||
image_copy_regions: Vec<ImageCopy2<'static>>,
|
image_copy_regions: Vec<ImageCopy2<'static>>,
|
||||||
|
sampler_descriptor_buffer_writer: VulkanDescriptorBufferWriter,
|
||||||
|
resource_descriptor_buffer_writer: VulkanDescriptorBufferWriter,
|
||||||
|
regions_1: Vec<Rect>,
|
||||||
|
regions_2: Vec<Rect<u32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Linearize, Eq, PartialEq)]
|
||||||
|
pub(super) enum RenderPass {
|
||||||
|
BlendBuffer,
|
||||||
|
FrameBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PaintRegion {
|
struct PaintRegion {
|
||||||
rect: Rect2D,
|
|
||||||
x1: f32,
|
x1: f32,
|
||||||
y1: f32,
|
y1: f32,
|
||||||
x2: f32,
|
x2: f32,
|
||||||
|
|
@ -158,15 +176,16 @@ pub(super) struct PendingFrame {
|
||||||
renderer: Rc<VulkanRenderer>,
|
renderer: Rc<VulkanRenderer>,
|
||||||
cmd: Cell<Option<Rc<VulkanCommandBuffer>>>,
|
cmd: Cell<Option<Rc<VulkanCommandBuffer>>>,
|
||||||
_fb: Rc<VulkanImage>,
|
_fb: Rc<VulkanImage>,
|
||||||
|
_bb: Option<Rc<VulkanImage>>,
|
||||||
_textures: Vec<UsedTexture>,
|
_textures: Vec<UsedTexture>,
|
||||||
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
||||||
waiter: Cell<Option<SpawnedFuture<()>>>,
|
waiter: Cell<Option<SpawnedFuture<()>>>,
|
||||||
_release_fence: Option<Rc<VulkanFence>>,
|
_release_fence: Option<Rc<VulkanFence>>,
|
||||||
_descriptor_buffer: Option<VulkanDescriptorBuffer>,
|
_descriptor_buffers: ArrayVec<VulkanDescriptorBuffer, 2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct VulkanFormatPipelines {
|
pub(super) struct VulkanFormatPipelines {
|
||||||
pub(super) fill: Rc<VulkanPipeline>,
|
pub(super) fill: StaticMap<TexSourceType, Rc<VulkanPipeline>>,
|
||||||
pub(super) tex: StaticMap<TexCopyType, StaticMap<TexSourceType, Rc<VulkanPipeline>>>,
|
pub(super) tex: StaticMap<TexCopyType, StaticMap<TexSourceType, Rc<VulkanPipeline>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,7 +198,14 @@ impl VulkanDevice {
|
||||||
let fill_vert_shader = self.create_shader(FILL_VERT)?;
|
let fill_vert_shader = self.create_shader(FILL_VERT)?;
|
||||||
let fill_frag_shader = self.create_shader(FILL_FRAG)?;
|
let fill_frag_shader = self.create_shader(FILL_FRAG)?;
|
||||||
let sampler = self.create_sampler()?;
|
let sampler = self.create_sampler()?;
|
||||||
let tex_descriptor_set_layout = self.create_descriptor_set_layout(&sampler)?;
|
let tex_descriptor_set_layout = self.create_tex_descriptor_set_layout(&sampler)?;
|
||||||
|
let out_descriptor_set_layout = self
|
||||||
|
.descriptor_buffer
|
||||||
|
.as_ref()
|
||||||
|
.map(|db| self.create_out_descriptor_set_layout(db))
|
||||||
|
.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_vert_shader = self.create_shader(TEX_VERT)?;
|
||||||
let tex_frag_shader = self.create_shader(TEX_FRAG)?;
|
let tex_frag_shader = self.create_shader(TEX_FRAG)?;
|
||||||
let gfx_command_buffers = self.create_command_pool(self.graphics_queue_idx)?;
|
let gfx_command_buffers = self.create_command_pool(self.graphics_queue_idx)?;
|
||||||
|
|
@ -220,18 +246,15 @@ impl VulkanDevice {
|
||||||
.collect();
|
.collect();
|
||||||
let allocator = self.create_allocator()?;
|
let allocator = self.create_allocator()?;
|
||||||
let shm_allocator = self.create_threaded_allocator()?;
|
let shm_allocator = self.create_threaded_allocator()?;
|
||||||
let tex_descriptor_buffer_cache = Rc::new(VulkanDescriptorBufferCache::new(
|
let sampler_descriptor_buffer_cache =
|
||||||
self,
|
Rc::new(VulkanDescriptorBufferCache::new(self, &allocator, true));
|
||||||
&allocator,
|
let resource_descriptor_buffer_cache =
|
||||||
&tex_descriptor_set_layout,
|
Rc::new(VulkanDescriptorBufferCache::new(self, &allocator, false));
|
||||||
));
|
|
||||||
let tex_descriptor_buffer_writer = RefCell::new(VulkanDescriptorBufferWriter::new(
|
|
||||||
&tex_descriptor_set_layout,
|
|
||||||
));
|
|
||||||
let render = Rc::new(VulkanRenderer {
|
let render = Rc::new(VulkanRenderer {
|
||||||
formats: Rc::new(formats),
|
formats: Rc::new(formats),
|
||||||
device: self.clone(),
|
device: self.clone(),
|
||||||
pipelines: Default::default(),
|
pipelines: Default::default(),
|
||||||
|
out_pipelines: Default::default(),
|
||||||
gfx_command_buffers,
|
gfx_command_buffers,
|
||||||
transfer_command_buffers,
|
transfer_command_buffers,
|
||||||
wait_semaphores: Default::default(),
|
wait_semaphores: Default::default(),
|
||||||
|
|
@ -247,15 +270,19 @@ impl VulkanDevice {
|
||||||
fill_frag_shader,
|
fill_frag_shader,
|
||||||
tex_vert_shader,
|
tex_vert_shader,
|
||||||
tex_frag_shader,
|
tex_frag_shader,
|
||||||
|
out_vert_shader,
|
||||||
|
out_frag_shader,
|
||||||
tex_descriptor_set_layout,
|
tex_descriptor_set_layout,
|
||||||
|
out_descriptor_set_layout,
|
||||||
defunct: Cell::new(false),
|
defunct: Cell::new(false),
|
||||||
pending_cpu_jobs: Default::default(),
|
pending_cpu_jobs: Default::default(),
|
||||||
shm_allocator,
|
shm_allocator,
|
||||||
sampler,
|
sampler,
|
||||||
tex_sampler_descriptor_buffer_cache: tex_descriptor_buffer_cache,
|
sampler_descriptor_buffer_cache,
|
||||||
tex_descriptor_buffer_writer,
|
resource_descriptor_buffer_cache,
|
||||||
|
blend_buffers: Default::default(),
|
||||||
});
|
});
|
||||||
render.get_or_create_pipelines(XRGB8888.vk_format)?;
|
render.get_or_create_pipelines(XRGB8888.vk_format, RenderPass::FrameBuffer)?;
|
||||||
Ok(render)
|
Ok(render)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,21 +291,28 @@ impl VulkanRenderer {
|
||||||
fn get_or_create_pipelines(
|
fn get_or_create_pipelines(
|
||||||
&self,
|
&self,
|
||||||
format: vk::Format,
|
format: vk::Format,
|
||||||
|
pass: RenderPass,
|
||||||
) -> Result<Rc<VulkanFormatPipelines>, VulkanError> {
|
) -> Result<Rc<VulkanFormatPipelines>, VulkanError> {
|
||||||
if let Some(pl) = self.pipelines.get(&format) {
|
let with_linear_output = pass == RenderPass::BlendBuffer;
|
||||||
|
let pipelines = &self.pipelines[pass];
|
||||||
|
if let Some(pl) = pipelines.get(&format) {
|
||||||
return Ok(pl);
|
return Ok(pl);
|
||||||
}
|
}
|
||||||
let fill = self
|
let create_fill_pipeline = |src_has_alpha| {
|
||||||
.device
|
self.device
|
||||||
.create_pipeline::<FillPushConstants>(PipelineCreateInfo {
|
.create_pipeline::<FillPushConstants>(PipelineCreateInfo {
|
||||||
format,
|
format,
|
||||||
vert: self.fill_vert_shader.clone(),
|
vert: self.fill_vert_shader.clone(),
|
||||||
frag: self.fill_frag_shader.clone(),
|
frag: self.fill_frag_shader.clone(),
|
||||||
blend: true,
|
blend: src_has_alpha,
|
||||||
src_has_alpha: true,
|
src_has_alpha,
|
||||||
has_alpha_mult: false,
|
has_alpha_mult: false,
|
||||||
frag_descriptor_set_layout: None,
|
with_linear_output,
|
||||||
})?;
|
frag_descriptor_set_layout: None,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let fill_opaque = create_fill_pipeline(false)?;
|
||||||
|
let fill_alpha = create_fill_pipeline(true)?;
|
||||||
let create_tex_pipeline = |src_has_alpha, has_alpha_mult| {
|
let create_tex_pipeline = |src_has_alpha, has_alpha_mult| {
|
||||||
self.device
|
self.device
|
||||||
.create_pipeline::<TexPushConstants>(PipelineCreateInfo {
|
.create_pipeline::<TexPushConstants>(PipelineCreateInfo {
|
||||||
|
|
@ -288,6 +322,7 @@ impl VulkanRenderer {
|
||||||
blend: src_has_alpha || has_alpha_mult,
|
blend: src_has_alpha || has_alpha_mult,
|
||||||
src_has_alpha,
|
src_has_alpha,
|
||||||
has_alpha_mult,
|
has_alpha_mult,
|
||||||
|
with_linear_output,
|
||||||
frag_descriptor_set_layout: Some(self.tex_descriptor_set_layout.clone()),
|
frag_descriptor_set_layout: Some(self.tex_descriptor_set_layout.clone()),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
@ -295,8 +330,11 @@ impl VulkanRenderer {
|
||||||
let tex_alpha = create_tex_pipeline(true, false)?;
|
let tex_alpha = create_tex_pipeline(true, false)?;
|
||||||
let tex_mult_opaque = create_tex_pipeline(false, true)?;
|
let tex_mult_opaque = create_tex_pipeline(false, true)?;
|
||||||
let tex_mult_alpha = create_tex_pipeline(true, true)?;
|
let tex_mult_alpha = create_tex_pipeline(true, true)?;
|
||||||
let pipelines = Rc::new(VulkanFormatPipelines {
|
let format_pipelines = Rc::new(VulkanFormatPipelines {
|
||||||
fill,
|
fill: static_map! {
|
||||||
|
TexSourceType::HasAlpha => fill_alpha.clone(),
|
||||||
|
TexSourceType::Opaque => fill_opaque.clone(),
|
||||||
|
},
|
||||||
tex: static_map! {
|
tex: static_map! {
|
||||||
TexCopyType::Identity => static_map! {
|
TexCopyType::Identity => static_map! {
|
||||||
TexSourceType::HasAlpha => tex_alpha.clone(),
|
TexSourceType::HasAlpha => tex_alpha.clone(),
|
||||||
|
|
@ -308,27 +346,38 @@ impl VulkanRenderer {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
self.pipelines.set(format, pipelines.clone());
|
pipelines.set(format, format_pipelines.clone());
|
||||||
Ok(pipelines)
|
Ok(format_pipelines)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn allocate_point(&self) -> u64 {
|
pub(super) fn allocate_point(&self) -> u64 {
|
||||||
self.last_point.fetch_add(1) + 1
|
self.last_point.fetch_add(1) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_descriptor_buffer(
|
fn create_descriptor_buffers(
|
||||||
&self,
|
&self,
|
||||||
buf: CommandBuffer,
|
buf: CommandBuffer,
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
|
bb: Option<&VulkanImage>,
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<(), VulkanError> {
|
||||||
let Some(db) = &self.device.descriptor_buffer else {
|
let Some(db) = &self.device.descriptor_buffer else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
zone!("create_descriptor_buffer");
|
zone!("create_descriptor_buffers");
|
||||||
let version = self.allocate_point();
|
let version = self.allocate_point();
|
||||||
let memory = &mut *self.memory.borrow_mut();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
let writer = &mut *self.tex_descriptor_buffer_writer.borrow_mut();
|
memory.descriptor_buffers.clear();
|
||||||
writer.clear();
|
let sampler_writer = &mut memory.sampler_descriptor_buffer_writer;
|
||||||
|
sampler_writer.clear();
|
||||||
|
let resource_writer = &mut memory.resource_descriptor_buffer_writer;
|
||||||
|
resource_writer.clear();
|
||||||
|
if let Some(bb) = bb {
|
||||||
|
let layout = self.out_descriptor_set_layout.as_ref().unwrap();
|
||||||
|
let offset = resource_writer.next_offset();
|
||||||
|
bb.descriptor_buffer_offset.set(offset);
|
||||||
|
let mut writer = resource_writer.add_set(layout);
|
||||||
|
writer.write(layout.offsets[0], &bb.sampled_image_descriptor);
|
||||||
|
}
|
||||||
for cmd in opts {
|
for cmd in opts {
|
||||||
let GfxApiOpt::CopyTexture(c) = cmd else {
|
let GfxApiOpt::CopyTexture(c) = cmd else {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -337,27 +386,32 @@ impl VulkanRenderer {
|
||||||
if tex.descriptor_buffer_version.replace(version) == version {
|
if tex.descriptor_buffer_version.replace(version) == version {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let offset = writer.next_offset();
|
let offset = sampler_writer.next_offset();
|
||||||
tex.descriptor_buffer_offset.set(offset);
|
tex.descriptor_buffer_offset.set(offset);
|
||||||
let mut writer = writer.add_set();
|
let mut writer = sampler_writer.add_set(&self.tex_descriptor_set_layout);
|
||||||
writer.write(
|
writer.write(
|
||||||
self.tex_descriptor_set_layout.offsets[0],
|
self.tex_descriptor_set_layout.offsets[0],
|
||||||
&tex.shader_read_only_optimal_descriptor,
|
&tex.shader_read_only_optimal_descriptor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let buffer = self
|
let mut infos = ArrayVec::<_, 2>::new();
|
||||||
.tex_sampler_descriptor_buffer_cache
|
for (writer, cache) in [
|
||||||
.allocate(writer.len() as DeviceSize)?;
|
(&sampler_writer, &self.sampler_descriptor_buffer_cache),
|
||||||
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
(&resource_writer, &self.resource_descriptor_buffer_cache),
|
||||||
ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len())
|
] {
|
||||||
})?;
|
let buffer = cache.allocate(writer.len() as DeviceSize)?;
|
||||||
let info = DescriptorBufferBindingInfoEXT::default()
|
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||||
.usage(self.tex_sampler_descriptor_buffer_cache.usage())
|
ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len())
|
||||||
.address(buffer.buffer.address);
|
})?;
|
||||||
unsafe {
|
let info = DescriptorBufferBindingInfoEXT::default()
|
||||||
db.cmd_bind_descriptor_buffers(buf, slice::from_ref(&info));
|
.usage(cache.usage())
|
||||||
|
.address(buffer.buffer.address);
|
||||||
|
infos.push(info);
|
||||||
|
memory.descriptor_buffers.push(buffer);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
db.cmd_bind_descriptor_buffers(buf, &infos);
|
||||||
}
|
}
|
||||||
memory.descriptor_buffer = Some(buffer);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,42 +554,61 @@ impl VulkanRenderer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_rendering(&self, buf: CommandBuffer, fb: &VulkanImage, clear: Option<&Color>) {
|
fn begin_rendering(
|
||||||
|
&self,
|
||||||
|
buf: CommandBuffer,
|
||||||
|
target: &VulkanImage,
|
||||||
|
clear: Option<&Color>,
|
||||||
|
pass: RenderPass,
|
||||||
|
) {
|
||||||
zone!("begin_rendering");
|
zone!("begin_rendering");
|
||||||
let memory = &mut *self.memory.borrow_mut();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
let clear_value = clear.map(|clear| ClearValue {
|
let mut load_clear = None;
|
||||||
color: ClearColorValue {
|
let mut manual_clear = None;
|
||||||
float32: clear.to_array_srgb(),
|
let clear_rects = &memory.clear_rects[pass];
|
||||||
},
|
if let Some(clear) = clear {
|
||||||
});
|
if clear_rects.is_not_empty() {
|
||||||
let load_clear = memory.paint_regions.len() == 1 && {
|
let clear_value = ClearValue {
|
||||||
let rect = &memory.paint_regions[0].rect;
|
color: ClearColorValue {
|
||||||
rect.offset.x == 0
|
float32: match pass {
|
||||||
&& rect.offset.y == 0
|
RenderPass::BlendBuffer => clear.to_array_linear(None),
|
||||||
&& rect.extent.width == fb.width
|
RenderPass::FrameBuffer => clear.to_array_srgb(None),
|
||||||
&& rect.extent.height == fb.height
|
},
|
||||||
};
|
},
|
||||||
let (load_clear, manual_clear) = match load_clear {
|
};
|
||||||
false => (None, clear_value),
|
let use_load_clear = clear_rects.len() == 1 && {
|
||||||
true => (clear_value, None),
|
let rect = &clear_rects[0].rect;
|
||||||
};
|
rect.offset.x == 0
|
||||||
let rendering_attachment_info = {
|
&& rect.offset.y == 0
|
||||||
let mut rai = RenderingAttachmentInfo::default()
|
&& rect.extent.width == target.width
|
||||||
.image_view(fb.render_view.unwrap_or(fb.texture_view))
|
&& rect.extent.height == target.height
|
||||||
.image_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
};
|
||||||
.load_op(AttachmentLoadOp::LOAD)
|
if use_load_clear {
|
||||||
.store_op(AttachmentStoreOp::STORE);
|
load_clear = Some(clear_value);
|
||||||
if let Some(clear) = load_clear {
|
} else {
|
||||||
rai = rai.clear_value(clear).load_op(AttachmentLoadOp::CLEAR);
|
manual_clear = Some(clear_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rai
|
}
|
||||||
|
let mut rendering_attachment_info = RenderingAttachmentInfo::default()
|
||||||
|
.image_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||||
|
.image_view(target.render_view.unwrap_or(target.texture_view))
|
||||||
|
.store_op(AttachmentStoreOp::STORE);
|
||||||
|
let load_op = if let Some(clear) = load_clear {
|
||||||
|
rendering_attachment_info = rendering_attachment_info.clear_value(clear);
|
||||||
|
AttachmentLoadOp::CLEAR
|
||||||
|
} else if pass == RenderPass::BlendBuffer {
|
||||||
|
AttachmentLoadOp::DONT_CARE
|
||||||
|
} else {
|
||||||
|
AttachmentLoadOp::LOAD
|
||||||
};
|
};
|
||||||
|
rendering_attachment_info = rendering_attachment_info.load_op(load_op);
|
||||||
let rendering_info = RenderingInfo::default()
|
let rendering_info = RenderingInfo::default()
|
||||||
.render_area(Rect2D {
|
.render_area(Rect2D {
|
||||||
offset: Default::default(),
|
offset: Default::default(),
|
||||||
extent: Extent2D {
|
extent: Extent2D {
|
||||||
width: fb.width,
|
width: target.width,
|
||||||
height: fb.height,
|
height: target.height,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.layer_count(1)
|
.layer_count(1)
|
||||||
|
|
@ -543,26 +616,16 @@ impl VulkanRenderer {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device.device.cmd_begin_rendering(buf, &rendering_info);
|
self.device.device.cmd_begin_rendering(buf, &rendering_info);
|
||||||
}
|
}
|
||||||
if memory.paint_regions.is_not_empty() {
|
if clear_rects.is_not_empty() {
|
||||||
if let Some(clear) = manual_clear {
|
if let Some(clear) = manual_clear {
|
||||||
let clear_attachment = ClearAttachment::default()
|
let clear_attachment = ClearAttachment::default()
|
||||||
.color_attachment(0)
|
.color_attachment(0)
|
||||||
.clear_value(clear)
|
.clear_value(clear)
|
||||||
.aspect_mask(ImageAspectFlags::COLOR);
|
.aspect_mask(ImageAspectFlags::COLOR);
|
||||||
memory.clear_rects.clear();
|
|
||||||
for region in &memory.paint_regions {
|
|
||||||
memory.clear_rects.push(ClearRect {
|
|
||||||
rect: region.rect,
|
|
||||||
base_array_layer: 0,
|
|
||||||
layer_count: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device.device.cmd_clear_attachments(
|
self.device
|
||||||
buf,
|
.device
|
||||||
&[clear_attachment],
|
.cmd_clear_attachments(buf, &[clear_attachment], clear_rects);
|
||||||
&memory.clear_rects,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -598,12 +661,14 @@ impl VulkanRenderer {
|
||||||
fn record_draws(
|
fn record_draws(
|
||||||
&self,
|
&self,
|
||||||
buf: CommandBuffer,
|
buf: CommandBuffer,
|
||||||
fb: &VulkanImage,
|
target: &VulkanImage,
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
|
pass: RenderPass,
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<(), VulkanError> {
|
||||||
zone!("record_draws");
|
zone!("record_draws");
|
||||||
let memory = &*self.memory.borrow();
|
let memory = &*self.memory.borrow();
|
||||||
let pipelines = self.get_or_create_pipelines(fb.format.vk_format)?;
|
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 dev = &self.device.device;
|
||||||
let mut current_pipeline = None;
|
let mut current_pipeline = None;
|
||||||
let mut bind = |pipeline: &VulkanPipeline| {
|
let mut bind = |pipeline: &VulkanPipeline| {
|
||||||
|
|
@ -620,19 +685,27 @@ impl VulkanRenderer {
|
||||||
GfxApiOpt::FillRect(r) => {
|
GfxApiOpt::FillRect(r) => {
|
||||||
let push = FillPushConstants {
|
let push = FillPushConstants {
|
||||||
pos: r.rect.to_points(),
|
pos: r.rect.to_points(),
|
||||||
color: r.color.to_array_srgb(),
|
color: match pass {
|
||||||
|
RenderPass::BlendBuffer => r.color.to_array_linear(r.alpha),
|
||||||
|
RenderPass::FrameBuffer => r.color.to_array_srgb(r.alpha),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
for region in &memory.paint_regions {
|
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 mut push = push;
|
||||||
let draw = region.constrain(&mut push.pos, None);
|
let draw = region.constrain(&mut push.pos, None);
|
||||||
if !draw {
|
if !draw {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bind(&pipelines.fill);
|
bind(pipeline);
|
||||||
unsafe {
|
unsafe {
|
||||||
dev.cmd_push_constants(
|
dev.cmd_push_constants(
|
||||||
buf,
|
buf,
|
||||||
pipelines.fill.pipeline_layout,
|
pipeline.pipeline_layout,
|
||||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||||
0,
|
0,
|
||||||
uapi::as_bytes(&push),
|
uapi::as_bytes(&push),
|
||||||
|
|
@ -656,7 +729,7 @@ impl VulkanRenderer {
|
||||||
true => TexCopyType::Multiply,
|
true => TexCopyType::Multiply,
|
||||||
false => TexCopyType::Identity,
|
false => TexCopyType::Identity,
|
||||||
};
|
};
|
||||||
let source_type = match tex.format.has_alpha {
|
let source_type = match tex.format.has_alpha && !c.opaque {
|
||||||
true => TexSourceType::HasAlpha,
|
true => TexSourceType::HasAlpha,
|
||||||
false => TexSourceType::Opaque,
|
false => TexSourceType::Opaque,
|
||||||
};
|
};
|
||||||
|
|
@ -670,7 +743,7 @@ impl VulkanRenderer {
|
||||||
.image_view(tex.texture_view)
|
.image_view(tex.texture_view)
|
||||||
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
||||||
let init = Once::default();
|
let init = Once::default();
|
||||||
for region in &memory.paint_regions {
|
for region in paint_regions {
|
||||||
let mut push = push;
|
let mut push = push;
|
||||||
let draw = region.constrain(&mut push.pos, Some(&mut push.tex_pos));
|
let draw = region.constrain(&mut push.pos, Some(&mut push.tex_pos));
|
||||||
if !draw {
|
if !draw {
|
||||||
|
|
@ -717,6 +790,112 @@ impl VulkanRenderer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn blend_buffer_initial_barrier(&self, buf: CommandBuffer, bb: &VulkanImage) {
|
||||||
|
zone!("blend_buffer_initial_barrier");
|
||||||
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
|
memory.image_barriers.clear();
|
||||||
|
let barrier = image_barrier()
|
||||||
|
.image(bb.image)
|
||||||
|
.old_layout(if bb.is_undefined.get() {
|
||||||
|
ImageLayout::UNDEFINED
|
||||||
|
} else {
|
||||||
|
ImageLayout::SHADER_READ_ONLY_OPTIMAL
|
||||||
|
})
|
||||||
|
.new_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||||
|
.src_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER)
|
||||||
|
.dst_stage_mask(PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT)
|
||||||
|
.src_access_mask(AccessFlags2::SHADER_READ)
|
||||||
|
.dst_access_mask(AccessFlags2::COLOR_ATTACHMENT_WRITE);
|
||||||
|
memory.image_barriers.push(barrier);
|
||||||
|
let dep_info = DependencyInfoKHR::default().image_memory_barriers(&memory.image_barriers);
|
||||||
|
unsafe {
|
||||||
|
self.device.device.cmd_pipeline_barrier2(buf, &dep_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blend_buffer_copy(
|
||||||
|
&self,
|
||||||
|
buf: CommandBuffer,
|
||||||
|
fb: &VulkanImage,
|
||||||
|
bb: &VulkanImage,
|
||||||
|
) -> Result<(), VulkanError> {
|
||||||
|
zone!("blend_buffer_copy");
|
||||||
|
let memory = &*self.memory.borrow();
|
||||||
|
let db = self.device.descriptor_buffer.as_ref().unwrap();
|
||||||
|
let pipeline = match self.out_pipelines.borrow_mut().entry(fb.format.vk_format) {
|
||||||
|
Entry::Occupied(pipeline) => pipeline.get().clone(),
|
||||||
|
Entry::Vacant(e) => {
|
||||||
|
let layout = self.out_descriptor_set_layout.as_ref().unwrap();
|
||||||
|
let out = self
|
||||||
|
.device
|
||||||
|
.create_pipeline::<OutPushConstants>(PipelineCreateInfo {
|
||||||
|
format: fb.format.vk_format,
|
||||||
|
vert: self.out_vert_shader.clone(),
|
||||||
|
frag: self.out_frag_shader.clone(),
|
||||||
|
blend: false,
|
||||||
|
src_has_alpha: true,
|
||||||
|
has_alpha_mult: false,
|
||||||
|
with_linear_output: true,
|
||||||
|
frag_descriptor_set_layout: Some(layout.clone()),
|
||||||
|
})?;
|
||||||
|
e.insert(out.clone());
|
||||||
|
out
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let dev = &self.device.device;
|
||||||
|
unsafe {
|
||||||
|
dev.cmd_bind_pipeline(buf, PipelineBindPoint::GRAPHICS, pipeline.pipeline);
|
||||||
|
db.cmd_set_descriptor_buffer_offsets(
|
||||||
|
buf,
|
||||||
|
PipelineBindPoint::GRAPHICS,
|
||||||
|
pipeline.pipeline_layout,
|
||||||
|
0,
|
||||||
|
&[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blend_buffer_final_barrier(&self, buf: CommandBuffer, bb: &VulkanImage) {
|
||||||
|
zone!("blend_buffer_final_barrier");
|
||||||
|
let image_barrier = image_barrier()
|
||||||
|
.image(bb.image)
|
||||||
|
.old_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||||
|
.new_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL)
|
||||||
|
.src_access_mask(AccessFlags2::COLOR_ATTACHMENT_WRITE)
|
||||||
|
.dst_access_mask(AccessFlags2::SHADER_SAMPLED_READ)
|
||||||
|
.src_stage_mask(PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT)
|
||||||
|
.dst_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER);
|
||||||
|
let dependency_info =
|
||||||
|
DependencyInfoKHR::default().image_memory_barriers(slice::from_ref(&image_barrier));
|
||||||
|
unsafe {
|
||||||
|
self.device
|
||||||
|
.device
|
||||||
|
.cmd_pipeline_barrier2(buf, &dependency_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn end_rendering(&self, buf: CommandBuffer) {
|
fn end_rendering(&self, buf: CommandBuffer) {
|
||||||
zone!("end_rendering");
|
zone!("end_rendering");
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -724,7 +903,7 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_bridge_to_dmabuf(&self, buf: CommandBuffer, fb: &VulkanImage) {
|
fn copy_bridge_to_dmabuf(&self, buf: CommandBuffer, fb: &VulkanImage, region: &Region) {
|
||||||
zone!("copy_bridge_to_dmabuf");
|
zone!("copy_bridge_to_dmabuf");
|
||||||
let Some(bridge) = &fb.bridge else {
|
let Some(bridge) = &fb.bridge else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -766,15 +945,14 @@ impl VulkanRenderer {
|
||||||
.base_array_layer(0)
|
.base_array_layer(0)
|
||||||
.mip_level(0);
|
.mip_level(0);
|
||||||
memory.image_copy_regions.clear();
|
memory.image_copy_regions.clear();
|
||||||
for region in &memory.paint_regions {
|
for rect in region.rects() {
|
||||||
let offset = Offset3D {
|
let Some([x1, y1, x2, y2]) = constrain_to_fb(fb, rect) else {
|
||||||
x: region.rect.offset.x,
|
continue;
|
||||||
y: region.rect.offset.y,
|
|
||||||
z: 0,
|
|
||||||
};
|
};
|
||||||
|
let offset = Offset3D { x: x1, y: y1, z: 0 };
|
||||||
let extent = Extent3D {
|
let extent = Extent3D {
|
||||||
width: region.rect.extent.width,
|
width: (x2 - x1) as _,
|
||||||
height: region.rect.extent.height,
|
height: (y2 - y1) as _,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
};
|
};
|
||||||
memory.image_copy_regions.push(
|
memory.image_copy_regions.push(
|
||||||
|
|
@ -786,14 +964,16 @@ impl VulkanRenderer {
|
||||||
.extent(extent),
|
.extent(extent),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let copy_image_info = CopyImageInfo2::default()
|
if memory.image_copy_regions.is_not_empty() {
|
||||||
.src_image(fb.image)
|
let copy_image_info = CopyImageInfo2::default()
|
||||||
.src_image_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
|
.src_image(fb.image)
|
||||||
.dst_image(bridge.dmabuf_image)
|
.src_image_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
|
||||||
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
.dst_image(bridge.dmabuf_image)
|
||||||
.regions(&memory.image_copy_regions);
|
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||||
unsafe {
|
.regions(&memory.image_copy_regions);
|
||||||
self.device.device.cmd_copy_image2(buf, ©_image_info);
|
unsafe {
|
||||||
|
self.device.device.cmd_copy_image2(buf, ©_image_info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -999,7 +1179,10 @@ impl VulkanRenderer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_layouts(&self, fb: &VulkanImage) {
|
fn store_layouts(&self, fb: &VulkanImage, bb: Option<&VulkanImage>) {
|
||||||
|
if let Some(bb) = bb {
|
||||||
|
bb.is_undefined.set(false);
|
||||||
|
}
|
||||||
fb.is_undefined.set(false);
|
fb.is_undefined.set(false);
|
||||||
fb.contents_are_undefined.set(false);
|
fb.contents_are_undefined.set(false);
|
||||||
fb.queue_state.set(QueueState::Acquired {
|
fb.queue_state.set(QueueState::Acquired {
|
||||||
|
|
@ -1013,7 +1196,12 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_pending_frame(self: &Rc<Self>, buf: Rc<VulkanCommandBuffer>, fb: &Rc<VulkanImage>) {
|
fn create_pending_frame(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
buf: Rc<VulkanCommandBuffer>,
|
||||||
|
fb: &Rc<VulkanImage>,
|
||||||
|
bb: Option<Rc<VulkanImage>>,
|
||||||
|
) {
|
||||||
zone!("create_pending_frame");
|
zone!("create_pending_frame");
|
||||||
let point = self.allocate_point();
|
let point = self.allocate_point();
|
||||||
let mut memory = self.memory.borrow_mut();
|
let mut memory = self.memory.borrow_mut();
|
||||||
|
|
@ -1022,11 +1210,12 @@ impl VulkanRenderer {
|
||||||
renderer: self.clone(),
|
renderer: self.clone(),
|
||||||
cmd: Cell::new(Some(buf)),
|
cmd: Cell::new(Some(buf)),
|
||||||
_fb: fb.clone(),
|
_fb: fb.clone(),
|
||||||
|
_bb: bb,
|
||||||
_textures: mem::take(&mut memory.textures),
|
_textures: mem::take(&mut memory.textures),
|
||||||
wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)),
|
wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)),
|
||||||
waiter: Cell::new(None),
|
waiter: Cell::new(None),
|
||||||
_release_fence: memory.release_fence.take(),
|
_release_fence: memory.release_fence.take(),
|
||||||
_descriptor_buffer: memory.descriptor_buffer.take(),
|
_descriptor_buffers: mem::take(&mut memory.descriptor_buffers),
|
||||||
});
|
});
|
||||||
self.pending_frames.set(frame.point, frame.clone());
|
self.pending_frames.set(frame.point, frame.clone());
|
||||||
let future = self.eng.spawn(
|
let future = self.eng.spawn(
|
||||||
|
|
@ -1049,9 +1238,18 @@ impl VulkanRenderer {
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
|
blend_buffer: Option<Rc<VulkanImage>>,
|
||||||
) -> Result<Option<SyncFile>, VulkanError> {
|
) -> Result<Option<SyncFile>, VulkanError> {
|
||||||
zone!("execute");
|
zone!("execute");
|
||||||
let res = self.try_execute(fb, fb_acquire_sync, fb_release_sync, opts, clear, region);
|
let res = self.try_execute(
|
||||||
|
fb,
|
||||||
|
fb_acquire_sync,
|
||||||
|
fb_release_sync,
|
||||||
|
opts,
|
||||||
|
clear,
|
||||||
|
region,
|
||||||
|
blend_buffer,
|
||||||
|
);
|
||||||
let sync_file = {
|
let sync_file = {
|
||||||
let mut memory = self.memory.borrow_mut();
|
let mut memory = self.memory.borrow_mut();
|
||||||
memory.textures.clear();
|
memory.textures.clear();
|
||||||
|
|
@ -1059,7 +1257,7 @@ impl VulkanRenderer {
|
||||||
memory.queue_transfer.clear();
|
memory.queue_transfer.clear();
|
||||||
memory.wait_semaphores.clear();
|
memory.wait_semaphores.clear();
|
||||||
memory.release_fence.take();
|
memory.release_fence.take();
|
||||||
memory.descriptor_buffer.take();
|
memory.descriptor_buffers.clear();
|
||||||
memory.release_sync_file.take()
|
memory.release_sync_file.take()
|
||||||
};
|
};
|
||||||
res.map(|_| sync_file)
|
res.map(|_| sync_file)
|
||||||
|
|
@ -1074,37 +1272,124 @@ impl VulkanRenderer {
|
||||||
Ok(semaphore)
|
Ok(semaphore)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_paint_regions(&self, fb: &VulkanImage, region: &Region) {
|
fn create_regions(
|
||||||
|
&self,
|
||||||
|
fb: &VulkanImage,
|
||||||
|
opts: &[GfxApiOpt],
|
||||||
|
clear: Option<&Color>,
|
||||||
|
region: &Region,
|
||||||
|
bb: Option<&VulkanImage>,
|
||||||
|
) {
|
||||||
|
zone!("create_paint_regions");
|
||||||
let memory = &mut *self.memory.borrow_mut();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
memory.paint_regions.clear();
|
memory.regions_1.clear();
|
||||||
for rect in region.rects() {
|
memory.regions_2.clear();
|
||||||
let x1 = rect.x1().max(0);
|
let width = fb.width as f32;
|
||||||
let y1 = rect.y1().max(0);
|
let height = fb.height as f32;
|
||||||
let x2 = rect.x2();
|
let mut tag = 0;
|
||||||
let y2 = rect.y2();
|
for opt in opts.iter().rev() {
|
||||||
if x1 as u32 > fb.width || y1 as u32 > fb.height || x2 <= 0 || y2 <= 0 {
|
let (opaque, fb_rect) = match opt {
|
||||||
continue;
|
GfxApiOpt::Sync => continue,
|
||||||
|
GfxApiOpt::FillRect(f) => (f.effective_color().a >= 1.0, f.rect),
|
||||||
|
GfxApiOpt::CopyTexture(c) => {
|
||||||
|
let opaque = 'opaque: {
|
||||||
|
if let Some(a) = c.alpha {
|
||||||
|
if a < 1.0 {
|
||||||
|
break 'opaque false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !c.opaque {
|
||||||
|
let tex = c.tex.as_vk(&self.device.device);
|
||||||
|
if tex.format.has_alpha {
|
||||||
|
break 'opaque false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
};
|
||||||
|
(opaque, c.target)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if opaque || bb.is_none() {
|
||||||
|
tag |= 1;
|
||||||
|
} else {
|
||||||
|
tag += tag & 1;
|
||||||
}
|
}
|
||||||
let x2 = x2.min(fb.width as i32);
|
let rect = fb_rect.to_rect(width, height);
|
||||||
let y2 = y2.min(fb.height as i32);
|
if opaque && clear.is_some() {
|
||||||
let to_fb = |c: i32, max: u32| 2.0 * (c as f32 / max as f32) - 1.0;
|
memory.regions_1.push(rect);
|
||||||
memory.paint_regions.push(PaintRegion {
|
}
|
||||||
rect: Rect2D {
|
memory.regions_2.push(rect.with_tag(tag));
|
||||||
offset: Offset2D {
|
}
|
||||||
x: x1 as _,
|
let clear_region = if clear.is_some() {
|
||||||
y: y1 as _,
|
let opaque_region = Region::from_rects2(&memory.regions_1);
|
||||||
},
|
region.subtract_cow(&opaque_region)
|
||||||
extent: Extent2D {
|
} else {
|
||||||
width: (x2 - x1) as u32,
|
Cow::Owned(Region::default())
|
||||||
height: (y2 - y1) as u32,
|
};
|
||||||
},
|
let tagged_region = Region::from_rects_tagged(&memory.regions_2).intersect_tagged(region);
|
||||||
},
|
memory.regions_1.clear();
|
||||||
|
memory.paint_regions[RenderPass::BlendBuffer].clear();
|
||||||
|
memory.paint_regions[RenderPass::FrameBuffer].clear();
|
||||||
|
let to_fb = |c: i32, max: u32| 2.0 * (c as f32 / max as f32) - 1.0;
|
||||||
|
for rect in tagged_region.rects() {
|
||||||
|
if rect.tag() == 0 && clear.is_some() {
|
||||||
|
memory.regions_1.push(rect.untag());
|
||||||
|
}
|
||||||
|
let Some([x1, y1, x2, y2]) = constrain_to_fb(fb, rect) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let region = match rect.tag() {
|
||||||
|
0 => &mut memory.paint_regions[RenderPass::BlendBuffer],
|
||||||
|
_ => &mut memory.paint_regions[RenderPass::FrameBuffer],
|
||||||
|
};
|
||||||
|
region.push(PaintRegion {
|
||||||
x1: to_fb(x1, fb.width),
|
x1: to_fb(x1, fb.width),
|
||||||
x2: to_fb(x2, fb.width),
|
x2: to_fb(x2, fb.width),
|
||||||
y1: to_fb(y1, fb.height),
|
y1: to_fb(y1, fb.height),
|
||||||
y2: to_fb(y2, fb.height),
|
y2: to_fb(y2, fb.height),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
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() {
|
||||||
|
// log::info!("blend_clear = {:?}", blend_clear);
|
||||||
|
// log::info!("opaque_clear = {:?}", opaque_clear);
|
||||||
|
// }
|
||||||
|
for (pass, clear_region) in [
|
||||||
|
(RenderPass::BlendBuffer, &blend_clear),
|
||||||
|
(RenderPass::FrameBuffer, &opaque_clear),
|
||||||
|
] {
|
||||||
|
memory.clear_rects[pass].clear();
|
||||||
|
for rect in clear_region.rects() {
|
||||||
|
let Some([x1, y1, x2, y2]) = constrain_to_fb(fb, rect) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
memory.clear_rects[pass].push(ClearRect {
|
||||||
|
rect: Rect2D {
|
||||||
|
offset: Offset2D {
|
||||||
|
x: x1 as _,
|
||||||
|
y: y1 as _,
|
||||||
|
},
|
||||||
|
extent: Extent2D {
|
||||||
|
width: (x2 - x1) as u32,
|
||||||
|
height: (y2 - y1) as u32,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
base_array_layer: 0,
|
||||||
|
layer_count: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn elide_blend_buffer(&self, blend_buffer: &mut Option<Rc<VulkanImage>>) {
|
||||||
|
if blend_buffer.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let memory = &*self.memory.borrow();
|
||||||
|
if memory.paint_regions[RenderPass::BlendBuffer].is_empty() {
|
||||||
|
*blend_buffer = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_execute(
|
fn try_execute(
|
||||||
|
|
@ -1115,26 +1400,43 @@ impl VulkanRenderer {
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
|
mut blend_buffer: Option<Rc<VulkanImage>>,
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<(), VulkanError> {
|
||||||
self.check_defunct()?;
|
self.check_defunct()?;
|
||||||
self.create_paint_regions(fb, region);
|
self.create_regions(fb, opts, clear, region, blend_buffer.as_deref());
|
||||||
|
self.elide_blend_buffer(&mut blend_buffer);
|
||||||
|
let bb = blend_buffer.as_deref();
|
||||||
let buf = self.gfx_command_buffers.allocate()?;
|
let buf = self.gfx_command_buffers.allocate()?;
|
||||||
self.collect_memory(opts);
|
self.collect_memory(opts);
|
||||||
self.begin_command_buffer(buf.buffer)?;
|
self.begin_command_buffer(buf.buffer)?;
|
||||||
self.create_descriptor_buffer(buf.buffer, opts)?;
|
self.create_descriptor_buffers(buf.buffer, opts, bb)?;
|
||||||
self.initial_barriers(buf.buffer, fb)?;
|
self.initial_barriers(buf.buffer, fb)?;
|
||||||
self.begin_rendering(buf.buffer, fb, clear);
|
|
||||||
self.set_viewport(buf.buffer, fb);
|
self.set_viewport(buf.buffer, fb);
|
||||||
self.record_draws(buf.buffer, fb, opts)?;
|
if let Some(bb) = bb {
|
||||||
self.end_rendering(buf.buffer);
|
zone!("blend buffer pass");
|
||||||
self.copy_bridge_to_dmabuf(buf.buffer, fb);
|
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.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)?;
|
||||||
|
if let Some(bb) = bb {
|
||||||
|
self.blend_buffer_copy(buf.buffer, fb, bb)?;
|
||||||
|
}
|
||||||
|
self.end_rendering(buf.buffer);
|
||||||
|
}
|
||||||
|
self.copy_bridge_to_dmabuf(buf.buffer, fb, region);
|
||||||
self.final_barriers(buf.buffer, fb);
|
self.final_barriers(buf.buffer, fb);
|
||||||
self.end_command_buffer(buf.buffer)?;
|
self.end_command_buffer(buf.buffer)?;
|
||||||
self.create_wait_semaphores(fb, &fb_acquire_sync)?;
|
self.create_wait_semaphores(fb, &fb_acquire_sync)?;
|
||||||
self.submit(buf.buffer)?;
|
self.submit(buf.buffer)?;
|
||||||
self.import_release_semaphore(fb, fb_release_sync);
|
self.import_release_semaphore(fb, fb_release_sync);
|
||||||
self.store_layouts(fb);
|
self.store_layouts(fb, bb);
|
||||||
self.create_pending_frame(buf, fb);
|
self.create_pending_frame(buf, fb, blend_buffer);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1206,6 +1508,17 @@ impl dyn GfxTexture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl dyn GfxBlendBuffer {
|
||||||
|
pub(super) fn into_vk(self: Rc<Self>, device: &Device) -> Rc<VulkanImage> {
|
||||||
|
let img: Rc<VulkanImage> = self
|
||||||
|
.into_any()
|
||||||
|
.downcast()
|
||||||
|
.expect("Non-vulkan blend buffer passed into vulkan");
|
||||||
|
img.assert_device(device);
|
||||||
|
img
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn image_barrier() -> ImageMemoryBarrier2<'static> {
|
pub(super) fn image_barrier() -> ImageMemoryBarrier2<'static> {
|
||||||
ImageMemoryBarrier2::default().subresource_range(
|
ImageMemoryBarrier2::default().subresource_range(
|
||||||
ImageSubresourceRange::default()
|
ImageSubresourceRange::default()
|
||||||
|
|
@ -1291,3 +1604,19 @@ impl PaintRegion {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn constrain_to_fb<T>(fb: &VulkanImage, rect: &Rect<T>) -> Option<[i32; 4]>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
let x1 = rect.x1().max(0);
|
||||||
|
let y1 = rect.y1().max(0);
|
||||||
|
let x2 = rect.x2();
|
||||||
|
let y2 = rect.y2();
|
||||||
|
if x1 as u32 > fb.width || y1 as u32 > fb.height || x2 <= 0 || y2 <= 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let x2 = x2.min(fb.width as i32);
|
||||||
|
let y2 = y2.min(fb.height as i32);
|
||||||
|
Some([x1, y1, x2, y2])
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ pub const FILL_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.vert
|
||||||
pub const FILL_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.frag.spv"));
|
pub const FILL_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.frag.spv"));
|
||||||
pub const TEX_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.vert.spv"));
|
pub const TEX_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.vert.spv"));
|
||||||
pub const TEX_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.frag.spv"));
|
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 struct VulkanShader {
|
pub struct VulkanShader {
|
||||||
pub(super) device: Rc<VulkanDevice>,
|
pub(super) device: Rc<VulkanDevice>,
|
||||||
|
|
@ -34,6 +36,14 @@ pub struct TexPushConstants {
|
||||||
|
|
||||||
unsafe impl Packed for TexPushConstants {}
|
unsafe impl Packed for TexPushConstants {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct OutPushConstants {
|
||||||
|
pub pos: [[f32; 2]; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Packed for OutPushConstants {}
|
||||||
|
|
||||||
impl VulkanDevice {
|
impl VulkanDevice {
|
||||||
pub(super) fn create_shader(
|
pub(super) fn create_shader(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,8 @@
|
||||||
|
#ifndef FRAG_SPEC_CONST_GLSL
|
||||||
|
#define FRAG_SPEC_CONST_GLSL
|
||||||
|
|
||||||
layout(constant_id = 0) const bool src_has_alpha = false;
|
layout(constant_id = 0) const bool src_has_alpha = false;
|
||||||
layout(constant_id = 1) const bool has_alpha_multiplier = false;
|
layout(constant_id = 1) const bool has_alpha_multiplier = false;
|
||||||
|
layout(constant_id = 2) const bool color_management = false;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
3
src/gfx_apis/vulkan/shaders/out.common.glsl
Normal file
3
src/gfx_apis/vulkan/shaders/out.common.glsl
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
layout(push_constant, std430) uniform Data {
|
||||||
|
layout(offset = 0) vec2 pos[4];
|
||||||
|
} data;
|
||||||
17
src/gfx_apis/vulkan/shaders/out.frag
Normal file
17
src/gfx_apis/vulkan/shaders/out.frag
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#version 450
|
||||||
|
#extension GL_EXT_samplerless_texture_functions : require
|
||||||
|
|
||||||
|
#include "frag_spec_const.glsl"
|
||||||
|
#include "transfer_functions.glsl"
|
||||||
|
#include "out.common.glsl"
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform texture2D in_color;
|
||||||
|
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;
|
||||||
|
out_color = c;
|
||||||
|
}
|
||||||
16
src/gfx_apis/vulkan/shaders/out.vert
Normal file
16
src/gfx_apis/vulkan/shaders/out.vert
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
gl_Position = vec4(pos, 0.0, 1.0);
|
||||||
|
// debugPrintfEXT("X gl_Position = %v4f, pos = %v2f", gl_Position, pos);
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
#include "frag_spec_const.glsl"
|
#include "frag_spec_const.glsl"
|
||||||
|
#include "transfer_functions.glsl"
|
||||||
#include "tex.common.glsl"
|
#include "tex.common.glsl"
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform sampler2D tex;
|
layout(set = 0, binding = 0) uniform sampler2D tex;
|
||||||
|
|
@ -8,13 +9,22 @@ layout(location = 0) in vec2 tex_pos;
|
||||||
layout(location = 0) out vec4 out_color;
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
vec4 c = textureLod(tex, tex_pos, 0);
|
||||||
|
if (color_management) {
|
||||||
|
if (src_has_alpha) {
|
||||||
|
c.rgb /= mix(c.a, 1.0, c.a == 0.0);
|
||||||
|
}
|
||||||
|
c.rgb = eotf_srgb(c.rgb);
|
||||||
|
if (src_has_alpha) {
|
||||||
|
c.rgb *= c.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (has_alpha_multiplier) {
|
if (has_alpha_multiplier) {
|
||||||
if (src_has_alpha) {
|
if (src_has_alpha) {
|
||||||
out_color = textureLod(tex, tex_pos, 0) * data.mul;
|
c *= data.mul;
|
||||||
} else {
|
} else {
|
||||||
out_color = vec4(textureLod(tex, tex_pos, 0).rgb * data.mul, data.mul);
|
c = vec4(c.rgb * data.mul, data.mul);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
out_color = textureLod(tex, tex_pos, 0);
|
|
||||||
}
|
}
|
||||||
|
out_color = c;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
src/gfx_apis/vulkan/shaders/transfer_functions.glsl
Normal file
21
src/gfx_apis/vulkan/shaders/transfer_functions.glsl
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef TRANSFER_FUNCTIONS_GLSL
|
||||||
|
#define TRANSFER_FUNCTIONS_GLSL
|
||||||
|
|
||||||
|
vec3 eotf_srgb(vec3 c) {
|
||||||
|
return mix(
|
||||||
|
c * vec3(1.0 / 12.92),
|
||||||
|
pow((c + vec3(0.055)) / vec3(1.055), vec3(2.4)),
|
||||||
|
greaterThan(c, vec3(0.04045))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 oetf_srgb(vec3 c) {
|
||||||
|
c = clamp(c, 0.0, 1.0);
|
||||||
|
return mix(
|
||||||
|
c * vec3(12.92),
|
||||||
|
vec3(1.055) * pow(c, vec3(1/2.4)) - vec3(0.055),
|
||||||
|
greaterThan(c, vec3(0.0031308))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -460,12 +460,14 @@ impl VulkanRenderer {
|
||||||
ty: VulkanImageMemory::Internal(shm),
|
ty: VulkanImageMemory::Internal(shm),
|
||||||
bridge: None,
|
bridge: None,
|
||||||
shader_read_only_optimal_descriptor: self.sampler_read_only_descriptor(view),
|
shader_read_only_optimal_descriptor: self.sampler_read_only_descriptor(view),
|
||||||
|
sampled_image_descriptor: Box::new([]),
|
||||||
descriptor_buffer_version: Cell::new(0),
|
descriptor_buffer_version: Cell::new(0),
|
||||||
descriptor_buffer_offset: Cell::new(0),
|
descriptor_buffer_offset: Cell::new(0),
|
||||||
execution_version: Cell::new(0),
|
execution_version: Cell::new(0),
|
||||||
});
|
});
|
||||||
let shm = match &img.ty {
|
let shm = match &img.ty {
|
||||||
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
||||||
|
VulkanImageMemory::Blend(_) => unreachable!(),
|
||||||
VulkanImageMemory::Internal(s) => s,
|
VulkanImageMemory::Internal(s) => s,
|
||||||
};
|
};
|
||||||
if data.is_not_empty() {
|
if data.is_not_empty() {
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
jay_config::video::Transform::None,
|
jay_config::video::Transform::None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,7 @@ impl JayScreencast {
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ pub struct WlSurface {
|
||||||
role: Cell<SurfaceRole>,
|
role: Cell<SurfaceRole>,
|
||||||
pending: RefCell<Box<PendingState>>,
|
pending: RefCell<Box<PendingState>>,
|
||||||
input_region: CloneCell<Option<Rc<Region>>>,
|
input_region: CloneCell<Option<Rc<Region>>>,
|
||||||
opaque_region: Cell<Option<Rc<Region>>>,
|
opaque_region: CloneCell<Option<Rc<Region>>>,
|
||||||
buffer_points: RefCell<BufferPoints>,
|
buffer_points: RefCell<BufferPoints>,
|
||||||
pub buffer_points_norm: RefCell<SampleRect>,
|
pub buffer_points_norm: RefCell<SampleRect>,
|
||||||
damage_matrix: Cell<DamageMatrix>,
|
damage_matrix: Cell<DamageMatrix>,
|
||||||
|
|
@ -331,6 +331,7 @@ pub struct WlSurface {
|
||||||
clear_fifo_on_vblank: Cell<bool>,
|
clear_fifo_on_vblank: Cell<bool>,
|
||||||
commit_timer: CloneCell<Option<Rc<WpCommitTimerV1>>>,
|
commit_timer: CloneCell<Option<Rc<WpCommitTimerV1>>>,
|
||||||
before_latch_listener: EventListener<dyn BeforeLatchListener>,
|
before_latch_listener: EventListener<dyn BeforeLatchListener>,
|
||||||
|
is_opaque: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for WlSurface {
|
impl Debug for WlSurface {
|
||||||
|
|
@ -668,6 +669,7 @@ impl WlSurface {
|
||||||
clear_fifo_on_vblank: Default::default(),
|
clear_fifo_on_vblank: Default::default(),
|
||||||
commit_timer: Default::default(),
|
commit_timer: Default::default(),
|
||||||
before_latch_listener: EventListener::new(slf.clone()),
|
before_latch_listener: EventListener::new(slf.clone()),
|
||||||
|
is_opaque: Cell::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1195,6 +1197,7 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let transform_changed = viewport_changed || scale_changed || buffer_transform_changed;
|
let transform_changed = viewport_changed || scale_changed || buffer_transform_changed;
|
||||||
|
let mut buffer_abs_pos_size_changed = false;
|
||||||
if buffer_changed || transform_changed {
|
if buffer_changed || transform_changed {
|
||||||
let mut buffer_points = self.buffer_points.borrow_mut();
|
let mut buffer_points = self.buffer_points.borrow_mut();
|
||||||
let mut buffer_points_norm = self.buffer_points_norm.borrow_mut();
|
let mut buffer_points_norm = self.buffer_points_norm.borrow_mut();
|
||||||
|
|
@ -1288,6 +1291,7 @@ impl WlSurface {
|
||||||
.set(buffer_abs_pos.with_size(width, height).unwrap());
|
.set(buffer_abs_pos.with_size(width, height).unwrap());
|
||||||
max_surface_size = (width.max(old_width), height.max(old_height));
|
max_surface_size = (width.max(old_width), height.max(old_height));
|
||||||
damage_full = true;
|
damage_full = true;
|
||||||
|
buffer_abs_pos_size_changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let has_new_frame_requests = pending.frame_request.is_not_empty();
|
let has_new_frame_requests = pending.frame_request.is_not_empty();
|
||||||
|
|
@ -1304,15 +1308,25 @@ impl WlSurface {
|
||||||
mem::swap(fbs.deref_mut(), &mut pending.presentation_feedback);
|
mem::swap(fbs.deref_mut(), &mut pending.presentation_feedback);
|
||||||
fbs.is_not_empty()
|
fbs.is_not_empty()
|
||||||
};
|
};
|
||||||
|
let mut opaque_region_changed = false;
|
||||||
{
|
{
|
||||||
if let Some(region) = pending.input_region.take() {
|
if let Some(region) = pending.input_region.take() {
|
||||||
self.input_region.set(region);
|
self.input_region.set(region);
|
||||||
self.client.state.tree_changed();
|
self.client.state.tree_changed();
|
||||||
}
|
}
|
||||||
if let Some(region) = pending.opaque_region.take() {
|
if let Some(region) = pending.opaque_region.take() {
|
||||||
|
opaque_region_changed = true;
|
||||||
self.opaque_region.set(region);
|
self.opaque_region.set(region);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if opaque_region_changed || buffer_abs_pos_size_changed {
|
||||||
|
let pos = self.buffer_abs_pos.get().at_point(0, 0);
|
||||||
|
let is_opaque = match self.opaque_region.get() {
|
||||||
|
None => false,
|
||||||
|
Some(o) => o.contains_rect(&pos),
|
||||||
|
};
|
||||||
|
self.is_opaque.set(is_opaque);
|
||||||
|
}
|
||||||
let mut tearing_changed = false;
|
let mut tearing_changed = false;
|
||||||
if let Some(tearing) = pending.tearing.take() {
|
if let Some(tearing) = pending.tearing.take() {
|
||||||
if self.tearing.replace(tearing) != tearing {
|
if self.tearing.replace(tearing) != tearing {
|
||||||
|
|
@ -1636,6 +1650,14 @@ impl WlSurface {
|
||||||
pub fn alpha(&self) -> Option<f32> {
|
pub fn alpha(&self) -> Option<f32> {
|
||||||
self.alpha.get()
|
self.alpha.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn opaque(&self) -> bool {
|
||||||
|
self.is_opaque.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn opaque_region(&self) -> Option<Rc<Region>> {
|
||||||
|
self.opaque_region.get()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@ use {
|
||||||
format::{ARGB8888, Format, XRGB8888},
|
format::{ARGB8888, Format, XRGB8888},
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, CopyTexture, FillRect,
|
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, CopyTexture, FillRect,
|
||||||
FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage,
|
FramebufferRect, GfxApiOpt, GfxBlendBuffer, GfxContext, GfxError, GfxFormat,
|
||||||
GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, GfxWriteModifier,
|
GfxFramebuffer, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
|
||||||
PendingShmTransfer, ReleaseSync, ResetStatus, ShmGfxTexture, ShmMemory, SyncFile,
|
GfxWriteModifier, PendingShmTransfer, ReleaseSync, ResetStatus, ShmGfxTexture,
|
||||||
|
ShmMemory, SyncFile,
|
||||||
},
|
},
|
||||||
rect::{Rect, Region},
|
rect::{Rect, Region},
|
||||||
theme::Color,
|
theme::Color,
|
||||||
|
|
@ -37,6 +38,8 @@ enum TestGfxError {
|
||||||
ImportDmaBuf(#[source] AllocatorError),
|
ImportDmaBuf(#[source] AllocatorError),
|
||||||
#[error("Could not access the client memory")]
|
#[error("Could not access the client memory")]
|
||||||
AccessFailed(#[source] Box<dyn Error + Sync + Send>),
|
AccessFailed(#[source] Box<dyn Error + Sync + Send>),
|
||||||
|
#[error("Text API does not support blend buffers")]
|
||||||
|
NoBlendBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TestGfxError> for GfxError {
|
impl From<TestGfxError> for GfxError {
|
||||||
|
|
@ -189,6 +192,14 @@ impl GfxContext for TestGfxCtx {
|
||||||
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn acquire_blend_buffer(
|
||||||
|
&self,
|
||||||
|
_width: i32,
|
||||||
|
_height: i32,
|
||||||
|
) -> Result<Rc<dyn GfxBlendBuffer>, GfxError> {
|
||||||
|
Err(GfxError(Box::new(TestGfxError::NoBlendBuffer)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TestGfxImage {
|
enum TestGfxImage {
|
||||||
|
|
@ -383,6 +394,7 @@ impl GfxFramebuffer for TestGfxFb {
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
_region: &Region,
|
_region: &Region,
|
||||||
|
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
||||||
let points = rect.to_points();
|
let points = rect.to_points();
|
||||||
|
|
@ -441,11 +453,12 @@ impl GfxFramebuffer for TestGfxFb {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let fill_rect = |f: &FillRect, staging: &mut [Color]| {
|
let fill_rect = |f: &FillRect, staging: &mut [Color]| {
|
||||||
|
let color = f.effective_color();
|
||||||
let (x1, y1, x2, y2) = fb_points(width, height, &f.rect);
|
let (x1, y1, x2, y2) = fb_points(width, height, &f.rect);
|
||||||
for y in y1..y2 {
|
for y in y1..y2 {
|
||||||
for x in x1..x2 {
|
for x in x1..x2 {
|
||||||
let dst = &mut staging[(y * width + x) as usize];
|
let dst = &mut staging[(y * width + x) as usize];
|
||||||
*dst = dst.and_then(&f.color);
|
*dst = dst.and_then(&color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,7 @@ impl GuiElement for Button {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -325,6 +326,7 @@ impl GuiElement for Label {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -634,6 +636,7 @@ impl WindowData {
|
||||||
ReleaseSync::Implicit,
|
ReleaseSync::Implicit,
|
||||||
self.scale.get(),
|
self.scale.get(),
|
||||||
Some(&Color::from_gray(0)),
|
Some(&Color::from_gray(0)),
|
||||||
|
None,
|
||||||
&mut |r| {
|
&mut |r| {
|
||||||
if let Some(content) = self.content.get() {
|
if let Some(content) = self.content.get() {
|
||||||
content.render_at(r, 0.0, 0.0)
|
content.render_at(r, 0.0, 0.0)
|
||||||
|
|
|
||||||
134
src/rect.rs
134
src/rect.rs
|
|
@ -5,20 +5,26 @@ mod tests;
|
||||||
|
|
||||||
pub use region::{DamageQueue, RegionBuilder};
|
pub use region::{DamageQueue, RegionBuilder};
|
||||||
use {
|
use {
|
||||||
jay_algorithms::rect::RectRaw,
|
jay_algorithms::rect::{NoTag, RectRaw, Tag},
|
||||||
smallvec::SmallVec,
|
smallvec::SmallVec,
|
||||||
std::fmt::{Debug, Formatter},
|
std::fmt::{Debug, Formatter},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Rect {
|
pub struct Rect<T = NoTag>
|
||||||
raw: RectRaw,
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
raw: RectRaw<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug, Default)]
|
#[derive(Clone, Eq, PartialEq, Debug, Default)]
|
||||||
pub struct Region {
|
pub struct Region<T = NoTag>
|
||||||
rects: SmallVec<[RectRaw; 1]>,
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
rects: SmallVec<[RectRaw<T>; 1]>,
|
||||||
extents: Rect,
|
extents: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,6 +56,23 @@ impl RectOverflow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Rect<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
pub fn untag(&self) -> Rect {
|
||||||
|
Rect {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1: self.raw.x1,
|
||||||
|
y1: self.raw.y1,
|
||||||
|
x2: self.raw.x2,
|
||||||
|
y2: self.raw.y2,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
pub fn new_empty(x: i32, y: i32) -> Self {
|
pub fn new_empty(x: i32, y: i32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -58,6 +81,7 @@ impl Rect {
|
||||||
y1: y,
|
y1: y,
|
||||||
x2: x,
|
x2: x,
|
||||||
y2: y,
|
y2: y,
|
||||||
|
tag: NoTag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +91,13 @@ impl Rect {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(Self {
|
Some(Self {
|
||||||
raw: RectRaw { x1, y1, x2, y2 },
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,10 +106,16 @@ impl Rect {
|
||||||
Self::new(x1, y1, x2, y2).unwrap()
|
Self::new(x1, y1, x2, y2).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
#[cfg_attr(not(test), expect(dead_code))]
|
||||||
fn new_unchecked_danger(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
|
fn new_unchecked_danger(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
raw: RectRaw { x1, y1, x2, y2 },
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,6 +138,57 @@ impl Rect {
|
||||||
y1: self.raw.y1.min(other.raw.y1),
|
y1: self.raw.y1.min(other.raw.y1),
|
||||||
x2: self.raw.x2.max(other.raw.x2),
|
x2: self.raw.x2.max(other.raw.x2),
|
||||||
y2: self.raw.y2.max(other.raw.y2),
|
y2: self.raw.y2.max(other.raw.y2),
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect(&self, other: Self) -> Self {
|
||||||
|
let x1 = self.raw.x1.max(other.raw.x1);
|
||||||
|
let y1 = self.raw.y1.max(other.raw.y1);
|
||||||
|
let x2 = self.raw.x2.min(other.raw.x2).max(x1);
|
||||||
|
let y2 = self.raw.y2.min(other.raw.y2).max(y1);
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag: NoTag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_size(&self, width: i32, height: i32) -> Option<Self> {
|
||||||
|
Self::new_sized(self.raw.x1, self.raw.y1, width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_tag(&self, tag: u32) -> Rect<u32> {
|
||||||
|
Rect {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1: self.raw.x1,
|
||||||
|
y1: self.raw.y1,
|
||||||
|
x2: self.raw.x2,
|
||||||
|
y2: self.raw.y2,
|
||||||
|
tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Rect<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
#[cfg_attr(not(test), expect(dead_code))]
|
||||||
|
fn new_unchecked_danger_tagged(x1: i32, y1: i32, x2: i32, y2: i32, tag: T) -> Self {
|
||||||
|
Self {
|
||||||
|
raw: RectRaw {
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -113,16 +200,6 @@ impl Rect {
|
||||||
&& other.raw.y1 < self.raw.y2
|
&& other.raw.y1 < self.raw.y2
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn intersect(&self, other: Self) -> Self {
|
|
||||||
let x1 = self.raw.x1.max(other.raw.x1);
|
|
||||||
let y1 = self.raw.y1.max(other.raw.y1);
|
|
||||||
let x2 = self.raw.x2.min(other.raw.x2).max(x1);
|
|
||||||
let y2 = self.raw.y2.min(other.raw.y2).max(y1);
|
|
||||||
Self {
|
|
||||||
raw: RectRaw { x1, y1, x2, y2 },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(&self, x: i32, y: i32) -> bool {
|
pub fn contains(&self, x: i32, y: i32) -> bool {
|
||||||
self.raw.x1 <= x && self.raw.y1 <= y && self.raw.x2 > x && self.raw.y2 > y
|
self.raw.x1 <= x && self.raw.y1 <= y && self.raw.x2 > x && self.raw.y2 > y
|
||||||
}
|
}
|
||||||
|
|
@ -144,14 +221,20 @@ impl Rect {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
pub fn contains_rect(&self, rect: &Self) -> bool {
|
pub fn contains_rect<U>(&self, rect: &Rect<U>) -> bool
|
||||||
|
where
|
||||||
|
U: Tag,
|
||||||
|
{
|
||||||
self.raw.x1 <= rect.raw.x1
|
self.raw.x1 <= rect.raw.x1
|
||||||
&& self.raw.y1 <= rect.raw.x1
|
&& self.raw.y1 <= rect.raw.x1
|
||||||
&& rect.raw.x2 <= self.raw.x2
|
&& rect.raw.x2 <= self.raw.x2
|
||||||
&& rect.raw.y2 <= self.raw.y2
|
&& rect.raw.y2 <= self.raw.y2
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_overflow(&self, child: &Self) -> RectOverflow {
|
pub fn get_overflow<U>(&self, child: &Rect<U>) -> RectOverflow
|
||||||
|
where
|
||||||
|
U: Tag,
|
||||||
|
{
|
||||||
RectOverflow {
|
RectOverflow {
|
||||||
left: self.raw.x1 - child.raw.x1,
|
left: self.raw.x1 - child.raw.x1,
|
||||||
right: child.raw.x2 - self.raw.x2,
|
right: child.raw.x2 - self.raw.x2,
|
||||||
|
|
@ -177,6 +260,7 @@ impl Rect {
|
||||||
y1: 0,
|
y1: 0,
|
||||||
x2: self.raw.x2 - self.raw.x1,
|
x2: self.raw.x2 - self.raw.x1,
|
||||||
y2: self.raw.y2 - self.raw.y1,
|
y2: self.raw.y2 - self.raw.y1,
|
||||||
|
tag: self.raw.tag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -188,6 +272,7 @@ impl Rect {
|
||||||
y1: self.raw.y1.saturating_add(dy),
|
y1: self.raw.y1.saturating_add(dy),
|
||||||
x2: self.raw.x2.saturating_add(dx),
|
x2: self.raw.x2.saturating_add(dx),
|
||||||
y2: self.raw.y2.saturating_add(dy),
|
y2: self.raw.y2.saturating_add(dy),
|
||||||
|
tag: self.raw.tag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -199,14 +284,11 @@ impl Rect {
|
||||||
y1,
|
y1,
|
||||||
x2: x1 + self.raw.x2 - self.raw.x1,
|
x2: x1 + self.raw.x2 - self.raw.x1,
|
||||||
y2: y1 + self.raw.y2 - self.raw.y1,
|
y2: y1 + self.raw.y2 - self.raw.y1,
|
||||||
|
tag: self.raw.tag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_size(&self, width: i32, height: i32) -> Option<Self> {
|
|
||||||
Self::new_sized(self.raw.x1, self.raw.y1, width, height)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn translate(&self, x: i32, y: i32) -> (i32, i32) {
|
pub fn translate(&self, x: i32, y: i32) -> (i32, i32) {
|
||||||
(x.wrapping_sub(self.raw.x1), y.wrapping_sub(self.raw.y1))
|
(x.wrapping_sub(self.raw.x1), y.wrapping_sub(self.raw.y1))
|
||||||
}
|
}
|
||||||
|
|
@ -253,4 +335,8 @@ impl Rect {
|
||||||
self.raw.y1 + self.height() / 2,
|
self.raw.y1 + self.height() / 2,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tag(&self) -> T {
|
||||||
|
self.raw.tag
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,15 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jay_algorithms::rect::{
|
jay_algorithms::rect::{
|
||||||
RectRaw,
|
RectRaw, Tag,
|
||||||
region::{extents, rects_to_bands, subtract, union},
|
region::{
|
||||||
|
extents, intersect, intersect_tagged, rects_to_bands, rects_to_bands_tagged, subtract,
|
||||||
|
union,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
smallvec::SmallVec,
|
smallvec::SmallVec,
|
||||||
std::{
|
std::{
|
||||||
|
borrow::Cow,
|
||||||
cell::UnsafeCell,
|
cell::UnsafeCell,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
mem,
|
mem,
|
||||||
|
|
@ -29,19 +33,6 @@ thread_local! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Region {
|
impl Region {
|
||||||
pub fn new(rect: Rect) -> Rc<Self> {
|
|
||||||
Rc::new(Self::new2(rect))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new2(rect: Rect) -> Self {
|
|
||||||
let mut rects = SmallVec::new();
|
|
||||||
rects.push(rect.raw);
|
|
||||||
Self {
|
|
||||||
rects,
|
|
||||||
extents: rect,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty() -> Rc<Self> {
|
pub fn empty() -> Rc<Self> {
|
||||||
EMPTY.with(|e| e.clone())
|
EMPTY.with(|e| e.clone())
|
||||||
}
|
}
|
||||||
|
|
@ -96,13 +87,92 @@ impl Region {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn subtract_cow<'a>(&'a self, other: &Self) -> Cow<'a, Self> {
|
||||||
|
if self.extents.is_empty() || other.extents.is_empty() {
|
||||||
|
return Cow::Borrowed(self);
|
||||||
|
}
|
||||||
|
let rects = subtract(&self.rects, &other.rects);
|
||||||
|
Cow::Owned(Self {
|
||||||
|
extents: Rect {
|
||||||
|
raw: extents(&rects),
|
||||||
|
},
|
||||||
|
rects,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect(&self, other: &Region) -> Self {
|
||||||
|
if self.is_empty() || other.is_empty() {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
let rects = intersect(&self.rects, &other.rects);
|
||||||
|
Self {
|
||||||
|
extents: Rect {
|
||||||
|
raw: extents(&rects),
|
||||||
|
},
|
||||||
|
rects,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Region<u32> {
|
||||||
|
pub fn from_rects_tagged(rects: &[Rect<u32>]) -> Self {
|
||||||
|
if rects.is_empty() {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
if rects.len() == 1 {
|
||||||
|
let mut rect = rects[0];
|
||||||
|
rect.raw.tag = rect.raw.tag.constrain();
|
||||||
|
return Self::new2(rect);
|
||||||
|
}
|
||||||
|
let rects = rects_to_bands_tagged(unsafe {
|
||||||
|
mem::transmute::<&[Rect<u32>], &[RectRaw<u32>]>(rects)
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
extents: Rect {
|
||||||
|
raw: extents(&rects),
|
||||||
|
},
|
||||||
|
rects,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect_tagged(&self, other: &Region) -> Self {
|
||||||
|
if self.is_empty() || other.is_empty() {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
let rects = intersect_tagged(&self.rects, &other.rects);
|
||||||
|
Self {
|
||||||
|
extents: Rect {
|
||||||
|
raw: extents(&rects),
|
||||||
|
},
|
||||||
|
rects,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Region<T>
|
||||||
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
pub fn new(rect: Rect<T>) -> Rc<Self> {
|
||||||
|
Rc::new(Self::new2(rect))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new2(rect: Rect<T>) -> Self {
|
||||||
|
let mut rects = SmallVec::new();
|
||||||
|
rects.push(rect.raw);
|
||||||
|
Self {
|
||||||
|
rects,
|
||||||
|
extents: rect.untag(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "it"), expect(dead_code))]
|
#[cfg_attr(not(feature = "it"), expect(dead_code))]
|
||||||
pub fn extents(&self) -> Rect {
|
pub fn extents(&self) -> Rect {
|
||||||
self.extents
|
self.extents
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rects(&self) -> &[Rect] {
|
pub fn rects(&self) -> &[Rect<T>] {
|
||||||
unsafe { mem::transmute::<&[RectRaw], &[Rect]>(&self.rects[..]) }
|
unsafe { mem::transmute::<&[RectRaw<T>], &[Rect<T>]>(&self.rects[..]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, x: i32, y: i32) -> bool {
|
pub fn contains(&self, x: i32, y: i32) -> bool {
|
||||||
|
|
@ -116,13 +186,41 @@ impl Region {
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains_rect(&self, rect: &Rect) -> bool {
|
||||||
|
self.contains_rect2(rect, |r| *r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_rect2(&self, rect: &Rect, map: impl Fn(&Rect<T>) -> Rect<T>) -> bool {
|
||||||
|
if rect.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let mut y1 = rect.y1();
|
||||||
|
for r in self.rects() {
|
||||||
|
let r = map(r);
|
||||||
|
if r.y2() <= y1 || r.x2() <= rect.x1() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if r.y1() > y1 || r.x1() > rect.x1() || r.x2() < rect.x2() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
y1 = r.y2();
|
||||||
|
if y1 >= rect.y2() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Region {
|
impl<T> Deref for Region<T>
|
||||||
type Target = [Rect];
|
where
|
||||||
|
T: Tag,
|
||||||
|
{
|
||||||
|
type Target = [Rect<T>];
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
unsafe { mem::transmute::<&[RectRaw], _>(&self.rects) }
|
unsafe { mem::transmute::<&[RectRaw<T>], _>(&self.rects) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::rect::{Rect, Region},
|
crate::rect::{Rect, Region},
|
||||||
jay_algorithms::rect::RectRaw,
|
jay_algorithms::rect::{NoTag, RectRaw},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -43,25 +43,29 @@ fn subtract1() {
|
||||||
x1: 0,
|
x1: 0,
|
||||||
y1: 0,
|
y1: 0,
|
||||||
x2: 20,
|
x2: 20,
|
||||||
y2: 5
|
y2: 5,
|
||||||
|
tag: NoTag,
|
||||||
},
|
},
|
||||||
RectRaw {
|
RectRaw {
|
||||||
x1: 0,
|
x1: 0,
|
||||||
y1: 5,
|
y1: 5,
|
||||||
x2: 5,
|
x2: 5,
|
||||||
y2: 15
|
y2: 15,
|
||||||
|
tag: NoTag,
|
||||||
},
|
},
|
||||||
RectRaw {
|
RectRaw {
|
||||||
x1: 15,
|
x1: 15,
|
||||||
y1: 5,
|
y1: 5,
|
||||||
x2: 20,
|
x2: 20,
|
||||||
y2: 15
|
y2: 15,
|
||||||
|
tag: NoTag,
|
||||||
},
|
},
|
||||||
RectRaw {
|
RectRaw {
|
||||||
x1: 0,
|
x1: 0,
|
||||||
y1: 15,
|
y1: 15,
|
||||||
x2: 20,
|
x2: 20,
|
||||||
y2: 20
|
y2: 20,
|
||||||
|
tag: NoTag,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
@ -83,19 +87,22 @@ fn rects_to_bands() {
|
||||||
x1: 0,
|
x1: 0,
|
||||||
y1: 0,
|
y1: 0,
|
||||||
x2: 30,
|
x2: 30,
|
||||||
y2: 5
|
y2: 5,
|
||||||
|
tag: NoTag,
|
||||||
},
|
},
|
||||||
RectRaw {
|
RectRaw {
|
||||||
x1: 0,
|
x1: 0,
|
||||||
y1: 5,
|
y1: 5,
|
||||||
x2: 50,
|
x2: 50,
|
||||||
y2: 10
|
y2: 10,
|
||||||
|
tag: NoTag,
|
||||||
},
|
},
|
||||||
RectRaw {
|
RectRaw {
|
||||||
x1: 30,
|
x1: 30,
|
||||||
y1: 10,
|
y1: 10,
|
||||||
x2: 50,
|
x2: 50,
|
||||||
y2: 15
|
y2: 15,
|
||||||
|
tag: NoTag,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
@ -111,3 +118,572 @@ fn rects_to_bands2() {
|
||||||
// println!("{:#?}", r.rects);
|
// println!("{:#?}", r.rects);
|
||||||
assert_eq!(&r.rects[..], &[Rect::new(0, 0, 10, 20).unwrap().raw,]);
|
assert_eq!(&r.rects[..], &[Rect::new(0, 0, 10, 20).unwrap().raw,]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged1() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 200, 200, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(50, 50, 150, 150, 0),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 200,
|
||||||
|
y2: 50,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 50,
|
||||||
|
x2: 50,
|
||||||
|
y2: 150,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 50,
|
||||||
|
y1: 50,
|
||||||
|
x2: 150,
|
||||||
|
y2: 150,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 150,
|
||||||
|
y1: 50,
|
||||||
|
x2: 200,
|
||||||
|
y2: 150,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 150,
|
||||||
|
x2: 200,
|
||||||
|
y2: 200,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged2() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 200, 200, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(50, 50, 150, 150, 0),
|
||||||
|
Rect::new_unchecked_danger_tagged(60, 60, 140, 140, 2),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 200,
|
||||||
|
y2: 50,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 50,
|
||||||
|
x2: 50,
|
||||||
|
y2: 150,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 50,
|
||||||
|
y1: 50,
|
||||||
|
x2: 150,
|
||||||
|
y2: 150,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 150,
|
||||||
|
y1: 50,
|
||||||
|
x2: 200,
|
||||||
|
y2: 150,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 150,
|
||||||
|
x2: 200,
|
||||||
|
y2: 200,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged3() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 200, 200, 2),
|
||||||
|
Rect::new_unchecked_danger_tagged(50, 50, 150, 150, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(60, 60, 140, 140, 0),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 200,
|
||||||
|
y2: 50,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 50,
|
||||||
|
x2: 50,
|
||||||
|
y2: 60,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 50,
|
||||||
|
y1: 50,
|
||||||
|
x2: 150,
|
||||||
|
y2: 60,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 150,
|
||||||
|
y1: 50,
|
||||||
|
x2: 200,
|
||||||
|
y2: 60,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 60,
|
||||||
|
x2: 50,
|
||||||
|
y2: 140,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 50,
|
||||||
|
y1: 60,
|
||||||
|
x2: 60,
|
||||||
|
y2: 140,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 60,
|
||||||
|
y1: 60,
|
||||||
|
x2: 140,
|
||||||
|
y2: 140,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 140,
|
||||||
|
y1: 60,
|
||||||
|
x2: 150,
|
||||||
|
y2: 140,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 150,
|
||||||
|
y1: 60,
|
||||||
|
x2: 200,
|
||||||
|
y2: 140,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 140,
|
||||||
|
x2: 50,
|
||||||
|
y2: 150,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 50,
|
||||||
|
y1: 140,
|
||||||
|
x2: 150,
|
||||||
|
y2: 150,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 150,
|
||||||
|
y1: 140,
|
||||||
|
x2: 200,
|
||||||
|
y2: 150,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 150,
|
||||||
|
x2: 200,
|
||||||
|
y2: 200,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged4() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(100, 0, 200, 200, 0),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 100,
|
||||||
|
y1: 0,
|
||||||
|
x2: 200,
|
||||||
|
y2: 100,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 100,
|
||||||
|
y1: 100,
|
||||||
|
x2: 200,
|
||||||
|
y2: 200,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged5() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 200, 100, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(100, 0, 200, 100, 0),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 100,
|
||||||
|
y1: 0,
|
||||||
|
x2: 200,
|
||||||
|
y2: 100,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged6() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 200, 100, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(100, 0, 300, 100, 0),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 100,
|
||||||
|
y1: 0,
|
||||||
|
x2: 300,
|
||||||
|
y2: 100,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged7() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 200, 100, 0),
|
||||||
|
Rect::new_unchecked_danger_tagged(100, 0, 300, 200, 1),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 200,
|
||||||
|
y2: 100,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 200,
|
||||||
|
y1: 0,
|
||||||
|
x2: 300,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 100,
|
||||||
|
y1: 100,
|
||||||
|
x2: 300,
|
||||||
|
y2: 200,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged8() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(100, 0, 200, 100, 0),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 100,
|
||||||
|
y1: 0,
|
||||||
|
x2: 200,
|
||||||
|
y2: 100,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged9() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(100, 0, 200, 100, 1),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 200,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged10() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 100, 100, 200, 1),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 200,
|
||||||
|
tag: 1,
|
||||||
|
},],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged11() {
|
||||||
|
let rects = [Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 11)];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged12() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 11),
|
||||||
|
Rect::new_unchecked_danger_tagged(200, 0, 300, 100, 10),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 200,
|
||||||
|
y1: 0,
|
||||||
|
x2: 300,
|
||||||
|
y2: 100,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged13() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 100, 100, 200, 0),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 100,
|
||||||
|
tag: 1,
|
||||||
|
},
|
||||||
|
RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 100,
|
||||||
|
x2: 100,
|
||||||
|
y2: 200,
|
||||||
|
tag: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rects_to_bands_tagged14() {
|
||||||
|
let rects = [
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 1),
|
||||||
|
Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 0),
|
||||||
|
];
|
||||||
|
let r = Region::from_rects_tagged(&rects[..]);
|
||||||
|
assert_eq!(
|
||||||
|
&r.rects[..],
|
||||||
|
&[RectRaw {
|
||||||
|
x1: 0,
|
||||||
|
y1: 0,
|
||||||
|
x2: 100,
|
||||||
|
y2: 100,
|
||||||
|
tag: 0,
|
||||||
|
},],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn intersect1() {
|
||||||
|
let rects = [Rect::new_unchecked_danger_tagged(0, 0, 100, 100, 0)];
|
||||||
|
let r1 = Region::from_rects_tagged(&rects[..]);
|
||||||
|
let rects = [Rect::new_unchecked_danger(100, 100, 200, 200)];
|
||||||
|
let r2 = Region::from_rects2(&rects[..]);
|
||||||
|
let r3 = r1.intersect_tagged(&r2);
|
||||||
|
assert_eq!(&r3.rects[..], &[],);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn intersect2() {
|
||||||
|
let rects = [Rect::new_unchecked_danger_tagged(0, 0, 200, 200, 0)];
|
||||||
|
let r1 = Region::from_rects_tagged(&rects[..]);
|
||||||
|
let rects = [Rect::new_unchecked_danger(50, 50, 150, 150)];
|
||||||
|
let r2 = Region::from_rects2(&rects[..]);
|
||||||
|
let r3 = r1.intersect_tagged(&r2);
|
||||||
|
assert_eq!(
|
||||||
|
&r3.rects[..],
|
||||||
|
&[RectRaw {
|
||||||
|
x1: 50,
|
||||||
|
y1: 50,
|
||||||
|
x2: 150,
|
||||||
|
y2: 150,
|
||||||
|
tag: 0,
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn intersect3() {
|
||||||
|
macro_rules! t {
|
||||||
|
($l:expr, $r:expr, $t:expr) => {
|
||||||
|
Rect::new_unchecked_danger_tagged($l, 0, $r, 1, $t)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! u {
|
||||||
|
($l:expr, $r:expr) => {
|
||||||
|
Rect::new_unchecked_danger($l, 0, $r, 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! r {
|
||||||
|
($l:expr, $r:expr, $t:expr) => {
|
||||||
|
RectRaw {
|
||||||
|
x1: $l,
|
||||||
|
y1: 0,
|
||||||
|
x2: $r,
|
||||||
|
y2: 1,
|
||||||
|
tag: $t,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let rects = [
|
||||||
|
t!(0, 100, 0),
|
||||||
|
t!(110, 130, 1),
|
||||||
|
t!(140, 160, 2),
|
||||||
|
t!(170, 180, 0),
|
||||||
|
];
|
||||||
|
let r1 = Region::from_rects_tagged(&rects[..]);
|
||||||
|
let rects = [
|
||||||
|
u!(10, 20),
|
||||||
|
u!(50, 60),
|
||||||
|
u!(70, 100),
|
||||||
|
u!(120, 150),
|
||||||
|
u!(170, 180),
|
||||||
|
];
|
||||||
|
let r2 = Region::from_rects2(&rects[..]);
|
||||||
|
let r3 = r1.intersect_tagged(&r2);
|
||||||
|
assert_eq!(
|
||||||
|
&r3.rects[..],
|
||||||
|
&[
|
||||||
|
r!(10, 20, 0),
|
||||||
|
r!(50, 60, 0),
|
||||||
|
r!(70, 100, 0),
|
||||||
|
r!(120, 130, 1),
|
||||||
|
r!(140, 150, 0),
|
||||||
|
r!(170, 180, 0),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ impl Renderer<'_> {
|
||||||
let bar_bg = self.base.scale_rect(bar_bg);
|
let bar_bg = self.base.scale_rect(bar_bg);
|
||||||
let c = theme.colors.bar_background.get();
|
let c = theme.colors.bar_background.get();
|
||||||
self.base
|
self.base
|
||||||
.fill_boxes3(slice::from_ref(&bar_bg), &c, x, y, true);
|
.fill_boxes3(slice::from_ref(&bar_bg), &c, None, x, y, true);
|
||||||
let rd = output.render_data.borrow_mut();
|
let rd = output.render_data.borrow_mut();
|
||||||
if let Some(aw) = &rd.active_workspace {
|
if let Some(aw) = &rd.active_workspace {
|
||||||
let c = match aw.captured {
|
let c = match aw.captured {
|
||||||
|
|
@ -124,6 +124,7 @@ impl Renderer<'_> {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(status) = &rd.status {
|
if let Some(status) = &rd.status {
|
||||||
|
|
@ -141,6 +142,7 @@ impl Renderer<'_> {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -219,6 +221,7 @@ impl Renderer<'_> {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,6 +267,7 @@ impl Renderer<'_> {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +340,8 @@ impl Renderer<'_> {
|
||||||
};
|
};
|
||||||
let color = self.state.theme.colors.highlight.get();
|
let color = self.state.theme.colors.highlight.get();
|
||||||
self.base.ops.push(GfxApiOpt::Sync);
|
self.base.ops.push(GfxApiOpt::Sync);
|
||||||
self.base.fill_scaled_boxes(slice::from_ref(bounds), &color);
|
self.base
|
||||||
|
.fill_scaled_boxes(slice::from_ref(bounds), &color, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_highlight(&mut self, rect: &Rect) {
|
pub fn render_highlight(&mut self, rect: &Rect) {
|
||||||
|
|
@ -378,7 +383,6 @@ impl Renderer<'_> {
|
||||||
} else {
|
} else {
|
||||||
size = self.base.scale_point(size.0, size.1);
|
size = self.base.scale_point(size.0, size.1);
|
||||||
}
|
}
|
||||||
let alpha = surface.alpha();
|
|
||||||
if let Some(children) = children.deref() {
|
if let Some(children) = children.deref() {
|
||||||
macro_rules! render {
|
macro_rules! render {
|
||||||
($children:expr) => {
|
($children:expr) => {
|
||||||
|
|
@ -400,10 +404,10 @@ impl Renderer<'_> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
render!(&children.below);
|
render!(&children.below);
|
||||||
self.render_buffer(surface, &buffer, alpha, x, y, *tpoints, size, bounds);
|
self.render_buffer(surface, &buffer, x, y, *tpoints, size, bounds);
|
||||||
render!(&children.above);
|
render!(&children.above);
|
||||||
} else {
|
} else {
|
||||||
self.render_buffer(surface, &buffer, alpha, x, y, *tpoints, size, bounds);
|
self.render_buffer(surface, &buffer, x, y, *tpoints, size, bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -411,14 +415,18 @@ impl Renderer<'_> {
|
||||||
&mut self,
|
&mut self,
|
||||||
surface: &WlSurface,
|
surface: &WlSurface,
|
||||||
buffer: &Rc<SurfaceBuffer>,
|
buffer: &Rc<SurfaceBuffer>,
|
||||||
alpha: Option<f32>,
|
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
tpoints: SampleRect,
|
tpoints: SampleRect,
|
||||||
tsize: (i32, i32),
|
tsize: (i32, i32),
|
||||||
bounds: Option<&Rect>,
|
bounds: Option<&Rect>,
|
||||||
) {
|
) {
|
||||||
|
let alpha = surface.alpha();
|
||||||
if let Some(tex) = buffer.buffer.get_texture(surface) {
|
if let Some(tex) = buffer.buffer.get_texture(surface) {
|
||||||
|
let mut opaque = surface.opaque();
|
||||||
|
if !opaque && tex.format().has_alpha {
|
||||||
|
opaque = self.bounds_are_opaque(x, y, bounds, surface);
|
||||||
|
}
|
||||||
self.base.render_texture(
|
self.base.render_texture(
|
||||||
&tex,
|
&tex,
|
||||||
alpha,
|
alpha,
|
||||||
|
|
@ -431,6 +439,7 @@ impl Renderer<'_> {
|
||||||
Some(buffer.clone()),
|
Some(buffer.clone()),
|
||||||
AcquireSync::Unnecessary,
|
AcquireSync::Unnecessary,
|
||||||
buffer.release_sync,
|
buffer.release_sync,
|
||||||
|
opaque,
|
||||||
);
|
);
|
||||||
} else if let Some(color) = &buffer.buffer.color {
|
} else if let Some(color) = &buffer.buffer.color {
|
||||||
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
||||||
|
|
@ -440,11 +449,7 @@ impl Renderer<'_> {
|
||||||
};
|
};
|
||||||
if !rect.is_empty() {
|
if !rect.is_empty() {
|
||||||
self.base.ops.push(GfxApiOpt::Sync);
|
self.base.ops.push(GfxApiOpt::Sync);
|
||||||
let mut color = *color;
|
self.base.fill_scaled_boxes(&[rect], color, alpha);
|
||||||
if let Some(alpha) = alpha {
|
|
||||||
color = color * alpha;
|
|
||||||
}
|
|
||||||
self.base.fill_scaled_boxes(&[rect], &color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -499,6 +504,7 @@ impl Renderer<'_> {
|
||||||
None,
|
None,
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -517,4 +523,23 @@ impl Renderer<'_> {
|
||||||
let (dx, dy) = surface.surface.extents.get().position();
|
let (dx, dy) = surface.surface.extents.get().position();
|
||||||
self.render_surface(&surface.surface, x - dx, y - dy, None);
|
self.render_surface(&surface.surface, x - dx, y - dy, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bounds_are_opaque(
|
||||||
|
&self,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
bounds: Option<&Rect>,
|
||||||
|
surface: &WlSurface,
|
||||||
|
) -> bool {
|
||||||
|
let Some(bounds) = bounds else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(region) = surface.opaque_region() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let surface_size = surface.buffer_abs_pos.get().at_point(0, 0);
|
||||||
|
let surface_size = self.base.scale_rect(surface_size);
|
||||||
|
let bounds = bounds.move_(-x, -y).intersect(surface_size);
|
||||||
|
region.contains_rect2(&bounds, |r| self.base.scale_rect(*r))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,19 +64,27 @@ impl RendererBase<'_> {
|
||||||
rect
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_scaled_boxes(&mut self, boxes: &[Rect], color: &Color) {
|
pub fn fill_scaled_boxes(&mut self, boxes: &[Rect], color: &Color, alpha: Option<f32>) {
|
||||||
self.fill_boxes3(boxes, color, 0, 0, true);
|
self.fill_boxes3(boxes, color, alpha, 0, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color) {
|
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color) {
|
||||||
self.fill_boxes3(boxes, color, 0, 0, false);
|
self.fill_boxes3(boxes, color, None, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_boxes2(&mut self, boxes: &[Rect], color: &Color, dx: i32, dy: i32) {
|
pub fn fill_boxes2(&mut self, boxes: &[Rect], color: &Color, dx: i32, dy: i32) {
|
||||||
self.fill_boxes3(boxes, color, dx, dy, false);
|
self.fill_boxes3(boxes, color, None, dx, dy, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_boxes3(&mut self, boxes: &[Rect], color: &Color, dx: i32, dy: i32, scaled: bool) {
|
pub fn fill_boxes3(
|
||||||
|
&mut self,
|
||||||
|
boxes: &[Rect],
|
||||||
|
color: &Color,
|
||||||
|
alpha: Option<f32>,
|
||||||
|
dx: i32,
|
||||||
|
dy: i32,
|
||||||
|
scaled: bool,
|
||||||
|
) {
|
||||||
if boxes.is_empty() || *color == Color::TRANSPARENT {
|
if boxes.is_empty() || *color == Color::TRANSPARENT {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +105,7 @@ impl RendererBase<'_> {
|
||||||
self.fb_height,
|
self.fb_height,
|
||||||
),
|
),
|
||||||
color: *color,
|
color: *color,
|
||||||
|
alpha,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +138,7 @@ impl RendererBase<'_> {
|
||||||
self.fb_height,
|
self.fb_height,
|
||||||
),
|
),
|
||||||
color: *color,
|
color: *color,
|
||||||
|
alpha: None,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -146,6 +156,7 @@ impl RendererBase<'_> {
|
||||||
buffer_resv: Option<Rc<dyn BufferResv>>,
|
buffer_resv: Option<Rc<dyn BufferResv>>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
opaque: bool,
|
||||||
) {
|
) {
|
||||||
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
||||||
|
|
||||||
|
|
@ -188,6 +199,7 @@ impl RendererBase<'_> {
|
||||||
buffer_resv,
|
buffer_resv,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
opaque,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ pub fn take_screenshot(
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
|
None,
|
||||||
)?;
|
)?;
|
||||||
let drm = match allocator.drm() {
|
let drm = match allocator.drm() {
|
||||||
Some(drm) => Some(drm.dup_render()?.fd().clone()),
|
Some(drm) => Some(drm.dup_render()?.fd().clone()),
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ use {
|
||||||
forker::ForkerProxy,
|
forker::ForkerProxy,
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture,
|
AcquireSync, BufferResv, GfxBlendBuffer, GfxContext, GfxError, GfxFramebuffer,
|
||||||
PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD, SampleRect, SyncFile,
|
GfxTexture, PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD, SampleRect, SyncFile,
|
||||||
},
|
},
|
||||||
gfx_apis::create_gfx_context,
|
gfx_apis::create_gfx_context,
|
||||||
globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal},
|
globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal},
|
||||||
|
|
@ -979,6 +979,7 @@ impl State {
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
tex: &Rc<dyn GfxTexture>,
|
tex: &Rc<dyn GfxTexture>,
|
||||||
render_hw_cursor: bool,
|
render_hw_cursor: bool,
|
||||||
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let sync_file = fb.render_output(
|
let sync_file = fb.render_output(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
|
|
@ -989,6 +990,7 @@ impl State {
|
||||||
output.global.persistent.scale.get(),
|
output.global.persistent.scale.get(),
|
||||||
render_hw_cursor,
|
render_hw_cursor,
|
||||||
true,
|
true,
|
||||||
|
blend_buffer,
|
||||||
)?;
|
)?;
|
||||||
output.latched(false);
|
output.latched(false);
|
||||||
output.perform_screencopies(
|
output.perform_screencopies(
|
||||||
|
|
@ -1046,6 +1048,7 @@ impl State {
|
||||||
resv.cloned(),
|
resv.cloned(),
|
||||||
acquire_sync.clone(),
|
acquire_sync.clone(),
|
||||||
release_sync,
|
release_sync,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
if render_hardware_cursors {
|
if render_hardware_cursors {
|
||||||
if let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() {
|
if let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() {
|
||||||
|
|
@ -1064,6 +1067,7 @@ impl State {
|
||||||
target_release_sync,
|
target_release_sync,
|
||||||
&ops,
|
&ops,
|
||||||
Some(&Color::SOLID_BLACK),
|
Some(&Color::SOLID_BLACK),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
20
src/theme.rs
20
src/theme.rs
|
|
@ -115,24 +115,26 @@ impl Color {
|
||||||
[to_u8(self.r), to_u8(self.g), to_u8(self.b), to_u8(self.a)]
|
[to_u8(self.r), to_u8(self.g), to_u8(self.b), to_u8(self.a)]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_array_srgb(self) -> [f32; 4] {
|
pub fn to_array_srgb(self, alpha: Option<f32>) -> [f32; 4] {
|
||||||
[self.r, self.g, self.b, self.a]
|
let a = alpha.unwrap_or(1.0);
|
||||||
|
[self.r * a, self.g * a, self.b * a, self.a * a]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
pub fn to_array_linear(self, alpha: Option<f32>) -> [f32; 4] {
|
||||||
pub fn to_array_linear(self) -> [f32; 4] {
|
|
||||||
fn to_linear(srgb: f32) -> f32 {
|
fn to_linear(srgb: f32) -> f32 {
|
||||||
if srgb <= 0.04045 {
|
if srgb <= 0.04045 {
|
||||||
srgb / 12.92
|
srgb / 12.92
|
||||||
} else {
|
} else {
|
||||||
(srgb + 0.055 / 1.055).powf(2.4)
|
((srgb + 0.055) / 1.055).powf(2.4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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),
|
to_linear(self.r / a1) * a2,
|
||||||
to_linear(self.g),
|
to_linear(self.g / a1) * a2,
|
||||||
to_linear(self.b),
|
to_linear(self.b / a1) * a2,
|
||||||
self.a,
|
a2,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue