Make rendering generic over renderer

This commit is contained in:
Ivan Molodetskikh
2024-01-03 11:38:36 +04:00
parent fd1d4b07fd
commit de3fc2def0
6 changed files with 448 additions and 51 deletions
+1 -1
View File
@@ -898,7 +898,7 @@ impl Tty {
let renderer = &mut device.gles;
// Render the elements.
let elements = niri.render(renderer, output, true);
let elements = niri.render::<GlesRenderer>(renderer, output, true);
// Hand them over to the DRM.
let drm_compositor = &mut surface.compositor;
+1 -1
View File
@@ -140,7 +140,7 @@ impl Winit {
let _span = tracy_client::span!("Winit::render");
// Render the elements.
let elements = niri.render(self.backend.renderer(), output, true);
let elements = niri.render::<GlesRenderer>(self.backend.renderer(), output, true);
// Hand them over to winit.
self.backend.bind().unwrap();
+1
View File
@@ -12,6 +12,7 @@ mod handlers;
mod input;
mod layout;
mod niri;
mod render_helpers;
mod screenshot_ui;
mod utils;
mod watcher;
+200 -34
View File
@@ -17,12 +17,15 @@ use smithay::backend::renderer::element::surface::{
};
use smithay::backend::renderer::element::texture::TextureRenderElement;
use smithay::backend::renderer::element::{
default_primary_scanout_output_compare, render_elements, AsRenderElements, Kind, RenderElement,
RenderElementStates,
default_primary_scanout_output_compare, AsRenderElements, Element, Id, Kind, RenderElement,
RenderElementStates, UnderlyingStorage,
};
use smithay::backend::renderer::gles::{
GlesError, GlesFrame, GlesMapping, GlesRenderer, GlesTexture,
};
use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture};
use smithay::backend::renderer::sync::SyncPoint;
use smithay::backend::renderer::{Bind, ExportMem, Frame, ImportAll, Offscreen, Renderer};
use smithay::backend::renderer::utils::CommitCounter;
use smithay::backend::renderer::{Bind, ExportMem, Frame, Offscreen, Renderer};
use smithay::desktop::utils::{
bbox_from_surface_tree, output_update, send_dmabuf_feedback_surface_tree,
send_frames_surface_tree, surface_presentation_feedback_flags_from_states,
@@ -50,7 +53,7 @@ use smithay::reexports::wayland_server::backend::{
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::reexports::wayland_server::{Display, DisplayHandle};
use smithay::utils::{
ClockSource, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Transform,
Buffer, ClockSource, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Transform,
SERIAL_COUNTER,
};
use smithay::wayland::compositor::{
@@ -92,6 +95,7 @@ use crate::handlers::configure_lock_surface;
use crate::input::TabletData;
use crate::layout::{Layout, MonitorRenderElement};
use crate::pw_utils::{Cast, PipeWire};
use crate::render_helpers::{NiriRenderer, PrimaryGpuTextureRenderElement};
use crate::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
use crate::utils::{
center, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
@@ -1303,11 +1307,11 @@ impl Niri {
};
}
pub fn pointer_element(
pub fn pointer_element<R: NiriRenderer>(
&self,
renderer: &mut GlesRenderer,
renderer: &mut R,
output: &Output,
) -> Vec<OutputRenderElements<GlesRenderer>> {
) -> Vec<OutputRenderElements<R>> {
let _span = tracy_client::span!("Niri::pointer_element");
let output_scale = output.current_scale();
let output_pos = self.global_space.output_geometry(output).unwrap().loc;
@@ -1351,19 +1355,23 @@ impl Niri {
let pointer_pos =
(pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);
let texture = self
.cursor_texture_cache
.get(renderer, icon, scale, &cursor, idx);
let texture = self.cursor_texture_cache.get(
renderer.as_gles_renderer(),
icon,
scale,
&cursor,
idx,
);
let pointer_elements = vec![OutputRenderElements::NamedPointer(
TextureRenderElement::from_texture_buffer(
PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
pointer_pos.to_f64(),
&texture,
None,
None,
None,
Kind::Cursor,
),
)),
)];
(pointer_elements, pointer_pos)
@@ -1496,12 +1504,12 @@ impl Niri {
}
}
pub fn render(
pub fn render<R: NiriRenderer>(
&self,
renderer: &mut GlesRenderer,
renderer: &mut R,
output: &Output,
include_pointer: bool,
) -> Vec<OutputRenderElements<GlesRenderer>> {
) -> Vec<OutputRenderElements<R>> {
let _span = tracy_client::span!("Niri::render");
let output_scale = Scale::from(output.current_scale().fractional_scale());
@@ -1573,8 +1581,7 @@ impl Niri {
// Get layer-shell elements.
let layer_map = layer_map_for_output(output);
let mut extend_from_layer = |elements: &mut Vec<OutputRenderElements<GlesRenderer>>,
layer| {
let mut extend_from_layer = |elements: &mut Vec<OutputRenderElements<R>>, layer| {
let iter = layer_map
.layers_on(layer)
.filter_map(|surface| {
@@ -2059,7 +2066,8 @@ impl Niri {
let dmabuf = cast.dmabufs.borrow()[&fd].clone();
// FIXME: Hidden / embedded / metadata cursor
let elements = elements.get_or_insert_with(|| self.render(renderer, output, true));
let elements = elements
.get_or_insert_with(|| self.render::<GlesRenderer>(renderer, output, true));
if let Err(err) = render_to_dmabuf(renderer, dmabuf, size, scale, elements) {
error!("error rendering to dmabuf: {err:?}");
@@ -2126,7 +2134,7 @@ impl Niri {
.filter_map(|output| {
let size = output.current_mode().unwrap().size;
let scale = Scale::from(output.current_scale().fractional_scale());
let elements = self.render(renderer, &output, true);
let elements = self.render::<GlesRenderer>(renderer, &output, true);
let res = render_to_texture(renderer, size, scale, Fourcc::Abgr8888, &elements);
let screenshot = match res {
@@ -2153,7 +2161,7 @@ impl Niri {
let size = output.current_mode().unwrap().size;
let scale = Scale::from(output.current_scale().fractional_scale());
let elements = self.render(renderer, output, true);
let elements = self.render::<GlesRenderer>(renderer, output, true);
let pixels = render_to_vec(renderer, size, scale, Fourcc::Abgr8888, &elements)?;
self.save_screenshot(size, pixels)
@@ -2277,7 +2285,7 @@ impl Niri {
size.w = max(size.w, geom.loc.x + geom.size.w);
size.h = max(size.h, geom.loc.y + geom.size.h);
let output_elements = self.render(renderer, &output, include_pointer);
let output_elements = self.render::<GlesRenderer>(renderer, &output, include_pointer);
elements.extend(output_elements.into_iter().map(|elem| {
RelocateRenderElement::from_element(elem, geom.loc, Relocate::Relative)
}));
@@ -2382,16 +2390,6 @@ impl Niri {
}
}
render_elements! {
#[derive(Debug)]
pub OutputRenderElements<R> where R: ImportAll;
Monitor = MonitorRenderElement<R>,
Wayland = WaylandSurfaceRenderElement<R>,
NamedPointer = TextureRenderElement<<R as Renderer>::TextureId>,
SolidColor = SolidColorRenderElement,
ScreenshotUi = ScreenshotUiRenderElement<R>,
}
#[derive(Default)]
pub struct ClientState {
pub compositor_state: CompositorClientState,
@@ -2482,8 +2480,6 @@ fn render_to_dmabuf(
scale: Scale<f64>,
elements: &[OutputRenderElements<GlesRenderer>],
) -> anyhow::Result<()> {
use smithay::backend::renderer::element::Element;
let _span = tracy_client::span!("render_to_dmabuf");
let output_rect = Rectangle::from_loc_and_size((0, 0), size);
@@ -2505,3 +2501,173 @@ fn render_to_dmabuf(
Ok(())
}
// Manual RenderElement implementation due to AsGlesFrame requirement.
#[derive(Debug)]
pub enum OutputRenderElements<R: NiriRenderer> {
Monitor(MonitorRenderElement<R>),
Wayland(WaylandSurfaceRenderElement<R>),
NamedPointer(PrimaryGpuTextureRenderElement),
SolidColor(SolidColorRenderElement),
ScreenshotUi(ScreenshotUiRenderElement),
}
impl<R: NiriRenderer> Element for OutputRenderElements<R> {
fn id(&self) -> &Id {
match self {
Self::Monitor(elem) => elem.id(),
Self::Wayland(elem) => elem.id(),
Self::NamedPointer(elem) => elem.id(),
Self::SolidColor(elem) => elem.id(),
Self::ScreenshotUi(elem) => elem.id(),
}
}
fn current_commit(&self) -> CommitCounter {
match self {
Self::Monitor(elem) => elem.current_commit(),
Self::Wayland(elem) => elem.current_commit(),
Self::NamedPointer(elem) => elem.current_commit(),
Self::SolidColor(elem) => elem.current_commit(),
Self::ScreenshotUi(elem) => elem.current_commit(),
}
}
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
match self {
Self::Monitor(elem) => elem.geometry(scale),
Self::Wayland(elem) => elem.geometry(scale),
Self::NamedPointer(elem) => elem.geometry(scale),
Self::SolidColor(elem) => elem.geometry(scale),
Self::ScreenshotUi(elem) => elem.geometry(scale),
}
}
fn transform(&self) -> Transform {
match self {
Self::Monitor(elem) => elem.transform(),
Self::Wayland(elem) => elem.transform(),
Self::NamedPointer(elem) => elem.transform(),
Self::SolidColor(elem) => elem.transform(),
Self::ScreenshotUi(elem) => elem.transform(),
}
}
fn src(&self) -> Rectangle<f64, Buffer> {
match self {
Self::Monitor(elem) => elem.src(),
Self::Wayland(elem) => elem.src(),
Self::NamedPointer(elem) => elem.src(),
Self::SolidColor(elem) => elem.src(),
Self::ScreenshotUi(elem) => elem.src(),
}
}
fn damage_since(
&self,
scale: Scale<f64>,
commit: Option<CommitCounter>,
) -> Vec<Rectangle<i32, Physical>> {
match self {
Self::Monitor(elem) => elem.damage_since(scale, commit),
Self::Wayland(elem) => elem.damage_since(scale, commit),
Self::NamedPointer(elem) => elem.damage_since(scale, commit),
Self::SolidColor(elem) => elem.damage_since(scale, commit),
Self::ScreenshotUi(elem) => elem.damage_since(scale, commit),
}
}
fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> {
match self {
Self::Monitor(elem) => elem.opaque_regions(scale),
Self::Wayland(elem) => elem.opaque_regions(scale),
Self::NamedPointer(elem) => elem.opaque_regions(scale),
Self::SolidColor(elem) => elem.opaque_regions(scale),
Self::ScreenshotUi(elem) => elem.opaque_regions(scale),
}
}
fn alpha(&self) -> f32 {
match self {
Self::Monitor(elem) => elem.alpha(),
Self::Wayland(elem) => elem.alpha(),
Self::NamedPointer(elem) => elem.alpha(),
Self::SolidColor(elem) => elem.alpha(),
Self::ScreenshotUi(elem) => elem.alpha(),
}
}
fn kind(&self) -> Kind {
match self {
Self::Monitor(elem) => elem.kind(),
Self::Wayland(elem) => elem.kind(),
Self::NamedPointer(elem) => elem.kind(),
Self::SolidColor(elem) => elem.kind(),
Self::ScreenshotUi(elem) => elem.kind(),
}
}
}
impl RenderElement<GlesRenderer> for OutputRenderElements<GlesRenderer> {
fn draw(
&self,
frame: &mut GlesFrame<'_>,
src: Rectangle<f64, Buffer>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
) -> Result<(), GlesError> {
match self {
Self::Monitor(elem) => elem.draw(frame, src, dst, damage),
Self::Wayland(elem) => elem.draw(frame, src, dst, damage),
Self::NamedPointer(elem) => {
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
}
Self::SolidColor(elem) => {
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
}
Self::ScreenshotUi(elem) => {
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
}
}
}
fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
match self {
Self::Monitor(elem) => elem.underlying_storage(renderer),
Self::Wayland(elem) => elem.underlying_storage(renderer),
Self::NamedPointer(elem) => elem.underlying_storage(renderer),
Self::SolidColor(elem) => elem.underlying_storage(renderer),
Self::ScreenshotUi(elem) => elem.underlying_storage(renderer),
}
}
}
impl<R: NiriRenderer> From<MonitorRenderElement<R>> for OutputRenderElements<R> {
fn from(x: MonitorRenderElement<R>) -> Self {
Self::Monitor(x)
}
}
impl<R: NiriRenderer> From<WaylandSurfaceRenderElement<R>> for OutputRenderElements<R> {
fn from(x: WaylandSurfaceRenderElement<R>) -> Self {
Self::Wayland(x)
}
}
impl<R: NiriRenderer> From<PrimaryGpuTextureRenderElement> for OutputRenderElements<R> {
fn from(x: PrimaryGpuTextureRenderElement) -> Self {
Self::NamedPointer(x)
}
}
impl<R: NiriRenderer> From<SolidColorRenderElement> for OutputRenderElements<R> {
fn from(x: SolidColorRenderElement) -> Self {
Self::SolidColor(x)
}
}
impl<R: NiriRenderer> From<ScreenshotUiRenderElement> for OutputRenderElements<R> {
fn from(x: ScreenshotUiRenderElement) -> Self {
Self::ScreenshotUi(x)
}
}
+126
View File
@@ -0,0 +1,126 @@
use smithay::backend::allocator::dmabuf::Dmabuf;
use smithay::backend::renderer::element::texture::TextureRenderElement;
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture};
use smithay::backend::renderer::utils::CommitCounter;
use smithay::backend::renderer::{Bind, ExportMem, ImportAll, Offscreen, Renderer, Texture};
use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform};
/// Trait with our main renderer requirements to save on the typing.
pub trait NiriRenderer:
ImportAll
+ ExportMem
+ Bind<Dmabuf>
+ Offscreen<GlesTexture>
+ Renderer<TextureId = Self::NiriTextureId, Error = Self::NiriError>
+ AsGlesRenderer
{
// Associated types to work around the instability of associated type bounds.
type NiriTextureId: Texture + Clone + 'static;
type NiriError: std::error::Error
+ Send
+ Sync
+ From<<GlesRenderer as Renderer>::Error>
+ 'static;
}
impl<R> NiriRenderer for R
where
R: ImportAll + ExportMem + Bind<Dmabuf> + Offscreen<GlesTexture> + AsGlesRenderer,
R::TextureId: Texture + Clone + 'static,
R::Error: std::error::Error + Send + Sync + From<<GlesRenderer as Renderer>::Error> + 'static,
{
type NiriTextureId = R::TextureId;
type NiriError = R::Error;
}
/// Trait for getting the underlying `GlesRenderer`.
pub trait AsGlesRenderer {
fn as_gles_renderer(&mut self) -> &mut GlesRenderer;
}
impl AsGlesRenderer for GlesRenderer {
fn as_gles_renderer(&mut self) -> &mut GlesRenderer {
self
}
}
/// Trait for getting the underlying `GlesFrame`.
pub trait AsGlesFrame<'frame>
where
Self: 'frame,
{
fn as_gles_frame(&mut self) -> &mut GlesFrame<'frame>;
}
impl<'frame> AsGlesFrame<'frame> for GlesFrame<'frame> {
fn as_gles_frame(&mut self) -> &mut GlesFrame<'frame> {
self
}
}
/// Wrapper for a texture from the primary GPU for rendering with the primary GPU.
#[derive(Debug)]
pub struct PrimaryGpuTextureRenderElement(pub TextureRenderElement<GlesTexture>);
impl Element for PrimaryGpuTextureRenderElement {
fn id(&self) -> &Id {
self.0.id()
}
fn current_commit(&self) -> CommitCounter {
self.0.current_commit()
}
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
self.0.geometry(scale)
}
fn transform(&self) -> Transform {
self.0.transform()
}
fn src(&self) -> Rectangle<f64, Buffer> {
self.0.src()
}
fn damage_since(
&self,
scale: Scale<f64>,
commit: Option<CommitCounter>,
) -> Vec<Rectangle<i32, Physical>> {
self.0.damage_since(scale, commit)
}
fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> {
self.0.opaque_regions(scale)
}
fn alpha(&self) -> f32 {
self.0.alpha()
}
fn kind(&self) -> Kind {
self.0.kind()
}
}
impl RenderElement<GlesRenderer> for PrimaryGpuTextureRenderElement {
fn draw(
&self,
frame: &mut GlesFrame<'_>,
src: Rectangle<f64, Buffer>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
) -> Result<(), GlesError> {
let gles_frame = frame.as_gles_frame();
RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?;
Ok(())
}
fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
// If scanout for things other than Wayland buffers is implemented, this will need to take
// the target GPU into account.
None
}
}
+119 -15
View File
@@ -9,15 +9,16 @@ use smithay::backend::allocator::Fourcc;
use smithay::backend::input::{ButtonState, MouseButton};
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement};
use smithay::backend::renderer::element::Kind;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture};
use smithay::backend::renderer::utils::CommitCounter;
use smithay::backend::renderer::ExportMem;
use smithay::input::keyboard::{Keysym, ModifiersState};
use smithay::output::{Output, WeakOutput};
use smithay::render_elements;
use smithay::utils::{Physical, Point, Rectangle, Size, Transform};
use smithay::utils::{Buffer, Physical, Point, Rectangle, Scale, Size, Transform};
use crate::config::Action;
use crate::render_helpers::PrimaryGpuTextureRenderElement;
const BORDER: i32 = 2;
@@ -45,11 +46,10 @@ pub struct OutputData {
locations: [Point<i32, Physical>; 8],
}
render_elements! {
#[derive(Debug)]
pub ScreenshotUiRenderElement<R>;
Screenshot = TextureRenderElement<R::TextureId>,
SolidColor = SolidColorRenderElement,
#[derive(Debug)]
pub enum ScreenshotUiRenderElement {
Screenshot(PrimaryGpuTextureRenderElement),
SolidColor(SolidColorRenderElement),
}
impl ScreenshotUi {
@@ -236,10 +236,7 @@ impl ScreenshotUi {
}
}
pub fn render_output(
&self,
output: &Output,
) -> ArrayVec<ScreenshotUiRenderElement<GlesRenderer>, 9> {
pub fn render_output(&self, output: &Output) -> ArrayVec<ScreenshotUiRenderElement, 9> {
let _span = tracy_client::span!("ScreenshotUi::render_output");
let Self::Open { output_data, .. } = self else {
@@ -266,14 +263,14 @@ impl ScreenshotUi {
// The screenshot itself goes last.
elements.push(
TextureRenderElement::from_texture_buffer(
PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
(0., 0.),
&output_data.texture_buffer,
None,
None,
None,
Kind::Unspecified,
)
))
.into(),
);
@@ -446,3 +443,110 @@ pub fn rect_from_corner_points(
let y2 = max(a.y, b.y);
Rectangle::from_extemities((x1, y1), (x2 + scale, y2 + scale))
}
// Manual RenderElement implementation due to AsGlesFrame requirement.
impl Element for ScreenshotUiRenderElement {
fn id(&self) -> &Id {
match self {
Self::Screenshot(elem) => elem.id(),
Self::SolidColor(elem) => elem.id(),
}
}
fn current_commit(&self) -> CommitCounter {
match self {
Self::Screenshot(elem) => elem.current_commit(),
Self::SolidColor(elem) => elem.current_commit(),
}
}
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
match self {
Self::Screenshot(elem) => elem.geometry(scale),
Self::SolidColor(elem) => elem.geometry(scale),
}
}
fn transform(&self) -> Transform {
match self {
Self::Screenshot(elem) => elem.transform(),
Self::SolidColor(elem) => elem.transform(),
}
}
fn src(&self) -> Rectangle<f64, Buffer> {
match self {
Self::Screenshot(elem) => elem.src(),
Self::SolidColor(elem) => elem.src(),
}
}
fn damage_since(
&self,
scale: Scale<f64>,
commit: Option<CommitCounter>,
) -> Vec<Rectangle<i32, Physical>> {
match self {
Self::Screenshot(elem) => elem.damage_since(scale, commit),
Self::SolidColor(elem) => elem.damage_since(scale, commit),
}
}
fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> {
match self {
Self::Screenshot(elem) => elem.opaque_regions(scale),
Self::SolidColor(elem) => elem.opaque_regions(scale),
}
}
fn alpha(&self) -> f32 {
match self {
Self::Screenshot(elem) => elem.alpha(),
Self::SolidColor(elem) => elem.alpha(),
}
}
fn kind(&self) -> Kind {
match self {
Self::Screenshot(elem) => elem.kind(),
Self::SolidColor(elem) => elem.kind(),
}
}
}
impl RenderElement<GlesRenderer> for ScreenshotUiRenderElement {
fn draw(
&self,
frame: &mut GlesFrame<'_>,
src: Rectangle<f64, Buffer>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
) -> Result<(), GlesError> {
match self {
Self::Screenshot(elem) => {
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
}
Self::SolidColor(elem) => {
RenderElement::<GlesRenderer>::draw(&elem, frame, src, dst, damage)
}
}
}
fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
// If scanout for things other than Wayland buffers is implemented, this will need to take
// the target GPU into account.
None
}
}
impl From<SolidColorRenderElement> for ScreenshotUiRenderElement {
fn from(x: SolidColorRenderElement) -> Self {
Self::SolidColor(x)
}
}
impl From<PrimaryGpuTextureRenderElement> for ScreenshotUiRenderElement {
fn from(x: PrimaryGpuTextureRenderElement) -> Self {
Self::Screenshot(x)
}
}