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(
|
||||
clippy::mem_replace_with_default,
|
||||
clippy::comparison_chain,
|
||||
clippy::collapsible_else_if,
|
||||
clippy::needless_lifetimes
|
||||
)]
|
||||
|
||||
|
|
|
|||
|
|
@ -5,31 +5,72 @@ use {
|
|||
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)]
|
||||
pub struct RectRaw {
|
||||
pub struct RectRaw<T = NoTag>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
pub x1: i32,
|
||||
pub y1: i32,
|
||||
pub x2: 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 {
|
||||
f.debug_struct("Rect")
|
||||
let mut debug = f.debug_struct("Rect");
|
||||
debug
|
||||
.field("x1", &self.x1)
|
||||
.field("y1", &self.y1)
|
||||
.field("x2", &self.x2)
|
||||
.field("y2", &self.y2)
|
||||
.field("width", &(self.x2 - self.x1))
|
||||
.field("height", &(self.y2 - self.y1))
|
||||
.finish()
|
||||
.field("height", &(self.y2 - self.y1));
|
||||
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 {
|
||||
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 {
|
||||
crate::{
|
||||
rect::{Container, RectRaw},
|
||||
rect::{Container, NoTag, RectRaw, Tag},
|
||||
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 {
|
||||
op::<Union>(left, right)
|
||||
op::<_, _, _, Union>(left, right)
|
||||
}
|
||||
|
||||
pub fn subtract(left: &Container, right: &Container) -> Container {
|
||||
op::<Subtract>(left, right)
|
||||
op::<_, _, _, Subtract>(left, right)
|
||||
}
|
||||
|
||||
struct Bands<'a> {
|
||||
rects: &'a [RectRaw],
|
||||
pub fn intersect(left: &Container, right: &Container) -> Container {
|
||||
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)]
|
||||
struct Band<'a> {
|
||||
rects: &'a [RectRaw],
|
||||
struct Band<'a, T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
rects: &'a [RectRaw<T>],
|
||||
y1: i32,
|
||||
y2: i32,
|
||||
}
|
||||
|
||||
impl<'a> Band<'a> {
|
||||
fn can_merge_with(&self, next: &Band) -> bool {
|
||||
impl<'a, T> Band<'a, T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
fn can_merge_with(&self, next: &Band<'_, T>) -> bool {
|
||||
next.rects.len() == self.rects.len()
|
||||
&& next.y1 == self.y2
|
||||
&& next
|
||||
.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> {
|
||||
type Item = Band<'a>;
|
||||
impl<'a, T> Iterator for Bands<'a, T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
type Item = Band<'a, T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.rects.is_empty() {
|
||||
|
|
@ -62,7 +82,10 @@ impl<'a> Iterator for Bands<'a> {
|
|||
}
|
||||
|
||||
#[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 res = match a.next() {
|
||||
Some(a) => *a,
|
||||
|
|
@ -74,10 +97,21 @@ pub fn extents(a: &[RectRaw]) -> RectRaw {
|
|||
res.x2 = res.x2.max(a.x2);
|
||||
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 prev_band_y2 = 0;
|
||||
|
|
@ -100,7 +134,7 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
|||
}
|
||||
|
||||
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 {
|
||||
let y2 = $a.y2.min($b.y1);
|
||||
cur_band_start = res.len();
|
||||
|
|
@ -111,6 +145,7 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
|||
y1: $a.y1,
|
||||
x2: rect.x2,
|
||||
y2,
|
||||
tag: O::$map(rect.tag),
|
||||
});
|
||||
}
|
||||
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) {
|
||||
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 {
|
||||
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 {
|
||||
let y2 = a.y2.min(b.y2);
|
||||
cur_band_start = res.len();
|
||||
|
|
@ -149,7 +184,7 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
|||
}
|
||||
|
||||
macro_rules! push_trailing {
|
||||
($a_opt:expr, $a_bands:expr) => {{
|
||||
($a_opt:expr, $a_bands:expr, $map:ident) => {{
|
||||
while let Some(a) = $a_opt {
|
||||
cur_band_start = res.len();
|
||||
res.reserve(a.rects.len());
|
||||
|
|
@ -159,6 +194,7 @@ fn op<O: Op>(a: &[RectRaw], b: &[RectRaw]) -> Container {
|
|||
y1: a.y1,
|
||||
x2: rect.x2,
|
||||
y2: a.y2,
|
||||
tag: O::$map(rect.tag),
|
||||
});
|
||||
}
|
||||
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 {
|
||||
push_trailing!(a_opt, a_bands);
|
||||
push_trailing!(a_opt, a_bands, map_t2_to_t1);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
let slice_a = &new[a..b];
|
||||
let slice_b = &new[b..];
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -197,16 +236,25 @@ fn coalesce(new: &mut Container, a: usize, b: usize, y2: i32) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
trait Op {
|
||||
trait Op<T1, T2, T3>
|
||||
where
|
||||
T1: Tag,
|
||||
T2: Tag,
|
||||
T3: Tag,
|
||||
{
|
||||
const APPEND_NON_A: 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;
|
||||
|
||||
impl Op for Union {
|
||||
impl Op<NoTag, NoTag, NoTag> for Union {
|
||||
const APPEND_NON_A: bool = true;
|
||||
const APPEND_NON_B: bool = true;
|
||||
|
||||
|
|
@ -216,7 +264,13 @@ impl Op for Union {
|
|||
|
||||
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!();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn map_t2_to_t1(_tag: NoTag) -> NoTag {
|
||||
NoTag
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn map_t3_to_t1(_tag: NoTag) -> NoTag {
|
||||
NoTag
|
||||
}
|
||||
}
|
||||
|
||||
struct Subtract;
|
||||
|
||||
impl Op for Subtract {
|
||||
impl Op<NoTag, NoTag, NoTag> for Subtract {
|
||||
const APPEND_NON_A: bool = true;
|
||||
const APPEND_NON_B: bool = false;
|
||||
|
||||
|
|
@ -291,6 +355,7 @@ impl Op for Subtract {
|
|||
y1,
|
||||
x2: $x2,
|
||||
y2,
|
||||
tag: NoTag,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -337,33 +402,145 @@ impl Op for Subtract {
|
|||
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 {
|
||||
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)]
|
||||
struct W(RectRaw);
|
||||
impl Eq for W {}
|
||||
impl PartialEq<Self> for W {
|
||||
struct W<T>(RectRaw<T>)
|
||||
where
|
||||
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 {
|
||||
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> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl Ord for W {
|
||||
impl<T> Ord for W<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0
|
||||
.y1
|
||||
.cmp(&other.0.y1)
|
||||
.then_with(|| self.0.x1.cmp(&other.0.x1))
|
||||
.then_with(|| self.0.tag.cmp(&other.0.tag))
|
||||
.reverse()
|
||||
}
|
||||
}
|
||||
impl Deref for W {
|
||||
type Target = RectRaw;
|
||||
impl<T> Deref for W<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
type Target = RectRaw<T>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
|
|
@ -411,17 +588,60 @@ pub fn rects_to_bands(rects_tmp: &[RectRaw]) -> Container {
|
|||
check_rect!(rect);
|
||||
let mut x1 = rect.x1;
|
||||
let mut x2 = rect.x2;
|
||||
let mut tag: T = rect.tag;
|
||||
while let Some(mut rect) = rects.peek().copied() {
|
||||
check_rect!(rect);
|
||||
if rect.x1 > x2 {
|
||||
res.push(RectRaw { x1, x2, y1, y2 });
|
||||
if rect.x1 > x2 || (rect.tag != tag && rect.x1 == x2) {
|
||||
res.push(RectRaw {
|
||||
x1,
|
||||
x2,
|
||||
y1,
|
||||
y2,
|
||||
tag: tag.constrain(),
|
||||
});
|
||||
x1 = rect.x1;
|
||||
x2 = rect.x2;
|
||||
tag = rect.tag;
|
||||
} 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ pub fn main() -> anyhow::Result<()> {
|
|||
compile_simple("fill.vert")?;
|
||||
compile_simple("tex.vert")?;
|
||||
compile_simple("tex.frag")?;
|
||||
compile_simple("out.vert")?;
|
||||
compile_simple("out.frag")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
# Unreleased
|
||||
|
||||
- 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -570,7 +570,7 @@ impl MetalConnector {
|
|||
match opt {
|
||||
GfxApiOpt::Sync => {}
|
||||
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.
|
||||
if fr.rect.is_covering() {
|
||||
// If fill covers the entire screen, we don't have to look further.
|
||||
|
|
@ -749,6 +749,7 @@ impl MetalConnector {
|
|||
ReleaseSync::Explicit,
|
||||
&latched.pass,
|
||||
&latched.damage,
|
||||
buffer.blend_buffer.as_ref(),
|
||||
)
|
||||
.map_err(MetalError::RenderFrame)?;
|
||||
sync_file = buffer.copy_to_dev(sf)?;
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ use {
|
|||
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
||||
format::{ARGB8888, Format, XRGB8888},
|
||||
gfx_api::{
|
||||
AcquireSync, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync, SyncFile,
|
||||
needs_render_usage,
|
||||
AcquireSync, GfxBlendBuffer, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync,
|
||||
SyncFile, needs_render_usage,
|
||||
},
|
||||
ifs::{
|
||||
wl_output::OutputId,
|
||||
|
|
@ -2607,6 +2607,15 @@ impl MetalBackend {
|
|||
ctx: &MetalRenderContext,
|
||||
cursor: bool,
|
||||
) -> 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 create = || {
|
||||
self.create_scanout_buffer(
|
||||
|
|
@ -2618,6 +2627,7 @@ impl MetalBackend {
|
|||
ctx,
|
||||
cursor,
|
||||
damage_queue.pop().unwrap(),
|
||||
blend_buffer.clone(),
|
||||
)
|
||||
};
|
||||
let mut array = ArrayVec::<_, N>::new();
|
||||
|
|
@ -2640,6 +2650,7 @@ impl MetalBackend {
|
|||
render_ctx: &MetalRenderContext,
|
||||
cursor: bool,
|
||||
damage_queue: DamageQueue,
|
||||
blend_buffer: Option<Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<RenderBuffer, MetalError> {
|
||||
let ctx = dev.ctx.get();
|
||||
let dev_gfx_formats = ctx.gfx.formats();
|
||||
|
|
@ -2771,6 +2782,7 @@ impl MetalBackend {
|
|||
damage_queue,
|
||||
dev_bo,
|
||||
_render_bo: render_bo,
|
||||
blend_buffer,
|
||||
dev_fb,
|
||||
dev_tex,
|
||||
render_tex,
|
||||
|
|
@ -2996,6 +3008,7 @@ pub struct RenderBuffer {
|
|||
pub damage_queue: DamageQueue,
|
||||
pub dev_bo: GbmBo,
|
||||
pub _render_bo: Option<GbmBo>,
|
||||
pub blend_buffer: Option<Rc<dyn GfxBlendBuffer>>,
|
||||
// ctx = dev
|
||||
// buffer location = dev
|
||||
pub dev_fb: Rc<dyn GfxFramebuffer>,
|
||||
|
|
|
|||
|
|
@ -755,6 +755,7 @@ impl XBackend {
|
|||
ReleaseSync::Implicit,
|
||||
&image.tex.get(),
|
||||
true,
|
||||
None,
|
||||
);
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not render screen: {}", ErrorFmt(e));
|
||||
|
|
|
|||
|
|
@ -391,6 +391,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -414,6 +415,7 @@ impl Cursor for StaticCursor {
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -455,6 +457,7 @@ impl Cursor for AnimatedCursor {
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ static XBGR16161616: &Format = &Format {
|
|||
..default(ConfigFormat::XBGR16161616)
|
||||
};
|
||||
|
||||
static ABGR16161616F: &Format = &Format {
|
||||
pub static ABGR16161616F: &Format = &Format {
|
||||
name: "abgr16161616f",
|
||||
vk_format: vk::Format::R16G16B16A16_SFLOAT,
|
||||
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 x1: f32,
|
||||
pub x2: f32,
|
||||
|
|
@ -165,12 +165,45 @@ impl FramebufferRect {
|
|||
pub fn is_covering(&self) -> bool {
|
||||
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)]
|
||||
pub struct FillRect {
|
||||
pub rect: FramebufferRect,
|
||||
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 {
|
||||
|
|
@ -181,6 +214,7 @@ pub struct CopyTexture {
|
|||
pub acquire_sync: AcquireSync,
|
||||
pub release_sync: ReleaseSync,
|
||||
pub alpha: Option<f32>,
|
||||
pub opaque: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -254,6 +288,10 @@ pub enum ResetStatus {
|
|||
Other(u32),
|
||||
}
|
||||
|
||||
pub trait GfxBlendBuffer: Debug {
|
||||
fn into_any(self: Rc<Self>) -> Rc<dyn Any>;
|
||||
}
|
||||
|
||||
pub trait GfxFramebuffer: Debug {
|
||||
fn physical_size(&self) -> (i32, i32);
|
||||
|
||||
|
|
@ -264,9 +302,15 @@ pub trait GfxFramebuffer: Debug {
|
|||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
region: &Region,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<Option<SyncFile>, GfxError>;
|
||||
|
||||
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 {
|
||||
|
|
@ -291,14 +335,16 @@ impl dyn GfxFramebuffer {
|
|||
release_sync: ReleaseSync,
|
||||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.clone()
|
||||
.render_with_region(acquire_sync, release_sync, ops, clear, &self.full_region())
|
||||
}
|
||||
|
||||
fn full_region(&self) -> Region {
|
||||
let (width, height) = self.physical_size();
|
||||
Region::new2(Rect::new_sized_unchecked(0, 0, width, height))
|
||||
self.clone().render_with_region(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
ops,
|
||||
clear,
|
||||
&self.full_region(),
|
||||
blend_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn clear(
|
||||
|
|
@ -318,7 +364,13 @@ impl dyn GfxFramebuffer {
|
|||
b: f32,
|
||||
a: f32,
|
||||
) -> 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) {
|
||||
|
|
@ -360,9 +412,10 @@ impl dyn GfxFramebuffer {
|
|||
resv.cloned(),
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
false,
|
||||
);
|
||||
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(
|
||||
|
|
@ -371,12 +424,13 @@ impl dyn GfxFramebuffer {
|
|||
release_sync: ReleaseSync,
|
||||
scale: Scale,
|
||||
clear: Option<&Color>,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
f: &mut dyn FnMut(&mut RendererBase),
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let mut ops = vec![];
|
||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
||||
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(
|
||||
|
|
@ -413,6 +467,7 @@ impl dyn GfxFramebuffer {
|
|||
release_sync: ReleaseSync,
|
||||
pass: &GfxRenderPass,
|
||||
region: &Region,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.clone().render_with_region(
|
||||
acquire_sync,
|
||||
|
|
@ -420,6 +475,7 @@ impl dyn GfxFramebuffer {
|
|||
&pass.ops,
|
||||
pass.clear.as_ref(),
|
||||
region,
|
||||
blend_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -433,6 +489,7 @@ impl dyn GfxFramebuffer {
|
|||
scale: Scale,
|
||||
render_hardware_cursor: bool,
|
||||
fill_black_in_grace_period: bool,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.render_node(
|
||||
acquire_sync,
|
||||
|
|
@ -446,6 +503,7 @@ impl dyn GfxFramebuffer {
|
|||
node.has_fullscreen(),
|
||||
fill_black_in_grace_period,
|
||||
node.global.persistent.transform.get(),
|
||||
blend_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -462,6 +520,7 @@ impl dyn GfxFramebuffer {
|
|||
black_background: bool,
|
||||
fill_black_in_grace_period: bool,
|
||||
transform: Transform,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let pass = self.create_render_pass(
|
||||
node,
|
||||
|
|
@ -475,7 +534,13 @@ impl dyn GfxFramebuffer {
|
|||
transform,
|
||||
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(
|
||||
|
|
@ -498,7 +563,13 @@ impl dyn GfxFramebuffer {
|
|||
},
|
||||
};
|
||||
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))
|
||||
}
|
||||
|
||||
fn acquire_blend_buffer(
|
||||
&self,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<Rc<dyn GfxBlendBuffer>, GfxError>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
|||
|
|
@ -197,12 +197,14 @@ enum RenderError {
|
|||
UnsupportedShmFormat(&'static str),
|
||||
#[error("Could not access the client memory")]
|
||||
AccessFailed(#[source] Box<dyn Error + Sync + Send>),
|
||||
#[error("OpenGL does not support blend buffers")]
|
||||
NoBlendBuffer,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct GfxGlState {
|
||||
triangles: RefCell<Vec<[f32; 2]>>,
|
||||
fill_rect: VecStorage<&'static FillRect>,
|
||||
fill_rect: VecStorage<FillRect>,
|
||||
copy_tex: VecStorage<&'static CopyTexture>,
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +235,11 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
|||
}
|
||||
}
|
||||
GfxApiOpt::FillRect(f) => {
|
||||
fill_rect.push(f);
|
||||
fill_rect.push(FillRect {
|
||||
rect: f.rect,
|
||||
color: f.effective_color(),
|
||||
alpha: None,
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
GfxApiOpt::CopyTexture(c) => {
|
||||
|
|
@ -249,7 +255,7 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
|||
triangles.clear();
|
||||
let mut color = None;
|
||||
while i < fill_rect.len() {
|
||||
let fr = fill_rect[i];
|
||||
let fr = &fill_rect[i];
|
||||
match color {
|
||||
None => color = Some(fr.color),
|
||||
Some(c) if c == fr.color => {}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use {
|
|||
cpu_worker::CpuWorker,
|
||||
format::{Format, XRGB8888},
|
||||
gfx_api::{
|
||||
AsyncShmGfxTexture, BufferResvUser, GfxContext, GfxError, GfxFormat, GfxFramebuffer,
|
||||
GfxImage, GfxInternalFramebuffer, ResetStatus, ShmGfxTexture,
|
||||
AsyncShmGfxTexture, BufferResvUser, GfxBlendBuffer, GfxContext, GfxError, GfxFormat,
|
||||
GfxFramebuffer, GfxImage, GfxInternalFramebuffer, ResetStatus, ShmGfxTexture,
|
||||
},
|
||||
gfx_apis::gl::{
|
||||
GfxGlState, RenderError, Texture,
|
||||
|
|
@ -339,4 +339,12 @@ impl GfxContext for GlRenderContext {
|
|||
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
||||
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::{
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxError, GfxFramebuffer,
|
||||
GfxInternalFramebuffer, GfxStagingBuffer, PendingShmTransfer, ReleaseSync, ShmMemory,
|
||||
SyncFile,
|
||||
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxBlendBuffer, GfxError,
|
||||
GfxFramebuffer, GfxInternalFramebuffer, GfxStagingBuffer, PendingShmTransfer,
|
||||
ReleaseSync, ShmMemory, SyncFile,
|
||||
},
|
||||
gfx_apis::gl::{
|
||||
RenderError,
|
||||
|
|
@ -106,6 +106,7 @@ impl GfxFramebuffer for Framebuffer {
|
|||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
_region: &Region,
|
||||
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
(*self)
|
||||
.render(acquire_sync, ops, clear)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
mod allocator;
|
||||
mod blend_buffer;
|
||||
mod bo_allocator;
|
||||
mod command;
|
||||
mod descriptor;
|
||||
|
|
@ -24,9 +25,9 @@ use {
|
|||
cpu_worker::{CpuWorker, jobs::read_write::ReadWriteJobError},
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
AsyncShmGfxTexture, GfxContext, GfxError, GfxFormat, GfxImage, GfxInternalFramebuffer,
|
||||
GfxStagingBuffer, ResetStatus, STAGING_DOWNLOAD, STAGING_UPLOAD, ShmGfxTexture,
|
||||
StagingBufferUsecase,
|
||||
AsyncShmGfxTexture, GfxBlendBuffer, GfxContext, GfxError, GfxFormat, GfxImage,
|
||||
GfxInternalFramebuffer, GfxStagingBuffer, ResetStatus, STAGING_DOWNLOAD,
|
||||
STAGING_UPLOAD, ShmGfxTexture, StagingBufferUsecase,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
|
||||
|
|
@ -204,6 +205,8 @@ pub enum VulkanError {
|
|||
UndefinedContents,
|
||||
#[error("The framebuffer is being used by the transfer queue")]
|
||||
BusyInTransfer,
|
||||
#[error("Driver does not support descriptor buffers")]
|
||||
NoDescriptorBuffer,
|
||||
}
|
||||
|
||||
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 shm = match &old.ty {
|
||||
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
||||
VulkanImageMemory::Blend(_) => unreachable!(),
|
||||
VulkanImageMemory::Internal(shm) => shm,
|
||||
};
|
||||
if old.width as i32 == width
|
||||
|
|
@ -350,6 +354,15 @@ impl GfxContext for Context {
|
|||
.device
|
||||
.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 {
|
||||
|
|
|
|||
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 {
|
||||
crate::gfx_apis::vulkan::{VulkanError, device::VulkanDevice, sampler::VulkanSampler},
|
||||
arrayvec::ArrayVec,
|
||||
ash::vk::{
|
||||
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateFlags,
|
||||
DescriptorSetLayoutCreateInfo, DescriptorType, DeviceSize, ShaderStageFlags,
|
||||
ash::{
|
||||
ext::descriptor_buffer,
|
||||
vk::{
|
||||
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateFlags,
|
||||
DescriptorSetLayoutCreateInfo, DescriptorType, DeviceSize, ShaderStageFlags,
|
||||
},
|
||||
},
|
||||
std::{rc::Rc, slice},
|
||||
};
|
||||
|
|
@ -14,7 +17,6 @@ pub(super) struct VulkanDescriptorSetLayout {
|
|||
pub(super) size: DeviceSize,
|
||||
pub(super) offsets: ArrayVec<DeviceSize, 1>,
|
||||
pub(super) _sampler: Option<Rc<VulkanSampler>>,
|
||||
pub(super) has_sampler: bool,
|
||||
}
|
||||
|
||||
impl Drop for VulkanDescriptorSetLayout {
|
||||
|
|
@ -28,7 +30,7 @@ impl Drop for VulkanDescriptorSetLayout {
|
|||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub(super) fn create_descriptor_set_layout(
|
||||
pub(super) fn create_tex_descriptor_set_layout(
|
||||
self: &Rc<Self>,
|
||||
sampler: &Rc<VulkanSampler>,
|
||||
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
|
||||
|
|
@ -52,9 +54,7 @@ impl VulkanDevice {
|
|||
let mut size = 0;
|
||||
let mut offsets = ArrayVec::new();
|
||||
if let Some(db) = &self.descriptor_buffer {
|
||||
size = unsafe { db.get_descriptor_set_layout_size(layout) };
|
||||
size =
|
||||
(size + self.descriptor_buffer_offset_mask) & !self.descriptor_buffer_offset_mask;
|
||||
size = self.get_descriptor_set_size(db, layout);
|
||||
unsafe {
|
||||
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 0));
|
||||
}
|
||||
|
|
@ -65,7 +65,43 @@ impl VulkanDevice {
|
|||
size,
|
||||
offsets,
|
||||
_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,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct VulkanDescriptorBufferWriter {
|
||||
set_size: usize,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
|
|
@ -49,13 +49,13 @@ impl VulkanDescriptorBufferCache {
|
|||
pub fn new(
|
||||
device: &Rc<VulkanDevice>,
|
||||
allocator: &Rc<VulkanAllocator>,
|
||||
layout: &VulkanDescriptorSetLayout,
|
||||
has_sampler: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
device: device.clone(),
|
||||
allocator: allocator.clone(),
|
||||
buffers: Default::default(),
|
||||
has_sampler: layout.has_sampler,
|
||||
has_sampler,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,13 +164,6 @@ impl Drop for VulkanDescriptorBufferUnused {
|
|||
}
|
||||
|
||||
impl VulkanDescriptorBufferWriter {
|
||||
pub fn new(layout: &VulkanDescriptorSetLayout) -> Self {
|
||||
Self {
|
||||
set_size: layout.size as usize,
|
||||
buffer: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.buffer.clear();
|
||||
}
|
||||
|
|
@ -179,10 +172,13 @@ impl VulkanDescriptorBufferWriter {
|
|||
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 lo = buffer.len();
|
||||
buffer.resize(lo + self.set_size, 0);
|
||||
buffer.resize(lo + layout.size as usize, 0);
|
||||
VulkanDescriptorBufferSetWriter {
|
||||
set: &mut buffer[lo..],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use {
|
|||
format::XRGB8888,
|
||||
gfx_apis::vulkan::{
|
||||
VulkanError,
|
||||
format::VulkanFormat,
|
||||
format::{VulkanBlendBufferLimits, VulkanFormat},
|
||||
instance::{
|
||||
API_VERSION, ApiVersionDisplay, Extensions, VulkanInstance,
|
||||
map_extension_properties,
|
||||
|
|
@ -63,6 +63,7 @@ pub struct VulkanDevice {
|
|||
pub(super) image_drm_format_modifier: image_drm_format_modifier::Device,
|
||||
pub(super) descriptor_buffer: Option<descriptor_buffer::Device>,
|
||||
pub(super) formats: AHashMap<u32, VulkanFormat>,
|
||||
pub(super) blend_limits: VulkanBlendBufferLimits,
|
||||
pub(super) memory_types: ArrayVec<MemoryType, MAX_MEMORY_TYPES>,
|
||||
pub(super) graphics_queue: Queue,
|
||||
pub(super) graphics_queue_idx: u32,
|
||||
|
|
@ -71,6 +72,7 @@ pub struct VulkanDevice {
|
|||
pub(super) transfer_granularity_mask: (u32, u32),
|
||||
pub(super) descriptor_buffer_offset_mask: DeviceSize,
|
||||
pub(super) combined_image_sampler_descriptor_size: usize,
|
||||
pub(super) sampled_image_descriptor_size: usize,
|
||||
}
|
||||
|
||||
impl Drop for VulkanDevice {
|
||||
|
|
@ -272,6 +274,9 @@ impl VulkanInstance {
|
|||
}
|
||||
}
|
||||
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 mut distinct_transfer_queue_family_idx = None;
|
||||
let mut transfer_granularity_mask = (0, 0);
|
||||
|
|
@ -334,6 +339,7 @@ impl VulkanInstance {
|
|||
Err(e) => return Err(VulkanError::CreateDevice(e)),
|
||||
};
|
||||
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 supports_xrgb8888 = formats
|
||||
.get(&XRGB8888.drm)
|
||||
|
|
@ -361,6 +367,7 @@ impl VulkanInstance {
|
|||
.then(|| descriptor_buffer::Device::new(&self.instance, &device));
|
||||
let mut descriptor_buffer_offset_mask = 0;
|
||||
let mut combined_image_sampler_descriptor_size = 0;
|
||||
let mut sampled_image_descriptor_size = 0;
|
||||
if supports_descriptor_buffer {
|
||||
let mut descriptor_buffer_props =
|
||||
PhysicalDeviceDescriptorBufferPropertiesEXT::default();
|
||||
|
|
@ -377,6 +384,7 @@ impl VulkanInstance {
|
|||
- 1;
|
||||
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 =
|
||||
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
|
||||
|
|
@ -415,6 +423,8 @@ impl VulkanInstance {
|
|||
transfer_granularity_mask,
|
||||
descriptor_buffer_offset_mask,
|
||||
combined_image_sampler_descriptor_size,
|
||||
sampled_image_descriptor_size,
|
||||
blend_limits,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
format::{FORMATS, Format},
|
||||
format::{ABGR16161616F, FORMATS, Format},
|
||||
gfx_apis::vulkan::{VulkanError, instance::VulkanInstance},
|
||||
video::{LINEAR_MODIFIER, Modifier},
|
||||
},
|
||||
|
|
@ -38,18 +38,24 @@ pub struct VulkanModifier {
|
|||
pub render_needs_bridge: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct VulkanModifierLimits {
|
||||
pub max_width: u32,
|
||||
pub max_height: u32,
|
||||
pub exportable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VulkanInternalFormat {
|
||||
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(
|
||||
0 | FormatFeatureFlags::COLOR_ATTACHMENT.as_raw()
|
||||
| FormatFeatureFlags::COLOR_ATTACHMENT_BLEND.as_raw(),
|
||||
|
|
@ -79,6 +85,16 @@ const TRANSFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
|
|||
const SHM_USAGE: ImageUsageFlags =
|
||||
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 {
|
||||
pub(super) fn load_formats(
|
||||
&self,
|
||||
|
|
@ -126,6 +142,29 @@ impl VulkanInstance {
|
|||
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(
|
||||
&self,
|
||||
phy_dev: PhysicalDevice,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ use {
|
|||
format::Format,
|
||||
gfx_api::{
|
||||
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
|
||||
AsyncShmGfxTextureTransferCancellable, GfxApiOpt, GfxError, GfxFramebuffer, GfxImage,
|
||||
GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, PendingShmTransfer, ReleaseSync,
|
||||
ShmGfxTexture, ShmMemory, SyncFile,
|
||||
AsyncShmGfxTextureTransferCancellable, GfxApiOpt, GfxBlendBuffer, GfxError,
|
||||
GfxFramebuffer, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
|
||||
PendingShmTransfer, ReleaseSync, ShmGfxTexture, ShmMemory, SyncFile,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
VulkanError, allocator::VulkanAllocation, device::VulkanDevice,
|
||||
|
|
@ -64,6 +64,7 @@ pub struct VulkanImage {
|
|||
pub(super) ty: VulkanImageMemory,
|
||||
pub(super) bridge: Option<VulkanFramebufferBridge>,
|
||||
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_offset: Cell<DeviceSize>,
|
||||
pub(super) execution_version: Cell<u64>,
|
||||
|
|
@ -102,6 +103,7 @@ pub enum QueueTransfer {
|
|||
pub enum VulkanImageMemory {
|
||||
DmaBuf(VulkanDmaBufImage),
|
||||
Internal(VulkanShmImage),
|
||||
Blend(#[expect(dead_code)] VulkanAllocation),
|
||||
}
|
||||
|
||||
pub struct VulkanDmaBufImage {
|
||||
|
|
@ -451,6 +453,7 @@ impl VulkanDmaBufImageTemplate {
|
|||
shader_read_only_optimal_descriptor: self
|
||||
.renderer
|
||||
.sampler_read_only_descriptor(texture_view),
|
||||
sampled_image_descriptor: Box::new([]),
|
||||
descriptor_buffer_version: Cell::new(0),
|
||||
descriptor_buffer_offset: Cell::new(0),
|
||||
execution_version: Cell::new(0),
|
||||
|
|
@ -539,9 +542,30 @@ impl GfxFramebuffer for VulkanImage {
|
|||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
region: &Region,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> 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
|
||||
.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())
|
||||
}
|
||||
|
||||
|
|
@ -609,6 +633,7 @@ impl GfxTexture for VulkanImage {
|
|||
match &self.ty {
|
||||
VulkanImageMemory::DmaBuf(b) => Some(&b.template.dmabuf),
|
||||
VulkanImageMemory::Internal(_) => None,
|
||||
VulkanImageMemory::Blend(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ pub(super) struct PipelineCreateInfo {
|
|||
pub(super) blend: bool,
|
||||
pub(super) src_has_alpha: bool,
|
||||
pub(super) has_alpha_mult: bool,
|
||||
pub(super) with_linear_output: bool,
|
||||
pub(super) frag_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
||||
}
|
||||
|
||||
|
|
@ -76,8 +77,8 @@ impl VulkanDevice {
|
|||
};
|
||||
let destroy_layout =
|
||||
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
|
||||
let mut frag_spec_data = ArrayVec::<_, 8>::new();
|
||||
let mut frag_spec_entries = ArrayVec::<_, 2>::new();
|
||||
let mut frag_spec_data = ArrayVec::<_, { 3 * 4 }>::new();
|
||||
let mut frag_spec_entries = ArrayVec::<_, 3>::new();
|
||||
let mut frag_spec_entry = |data: &[u8]| {
|
||||
let entry = SpecializationMapEntry::default()
|
||||
.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.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()
|
||||
.map_entries(&frag_spec_entries)
|
||||
.data(&frag_spec_data);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use {
|
|||
cpu_worker::PendingJob,
|
||||
format::XRGB8888,
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxTexture,
|
||||
GfxWriteModifier, ReleaseSync, SyncFile,
|
||||
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, GfxFormat,
|
||||
GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
VulkanError,
|
||||
|
|
@ -22,12 +22,12 @@ use {
|
|||
sampler::VulkanSampler,
|
||||
semaphore::VulkanSemaphore,
|
||||
shaders::{
|
||||
FILL_FRAG, FILL_VERT, FillPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants,
|
||||
VulkanShader,
|
||||
FILL_FRAG, FILL_VERT, FillPushConstants, OUT_FRAG, OUT_VERT, OutPushConstants,
|
||||
TEX_FRAG, TEX_VERT, TexPushConstants, VulkanShader,
|
||||
},
|
||||
},
|
||||
io_uring::IoUring,
|
||||
rect::Region,
|
||||
rect::{Rect, Region},
|
||||
theme::Color,
|
||||
utils::{
|
||||
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},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
arrayvec::ArrayVec,
|
||||
ash::{
|
||||
Device,
|
||||
vk::{
|
||||
|
|
@ -51,12 +52,15 @@ use {
|
|||
},
|
||||
},
|
||||
isnt::std_1::{collections::IsntHashMapExt, primitive::IsntSliceExt},
|
||||
jay_algorithms::rect::Tag,
|
||||
linearize::{Linearize, StaticMap, static_map},
|
||||
std::{
|
||||
borrow::Cow,
|
||||
cell::{Cell, RefCell},
|
||||
collections::hash_map::Entry,
|
||||
fmt::{Debug, Formatter},
|
||||
mem, ptr,
|
||||
rc::Rc,
|
||||
rc::{Rc, Weak},
|
||||
slice,
|
||||
},
|
||||
uapi::OwnedFd,
|
||||
|
|
@ -65,7 +69,8 @@ use {
|
|||
pub struct VulkanRenderer {
|
||||
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
|
||||
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) transfer_command_buffers: Option<CachedCommandBuffers>,
|
||||
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
|
||||
|
|
@ -81,13 +86,17 @@ pub struct VulkanRenderer {
|
|||
pub(super) fill_frag_shader: Rc<VulkanShader>,
|
||||
pub(super) tex_vert_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) out_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
||||
pub(super) defunct: Cell<bool>,
|
||||
pub(super) pending_cpu_jobs: CopyHashMap<u64, PendingJob>,
|
||||
pub(super) shm_allocator: Rc<VulkanThreadedAllocator>,
|
||||
pub(super) sampler: Rc<VulkanSampler>,
|
||||
pub(super) tex_sampler_descriptor_buffer_cache: Rc<VulkanDescriptorBufferCache>,
|
||||
pub(super) tex_descriptor_buffer_writer: RefCell<VulkanDescriptorBufferWriter>,
|
||||
pub(super) sampler_descriptor_buffer_cache: Rc<VulkanDescriptorBufferCache>,
|
||||
pub(super) resource_descriptor_buffer_cache: Rc<VulkanDescriptorBufferCache>,
|
||||
pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>,
|
||||
}
|
||||
|
||||
pub(super) struct CachedCommandBuffers {
|
||||
|
|
@ -139,14 +148,23 @@ pub(super) struct Memory {
|
|||
wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>,
|
||||
release_fence: Option<Rc<VulkanFence>>,
|
||||
release_sync_file: Option<SyncFile>,
|
||||
descriptor_buffer: Option<VulkanDescriptorBuffer>,
|
||||
paint_regions: Vec<PaintRegion>,
|
||||
clear_rects: Vec<ClearRect>,
|
||||
descriptor_buffers: ArrayVec<VulkanDescriptorBuffer, 2>,
|
||||
paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>,
|
||||
clear_rects: StaticMap<RenderPass, Vec<ClearRect>>,
|
||||
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 {
|
||||
rect: Rect2D,
|
||||
x1: f32,
|
||||
y1: f32,
|
||||
x2: f32,
|
||||
|
|
@ -158,15 +176,16 @@ pub(super) struct PendingFrame {
|
|||
renderer: Rc<VulkanRenderer>,
|
||||
cmd: Cell<Option<Rc<VulkanCommandBuffer>>>,
|
||||
_fb: Rc<VulkanImage>,
|
||||
_bb: Option<Rc<VulkanImage>>,
|
||||
_textures: Vec<UsedTexture>,
|
||||
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
||||
waiter: Cell<Option<SpawnedFuture<()>>>,
|
||||
_release_fence: Option<Rc<VulkanFence>>,
|
||||
_descriptor_buffer: Option<VulkanDescriptorBuffer>,
|
||||
_descriptor_buffers: ArrayVec<VulkanDescriptorBuffer, 2>,
|
||||
}
|
||||
|
||||
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>>>,
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +198,14 @@ impl VulkanDevice {
|
|||
let fill_vert_shader = self.create_shader(FILL_VERT)?;
|
||||
let fill_frag_shader = self.create_shader(FILL_FRAG)?;
|
||||
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_frag_shader = self.create_shader(TEX_FRAG)?;
|
||||
let gfx_command_buffers = self.create_command_pool(self.graphics_queue_idx)?;
|
||||
|
|
@ -220,18 +246,15 @@ impl VulkanDevice {
|
|||
.collect();
|
||||
let allocator = self.create_allocator()?;
|
||||
let shm_allocator = self.create_threaded_allocator()?;
|
||||
let tex_descriptor_buffer_cache = Rc::new(VulkanDescriptorBufferCache::new(
|
||||
self,
|
||||
&allocator,
|
||||
&tex_descriptor_set_layout,
|
||||
));
|
||||
let tex_descriptor_buffer_writer = RefCell::new(VulkanDescriptorBufferWriter::new(
|
||||
&tex_descriptor_set_layout,
|
||||
));
|
||||
let sampler_descriptor_buffer_cache =
|
||||
Rc::new(VulkanDescriptorBufferCache::new(self, &allocator, true));
|
||||
let resource_descriptor_buffer_cache =
|
||||
Rc::new(VulkanDescriptorBufferCache::new(self, &allocator, false));
|
||||
let render = Rc::new(VulkanRenderer {
|
||||
formats: Rc::new(formats),
|
||||
device: self.clone(),
|
||||
pipelines: Default::default(),
|
||||
out_pipelines: Default::default(),
|
||||
gfx_command_buffers,
|
||||
transfer_command_buffers,
|
||||
wait_semaphores: Default::default(),
|
||||
|
|
@ -247,15 +270,19 @@ impl VulkanDevice {
|
|||
fill_frag_shader,
|
||||
tex_vert_shader,
|
||||
tex_frag_shader,
|
||||
out_vert_shader,
|
||||
out_frag_shader,
|
||||
tex_descriptor_set_layout,
|
||||
out_descriptor_set_layout,
|
||||
defunct: Cell::new(false),
|
||||
pending_cpu_jobs: Default::default(),
|
||||
shm_allocator,
|
||||
sampler,
|
||||
tex_sampler_descriptor_buffer_cache: tex_descriptor_buffer_cache,
|
||||
tex_descriptor_buffer_writer,
|
||||
sampler_descriptor_buffer_cache,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -264,21 +291,28 @@ impl VulkanRenderer {
|
|||
fn get_or_create_pipelines(
|
||||
&self,
|
||||
format: vk::Format,
|
||||
pass: RenderPass,
|
||||
) -> 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);
|
||||
}
|
||||
let fill = self
|
||||
.device
|
||||
.create_pipeline::<FillPushConstants>(PipelineCreateInfo {
|
||||
format,
|
||||
vert: self.fill_vert_shader.clone(),
|
||||
frag: self.fill_frag_shader.clone(),
|
||||
blend: true,
|
||||
src_has_alpha: true,
|
||||
has_alpha_mult: false,
|
||||
frag_descriptor_set_layout: None,
|
||||
})?;
|
||||
let create_fill_pipeline = |src_has_alpha| {
|
||||
self.device
|
||||
.create_pipeline::<FillPushConstants>(PipelineCreateInfo {
|
||||
format,
|
||||
vert: self.fill_vert_shader.clone(),
|
||||
frag: self.fill_frag_shader.clone(),
|
||||
blend: src_has_alpha,
|
||||
src_has_alpha,
|
||||
has_alpha_mult: false,
|
||||
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| {
|
||||
self.device
|
||||
.create_pipeline::<TexPushConstants>(PipelineCreateInfo {
|
||||
|
|
@ -288,6 +322,7 @@ impl VulkanRenderer {
|
|||
blend: src_has_alpha || has_alpha_mult,
|
||||
src_has_alpha,
|
||||
has_alpha_mult,
|
||||
with_linear_output,
|
||||
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_mult_opaque = create_tex_pipeline(false, true)?;
|
||||
let tex_mult_alpha = create_tex_pipeline(true, true)?;
|
||||
let pipelines = Rc::new(VulkanFormatPipelines {
|
||||
fill,
|
||||
let format_pipelines = Rc::new(VulkanFormatPipelines {
|
||||
fill: static_map! {
|
||||
TexSourceType::HasAlpha => fill_alpha.clone(),
|
||||
TexSourceType::Opaque => fill_opaque.clone(),
|
||||
},
|
||||
tex: static_map! {
|
||||
TexCopyType::Identity => static_map! {
|
||||
TexSourceType::HasAlpha => tex_alpha.clone(),
|
||||
|
|
@ -308,27 +346,38 @@ impl VulkanRenderer {
|
|||
},
|
||||
},
|
||||
});
|
||||
self.pipelines.set(format, pipelines.clone());
|
||||
Ok(pipelines)
|
||||
pipelines.set(format, format_pipelines.clone());
|
||||
Ok(format_pipelines)
|
||||
}
|
||||
|
||||
pub(super) fn allocate_point(&self) -> u64 {
|
||||
self.last_point.fetch_add(1) + 1
|
||||
}
|
||||
|
||||
fn create_descriptor_buffer(
|
||||
fn create_descriptor_buffers(
|
||||
&self,
|
||||
buf: CommandBuffer,
|
||||
opts: &[GfxApiOpt],
|
||||
bb: Option<&VulkanImage>,
|
||||
) -> Result<(), VulkanError> {
|
||||
let Some(db) = &self.device.descriptor_buffer else {
|
||||
return Ok(());
|
||||
};
|
||||
zone!("create_descriptor_buffer");
|
||||
zone!("create_descriptor_buffers");
|
||||
let version = self.allocate_point();
|
||||
let memory = &mut *self.memory.borrow_mut();
|
||||
let writer = &mut *self.tex_descriptor_buffer_writer.borrow_mut();
|
||||
writer.clear();
|
||||
memory.descriptor_buffers.clear();
|
||||
let sampler_writer = &mut memory.sampler_descriptor_buffer_writer;
|
||||
sampler_writer.clear();
|
||||
let resource_writer = &mut memory.resource_descriptor_buffer_writer;
|
||||
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 {
|
||||
let GfxApiOpt::CopyTexture(c) = cmd else {
|
||||
continue;
|
||||
|
|
@ -337,27 +386,32 @@ impl VulkanRenderer {
|
|||
if tex.descriptor_buffer_version.replace(version) == version {
|
||||
continue;
|
||||
}
|
||||
let offset = writer.next_offset();
|
||||
let offset = sampler_writer.next_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(
|
||||
self.tex_descriptor_set_layout.offsets[0],
|
||||
&tex.shader_read_only_optimal_descriptor,
|
||||
);
|
||||
}
|
||||
let buffer = self
|
||||
.tex_sampler_descriptor_buffer_cache
|
||||
.allocate(writer.len() as DeviceSize)?;
|
||||
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||
ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len())
|
||||
})?;
|
||||
let info = DescriptorBufferBindingInfoEXT::default()
|
||||
.usage(self.tex_sampler_descriptor_buffer_cache.usage())
|
||||
.address(buffer.buffer.address);
|
||||
unsafe {
|
||||
db.cmd_bind_descriptor_buffers(buf, slice::from_ref(&info));
|
||||
let mut infos = ArrayVec::<_, 2>::new();
|
||||
for (writer, cache) in [
|
||||
(&sampler_writer, &self.sampler_descriptor_buffer_cache),
|
||||
(&resource_writer, &self.resource_descriptor_buffer_cache),
|
||||
] {
|
||||
let buffer = cache.allocate(writer.len() as DeviceSize)?;
|
||||
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||
ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len())
|
||||
})?;
|
||||
let info = DescriptorBufferBindingInfoEXT::default()
|
||||
.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(())
|
||||
}
|
||||
|
||||
|
|
@ -500,42 +554,61 @@ impl VulkanRenderer {
|
|||
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");
|
||||
let memory = &mut *self.memory.borrow_mut();
|
||||
let clear_value = clear.map(|clear| ClearValue {
|
||||
color: ClearColorValue {
|
||||
float32: clear.to_array_srgb(),
|
||||
},
|
||||
});
|
||||
let load_clear = memory.paint_regions.len() == 1 && {
|
||||
let rect = &memory.paint_regions[0].rect;
|
||||
rect.offset.x == 0
|
||||
&& rect.offset.y == 0
|
||||
&& rect.extent.width == fb.width
|
||||
&& rect.extent.height == fb.height
|
||||
};
|
||||
let (load_clear, manual_clear) = match load_clear {
|
||||
false => (None, clear_value),
|
||||
true => (clear_value, None),
|
||||
};
|
||||
let rendering_attachment_info = {
|
||||
let mut rai = RenderingAttachmentInfo::default()
|
||||
.image_view(fb.render_view.unwrap_or(fb.texture_view))
|
||||
.image_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||
.load_op(AttachmentLoadOp::LOAD)
|
||||
.store_op(AttachmentStoreOp::STORE);
|
||||
if let Some(clear) = load_clear {
|
||||
rai = rai.clear_value(clear).load_op(AttachmentLoadOp::CLEAR);
|
||||
let mut load_clear = None;
|
||||
let mut manual_clear = None;
|
||||
let clear_rects = &memory.clear_rects[pass];
|
||||
if let Some(clear) = clear {
|
||||
if clear_rects.is_not_empty() {
|
||||
let clear_value = ClearValue {
|
||||
color: ClearColorValue {
|
||||
float32: match pass {
|
||||
RenderPass::BlendBuffer => clear.to_array_linear(None),
|
||||
RenderPass::FrameBuffer => clear.to_array_srgb(None),
|
||||
},
|
||||
},
|
||||
};
|
||||
let use_load_clear = clear_rects.len() == 1 && {
|
||||
let rect = &clear_rects[0].rect;
|
||||
rect.offset.x == 0
|
||||
&& rect.offset.y == 0
|
||||
&& rect.extent.width == target.width
|
||||
&& rect.extent.height == target.height
|
||||
};
|
||||
if use_load_clear {
|
||||
load_clear = Some(clear_value);
|
||||
} else {
|
||||
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()
|
||||
.render_area(Rect2D {
|
||||
offset: Default::default(),
|
||||
extent: Extent2D {
|
||||
width: fb.width,
|
||||
height: fb.height,
|
||||
width: target.width,
|
||||
height: target.height,
|
||||
},
|
||||
})
|
||||
.layer_count(1)
|
||||
|
|
@ -543,26 +616,16 @@ impl VulkanRenderer {
|
|||
unsafe {
|
||||
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 {
|
||||
let clear_attachment = ClearAttachment::default()
|
||||
.color_attachment(0)
|
||||
.clear_value(clear)
|
||||
.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 {
|
||||
self.device.device.cmd_clear_attachments(
|
||||
buf,
|
||||
&[clear_attachment],
|
||||
&memory.clear_rects,
|
||||
);
|
||||
self.device
|
||||
.device
|
||||
.cmd_clear_attachments(buf, &[clear_attachment], clear_rects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -598,12 +661,14 @@ impl VulkanRenderer {
|
|||
fn record_draws(
|
||||
&self,
|
||||
buf: CommandBuffer,
|
||||
fb: &VulkanImage,
|
||||
target: &VulkanImage,
|
||||
opts: &[GfxApiOpt],
|
||||
pass: RenderPass,
|
||||
) -> Result<(), VulkanError> {
|
||||
zone!("record_draws");
|
||||
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 mut current_pipeline = None;
|
||||
let mut bind = |pipeline: &VulkanPipeline| {
|
||||
|
|
@ -620,19 +685,27 @@ impl VulkanRenderer {
|
|||
GfxApiOpt::FillRect(r) => {
|
||||
let push = FillPushConstants {
|
||||
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 draw = region.constrain(&mut push.pos, None);
|
||||
if !draw {
|
||||
continue;
|
||||
}
|
||||
bind(&pipelines.fill);
|
||||
bind(pipeline);
|
||||
unsafe {
|
||||
dev.cmd_push_constants(
|
||||
buf,
|
||||
pipelines.fill.pipeline_layout,
|
||||
pipeline.pipeline_layout,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
0,
|
||||
uapi::as_bytes(&push),
|
||||
|
|
@ -656,7 +729,7 @@ impl VulkanRenderer {
|
|||
true => TexCopyType::Multiply,
|
||||
false => TexCopyType::Identity,
|
||||
};
|
||||
let source_type = match tex.format.has_alpha {
|
||||
let source_type = match tex.format.has_alpha && !c.opaque {
|
||||
true => TexSourceType::HasAlpha,
|
||||
false => TexSourceType::Opaque,
|
||||
};
|
||||
|
|
@ -670,7 +743,7 @@ impl VulkanRenderer {
|
|||
.image_view(tex.texture_view)
|
||||
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
||||
let init = Once::default();
|
||||
for region in &memory.paint_regions {
|
||||
for region in paint_regions {
|
||||
let mut push = push;
|
||||
let draw = region.constrain(&mut push.pos, Some(&mut push.tex_pos));
|
||||
if !draw {
|
||||
|
|
@ -717,6 +790,112 @@ impl VulkanRenderer {
|
|||
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) {
|
||||
zone!("end_rendering");
|
||||
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");
|
||||
let Some(bridge) = &fb.bridge else {
|
||||
return;
|
||||
|
|
@ -766,15 +945,14 @@ impl VulkanRenderer {
|
|||
.base_array_layer(0)
|
||||
.mip_level(0);
|
||||
memory.image_copy_regions.clear();
|
||||
for region in &memory.paint_regions {
|
||||
let offset = Offset3D {
|
||||
x: region.rect.offset.x,
|
||||
y: region.rect.offset.y,
|
||||
z: 0,
|
||||
for rect in region.rects() {
|
||||
let Some([x1, y1, x2, y2]) = constrain_to_fb(fb, rect) else {
|
||||
continue;
|
||||
};
|
||||
let offset = Offset3D { x: x1, y: y1, z: 0 };
|
||||
let extent = Extent3D {
|
||||
width: region.rect.extent.width,
|
||||
height: region.rect.extent.height,
|
||||
width: (x2 - x1) as _,
|
||||
height: (y2 - y1) as _,
|
||||
depth: 1,
|
||||
};
|
||||
memory.image_copy_regions.push(
|
||||
|
|
@ -786,14 +964,16 @@ impl VulkanRenderer {
|
|||
.extent(extent),
|
||||
);
|
||||
}
|
||||
let copy_image_info = CopyImageInfo2::default()
|
||||
.src_image(fb.image)
|
||||
.src_image_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
|
||||
.dst_image(bridge.dmabuf_image)
|
||||
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.regions(&memory.image_copy_regions);
|
||||
unsafe {
|
||||
self.device.device.cmd_copy_image2(buf, ©_image_info);
|
||||
if memory.image_copy_regions.is_not_empty() {
|
||||
let copy_image_info = CopyImageInfo2::default()
|
||||
.src_image(fb.image)
|
||||
.src_image_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
|
||||
.dst_image(bridge.dmabuf_image)
|
||||
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.regions(&memory.image_copy_regions);
|
||||
unsafe {
|
||||
self.device.device.cmd_copy_image2(buf, ©_image_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -999,7 +1179,10 @@ impl VulkanRenderer {
|
|||
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.contents_are_undefined.set(false);
|
||||
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");
|
||||
let point = self.allocate_point();
|
||||
let mut memory = self.memory.borrow_mut();
|
||||
|
|
@ -1022,11 +1210,12 @@ impl VulkanRenderer {
|
|||
renderer: self.clone(),
|
||||
cmd: Cell::new(Some(buf)),
|
||||
_fb: fb.clone(),
|
||||
_bb: bb,
|
||||
_textures: mem::take(&mut memory.textures),
|
||||
wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)),
|
||||
waiter: Cell::new(None),
|
||||
_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());
|
||||
let future = self.eng.spawn(
|
||||
|
|
@ -1049,9 +1238,18 @@ impl VulkanRenderer {
|
|||
opts: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
region: &Region,
|
||||
blend_buffer: Option<Rc<VulkanImage>>,
|
||||
) -> Result<Option<SyncFile>, VulkanError> {
|
||||
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 mut memory = self.memory.borrow_mut();
|
||||
memory.textures.clear();
|
||||
|
|
@ -1059,7 +1257,7 @@ impl VulkanRenderer {
|
|||
memory.queue_transfer.clear();
|
||||
memory.wait_semaphores.clear();
|
||||
memory.release_fence.take();
|
||||
memory.descriptor_buffer.take();
|
||||
memory.descriptor_buffers.clear();
|
||||
memory.release_sync_file.take()
|
||||
};
|
||||
res.map(|_| sync_file)
|
||||
|
|
@ -1074,37 +1272,124 @@ impl VulkanRenderer {
|
|||
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();
|
||||
memory.paint_regions.clear();
|
||||
for rect in region.rects() {
|
||||
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 {
|
||||
continue;
|
||||
memory.regions_1.clear();
|
||||
memory.regions_2.clear();
|
||||
let width = fb.width as f32;
|
||||
let height = fb.height as f32;
|
||||
let mut tag = 0;
|
||||
for opt in opts.iter().rev() {
|
||||
let (opaque, fb_rect) = match opt {
|
||||
GfxApiOpt::Sync => continue,
|
||||
GfxApiOpt::FillRect(f) => (f.effective_color().a >= 1.0, f.rect),
|
||||
GfxApiOpt::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 y2 = y2.min(fb.height as i32);
|
||||
let to_fb = |c: i32, max: u32| 2.0 * (c as f32 / max as f32) - 1.0;
|
||||
memory.paint_regions.push(PaintRegion {
|
||||
rect: Rect2D {
|
||||
offset: Offset2D {
|
||||
x: x1 as _,
|
||||
y: y1 as _,
|
||||
},
|
||||
extent: Extent2D {
|
||||
width: (x2 - x1) as u32,
|
||||
height: (y2 - y1) as u32,
|
||||
},
|
||||
},
|
||||
let rect = fb_rect.to_rect(width, height);
|
||||
if opaque && clear.is_some() {
|
||||
memory.regions_1.push(rect);
|
||||
}
|
||||
memory.regions_2.push(rect.with_tag(tag));
|
||||
}
|
||||
let clear_region = if clear.is_some() {
|
||||
let opaque_region = Region::from_rects2(&memory.regions_1);
|
||||
region.subtract_cow(&opaque_region)
|
||||
} else {
|
||||
Cow::Owned(Region::default())
|
||||
};
|
||||
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),
|
||||
x2: to_fb(x2, fb.width),
|
||||
y1: to_fb(y1, 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(
|
||||
|
|
@ -1115,26 +1400,43 @@ impl VulkanRenderer {
|
|||
opts: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
region: &Region,
|
||||
mut blend_buffer: Option<Rc<VulkanImage>>,
|
||||
) -> Result<(), VulkanError> {
|
||||
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()?;
|
||||
self.collect_memory(opts);
|
||||
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.begin_rendering(buf.buffer, fb, clear);
|
||||
self.set_viewport(buf.buffer, fb);
|
||||
self.record_draws(buf.buffer, fb, opts)?;
|
||||
self.end_rendering(buf.buffer);
|
||||
self.copy_bridge_to_dmabuf(buf.buffer, fb);
|
||||
if let Some(bb) = bb {
|
||||
zone!("blend buffer pass");
|
||||
self.blend_buffer_initial_barrier(buf.buffer, bb);
|
||||
self.begin_rendering(buf.buffer, bb, clear, RenderPass::BlendBuffer);
|
||||
self.record_draws(buf.buffer, bb, opts, RenderPass::BlendBuffer)?;
|
||||
self.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.end_command_buffer(buf.buffer)?;
|
||||
self.create_wait_semaphores(fb, &fb_acquire_sync)?;
|
||||
self.submit(buf.buffer)?;
|
||||
self.import_release_semaphore(fb, fb_release_sync);
|
||||
self.store_layouts(fb);
|
||||
self.create_pending_frame(buf, fb);
|
||||
self.store_layouts(fb, bb);
|
||||
self.create_pending_frame(buf, fb, blend_buffer);
|
||||
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> {
|
||||
ImageMemoryBarrier2::default().subresource_range(
|
||||
ImageSubresourceRange::default()
|
||||
|
|
@ -1291,3 +1604,19 @@ impl PaintRegion {
|
|||
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 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 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(super) device: Rc<VulkanDevice>,
|
||||
|
|
@ -34,6 +36,14 @@ pub struct 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 {
|
||||
pub(super) fn create_shader(
|
||||
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 = 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
|
||||
|
||||
#include "frag_spec_const.glsl"
|
||||
#include "transfer_functions.glsl"
|
||||
#include "tex.common.glsl"
|
||||
|
||||
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;
|
||||
|
||||
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 (src_has_alpha) {
|
||||
out_color = textureLod(tex, tex_pos, 0) * data.mul;
|
||||
c *= data.mul;
|
||||
} 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),
|
||||
bridge: None,
|
||||
shader_read_only_optimal_descriptor: self.sampler_read_only_descriptor(view),
|
||||
sampled_image_descriptor: Box::new([]),
|
||||
descriptor_buffer_version: Cell::new(0),
|
||||
descriptor_buffer_offset: Cell::new(0),
|
||||
execution_version: Cell::new(0),
|
||||
});
|
||||
let shm = match &img.ty {
|
||||
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
||||
VulkanImageMemory::Blend(_) => unreachable!(),
|
||||
VulkanImageMemory::Internal(s) => s,
|
||||
};
|
||||
if data.is_not_empty() {
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
|||
true,
|
||||
false,
|
||||
jay_config::video::Transform::None,
|
||||
None,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ impl JayScreencast {
|
|||
false,
|
||||
false,
|
||||
Transform::None,
|
||||
None,
|
||||
);
|
||||
match res {
|
||||
Ok(_) => {
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ pub struct WlSurface {
|
|||
role: Cell<SurfaceRole>,
|
||||
pending: RefCell<Box<PendingState>>,
|
||||
input_region: CloneCell<Option<Rc<Region>>>,
|
||||
opaque_region: Cell<Option<Rc<Region>>>,
|
||||
opaque_region: CloneCell<Option<Rc<Region>>>,
|
||||
buffer_points: RefCell<BufferPoints>,
|
||||
pub buffer_points_norm: RefCell<SampleRect>,
|
||||
damage_matrix: Cell<DamageMatrix>,
|
||||
|
|
@ -331,6 +331,7 @@ pub struct WlSurface {
|
|||
clear_fifo_on_vblank: Cell<bool>,
|
||||
commit_timer: CloneCell<Option<Rc<WpCommitTimerV1>>>,
|
||||
before_latch_listener: EventListener<dyn BeforeLatchListener>,
|
||||
is_opaque: Cell<bool>,
|
||||
}
|
||||
|
||||
impl Debug for WlSurface {
|
||||
|
|
@ -668,6 +669,7 @@ impl WlSurface {
|
|||
clear_fifo_on_vblank: Default::default(),
|
||||
commit_timer: Default::default(),
|
||||
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 mut buffer_abs_pos_size_changed = false;
|
||||
if buffer_changed || transform_changed {
|
||||
let mut buffer_points = self.buffer_points.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());
|
||||
max_surface_size = (width.max(old_width), height.max(old_height));
|
||||
damage_full = true;
|
||||
buffer_abs_pos_size_changed = true;
|
||||
}
|
||||
}
|
||||
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);
|
||||
fbs.is_not_empty()
|
||||
};
|
||||
let mut opaque_region_changed = false;
|
||||
{
|
||||
if let Some(region) = pending.input_region.take() {
|
||||
self.input_region.set(region);
|
||||
self.client.state.tree_changed();
|
||||
}
|
||||
if let Some(region) = pending.opaque_region.take() {
|
||||
opaque_region_changed = true;
|
||||
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;
|
||||
if let Some(tearing) = pending.tearing.take() {
|
||||
if self.tearing.replace(tearing) != tearing {
|
||||
|
|
@ -1636,6 +1650,14 @@ impl WlSurface {
|
|||
pub fn alpha(&self) -> Option<f32> {
|
||||
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! {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ use {
|
|||
format::{ARGB8888, Format, XRGB8888},
|
||||
gfx_api::{
|
||||
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, CopyTexture, FillRect,
|
||||
FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage,
|
||||
GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, GfxWriteModifier,
|
||||
PendingShmTransfer, ReleaseSync, ResetStatus, ShmGfxTexture, ShmMemory, SyncFile,
|
||||
FramebufferRect, GfxApiOpt, GfxBlendBuffer, GfxContext, GfxError, GfxFormat,
|
||||
GfxFramebuffer, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
|
||||
GfxWriteModifier, PendingShmTransfer, ReleaseSync, ResetStatus, ShmGfxTexture,
|
||||
ShmMemory, SyncFile,
|
||||
},
|
||||
rect::{Rect, Region},
|
||||
theme::Color,
|
||||
|
|
@ -37,6 +38,8 @@ enum TestGfxError {
|
|||
ImportDmaBuf(#[source] AllocatorError),
|
||||
#[error("Could not access the client memory")]
|
||||
AccessFailed(#[source] Box<dyn Error + Sync + Send>),
|
||||
#[error("Text API does not support blend buffers")]
|
||||
NoBlendBuffer,
|
||||
}
|
||||
|
||||
impl From<TestGfxError> for GfxError {
|
||||
|
|
@ -189,6 +192,14 @@ impl GfxContext for TestGfxCtx {
|
|||
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn acquire_blend_buffer(
|
||||
&self,
|
||||
_width: i32,
|
||||
_height: i32,
|
||||
) -> Result<Rc<dyn GfxBlendBuffer>, GfxError> {
|
||||
Err(GfxError(Box::new(TestGfxError::NoBlendBuffer)))
|
||||
}
|
||||
}
|
||||
|
||||
enum TestGfxImage {
|
||||
|
|
@ -383,6 +394,7 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
_region: &Region,
|
||||
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
||||
let points = rect.to_points();
|
||||
|
|
@ -441,11 +453,12 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
}
|
||||
};
|
||||
let fill_rect = |f: &FillRect, staging: &mut [Color]| {
|
||||
let color = f.effective_color();
|
||||
let (x1, y1, x2, y2) = fb_points(width, height, &f.rect);
|
||||
for y in y1..y2 {
|
||||
for x in x1..x2 {
|
||||
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,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -325,6 +326,7 @@ impl GuiElement for Label {
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -634,6 +636,7 @@ impl WindowData {
|
|||
ReleaseSync::Implicit,
|
||||
self.scale.get(),
|
||||
Some(&Color::from_gray(0)),
|
||||
None,
|
||||
&mut |r| {
|
||||
if let Some(content) = self.content.get() {
|
||||
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};
|
||||
use {
|
||||
jay_algorithms::rect::RectRaw,
|
||||
jay_algorithms::rect::{NoTag, RectRaw, Tag},
|
||||
smallvec::SmallVec,
|
||||
std::fmt::{Debug, Formatter},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct Rect {
|
||||
raw: RectRaw,
|
||||
pub struct Rect<T = NoTag>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
raw: RectRaw<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Default)]
|
||||
pub struct Region {
|
||||
rects: SmallVec<[RectRaw; 1]>,
|
||||
pub struct Region<T = NoTag>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
rects: SmallVec<[RectRaw<T>; 1]>,
|
||||
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 {
|
||||
pub fn new_empty(x: i32, y: i32) -> Self {
|
||||
Self {
|
||||
|
|
@ -58,6 +81,7 @@ impl Rect {
|
|||
y1: y,
|
||||
x2: x,
|
||||
y2: y,
|
||||
tag: NoTag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +91,13 @@ impl Rect {
|
|||
return None;
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
#[cfg_attr(not(test), expect(dead_code))]
|
||||
fn new_unchecked_danger(x1: i32, y1: i32, x2: i32, y2: i32) -> 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),
|
||||
x2: self.raw.x2.max(other.raw.x2),
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
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)]
|
||||
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.y1 <= rect.raw.x1
|
||||
&& rect.raw.x2 <= self.raw.x2
|
||||
&& 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 {
|
||||
left: self.raw.x1 - child.raw.x1,
|
||||
right: child.raw.x2 - self.raw.x2,
|
||||
|
|
@ -177,6 +260,7 @@ impl Rect {
|
|||
y1: 0,
|
||||
x2: self.raw.x2 - self.raw.x1,
|
||||
y2: self.raw.y2 - self.raw.y1,
|
||||
tag: self.raw.tag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -188,6 +272,7 @@ impl Rect {
|
|||
y1: self.raw.y1.saturating_add(dy),
|
||||
x2: self.raw.x2.saturating_add(dx),
|
||||
y2: self.raw.y2.saturating_add(dy),
|
||||
tag: self.raw.tag,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -199,14 +284,11 @@ impl Rect {
|
|||
y1,
|
||||
x2: x1 + self.raw.x2 - self.raw.x1,
|
||||
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) {
|
||||
(x.wrapping_sub(self.raw.x1), y.wrapping_sub(self.raw.y1))
|
||||
}
|
||||
|
|
@ -253,4 +335,8 @@ impl Rect {
|
|||
self.raw.y1 + self.height() / 2,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> T {
|
||||
self.raw.tag
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,15 @@ use {
|
|||
},
|
||||
},
|
||||
jay_algorithms::rect::{
|
||||
RectRaw,
|
||||
region::{extents, rects_to_bands, subtract, union},
|
||||
RectRaw, Tag,
|
||||
region::{
|
||||
extents, intersect, intersect_tagged, rects_to_bands, rects_to_bands_tagged, subtract,
|
||||
union,
|
||||
},
|
||||
},
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
borrow::Cow,
|
||||
cell::UnsafeCell,
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
|
|
@ -29,19 +33,6 @@ thread_local! {
|
|||
}
|
||||
|
||||
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> {
|
||||
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))]
|
||||
pub fn extents(&self) -> Rect {
|
||||
self.extents
|
||||
}
|
||||
|
||||
pub fn rects(&self) -> &[Rect] {
|
||||
unsafe { mem::transmute::<&[RectRaw], &[Rect]>(&self.rects[..]) }
|
||||
pub fn rects(&self) -> &[Rect<T>] {
|
||||
unsafe { mem::transmute::<&[RectRaw<T>], &[Rect<T>]>(&self.rects[..]) }
|
||||
}
|
||||
|
||||
pub fn contains(&self, x: i32, y: i32) -> bool {
|
||||
|
|
@ -116,13 +186,41 @@ impl Region {
|
|||
}
|
||||
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 {
|
||||
type Target = [Rect];
|
||||
impl<T> Deref for Region<T>
|
||||
where
|
||||
T: Tag,
|
||||
{
|
||||
type Target = [Rect<T>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { mem::transmute::<&[RectRaw], _>(&self.rects) }
|
||||
unsafe { mem::transmute::<&[RectRaw<T>], _>(&self.rects) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::rect::{Rect, Region},
|
||||
jay_algorithms::rect::RectRaw,
|
||||
jay_algorithms::rect::{NoTag, RectRaw},
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
@ -43,25 +43,29 @@ fn subtract1() {
|
|||
x1: 0,
|
||||
y1: 0,
|
||||
x2: 20,
|
||||
y2: 5
|
||||
y2: 5,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 0,
|
||||
y1: 5,
|
||||
x2: 5,
|
||||
y2: 15
|
||||
y2: 15,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 15,
|
||||
y1: 5,
|
||||
x2: 20,
|
||||
y2: 15
|
||||
y2: 15,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 0,
|
||||
y1: 15,
|
||||
x2: 20,
|
||||
y2: 20
|
||||
y2: 20,
|
||||
tag: NoTag,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
|
@ -83,19 +87,22 @@ fn rects_to_bands() {
|
|||
x1: 0,
|
||||
y1: 0,
|
||||
x2: 30,
|
||||
y2: 5
|
||||
y2: 5,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 0,
|
||||
y1: 5,
|
||||
x2: 50,
|
||||
y2: 10
|
||||
y2: 10,
|
||||
tag: NoTag,
|
||||
},
|
||||
RectRaw {
|
||||
x1: 30,
|
||||
y1: 10,
|
||||
x2: 50,
|
||||
y2: 15
|
||||
y2: 15,
|
||||
tag: NoTag,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
|
@ -111,3 +118,572 @@ fn rects_to_bands2() {
|
|||
// println!("{:#?}", r.rects);
|
||||
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 c = theme.colors.bar_background.get();
|
||||
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();
|
||||
if let Some(aw) = &rd.active_workspace {
|
||||
let c = match aw.captured {
|
||||
|
|
@ -124,6 +124,7 @@ impl Renderer<'_> {
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
if let Some(status) = &rd.status {
|
||||
|
|
@ -141,6 +142,7 @@ impl Renderer<'_> {
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -219,6 +221,7 @@ impl Renderer<'_> {
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -264,6 +267,7 @@ impl Renderer<'_> {
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -336,7 +340,8 @@ impl Renderer<'_> {
|
|||
};
|
||||
let color = self.state.theme.colors.highlight.get();
|
||||
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) {
|
||||
|
|
@ -378,7 +383,6 @@ impl Renderer<'_> {
|
|||
} else {
|
||||
size = self.base.scale_point(size.0, size.1);
|
||||
}
|
||||
let alpha = surface.alpha();
|
||||
if let Some(children) = children.deref() {
|
||||
macro_rules! render {
|
||||
($children:expr) => {
|
||||
|
|
@ -400,10 +404,10 @@ impl Renderer<'_> {
|
|||
};
|
||||
}
|
||||
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);
|
||||
} 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,
|
||||
surface: &WlSurface,
|
||||
buffer: &Rc<SurfaceBuffer>,
|
||||
alpha: Option<f32>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
tpoints: SampleRect,
|
||||
tsize: (i32, i32),
|
||||
bounds: Option<&Rect>,
|
||||
) {
|
||||
let alpha = surface.alpha();
|
||||
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(
|
||||
&tex,
|
||||
alpha,
|
||||
|
|
@ -431,6 +439,7 @@ impl Renderer<'_> {
|
|||
Some(buffer.clone()),
|
||||
AcquireSync::Unnecessary,
|
||||
buffer.release_sync,
|
||||
opaque,
|
||||
);
|
||||
} else if let Some(color) = &buffer.buffer.color {
|
||||
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
||||
|
|
@ -440,11 +449,7 @@ impl Renderer<'_> {
|
|||
};
|
||||
if !rect.is_empty() {
|
||||
self.base.ops.push(GfxApiOpt::Sync);
|
||||
let mut color = *color;
|
||||
if let Some(alpha) = alpha {
|
||||
color = color * alpha;
|
||||
}
|
||||
self.base.fill_scaled_boxes(&[rect], &color);
|
||||
self.base.fill_scaled_boxes(&[rect], color, alpha);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -499,6 +504,7 @@ impl Renderer<'_> {
|
|||
None,
|
||||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -517,4 +523,23 @@ impl Renderer<'_> {
|
|||
let (dx, dy) = surface.surface.extents.get().position();
|
||||
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
|
||||
}
|
||||
|
||||
pub fn fill_scaled_boxes(&mut self, boxes: &[Rect], color: &Color) {
|
||||
self.fill_boxes3(boxes, color, 0, 0, true);
|
||||
pub fn fill_scaled_boxes(&mut self, boxes: &[Rect], color: &Color, alpha: Option<f32>) {
|
||||
self.fill_boxes3(boxes, color, alpha, 0, 0, true);
|
||||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
|
|
@ -97,6 +105,7 @@ impl RendererBase<'_> {
|
|||
self.fb_height,
|
||||
),
|
||||
color: *color,
|
||||
alpha,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -129,6 +138,7 @@ impl RendererBase<'_> {
|
|||
self.fb_height,
|
||||
),
|
||||
color: *color,
|
||||
alpha: None,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -146,6 +156,7 @@ impl RendererBase<'_> {
|
|||
buffer_resv: Option<Rc<dyn BufferResv>>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
opaque: bool,
|
||||
) {
|
||||
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
||||
|
||||
|
|
@ -188,6 +199,7 @@ impl RendererBase<'_> {
|
|||
buffer_resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
opaque,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ pub fn take_screenshot(
|
|||
false,
|
||||
false,
|
||||
Transform::None,
|
||||
None,
|
||||
)?;
|
||||
let drm = match allocator.drm() {
|
||||
Some(drm) => Some(drm.dup_render()?.fd().clone()),
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ use {
|
|||
forker::ForkerProxy,
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture,
|
||||
PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD, SampleRect, SyncFile,
|
||||
AcquireSync, BufferResv, GfxBlendBuffer, GfxContext, GfxError, GfxFramebuffer,
|
||||
GfxTexture, PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD, SampleRect, SyncFile,
|
||||
},
|
||||
gfx_apis::create_gfx_context,
|
||||
globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal},
|
||||
|
|
@ -979,6 +979,7 @@ impl State {
|
|||
release_sync: ReleaseSync,
|
||||
tex: &Rc<dyn GfxTexture>,
|
||||
render_hw_cursor: bool,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let sync_file = fb.render_output(
|
||||
acquire_sync,
|
||||
|
|
@ -989,6 +990,7 @@ impl State {
|
|||
output.global.persistent.scale.get(),
|
||||
render_hw_cursor,
|
||||
true,
|
||||
blend_buffer,
|
||||
)?;
|
||||
output.latched(false);
|
||||
output.perform_screencopies(
|
||||
|
|
@ -1046,6 +1048,7 @@ impl State {
|
|||
resv.cloned(),
|
||||
acquire_sync.clone(),
|
||||
release_sync,
|
||||
false,
|
||||
);
|
||||
if render_hardware_cursors {
|
||||
if let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() {
|
||||
|
|
@ -1064,6 +1067,7 @@ impl State {
|
|||
target_release_sync,
|
||||
&ops,
|
||||
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)]
|
||||
}
|
||||
|
||||
pub fn to_array_srgb(self) -> [f32; 4] {
|
||||
[self.r, self.g, self.b, self.a]
|
||||
pub fn to_array_srgb(self, alpha: Option<f32>) -> [f32; 4] {
|
||||
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) -> [f32; 4] {
|
||||
pub fn to_array_linear(self, alpha: Option<f32>) -> [f32; 4] {
|
||||
fn to_linear(srgb: f32) -> f32 {
|
||||
if srgb <= 0.04045 {
|
||||
srgb / 12.92
|
||||
} 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.g),
|
||||
to_linear(self.b),
|
||||
self.a,
|
||||
to_linear(self.r / a1) * a2,
|
||||
to_linear(self.g / a1) * a2,
|
||||
to_linear(self.b / a1) * a2,
|
||||
a2,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue