use aliasable::boxed::AliasableBox;
use std::{
any::{Any, TypeId},
collections::HashMap,
fmt,
sync::{Arc, Mutex},
};
use super::{
sync::SyncPoint, Bind, Blit, BlitFrame, Color32F, ContextId, DebugFlags, ExportMem, Frame, ImportDma,
ImportMem, Offscreen, Renderer, RendererSuper, Texture, TextureFilter, TextureMapping,
};
#[cfg(feature = "wayland_frontend")]
use super::{ImportDmaWl, ImportMemWl};
#[cfg(feature = "wayland_frontend")]
use crate::{
backend::renderer::{buffer_type, BufferType},
wayland::{compositor::SurfaceData, dmabuf::get_dmabuf, shm},
};
use crate::{
backend::{
allocator::{
dmabuf::{AnyError, Dmabuf},
format::FormatSet,
Allocator, Buffer as BufferTrait, Format, Fourcc, Modifier,
},
drm::DrmNode,
renderer::sync,
SwapBuffersError,
},
utils::{Buffer as BufferCoords, Physical, Rectangle, Size, Transform},
};
use tracing::{debug, info, info_span, instrument, trace, trace_span, warn};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm, wl_surface::WlSurface};
#[cfg(all(feature = "backend_gbm", feature = "backend_egl", feature = "renderer_gl"))]
pub mod gbm;
#[derive(Debug)]
pub struct GpuManager<A: GraphicsApi> {
api: A,
devices: Vec<A::Device>,
dmabuf_cache: HashMap<(DrmNode, DrmNode), Option<(bool, Dmabuf)>>,
span: tracing::Span,
}
#[derive(thiserror::Error)]
pub enum Error<R: GraphicsApi, T: GraphicsApi>
where
R::Error: 'static,
T::Error: 'static,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[error("The render graphics api failed enumerating devices {0:?}")]
RenderApiError(#[source] R::Error),
#[error("The target graphics api failed enumerating devices {0:?}")]
TargetApiError(#[source] T::Error),
#[error("The graphics api has found no node matching {0:?}")]
NoDevice(DrmNode),
#[error("The devices requested {0:?} did not match the expected")]
MismatchedDevice(DrmNode),
#[error("The device has gone missing")]
DeviceMissing,
#[error("Error on the rendering device: {0:}")]
Render(#[source] <<R::Device as ApiDevice>::Renderer as RendererSuper>::Error),
#[error("Error on the target device: {0:}")]
Target(#[source] <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error),
#[error("Failed to import buffer")]
ImportFailed,
#[error("Failed to allocate buffer")]
AllocatorError(AnyError),
}
impl<R: GraphicsApi, T: GraphicsApi> Error<R, T> {
fn transpose(self) -> Error<T, R> {
match self {
Error::Render(err) => Error::Target(err),
Error::Target(err) => Error::Render(err),
Error::RenderApiError(err) => Error::TargetApiError(err),
Error::TargetApiError(err) => Error::RenderApiError(err),
Error::NoDevice(d) => Error::NoDevice(d),
Error::MismatchedDevice(d) => Error::MismatchedDevice(d),
Error::DeviceMissing => Error::DeviceMissing,
Error::ImportFailed => Error::ImportFailed,
Error::AllocatorError(a) => Error::AllocatorError(a),
}
}
}
impl<R: GraphicsApi> Error<R, R> {
fn generalize<T: GraphicsApi>(self) -> Error<R, T> {
match self {
Error::Render(err) => Error::Render(err),
Error::Target(err) => Error::Render(err),
Error::RenderApiError(err) => Error::RenderApiError(err),
Error::TargetApiError(err) => Error::RenderApiError(err),
Error::NoDevice(d) => Error::NoDevice(d),
Error::MismatchedDevice(d) => Error::MismatchedDevice(d),
Error::DeviceMissing => Error::DeviceMissing,
Error::ImportFailed => Error::ImportFailed,
Error::AllocatorError(a) => Error::AllocatorError(a),
}
}
}
impl<R: GraphicsApi, T: GraphicsApi> fmt::Debug for Error<R, T>
where
R::Error: 'static,
T::Error: 'static,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::RenderApiError(err) => write!(f, "Error::RenderApiError({:?})", err),
Error::TargetApiError(err) => write!(f, "Error::TargetApiError({:?})", err),
Error::NoDevice(dev) => write!(f, "Error::NoDevice({:?})", dev),
Error::MismatchedDevice(dev) => write!(f, "Error::MismatchedDevice({:?})", dev),
Error::DeviceMissing => write!(f, "Error::DeviceMissing"),
Error::Render(err) => write!(f, "Error::Render({:?})", err),
Error::Target(err) => write!(f, "Error::Target({:?})", err),
Error::ImportFailed => write!(f, "Error::ImportFailed"),
Error::AllocatorError(err) => write!(f, "Error::AllocationError({})", err),
}
}
}
impl<R: GraphicsApi + 'static, T: GraphicsApi + 'static> From<Error<R, T>> for SwapBuffersError
where
R::Error: Into<SwapBuffersError> + Send + Sync,
T::Error: Into<SwapBuffersError> + Send + Sync,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: Into<SwapBuffersError> + Send + Sync,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: Into<SwapBuffersError> + Send + Sync,
{
#[inline]
fn from(err: Error<R, T>) -> SwapBuffersError {
match err {
x @ Error::NoDevice(_) | x @ Error::DeviceMissing | x @ Error::AllocatorError(_) => {
SwapBuffersError::ContextLost(Box::new(x))
}
x @ Error::MismatchedDevice(_) | x @ Error::ImportFailed => {
SwapBuffersError::TemporaryFailure(Box::new(x))
}
Error::RenderApiError(x) => x.into(),
Error::TargetApiError(x) => x.into(),
Error::Render(x) => x.into(),
Error::Target(x) => x.into(),
}
}
}
impl<A: GraphicsApi> AsRef<A> for GpuManager<A> {
fn as_ref(&self) -> &A {
&self.api
}
}
impl<A: GraphicsApi> AsMut<A> for GpuManager<A> {
fn as_mut(&mut self) -> &mut A {
&mut self.api
}
}
impl<A: GraphicsApi> GpuManager<A> {
pub fn new(api: A) -> Result<GpuManager<A>, Error<A, A>> {
let span = info_span!("renderer_multi", backend = A::identifier());
let mut devices = Vec::new();
api.enumerate(&mut devices).map_err(Error::RenderApiError)?;
Ok(GpuManager {
api,
devices,
dmabuf_cache: HashMap::new(),
span,
})
}
pub fn devices(&mut self) -> Result<impl Iterator<Item = &A::Device>, A::Error> {
if self.api.needs_enumeration() {
self.api.enumerate(&mut self.devices)?;
}
Ok(self.devices.iter())
}
pub fn devices_mut(&mut self) -> Result<impl Iterator<Item = &mut A::Device>, A::Error> {
if self.api.needs_enumeration() {
self.api.enumerate(&mut self.devices)?;
}
Ok(self.devices.iter_mut())
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
pub fn single_renderer<'api>(
&'api mut self,
device: &DrmNode,
) -> Result<MultiRenderer<'api, 'api, A, A>, Error<A, A>> {
if !self.devices.iter().any(|dev| dev.node() == device) || self.api.needs_enumeration() {
self.api
.enumerate(&mut self.devices)
.map_err(Error::RenderApiError)?;
}
if !self.devices.iter().any(|dev| dev.node() == device) {
return Err(Error::NoDevice(*device));
}
let (mut render, others) = self
.devices
.iter_mut()
.partition::<Vec<_>, _>(|dev| dev.node() == device);
Ok(MultiRenderer {
render: render.remove(0),
target: None,
other_renderers: others,
span: tracing::Span::current(),
})
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
pub fn renderer<'api>(
&'api mut self,
render_device: &DrmNode,
target_device: &DrmNode,
copy_format: Fourcc,
) -> Result<MultiRenderer<'api, 'api, A, A>, Error<A, A>>
where
<A::Device as ApiDevice>::Renderer: Bind<Dmabuf>,
{
if !self.devices.iter().any(|device| device.node() == render_device)
|| !self.devices.iter().any(|device| device.node() == target_device)
|| self.api.needs_enumeration()
{
self.api
.enumerate(&mut self.devices)
.map_err(Error::RenderApiError)?;
}
if !self.devices.iter().any(|device| device.node() == render_device) {
return Err(Error::NoDevice(*render_device));
}
if !self.devices.iter().any(|device| device.node() == target_device) {
return Err(Error::NoDevice(*target_device));
}
let (mut render, others) = self
.devices
.iter_mut()
.partition::<Vec<_>, _>(|device| device.node() == render_device);
if target_device != render_device {
let (mut target, others) = others
.into_iter()
.partition::<Vec<_>, _>(|device| device.node() == target_device);
Ok(MultiRenderer {
render: render.remove(0),
target: Some(TargetData {
device: target.remove(0),
cached_buffer: self
.dmabuf_cache
.entry((*render_device, *target_device))
.or_default(),
format: copy_format,
}),
other_renderers: others,
span: tracing::Span::current(),
})
} else {
Ok(MultiRenderer {
render: render.remove(0),
target: None,
other_renderers: others,
span: tracing::Span::current(),
})
}
}
#[instrument(level = "trace", skip(render_api, target_api), follows_from = [&render_api.span, &target_api.span])]
#[profiling::function]
pub fn cross_renderer<'render, 'target, B: GraphicsApi, Alloc: Allocator>(
render_api: &'render mut Self,
target_api: &'target mut GpuManager<B>,
render_device: &DrmNode,
target_device: &DrmNode,
copy_format: Fourcc,
) -> Result<MultiRenderer<'render, 'target, A, B>, Error<A, B>>
where
<A::Device as ApiDevice>::Renderer: Bind<Dmabuf>,
<B::Device as ApiDevice>::Renderer: ImportDma,
{
if !render_api
.devices
.iter()
.any(|device| device.node() == render_device)
|| render_api.api.needs_enumeration()
{
render_api
.api
.enumerate(&mut render_api.devices)
.map_err(Error::RenderApiError)?;
}
if !target_api
.devices
.iter()
.any(|device| device.node() == target_device)
|| target_api.api.needs_enumeration()
{
target_api
.api
.enumerate(&mut target_api.devices)
.map_err(Error::TargetApiError)?;
}
if !render_api
.devices
.iter()
.any(|device| device.node() == render_device)
{
return Err(Error::NoDevice(*render_device));
}
if !target_api
.devices
.iter()
.any(|device| device.node() == target_device)
{
return Err(Error::NoDevice(*target_device));
}
let (mut render, others) = render_api
.devices
.iter_mut()
.partition::<Vec<_>, _>(|device| device.node() == render_device);
if target_device != render_device {
let target = target_api
.devices
.iter_mut()
.find(|device| device.node() == target_device)
.unwrap();
Ok(MultiRenderer {
render: render.remove(0),
target: Some(TargetData {
device: target,
cached_buffer: target_api
.dmabuf_cache
.entry((*render_device, *target_device))
.or_default(),
format: copy_format,
}),
other_renderers: others,
span: tracing::Span::current(),
})
} else {
Ok(MultiRenderer {
render: render.remove(0),
target: None,
other_renderers: others,
span: tracing::Span::current(),
})
}
}
#[cfg(feature = "wayland_frontend")]
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
pub fn early_import(&mut self, target: DrmNode, surface: &WlSurface) -> Result<(), Error<A, A>>
where
A: 'static,
<A::Device as ApiDevice>::Renderer: ImportMemWl + ImportDmaWl + ExportMem,
<<A::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
{
use crate::{
backend::renderer::utils::RendererSurfaceStateUserData,
wayland::compositor::{with_surface_tree_upward, TraversalAction},
};
if self.devices.is_empty() {
return Err(Error::DeviceMissing);
}
let mut result = Ok(());
with_surface_tree_upward(
surface,
(),
|_surface, states, _| {
if let Some(data) = states.data_map.get::<RendererSurfaceStateUserData>() {
let mut data_ref = data.lock().unwrap();
let data = &mut *data_ref;
if data.textures.is_empty() {
if let Some(buffer) = data.buffer.as_ref() {
let buffer_damage = data.damage().raw().take(1).flatten().cloned().fold(
Vec::<Rectangle<i32, BufferCoords>>::new(),
|damage, mut rect| {
let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = damage
.into_iter()
.partition(|other| other.overlaps_or_touches(rect));
for overlap in overlapping {
rect = rect.merge(overlap);
}
new_damage.push(rect);
new_damage
},
);
if let Err(err) = self.early_import_buffer(target, buffer, states, &buffer_damage)
{
result = Err(err);
}
}
if result.is_ok() {
TraversalAction::DoChildren(())
} else {
TraversalAction::SkipChildren
}
} else {
TraversalAction::SkipChildren
}
} else {
TraversalAction::SkipChildren
}
},
|_, _, _| {},
|_, _, _| true,
);
result
}
#[cfg(feature = "wayland_frontend")]
#[profiling::function]
fn early_import_buffer(
&mut self,
target_node: DrmNode,
buffer: &wl_buffer::WlBuffer,
surface: &SurfaceData,
damage: &[Rectangle<i32, BufferCoords>],
) -> Result<(), Error<A, A>>
where
A: 'static,
<A::Device as ApiDevice>::Renderer: ImportMemWl + ImportDmaWl + ExportMem,
<<A::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
{
match buffer_type(buffer) {
Some(BufferType::Dma) => {
let dmabuf = get_dmabuf(buffer).unwrap();
let mut texture = MultiTexture::from_surface(Some(surface), dmabuf.size(), dmabuf.format());
if !self.devices.iter().any(|device| target_node == *device.node()) {
return Err(Error::DeviceMissing);
}
let mut devices = self.devices.iter_mut();
let first = devices.next().unwrap();
let src_node = import_on_src_node(dmabuf, Some(damage), &mut texture, first, None, devices)?;
if src_node != target_node {
let mut texture_internal = texture.0.lock().unwrap();
let api_textures = texture_internal.textures.get_mut(&TypeId::of::<A>()).unwrap();
{
let target_texture = api_textures.get(&target_node);
if !matches!(
target_texture,
Some(GpuSingleTexture::Mem {
external_shadow: None,
..
})
) {
return Ok(());
}
}
let src_texture = match api_textures.get(&src_node).unwrap() {
GpuSingleTexture::Direct(tex) => tex
.downcast_ref::<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>(
)
.unwrap(),
_ => unreachable!(),
};
let format = src_texture
.format()
.filter(|format| {
self.devices
.iter()
.find(|device| target_node == *device.node())
.unwrap()
.renderer()
.mem_formats()
.any(|fmt| fmt == *format)
})
.unwrap_or(Fourcc::Abgr8888);
let src_device = self
.devices
.iter_mut()
.find(|device| src_node == *device.node())
.unwrap();
let mappings = {
let damage = damage
.iter()
.flat_map(|rect| rect.intersection(Rectangle::from_size(src_texture.size())))
.fold(Vec::<Rectangle<i32, BufferCoords>>::new(), |damage, mut rect| {
let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = damage
.into_iter()
.partition(|other| other.overlaps_or_touches(rect));
for overlap in overlapping {
rect = rect.merge(overlap);
}
new_damage.push(rect);
new_damage
});
damage
.iter()
.copied()
.map(|damage| {
let mapping = src_device
.renderer_mut()
.copy_texture(src_texture, damage, format)
.map_err(Error::Target)?;
Ok((damage, mapping))
})
.collect::<Result<Vec<_>, Error<A, A>>>()?
};
std::mem::drop(texture_internal);
texture.insert_mapping::<A, A, _>(
src_node,
target_node,
texture.size(),
mappings.into_iter(),
);
surface.data_map.insert_if_missing_threadsafe(|| texture.0);
}
Ok(())
}
#[cfg(all(
feature = "wayland_frontend",
feature = "backend_egl",
feature = "use_system_lib"
))]
Some(BufferType::Egl) => {
Ok(())
}
Some(BufferType::Shm) => {
Ok(())
}
Some(BufferType::SinglePixel) => {
Ok(())
}
None => {
Ok(())
}
}
}
}
pub trait GraphicsApi {
type Device: ApiDevice;
type Error: std::error::Error;
fn enumerate(&self, list: &mut Vec<Self::Device>) -> Result<(), Self::Error>;
fn needs_enumeration(&self) -> bool {
false
}
fn identifier() -> &'static str;
}
pub trait ApiDevice: fmt::Debug {
type Renderer: Renderer;
fn renderer(&self) -> &Self::Renderer;
fn renderer_mut(&mut self) -> &mut Self::Renderer;
fn allocator(&mut self) -> &mut dyn Allocator<Buffer = Dmabuf, Error = AnyError>;
fn node(&self) -> &DrmNode;
}
pub struct MultiRenderer<'render, 'target, R: GraphicsApi, T: GraphicsApi> {
render: &'render mut R::Device,
target: Option<TargetData<'target, T>>,
other_renderers: Vec<&'render mut R::Device>,
span: tracing::Span,
}
impl<R: GraphicsApi, T: GraphicsApi> fmt::Debug for MultiRenderer<'_, '_, R, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MultiRenderer")
.field("render", &self.render)
.field("target", &self.target)
.field("other_renderers", &self.other_renderers)
.finish()
}
}
impl<R: GraphicsApi, T: GraphicsApi> AsRef<<R::Device as ApiDevice>::Renderer>
for MultiRenderer<'_, '_, R, T>
{
fn as_ref(&self) -> &<R::Device as ApiDevice>::Renderer {
self.render.renderer()
}
}
impl<R: GraphicsApi, T: GraphicsApi> AsMut<<R::Device as ApiDevice>::Renderer>
for MultiRenderer<'_, '_, R, T>
{
fn as_mut(&mut self) -> &mut <R::Device as ApiDevice>::Renderer {
self.render.renderer_mut()
}
}
pub struct MultiFramebuffer<'buffer, R: GraphicsApi, T: GraphicsApi>(MultiFramebufferInternal<'buffer, R, T>);
enum MultiFramebufferInternal<'buffer, R: GraphicsApi, T: GraphicsApi> {
Render(<<R::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>),
Target(<<T::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>),
}
impl<'buffer, R: GraphicsApi, T: GraphicsApi> fmt::Debug for MultiFramebuffer<'buffer, R, T>
where
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>: fmt::Debug,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
MultiFramebufferInternal::Render(framebuffer) => framebuffer.fmt(f),
MultiFramebufferInternal::Target(framebuffer) => framebuffer.fmt(f),
}
}
}
impl<R: GraphicsApi, T: GraphicsApi> Texture for MultiFramebuffer<'_, R, T> {
fn size(&self) -> Size<i32, BufferCoords> {
match &self.0 {
MultiFramebufferInternal::Render(framebuffer) => framebuffer.size(),
MultiFramebufferInternal::Target(framebuffer) => framebuffer.size(),
}
}
fn width(&self) -> u32 {
match &self.0 {
MultiFramebufferInternal::Render(framebuffer) => framebuffer.width(),
MultiFramebufferInternal::Target(framebuffer) => framebuffer.width(),
}
}
fn height(&self) -> u32 {
match &self.0 {
MultiFramebufferInternal::Render(framebuffer) => framebuffer.height(),
MultiFramebufferInternal::Target(framebuffer) => framebuffer.height(),
}
}
fn format(&self) -> Option<Fourcc> {
match &self.0 {
MultiFramebufferInternal::Render(framebuffer) => framebuffer.format(),
MultiFramebufferInternal::Target(framebuffer) => framebuffer.format(),
}
}
}
pub struct MultiFrame<'render, 'target, 'frame, 'buffer, R: GraphicsApi, T: GraphicsApi>
where
'buffer: 'frame,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
node: DrmNode,
frame: Option<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>>,
framebuffer:
Option<AliasableBox<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'frame>>>,
target: Option<TargetFrameData<'target, 'frame, 'buffer, T>>,
render: *mut &'render mut R::Device,
dst_transform: Transform,
size: Size<i32, Physical>,
damage: Vec<Rectangle<i32, Physical>>,
span: tracing::span::EnteredSpan,
}
struct TargetData<'target, T: GraphicsApi> {
device: &'target mut T::Device,
cached_buffer: &'target mut Option<(bool, Dmabuf)>,
format: Fourcc,
}
struct TargetFrameData<'target, 'frame, 'buffer, T: GraphicsApi> {
device: &'frame mut &'target mut T::Device,
framebuffer: &'frame mut <<T::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>,
texture: Option<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>,
format: Fourcc,
}
impl<'frame, 'buffer, R: GraphicsApi + 'frame, T: GraphicsApi> fmt::Debug
for MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
'buffer: 'frame,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: fmt::Debug,
R::Device: fmt::Debug,
T::Device: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MultiFrame")
.field("node", &self.node)
.field("render", unsafe { &*self.render })
.field("target", &self.target)
.field("dst_transform", &self.dst_transform)
.field("size", &self.size)
.field("damage", &self.damage)
.finish()
}
}
impl<T: GraphicsApi> fmt::Debug for TargetData<'_, T>
where
T::Device: fmt::Debug,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TargetData")
.field("device", self.device)
.field("cached_buffer", &self.cached_buffer)
.field("format", &self.format)
.finish()
}
}
impl<T: GraphicsApi> fmt::Debug for TargetFrameData<'_, '_, '_, T>
where
T::Device: fmt::Debug,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TargetFrameData")
.field("device", self.device)
.field("format", &self.format)
.finish_non_exhaustive()
}
}
impl<'frame, 'buffer, R: GraphicsApi, T: GraphicsApi>
AsRef<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>>
for MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
'buffer: 'frame,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
fn as_ref(&self) -> &<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer> {
self.frame.as_ref().unwrap()
}
}
impl<'frame, 'buffer, R: GraphicsApi, T: GraphicsApi>
AsMut<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>>
for MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
'buffer: 'frame,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
fn as_mut(
&mut self,
) -> &mut <<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer> {
self.frame.as_mut().unwrap()
}
}
impl<R: GraphicsApi, T: GraphicsApi, Target> Offscreen<Target> for MultiRenderer<'_, '_, R, T>
where
<T::Device as ApiDevice>::Renderer: Offscreen<Target>,
<R::Device as ApiDevice>::Renderer: Offscreen<Target>,
<T::Device as ApiDevice>::Renderer: Bind<Target>,
<R::Device as ApiDevice>::Renderer: Bind<Target>,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn create_buffer(
&mut self,
format: Fourcc,
size: Size<i32, BufferCoords>,
) -> Result<Target, <Self as RendererSuper>::Error> {
if let Some(target) = self.target.as_mut() {
target
.device
.renderer_mut()
.create_buffer(format, size)
.map_err(Error::Target)
} else {
self.render
.renderer_mut()
.create_buffer(format, size)
.map_err(Error::Render)
}
}
}
impl<R: GraphicsApi, T: GraphicsApi, Target> Bind<Target> for MultiRenderer<'_, '_, R, T>
where
<T::Device as ApiDevice>::Renderer: Bind<Target>,
<R::Device as ApiDevice>::Renderer: Bind<Target>,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[instrument(level = "trace", parent = &self.span, skip(self, bind))]
#[profiling::function]
fn bind<'a>(
&mut self,
bind: &'a mut Target,
) -> Result<<Self as RendererSuper>::Framebuffer<'a>, <Self as RendererSuper>::Error> {
if let Some(target) = self.target.as_mut() {
target
.device
.renderer_mut()
.bind(bind)
.map(MultiFramebufferInternal::Target)
.map(MultiFramebuffer)
.map_err(Error::Target)
} else {
self.render
.renderer_mut()
.bind(bind)
.map(MultiFramebufferInternal::Render)
.map(MultiFramebuffer)
.map_err(Error::Render)
}
}
fn supported_formats(&self) -> Option<FormatSet> {
if let Some(target) = self.target.as_ref() {
Bind::<Target>::supported_formats(target.device.renderer())
} else {
Bind::<Target>::supported_formats(self.render.renderer())
}
}
}
const MAX_CPU_COPIES: usize = 3;
impl<'render, 'target, R: GraphicsApi, T: GraphicsApi> RendererSuper for MultiRenderer<'render, 'target, R, T>
where
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
type Error = Error<R, T>;
type TextureId = MultiTexture;
type Framebuffer<'buffer> = MultiFramebuffer<'buffer, R, T>;
type Frame<'frame, 'buffer>
= MultiFrame<'render, 'target, 'frame, 'buffer, R, T>
where
'buffer: 'frame,
Self: 'frame;
}
impl<'render, 'target, R: GraphicsApi, T: GraphicsApi> Renderer for MultiRenderer<'render, 'target, R, T>
where
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
fn context_id(&self) -> ContextId<MultiTexture> {
self.render.renderer().context_id().map()
}
fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> {
self.render
.renderer_mut()
.downscale_filter(filter)
.map_err(Error::Render)
}
fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> {
self.render
.renderer_mut()
.upscale_filter(filter)
.map_err(Error::Render)
}
fn set_debug_flags(&mut self, flags: DebugFlags) {
self.render.renderer_mut().set_debug_flags(flags)
}
fn debug_flags(&self) -> DebugFlags {
self.render.renderer().debug_flags()
}
#[instrument(level = "trace", parent = &self.span, skip(self, framebuffer))]
#[profiling::function]
fn render<'frame, 'buffer>(
&'frame mut self,
framebuffer: &'frame mut Self::Framebuffer<'buffer>,
size: Size<i32, Physical>,
dst_transform: Transform,
) -> Result<MultiFrame<'render, 'target, 'frame, 'buffer, R, T>, Self::Error>
where
'buffer: 'frame,
{
let target_state = if let Some(target) = self.target.as_mut() {
let buffer_size = size.to_logical(1).to_buffer(1, Transform::Normal);
if let Some((_, dmabuf)) = &target.cached_buffer {
if dmabuf.size() != buffer_size || BufferTrait::format(dmabuf).code != target.format {
*target.cached_buffer = None;
}
};
if target.cached_buffer.is_none() {
match create_shared_dma_framebuffer::<R, T>(buffer_size, self.render, target) {
Ok(dmabuf) => {
*target.cached_buffer = Some((true, dmabuf));
}
Err(err) => {
warn!(
"Error importing dmabuf from {} to {}: {}",
*self.render.node(),
*target.device.node(),
err
);
info!("Falling back to cpu-copy.");
let modifiers = Bind::<Dmabuf>::supported_formats(self.render.renderer())
.unwrap_or_default()
.into_iter()
.filter(|format| format.code == target.format)
.map(|f| f.modifier)
.collect::<Vec<_>>();
let mut dmabuf = self
.render
.allocator()
.create_buffer(
buffer_size.w as u32,
buffer_size.h as u32,
target.format,
&modifiers,
)
.map_err(Error::AllocatorError)?;
{
let mut render_framebuffer = self
.render
.renderer_mut()
.bind(&mut dmabuf)
.map_err(Error::Render)?;
self.render
.renderer_mut()
.render(&mut render_framebuffer, size, dst_transform)
.map_err(Error::Render)?;
}
*target.cached_buffer = Some((false, dmabuf));
}
}
};
let (direct, ref mut dmabuf) = target.cached_buffer.as_mut().unwrap();
let texture = (*direct)
.then(|| {
target
.device
.renderer_mut()
.import_dmabuf(dmabuf, Some(&[Rectangle::from_size(buffer_size)]))
.map_err(Error::Target)
})
.transpose()?;
let framebuffer = self.render.renderer_mut().bind(dmabuf).map_err(Error::Render)?;
Some((&mut target.device, framebuffer, texture, target.format))
} else {
None
};
let node = *self.render.node();
let ptr = &mut self.render as *mut _;
let mut target = None;
let mut new_framebuffer = None;
let frame = match &mut framebuffer.0 {
MultiFramebufferInternal::Render(framebuffer) => self
.render
.renderer_mut()
.render(framebuffer, size, dst_transform)
.map_err(Error::Render)?,
MultiFramebufferInternal::Target(target_framebuffer) => {
let (target_device, render_framebuffer, texture, format) = target_state.unwrap();
target = Some(TargetFrameData {
device: target_device,
framebuffer: target_framebuffer,
texture,
format,
});
let mut render_framebuffer = AliasableBox::from_unique(Box::new(render_framebuffer));
let frame = unsafe {
std::mem::transmute::<
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'_, '_>,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>,
>(
self.render
.renderer_mut()
.render(&mut *render_framebuffer, size, dst_transform)
.map_err(Error::Render)?,
)
};
new_framebuffer = Some(render_framebuffer);
frame
}
};
let span = trace_span!(
parent: &self.span,
"renderer_multi_frame",
direct = target.as_ref().is_some_and(|t| t.texture.is_some()),
)
.entered();
Ok(MultiFrame {
node,
frame: Some(frame),
framebuffer: new_framebuffer,
render: ptr, target,
dst_transform,
size,
damage: Vec::new(),
span,
})
}
#[profiling::function]
fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error> {
self.render.renderer_mut().wait(sync).map_err(Error::Render)
}
#[profiling::function]
fn cleanup_texture_cache(&mut self) -> Result<(), Self::Error> {
if let Some(target) = self.target.as_mut() {
target
.device
.renderer_mut()
.cleanup_texture_cache()
.map_err(Error::Target)?;
}
self.render
.renderer_mut()
.cleanup_texture_cache()
.map_err(Error::Render)?;
Ok(())
}
}
fn create_shared_dma_framebuffer<R, T: GraphicsApi>(
buffer_size: Size<i32, BufferCoords>,
src: &mut R::Device,
target: &mut TargetData<'_, T>,
) -> Result<Dmabuf, Error<R, T>>
where
R: GraphicsApi + 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
let target_formats = ImportDma::dmabuf_formats(target.device.renderer())
.iter()
.filter(|format| format.code == target.format)
.copied()
.collect::<FormatSet>();
let render_formats = Bind::<Dmabuf>::supported_formats(src.renderer()).unwrap_or_default();
let formats = target_formats.intersection(&render_formats);
let target_modifiers = formats
.map(|format| format.modifier)
.filter(|modifier| *modifier != Modifier::Invalid)
.collect::<Vec<_>>();
if target_modifiers.is_empty() {
return Err(Error::ImportFailed);
}
info!(
"Found dma-copy set for {:?} <-> {:?}: {:?}@{:?}",
src.node(),
target.device.node(),
target.format,
target_modifiers,
);
let mut dmabuf = src
.allocator()
.create_buffer(
buffer_size.w as u32,
buffer_size.h as u32,
target.format,
&target_modifiers,
)
.map_err(Error::AllocatorError)?;
src.renderer_mut().bind(&mut dmabuf).map_err(Error::Render)?;
target
.device
.renderer_mut()
.import_dmabuf(&dmabuf, Some(&[Rectangle::from_size(buffer_size)]))
.map_err(Error::Target)?;
Ok(dmabuf)
}
impl<'frame, 'buffer, R: GraphicsApi, T: GraphicsApi> MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
fn flush_frame(&mut self) -> Result<(), Error<R, T>> {
if self.target.is_some() {
let _ = self.finish_internal()?;
let render = unsafe { &mut *self.render };
let frame = unsafe {
std::mem::transmute::<
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'_, '_>,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>,
>(
render
.renderer_mut()
.render(self.framebuffer.as_mut().unwrap(), self.size, self.dst_transform)
.map_err(Error::Render)?,
)
};
self.frame = Some(frame);
}
Ok(())
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn finish_internal(&mut self) -> Result<sync::SyncPoint, Error<R, T>> {
if let Some(frame) = self.frame.take() {
let sync = frame.finish().map_err(Error::Render)?;
let render = unsafe { &mut *self.render };
let damage_area = self.dst_transform.transform_size(self.size.to_logical(1));
let mut damage = std::mem::take(&mut self.damage)
.into_iter()
.map(|rect| rect.to_logical(1).to_buffer(1, self.dst_transform, &damage_area))
.collect::<Vec<_>>();
let buffer_size = self.size.to_logical(1).to_buffer(1, Transform::Normal);
if let Some(target) = self.target.as_mut() {
if let Some(texture) = target.texture.as_ref() {
let damage = damage
.iter()
.map(|rect| rect.to_logical(1, Transform::Normal, &buffer_size).to_physical(1))
.collect::<Vec<_>>();
let mut frame = target
.device
.renderer_mut()
.render(target.framebuffer, self.size, Transform::Normal)
.map_err(Error::Target)?;
frame.wait(&sync).map_err(Error::Target)?;
frame
.clear(Color32F::TRANSPARENT, &damage)
.map_err(Error::Target)?;
frame
.render_texture_from_to(
texture,
Rectangle::from_size(buffer_size).to_f64(),
Rectangle::from_size(self.size),
&damage,
&[Rectangle::from_size(self.size)],
Transform::Normal,
1.0,
)
.map_err(Error::Target)?;
let sync = frame.finish().map_err(Error::Target)?;
render
.renderer_mut()
.cleanup_texture_cache()
.map_err(Error::Render)?;
return Ok(sync);
}
let format = if target
.device
.renderer()
.mem_formats()
.any(|fmt| fmt == target.format)
{
target.format
} else {
Fourcc::Abgr8888
};
damage.dedup();
damage.retain(|rect| rect.overlaps_or_touches(Rectangle::from_size(buffer_size)));
damage.retain(|rect| rect.size.h > 0 && rect.size.w > 0);
let mut copy_rects = damage.iter().cloned().fold(Vec::new(), |new_damage, mut rect| {
let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = new_damage
.into_iter()
.partition(|other: &Rectangle<i32, BufferCoords>| other.overlaps_or_touches(rect));
for overlap in overlapping {
rect = rect.merge(overlap);
}
new_damage.push(rect);
new_damage
});
if copy_rects.len() > MAX_CPU_COPIES {
copy_rects = Vec::from([Rectangle::from_size(buffer_size)]);
}
let mut mappings = Vec::new();
for rect in copy_rects {
let mapping = (
ExportMem::copy_framebuffer(
render.renderer_mut(),
self.framebuffer.as_ref().unwrap(),
rect,
format,
)
.map_err(Error::Render)?,
rect,
);
mappings.push(mapping);
}
if mappings.is_empty() {
render
.renderer_mut()
.cleanup_texture_cache()
.map_err(Error::Render)?;
return Ok(sync::SyncPoint::signaled());
}
let textures = mappings
.into_iter()
.map(|(mapping, rect)| {
let slice = ExportMem::map_texture(render.renderer_mut(), &mapping)
.map_err(Error::Render::<R, T>)?;
let texture = target
.device
.renderer_mut()
.import_memory(slice, TextureMapping::format(&mapping), rect.size, false)
.map_err(Error::Target)?;
Ok((texture, rect))
})
.collect::<Result<Vec<_>, _>>()?;
let mut frame = target
.device
.renderer_mut()
.render(target.framebuffer, self.size, Transform::Normal)
.map_err(Error::Target)?;
for (texture, rect) in textures {
for damage_rect in damage.iter().filter_map(|dmg_rect| dmg_rect.intersection(rect)) {
let dst = damage_rect
.to_logical(1, Transform::Normal, &buffer_size)
.to_physical(1);
let src = Rectangle::new(damage_rect.loc - rect.loc, damage_rect.size).to_f64();
let damage = &[Rectangle::from_size(dst.size)];
frame
.clear(Color32F::TRANSPARENT, &[dst])
.map_err(Error::Target)?;
frame
.render_texture_from_to(
&texture,
src,
dst,
damage,
&[Rectangle::from_size(self.size)],
Transform::Normal,
1.0,
)
.map_err(Error::Target)?;
}
}
let sync = frame.finish().map_err(Error::Target)?;
render
.renderer_mut()
.cleanup_texture_cache()
.map_err(Error::Render)?;
return Ok(sync);
}
render
.renderer_mut()
.cleanup_texture_cache()
.map_err(Error::Render)?;
return Ok(sync);
}
Ok(sync::SyncPoint::signaled())
}
}
impl<R: GraphicsApi, T: GraphicsApi> Drop for MultiFrame<'_, '_, '_, '_, R, T>
where
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
fn drop(&mut self) {
if let Err(err) = self.finish_internal() {
warn!("Ignored error finishing MultiFrame on drop: {}", err);
}
}
}
#[derive(Debug, Clone)]
pub struct MultiTexture(Arc<Mutex<MultiTextureInternal>>);
#[derive(Debug)]
struct MultiTextureInternal {
textures: HashMap<TypeId, HashMap<DrmNode, GpuSingleTexture>>,
size: Size<i32, BufferCoords>,
format: Option<Fourcc>,
#[allow(dead_code)]
buffer_format: Format,
}
unsafe impl Send for MultiTextureInternal {}
type DamageAnyTextureMappings = Vec<(Rectangle<i32, BufferCoords>, Box<dyn Any + 'static>)>;
#[derive(Debug)]
enum GpuSingleTexture {
Direct(Box<dyn Any + 'static>),
Dma {
texture: Box<dyn Any + 'static>,
dmabuf: Dmabuf,
sync: Option<SyncPoint>,
},
Mem {
external_shadow: Option<(Dmabuf, Box<dyn Any + 'static>)>,
texture: Option<Box<dyn Any + 'static>>,
mappings: Option<(DrmNode, DamageAnyTextureMappings)>,
},
}
impl MultiTexture {
#[cfg(feature = "wayland_frontend")]
#[profiling::function]
fn from_surface(
surface: Option<&crate::wayland::compositor::SurfaceData>,
size: Size<i32, BufferCoords>,
buffer_format: Format,
) -> MultiTexture {
let internal = surface
.and_then(|surface| {
surface
.data_map
.get::<Arc<Mutex<MultiTextureInternal>>>()
.cloned()
})
.unwrap_or_else(|| {
Arc::new(Mutex::new(MultiTextureInternal {
textures: HashMap::new(),
size,
format: None,
buffer_format,
}))
});
{
let mut internal = internal.lock().unwrap();
if internal.size != size || internal.buffer_format != buffer_format {
internal.textures.clear();
internal.format = None;
internal.size = size;
internal.buffer_format = buffer_format;
}
}
MultiTexture(internal)
}
fn new(size: Size<i32, BufferCoords>, buffer_format: Format) -> MultiTexture {
MultiTexture(Arc::new(Mutex::new(MultiTextureInternal {
textures: HashMap::new(),
size,
format: None,
buffer_format,
})))
}
pub fn get<A: GraphicsApi + 'static>(
&self,
render: &DrmNode,
) -> Option<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>
where
<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send + 'static,
{
let tex = self.0.lock().unwrap();
tex.textures
.get(&TypeId::of::<A>())
.and_then(|textures| textures.get(render))
.and_then(|texture| match texture {
GpuSingleTexture::Direct(texture) => Some(texture),
GpuSingleTexture::Dma { texture, .. } => Some(texture),
GpuSingleTexture::Mem { texture, .. } => texture.as_ref(),
})
.and_then(|texture| {
<dyn Any>::downcast_ref::<<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>(
&**texture,
)
})
.cloned()
}
fn needs_synchronization<A: GraphicsApi + 'static>(&self, render: &DrmNode) -> Option<SyncPoint>
where
<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
{
let mut tex = self.0.lock().unwrap();
tex.textures
.get_mut(&TypeId::of::<A>())
.and_then(|textures| textures.get_mut(render))
.and_then(|texture| match texture {
GpuSingleTexture::Direct(_) => None,
GpuSingleTexture::Dma { sync, .. } => sync.take(),
GpuSingleTexture::Mem { .. } => None,
})
}
fn insert_texture<A: GraphicsApi + 'static>(
&mut self,
render: DrmNode,
texture: <<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
) where
<<A::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
{
let mut tex = self.0.lock().unwrap();
let format = texture.format();
if format != tex.format && !tex.textures.is_empty() {
warn!(has = ?tex.format, got = ?format, "Multi-SubTexture with wrong format!");
return;
}
tex.format = format;
trace!(
"Inserting into: {:p} for {:?}: {:?}",
Arc::as_ptr(&self.0),
render,
tex
);
let textures = tex.textures.entry(TypeId::of::<A>()).or_default();
textures.insert(render, GpuSingleTexture::Direct(Box::new(texture) as Box<_>));
}
#[cfg(feature = "wayland_frontend")]
fn insert_mapping<
R: GraphicsApi + 'static,
T: GraphicsApi + 'static,
I: Iterator<
Item = (
Rectangle<i32, BufferCoords>,
<<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping,
),
>,
>(
&mut self,
source: DrmNode,
render: DrmNode,
size: Size<i32, BufferCoords>,
new_mappings: I,
) where
<T::Device as ApiDevice>::Renderer: ExportMem,
<<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
{
let mut tex_ref = self.0.lock().unwrap();
let tex = &mut *tex_ref;
let textures = tex.textures.entry(TypeId::of::<R>()).or_default();
let (old_texture, old_mapping, external_shadow) = textures
.remove(&render)
.map(|single| match single {
GpuSingleTexture::Mem {
texture,
mappings,
external_shadow,
} => (texture, mappings, external_shadow),
_ => (None, None, None),
})
.unwrap_or((None, None, None));
let old_texture = old_texture.filter(|tex| {
<dyn Any>::downcast_ref::<<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>(tex)
.map(|tex| tex.size())
== Some(size)
});
let mut mappings = old_mapping
.filter(|(old_src, _)| *old_src == source)
.map(|(_, mappings)| mappings)
.unwrap_or_default();
let format = tex.format;
let new_mappings = new_mappings
.filter(|(_, mapping)| {
let mapping_fmt = TextureMapping::format(mapping);
if old_texture.is_some() && Some(mapping_fmt) != format {
warn!(has = ?format, got = ?mapping_fmt, "Multi-SubTexture Mapping with wrong format!");
false
} else {
tex.format = Some(mapping_fmt);
true
}
})
.map(|(r, m)| (r, Box::new(m) as Box<dyn Any + 'static>))
.collect::<Vec<_>>();
mappings.retain(|(region, _)| {
!new_mappings
.iter()
.any(|(new_region, _)| new_region.contains_rect(*region))
});
mappings.extend(new_mappings);
textures.insert(
render,
GpuSingleTexture::Mem {
mappings: Some((source, mappings)),
texture: old_texture,
external_shadow,
},
);
}
}
impl Texture for MultiTexture {
fn size(&self) -> Size<i32, BufferCoords> {
self.0.lock().unwrap().size
}
fn width(&self) -> u32 {
self.0.lock().unwrap().size.w as u32
}
fn height(&self) -> u32 {
self.0.lock().unwrap().size.h as u32
}
fn format(&self) -> Option<Fourcc> {
self.0.lock().unwrap().format
}
}
impl<R: GraphicsApi, T: GraphicsApi> Frame for MultiFrame<'_, '_, '_, '_, R, T>
where
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
type Error = Error<R, T>;
type TextureId = MultiTexture;
fn context_id(&self) -> ContextId<MultiTexture> {
self.frame.as_ref().unwrap().context_id().map()
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn clear(&mut self, color: Color32F, at: &[Rectangle<i32, Physical>]) -> Result<(), Error<R, T>> {
self.damage.extend(at);
self.frame
.as_mut()
.unwrap()
.clear(color, at)
.map_err(Error::Render)
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn draw_solid(
&mut self,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
color: Color32F,
) -> Result<(), Self::Error> {
self.damage.extend(damage.iter().copied().map(|mut rect| {
rect.loc += dst.loc;
rect
}));
self.frame
.as_mut()
.unwrap()
.draw_solid(dst, damage, color)
.map_err(Error::Render)
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn render_texture_from_to(
&mut self,
texture: &MultiTexture,
src: Rectangle<f64, BufferCoords>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
src_transform: Transform,
alpha: f32,
) -> Result<(), Error<R, T>> {
let sync = texture.needs_synchronization::<R>(&self.node);
if let Some(texture) = texture.get::<R>(&self.node) {
self.damage.extend(damage.iter().copied().map(|mut rect| {
rect.loc += dst.loc;
rect
}));
if let Some(sync) = sync {
if let Err(err) = self.frame.as_mut().unwrap().wait(&sync) {
trace!(?err, "Failed to import sync point, blocking");
let _ = sync.wait(); }
}
self.frame
.as_mut()
.unwrap()
.render_texture_from_to(&texture, src, dst, damage, opaque_regions, src_transform, alpha)
.map_err(Error::Render)
} else {
warn!(
"Failed to render texture {:?}, import for wrong devices {:?}? {:?}",
Arc::as_ptr(&texture.0),
self.node,
texture.0.lock().unwrap(),
);
Ok(())
}
}
fn transformation(&self) -> Transform {
self.frame.as_ref().unwrap().transformation()
}
#[profiling::function]
fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error> {
self.frame.as_mut().unwrap().wait(sync).map_err(Error::Render)
}
#[profiling::function]
fn finish(mut self) -> Result<sync::SyncPoint, Self::Error> {
self.finish_internal()
}
}
#[cfg(feature = "wayland_frontend")]
impl<R: GraphicsApi, T: GraphicsApi> ImportMemWl for MultiRenderer<'_, '_, R, T>
where
<R::Device as ApiDevice>::Renderer: ImportMemWl,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn import_shm_buffer(
&mut self,
buffer: &wl_buffer::WlBuffer,
surface: Option<&crate::wayland::compositor::SurfaceData>,
damage: &[Rectangle<i32, BufferCoords>],
) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
let shm_texture = self
.render
.renderer_mut()
.import_shm_buffer(buffer, surface, damage)
.map_err(Error::Render)?;
let (dimensions, format) = shm::with_buffer_contents(buffer, |_, _, data| {
Ok((
(data.width, data.height).into(),
shm::shm_format_to_fourcc(data.format)
.map(|code| Format {
code,
modifier: Modifier::Linear,
})
.ok_or(Error::ImportFailed)?,
))
})
.map_err(|_| Error::ImportFailed)??;
let mut texture = MultiTexture::from_surface(surface, dimensions, format);
texture.insert_texture::<R>(*self.render.node(), shm_texture);
Ok(texture)
}
fn shm_formats(&self) -> Box<dyn Iterator<Item = wl_shm::Format>> {
ImportMemWl::shm_formats(self.render.renderer())
}
}
impl<R: GraphicsApi, T: GraphicsApi> ImportMem for MultiRenderer<'_, '_, R, T>
where
<R::Device as ApiDevice>::Renderer: ImportMem,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn import_memory(
&mut self,
data: &[u8],
format: Fourcc,
size: Size<i32, BufferCoords>,
flipped: bool,
) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
let mem_texture = self
.render
.renderer_mut()
.import_memory(data, format, size, flipped)
.map_err(Error::Render)?;
let mut texture = MultiTexture::new(
size,
Format {
code: format,
modifier: Modifier::Linear,
},
);
texture.insert_texture::<R>(*self.render.node(), mem_texture);
Ok(texture)
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn update_memory(
&mut self,
texture: &<Self as RendererSuper>::TextureId,
data: &[u8],
region: Rectangle<i32, BufferCoords>,
) -> Result<(), <Self as RendererSuper>::Error> {
let mem_texture = texture
.get::<R>(self.render.node())
.ok_or_else(|| Error::MismatchedDevice(*self.render.node()))?;
self.render
.renderer_mut()
.update_memory(&mem_texture, data, region)
.map_err(Error::Render)
}
fn mem_formats(&self) -> Box<dyn Iterator<Item = Fourcc>> {
ImportMem::mem_formats(self.render.renderer())
}
}
#[cfg(feature = "wayland_frontend")]
impl<R: GraphicsApi, T: GraphicsApi> ImportDmaWl for MultiRenderer<'_, '_, R, T>
where
<R::Device as ApiDevice>::Renderer: ImportDmaWl + ImportMem + ExportMem,
<T::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem,
<<R::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
<<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
T: 'static,
R: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn import_dma_buffer(
&mut self,
buffer: &wl_buffer::WlBuffer,
surface: Option<&SurfaceData>,
damage: &[Rectangle<i32, BufferCoords>],
) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
let dmabuf = get_dmabuf(buffer).expect("import_dma_buffer without checking buffer type?");
let texture = MultiTexture::from_surface(surface, dmabuf.size(), dmabuf.format());
let texture_ref = texture.0.clone();
let res = self.import_dmabuf_internal(dmabuf, texture, Some(damage));
if res.is_ok() {
if let Some(surface) = surface {
surface.data_map.insert_if_missing_threadsafe(|| texture_ref);
}
}
res
}
}
impl<R: GraphicsApi, T: GraphicsApi> ImportDma for MultiRenderer<'_, '_, R, T>
where
<R::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem,
<<R::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
<<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
T: 'static,
R: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
fn dmabuf_formats(&self) -> FormatSet {
ImportDma::dmabuf_formats(self.render.renderer())
}
fn has_dmabuf_format(&self, format: Format) -> bool {
ImportDma::has_dmabuf_format(self.render.renderer(), format)
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn import_dmabuf(
&mut self,
dmabuf: &Dmabuf,
damage: Option<&[Rectangle<i32, BufferCoords>]>,
) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
let texture = MultiTexture::new(dmabuf.size(), dmabuf.format());
self.import_dmabuf_internal(dmabuf, texture, damage)
}
}
fn import_on_src_node<'a, R, T>(
dmabuf: &Dmabuf,
damage: Option<&[Rectangle<i32, BufferCoords>]>,
texture: &mut MultiTexture,
render: &mut R::Device,
mut target: Option<&mut T::Device>,
mut others: impl Iterator<Item = &'a mut R::Device>,
) -> Result<DrmNode, Error<R, T>>
where
R: GraphicsApi + 'static,
<R as GraphicsApi>::Device: 'a,
<<R as GraphicsApi>::Device as ApiDevice>::Renderer: Renderer + ImportDma,
<<<R as GraphicsApi>::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
T: GraphicsApi + 'static,
<<T as GraphicsApi>::Device as ApiDevice>::Renderer: Renderer + ImportDma,
<<<T as GraphicsApi>::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
{
match dmabuf.node() {
Some(node) => {
if node == *render.node() {
let imported = render
.renderer_mut()
.import_dmabuf(dmabuf, damage)
.map_err(Error::Render)?;
texture.insert_texture::<R>(node, imported);
} else if target.as_ref().is_some_and(|target| node == *target.node()) {
let imported = target
.unwrap()
.renderer_mut()
.import_dmabuf(dmabuf, damage)
.map_err(Error::Target)?;
texture.insert_texture::<T>(node, imported);
} else if let Some(other) = others.find(|other| node == *other.node()) {
let imported = other
.renderer_mut()
.import_dmabuf(dmabuf, damage)
.map_err(Error::Render)?;
texture.insert_texture::<R>(node, imported);
} else {
return Err(Error::DeviceMissing);
};
Ok(node)
}
None => {
let node = if let Ok(imported) = render.renderer_mut().import_dmabuf(dmabuf, damage) {
let node = *render.node();
texture.insert_texture::<R>(node, imported);
node
} else if let Some(imported) = target
.as_mut()
.and_then(|target| target.renderer_mut().import_dmabuf(dmabuf, damage).ok())
{
let node = *target.as_ref().unwrap().node();
texture.insert_texture::<T>(node, imported);
node
} else if let Some((node, imported)) = others.find_map(|other| {
other
.renderer_mut()
.import_dmabuf(dmabuf, damage)
.ok()
.map(|imported| (*other.node(), imported))
}) {
texture.insert_texture::<R>(node, imported);
node
} else {
return Err(Error::DeviceMissing);
};
dmabuf.set_node(node);
Ok(node)
}
}
}
fn dma_shadow_copy<S, T>(
src_texture: &<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
damage: Option<&[Rectangle<i32, BufferCoords>]>,
slot: &mut Option<(Dmabuf, Box<dyn Any + 'static>, Option<SyncPoint>)>,
src: &mut S::Device,
mut target: Option<&mut T::Device>,
) -> Result<(), Error<S, T>>
where
S: GraphicsApi,
T: GraphicsApi,
<S::Device as ApiDevice>::Renderer: Renderer + ImportDma + Bind<Dmabuf>,
<T::Device as ApiDevice>::Renderer: Renderer + ImportDma,
<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
{
let format = src_texture.format().unwrap_or(Fourcc::Abgr8888);
let read_formats = if let Some(target) = target.as_ref() {
ImportDma::dmabuf_formats(target.renderer())
} else {
ImportDma::dmabuf_formats(src.renderer())
}
.iter()
.filter(|f| f.code == format)
.copied()
.collect::<FormatSet>();
let write_formats = Bind::<Dmabuf>::supported_formats(src.renderer()).unwrap_or_default();
let modifiers = read_formats
.intersection(&write_formats)
.map(|f| f.modifier)
.collect::<Vec<_>>();
if modifiers.is_empty() {
return Err(Error::ImportFailed);
}
let ((shadow_buffer, _, existing_sync_point), is_new_buffer) = if slot
.as_ref()
.is_some_and(|(buffer, _, _)| buffer.format().code == format)
{
(slot.as_mut().unwrap(), false)
} else {
let shadow_buffer = src
.allocator()
.create_buffer(src_texture.width(), src_texture.height(), format, &modifiers)
.map_err(Error::AllocatorError)?;
let target_texture = if let Some(target) = target.as_mut() {
Box::<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>::new(
target
.renderer_mut()
.import_dmabuf(&shadow_buffer, None)
.map_err(Error::Target)?,
) as Box<dyn Any + 'static>
} else {
Box::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>::new(
src.renderer_mut()
.import_dmabuf(&shadow_buffer, None)
.map_err(Error::Render)?,
) as Box<dyn Any + 'static>
};
(slot.get_or_insert((shadow_buffer, target_texture, None)), true)
};
let src_renderer = src.renderer_mut();
if let Some(sync) = existing_sync_point.take() {
if let Err(err) = src_renderer.wait(&sync) {
debug!(?err, "Unable to wait for existing sync_point, blocking..");
let _ = sync.wait(); }
}
let mut framebuffer = src_renderer.bind(shadow_buffer).map_err(Error::Render)?;
let shadow_size = Size::from((src_texture.width() as i32, src_texture.height() as i32));
let mut frame = src_renderer
.render(&mut framebuffer, shadow_size, Transform::Normal)
.map_err(Error::Render)?;
let damage_slice = [Rectangle::from_size(shadow_size)];
let damage = unsafe {
std::mem::transmute::<Option<&[Rectangle<i32, BufferCoords>]>, Option<&[Rectangle<i32, Physical>]>>(
damage,
)
} .filter(|_| !is_new_buffer)
.unwrap_or(&damage_slice);
frame
.clear(Color32F::TRANSPARENT, damage)
.map_err(Error::Render)?;
frame
.render_texture_from_to(
src_texture,
Rectangle::from_size(src_texture.size()).to_f64(),
Rectangle::from_size(shadow_size),
damage,
&[],
Transform::Normal,
1.0,
)
.map_err(Error::Render)?;
*existing_sync_point = Some(frame.finish().map_err(Error::Render)?);
Ok(())
}
type BoxedTextureMappingAndDamage<S> = (
Box<<<<S as GraphicsApi>::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping>,
Rectangle<i32, BufferCoords>,
);
type MemTexture<S, T> = (
Option<Vec<BoxedTextureMappingAndDamage<S>>>,
Option<Box<<<<T as GraphicsApi>::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>>,
);
fn mem_copy<S, T>(
src_texture: &<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
damage: Option<&[Rectangle<i32, BufferCoords>]>,
slot: &mut Option<MemTexture<S, T>>,
src: &mut S::Device,
target: &mut T::Device,
) -> Result<(), Error<S, T>>
where
S: GraphicsApi,
T: GraphicsApi,
<S::Device as ApiDevice>::Renderer: Renderer + ExportMem + Bind<Dmabuf>,
<T::Device as ApiDevice>::Renderer: Renderer + ImportMem,
{
let format = src_texture
.format()
.filter(|format| target.renderer().mem_formats().any(|fmt| fmt == *format))
.unwrap_or(Fourcc::Abgr8888);
let texture_rect = Rectangle::from_size((src_texture.width() as i32, src_texture.height() as i32).into());
let damage = damage.map(|damage| {
damage
.iter()
.flat_map(|rect| rect.intersection(texture_rect))
.fold(Vec::<Rectangle<i32, BufferCoords>>::new(), |damage, mut rect| {
let (overlapping, mut new_damage): (Vec<_>, Vec<_>) = damage
.into_iter()
.partition(|other| other.overlaps_or_touches(rect));
for overlap in overlapping {
rect = rect.merge(overlap);
}
new_damage.push(rect);
new_damage
})
});
if slot.is_some() {
let (mapping, texture) = slot.as_mut().unwrap();
let mappings = match mapping.take() {
Some(mut mappings) => {
mappings.retain(|(mapping, _)| TextureMapping::format(&**mapping) == format);
let damage_slice = [texture_rect];
let new_damage = damage
.as_deref()
.unwrap_or(&damage_slice)
.iter()
.filter(|rect| !mappings.iter().any(|(_, region)| region.contains_rect(**rect)))
.copied()
.collect::<Vec<_>>();
if texture.is_none()
&& (mappings.len() != 1
|| <dyn TextureMapping>::size(&*mappings[0].0) != texture_rect.size
|| !new_damage.is_empty())
{
let mapping = src
.renderer_mut()
.copy_texture(src_texture, texture_rect, format)
.map_err(Error::Render)?;
trace!("Creating mapping for: {:?}", damage);
mappings = vec![(Box::new(mapping), texture_rect)];
} else {
mappings.extend(
new_damage
.into_iter()
.map(|damage| {
let mapping = src
.renderer_mut()
.copy_texture(src_texture, damage, format)
.map_err(Error::Render)?;
trace!("Creating mapping for: {:?}", damage);
Ok((Box::new(mapping), damage))
})
.collect::<Result<Vec<_>, Error<S, T>>>()?,
);
}
mappings
}
None => {
let mapping = src
.renderer_mut()
.copy_texture(src_texture, texture_rect, format)
.map_err(Error::Render)?;
trace!("Creating mapping for: {:?}", damage);
vec![(Box::new(mapping), texture_rect)]
}
};
for (mapping, damage) in mappings {
let data = src.renderer_mut().map_texture(&mapping).map_err(Error::Render)?;
if let Some(texture) = texture.as_mut() {
trace!(
"Updating texture {:?} with mapping at {:?}",
texture.size(),
damage,
);
target
.renderer_mut()
.update_memory(texture, data, damage)
.map_err(Error::Target)?;
} else {
trace!("Importing mapping as full buffer {:?}", mapping.size());
let target_texture = target
.renderer_mut()
.import_memory(data, format, texture_rect.size, false)
.map_err(Error::Target)?;
*texture = Some(Box::new(target_texture));
}
}
} else {
let mapping = src
.renderer_mut()
.copy_texture(src_texture, texture_rect, format)
.map_err(Error::Render)?;
trace!("Importing mapping as full buffer {:?}", mapping.size());
let data = src.renderer_mut().map_texture(&mapping).map_err(Error::Render)?;
let target_texture = target
.renderer_mut()
.import_memory(data, format, texture_rect.size, false)
.map_err(Error::Target)?;
*slot = Some((None, Some(Box::new(target_texture))));
};
Ok(())
}
fn texture_copy<S, T>(
src: &mut S::Device,
target: &mut T::Device,
src_texture: &<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId,
target_texture: &mut Option<GpuSingleTexture>,
damage: Option<&[Rectangle<i32, BufferCoords>]>,
) -> Result<(), Error<S, T>>
where
S: GraphicsApi,
T: GraphicsApi,
<S::Device as ApiDevice>::Renderer: Renderer + ImportDma + ExportMem + Bind<Dmabuf>,
<<S::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
<T::Device as ApiDevice>::Renderer: Renderer + ImportDma + ImportMem,
<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: 'static,
{
if matches!(&target_texture, Some(GpuSingleTexture::Direct(_)))
|| matches!(
&target_texture,
Some(GpuSingleTexture::Mem { mappings: Some((node, _)), .. }) if node != src.node()
)
{
*target_texture = None;
}
match target_texture.take() {
Some(GpuSingleTexture::Direct(_)) => unreachable!(),
Some(GpuSingleTexture::Mem {
mut external_shadow,
texture,
mappings,
}) => {
if let Some((dmabuf, texture)) = external_shadow.take() {
let mut slot = Some((dmabuf, texture, None));
dma_shadow_copy::<S, S>(src_texture, damage, &mut slot, src, None)
.map_err(Error::generalize::<T>)?;
external_shadow = slot.map(|(dmabuf, texture, sync_point)| {
if let Some(sync) = sync_point {
src.renderer_mut().wait(&sync).unwrap_or_else(|_| {
let _ = sync.wait();
});
}
(dmabuf, texture)
});
}
let mut slot = Some((
mappings.map(|(_, mappings)| mappings.into_iter().map(|(damage, mapping)|
(mapping.downcast::<<<S::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping>().unwrap(), damage)
).collect::<Vec<_>>()),
texture.map(|texture| texture.downcast::<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>().unwrap())
));
let src_texture = external_shadow
.as_ref()
.map(|(_, texture)| {
texture
.downcast_ref::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
.unwrap()
})
.unwrap_or(src_texture);
let res = mem_copy::<S, T>(src_texture, damage, &mut slot, src, target);
*target_texture = slot.map(|(mappings, texture)| GpuSingleTexture::Mem {
external_shadow,
texture: texture.map(|texture| texture as Box<dyn Any + 'static>),
mappings: mappings.map(|mappings| {
(
*src.node(),
mappings
.into_iter()
.map(|(mapping, damage)| (damage, mapping as Box<dyn Any + 'static>))
.collect(),
)
}),
});
res
}
Some(GpuSingleTexture::Dma {
texture,
dmabuf,
sync,
}) => {
let mut slot = Some((dmabuf, texture, sync));
let res = dma_shadow_copy::<S, T>(src_texture, damage, &mut slot, src, Some(target));
*target_texture = slot.map(|(dmabuf, texture, sync)| GpuSingleTexture::Dma {
texture,
dmabuf,
sync,
});
res
}
None => {
let mut slot = None;
match dma_shadow_copy::<S, T>(src_texture, damage, &mut slot, src, Some(target)) {
Ok(()) => {
*target_texture = slot.map(|(dmabuf, texture, sync)| GpuSingleTexture::Dma {
texture: texture as Box<dyn Any + 'static>,
dmabuf,
sync,
});
Ok(())
}
Err(err) => {
trace!(?err, "Dma shadow copy failed, falling back to cpu");
let mut external_shadow = None;
if !ExportMem::can_read_texture(src.renderer_mut(), src_texture).map_err(Error::Render)? {
let mut slot = None;
dma_shadow_copy::<S, S>(src_texture, damage, &mut slot, src, None)
.map_err(Error::generalize::<T>)?;
external_shadow = slot.map(|(dmabuf, texture, sync_point)| {
if let Some(sync) = sync_point {
src.renderer_mut().wait(&sync).unwrap_or_else(|_| {
let _ = sync.wait();
});
}
(dmabuf, texture as Box<dyn Any + 'static>)
});
};
let mut slot = None;
let src_texture = external_shadow
.as_ref()
.map(|(_, texture)| {
texture
.downcast_ref::<<<S::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
.unwrap()
})
.unwrap_or(src_texture);
let res = mem_copy::<S, T>(src_texture, damage, &mut slot, src, target);
*target_texture = slot.map(|(mappings, texture)| GpuSingleTexture::Mem {
texture: texture.map(|texture| texture as Box<dyn Any + 'static>),
mappings: mappings.map(|mappings| {
(
*src.node(),
mappings
.into_iter()
.map(|(mapping, damage)| (damage, mapping as Box<dyn Any + 'static>))
.collect(),
)
}),
external_shadow,
});
res
}
}
}
}
}
impl<R: GraphicsApi, T: GraphicsApi> MultiRenderer<'_, '_, R, T>
where
<R::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem,
<<R::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
<<T::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: 'static,
T: 'static,
R: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[profiling::function]
fn import_dmabuf_internal(
&mut self,
dmabuf: &Dmabuf,
mut texture: MultiTexture,
damage: Option<&[Rectangle<i32, BufferCoords>]>,
) -> Result<<Self as RendererSuper>::TextureId, <Self as RendererSuper>::Error> {
let src_node = import_on_src_node::<R, T>(
dmabuf,
damage,
&mut texture,
self.render,
self.target.as_mut().map(|target| &mut *target.device),
self.other_renderers.iter_mut().map(|d| &mut **d),
)?;
if src_node == *self.render.node() {
Ok(texture)
} else {
if self
.target
.as_ref()
.is_some_and(|target| src_node == *target.device.node())
{
let mut texture_internal = texture.0.lock().unwrap();
texture_internal.textures.entry(TypeId::of::<R>()).or_default();
let (mut src_api_textures, other_api_textures) = texture_internal
.textures
.iter_mut()
.partition::<Vec<_>, _>(|(k, _v)| **k == TypeId::of::<T>());
let mut target_api_textures = other_api_textures
.into_iter()
.find(|(k, _)| **k == TypeId::of::<R>());
let mut target_texture = if TypeId::of::<T>() == TypeId::of::<R>() {
src_api_textures[0].1.remove(self.render.node())
} else {
target_api_textures.as_mut().unwrap().1.remove(self.render.node())
};
let src_texture = match src_api_textures[0].1.get(&src_node).unwrap() {
GpuSingleTexture::Direct(tex) => tex
.downcast_ref::<<<T::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
.unwrap(),
_ => unreachable!(),
};
let res = texture_copy::<T, R>(
self.target.as_mut().unwrap().device,
self.render,
src_texture,
&mut target_texture,
damage,
)
.map_err(Error::transpose);
if let Some(target_texture) = target_texture.filter(|_| res.is_ok()) {
if TypeId::of::<T>() == TypeId::of::<R>() {
src_api_textures[0].1.insert(*self.render.node(), target_texture);
} else {
target_api_textures
.unwrap()
.1
.insert(*self.render.node(), target_texture);
}
}
std::mem::drop(texture_internal);
res.map(|_| texture)
} else if let Some(other) = self
.other_renderers
.iter_mut()
.find(|other| src_node == *other.node())
{
let mut texture_internal = texture.0.lock().unwrap();
let api_textures = texture_internal.textures.get_mut(&TypeId::of::<R>()).unwrap();
let mut target_texture = api_textures.remove(self.render.node());
let src_texture = match api_textures.get(&src_node).unwrap() {
GpuSingleTexture::Direct(tex) => tex
.downcast_ref::<<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId>()
.unwrap(),
_ => unreachable!(),
};
let res = texture_copy::<R, R>(other, self.render, src_texture, &mut target_texture, damage)
.map_err(Error::generalize::<T>);
if let Some(target_texture) = target_texture.filter(|_| res.is_ok()) {
api_textures.insert(*self.render.node(), target_texture);
}
std::mem::drop(texture_internal);
res.map(|_| texture)
} else {
Err(Error::DeviceMissing)
}
}
}
}
pub struct MultiTextureMapping<A: GraphicsApi, B: GraphicsApi>(TextureMappingInternal<A, B>)
where
<A::Device as ApiDevice>::Renderer: ExportMem,
<B::Device as ApiDevice>::Renderer: ExportMem;
enum TextureMappingInternal<A: GraphicsApi, B: GraphicsApi>
where
<A::Device as ApiDevice>::Renderer: ExportMem,
<B::Device as ApiDevice>::Renderer: ExportMem,
{
Either(<<A::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping),
Or(<<B::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping),
}
impl<A: GraphicsApi, B: GraphicsApi> fmt::Debug for MultiTextureMapping<A, B>
where
<A::Device as ApiDevice>::Renderer: ExportMem,
<B::Device as ApiDevice>::Renderer: ExportMem,
<<A::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: fmt::Debug,
<<B::Device as ApiDevice>::Renderer as ExportMem>::TextureMapping: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
TextureMappingInternal::Either(ref mapping) => mapping.fmt(f),
TextureMappingInternal::Or(ref mapping) => mapping.fmt(f),
}
}
}
impl<A: GraphicsApi, B: GraphicsApi> Texture for MultiTextureMapping<A, B>
where
<A::Device as ApiDevice>::Renderer: ExportMem,
<B::Device as ApiDevice>::Renderer: ExportMem,
{
fn size(&self) -> Size<i32, BufferCoords> {
match self {
MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => x.size(),
MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => x.size(),
}
}
fn width(&self) -> u32 {
match self {
MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => x.width(),
MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => x.width(),
}
}
fn height(&self) -> u32 {
match self {
MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => x.height(),
MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => x.height(),
}
}
fn format(&self) -> Option<Fourcc> {
match self {
MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => Texture::format(x),
MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => Texture::format(x),
}
}
}
impl<A: GraphicsApi, B: GraphicsApi> TextureMapping for MultiTextureMapping<A, B>
where
<A::Device as ApiDevice>::Renderer: ExportMem,
<B::Device as ApiDevice>::Renderer: ExportMem,
{
fn flipped(&self) -> bool {
match self {
MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => x.flipped(),
MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => x.flipped(),
}
}
fn format(&self) -> Fourcc {
match self {
MultiTextureMapping::<A, B>(TextureMappingInternal::Either(x)) => TextureMapping::format(x),
MultiTextureMapping::<A, B>(TextureMappingInternal::Or(x)) => TextureMapping::format(x),
}
}
}
impl<R: GraphicsApi, T: GraphicsApi> ExportMem for MultiRenderer<'_, '_, R, T>
where
<T::Device as ApiDevice>::Renderer: ExportMem,
<R::Device as ApiDevice>::Renderer: ExportMem,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
type TextureMapping = MultiTextureMapping<T, R>;
#[instrument(level = "trace", parent = &self.span, skip(self, framebuffer))]
#[profiling::function]
fn copy_framebuffer(
&mut self,
framebuffer: &MultiFramebuffer<'_, R, T>,
region: Rectangle<i32, BufferCoords>,
format: Fourcc,
) -> Result<Self::TextureMapping, <Self as RendererSuper>::Error> {
match &framebuffer.0 {
MultiFramebufferInternal::Target(fb) => {
let target = self.target.as_mut().unwrap();
target
.device
.renderer_mut()
.copy_framebuffer(fb, region, format)
.map(|mapping| MultiTextureMapping(TextureMappingInternal::Either(mapping)))
.map_err(Error::Target)
}
MultiFramebufferInternal::Render(fb) => self
.render
.renderer_mut()
.copy_framebuffer(fb, region, format)
.map(|mapping| MultiTextureMapping(TextureMappingInternal::Or(mapping)))
.map_err(Error::Render),
}
}
#[instrument(level = "trace", parent = &self.span, skip(self))]
#[profiling::function]
fn copy_texture(
&mut self,
texture: &Self::TextureId,
region: Rectangle<i32, BufferCoords>,
format: Fourcc,
) -> Result<Self::TextureMapping, Self::Error> {
let tex = texture
.get::<R>(self.render.node())
.ok_or_else(|| Error::MismatchedDevice(*self.render.node()))?;
self.render
.renderer_mut()
.copy_texture(&tex, region, format)
.map(|mapping| MultiTextureMapping(TextureMappingInternal::Or(mapping)))
.map_err(Error::Render)
}
fn can_read_texture(&mut self, texture: &Self::TextureId) -> Result<bool, Self::Error> {
let tex = texture
.get::<R>(self.render.node())
.ok_or_else(|| Error::MismatchedDevice(*self.render.node()))?;
self.render
.renderer_mut()
.can_read_texture(&tex)
.map_err(Error::Render)
}
#[instrument(level = "trace", parent = &self.span, skip(self, texture_mapping))]
#[profiling::function]
fn map_texture<'c>(
&mut self,
texture_mapping: &'c Self::TextureMapping,
) -> Result<&'c [u8], <Self as RendererSuper>::Error> {
match texture_mapping {
MultiTextureMapping(TextureMappingInternal::Either(target_mapping)) => self
.target
.as_mut()
.unwrap()
.device
.renderer_mut()
.map_texture(target_mapping)
.map_err(Error::Target),
MultiTextureMapping(TextureMappingInternal::Or(render_mapping)) => self
.render
.renderer_mut()
.map_texture(render_mapping)
.map_err(Error::Render),
}
}
}
impl<'frame, 'buffer, R: GraphicsApi, T: GraphicsApi> BlitFrame<MultiFramebuffer<'buffer, R, T>>
for MultiFrame<'_, '_, 'frame, 'buffer, R, T>
where
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Frame<'frame, 'buffer>:
BlitFrame<<<R::Device as ApiDevice>::Renderer as RendererSuper>::Framebuffer<'buffer>>,
<T::Device as ApiDevice>::Renderer: Blit,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[instrument(level = "trace", parent = &self.span, skip(self, to))]
#[profiling::function]
fn blit_to(
&mut self,
to: &mut MultiFramebuffer<'buffer, R, T>,
src: Rectangle<i32, Physical>,
dst: Rectangle<i32, Physical>,
filter: TextureFilter,
) -> Result<(), Self::Error> {
self.flush_frame()?;
if let Some(target) = self.target.as_mut() {
let MultiFramebufferInternal::Target(ref mut to_fb) = &mut to.0 else {
unreachable!()
};
target
.device
.renderer_mut()
.blit(target.framebuffer, to_fb, src, dst, filter)
.map_err(Error::Target)
} else {
let MultiFramebufferInternal::Render(ref mut to_fb) = &mut to.0 else {
unreachable!()
};
self.frame
.as_mut()
.unwrap()
.blit_to(to_fb, src, dst, filter)
.map_err(Error::Render)
}
}
#[instrument(level = "trace", parent = &self.span, skip(self, from))]
#[profiling::function]
fn blit_from(
&mut self,
from: &MultiFramebuffer<'buffer, R, T>,
src: Rectangle<i32, Physical>,
dst: Rectangle<i32, Physical>,
filter: TextureFilter,
) -> Result<(), Self::Error> {
self.flush_frame()?;
if let Some(target) = self.target.as_mut() {
let MultiFramebufferInternal::Target(ref from_fb) = &from.0 else {
unreachable!()
};
target
.device
.renderer_mut()
.blit(from_fb, target.framebuffer, src, dst, filter)
.map_err(Error::Target)
} else {
let MultiFramebufferInternal::Render(ref from_fb) = &from.0 else {
unreachable!()
};
self.frame
.as_mut()
.unwrap()
.blit_from(from_fb, src, dst, filter)
.map_err(Error::Render)
}
}
}
impl<R: GraphicsApi, T: GraphicsApi> Blit for MultiRenderer<'_, '_, R, T>
where
<R::Device as ApiDevice>::Renderer: Blit,
<T::Device as ApiDevice>::Renderer: Blit,
R: 'static,
R::Error: 'static,
T::Error: 'static,
<R::Device as ApiDevice>::Renderer: Bind<Dmabuf> + ExportMem + ImportDma + ImportMem,
<T::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::TextureId: Clone + Send,
<<R::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[instrument(level = "trace", parent = &self.span, skip(self, from, to))]
#[profiling::function]
fn blit(
&mut self,
from: &MultiFramebuffer<'_, R, T>,
to: &mut MultiFramebuffer<'_, R, T>,
src: Rectangle<i32, Physical>,
dst: Rectangle<i32, Physical>,
filter: TextureFilter,
) -> Result<(), <Self as RendererSuper>::Error> {
if let Some(target) = self.target.as_mut() {
let MultiFramebufferInternal::Target(ref from_fb) = &from.0 else {
unreachable!()
};
let MultiFramebufferInternal::Target(ref mut to_fb) = &mut to.0 else {
unreachable!()
};
target
.device
.renderer_mut()
.blit(from_fb, to_fb, src, dst, filter)
.map_err(Error::Target)
} else {
let MultiFramebufferInternal::Render(ref from_fb) = &from.0 else {
unreachable!()
};
let MultiFramebufferInternal::Render(ref mut to_fb) = &mut to.0 else {
unreachable!()
};
self.render
.renderer_mut()
.blit(from_fb, to_fb, src, dst, filter)
.map_err(Error::Render)
}
}
}