🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
//! Integration for using [`glow`] on top of smithays OpenGL ES 2 renderer
use tracing::warn;
#[cfg(feature = "wayland_frontend")]
use crate::backend::renderer::{ImportDmaWl, ImportMemWl};
#[cfg(all(
feature = "wayland_frontend",
feature = "backend_egl",
feature = "use_system_lib"
))]
use crate::backend::{egl::display::EGLBufferReader, renderer::ImportEgl};
use crate::{
backend::{
allocator::{dmabuf::Dmabuf, format::FormatSet, Format, Fourcc},
egl::EGLContext,
renderer::{
element::UnderlyingStorage,
gles::{element::*, *},
sync, Bind, Blit, BlitFrame, Color32F, DebugFlags, ExportMem, ImportDma, ImportMem, Offscreen,
Renderer, RendererSuper, TextureFilter,
},
},
utils::{Buffer as BufferCoord, Physical, Rectangle, Size, Transform},
};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};
use glow::Context;
use std::{
borrow::{Borrow, BorrowMut},
sync::Arc,
};
use super::{element::RenderElement, ContextId, Frame};
#[derive(Debug)]
/// A renderer utilizing OpenGL ES 2 and [`glow`] on top for easier custom rendering.
pub struct GlowRenderer {
gl: GlesRenderer,
glow: Arc<Context>,
}
#[derive(Debug)]
/// [`Frame`] implementation of a [`GlowRenderer`].
///
/// Leaking the frame will cause the same problems as leaking a [`GlesFrame`].
pub struct GlowFrame<'frame, 'buffer> {
frame: Option<GlesFrame<'frame, 'buffer>>,
glow: Arc<Context>,
}
impl GlowRenderer {
/// Get the supported [`Capabilities`](Capability) of the renderer
///
/// # Safety
///
/// This operation will cause undefined behavior if the given EGLContext is active in another thread.
pub unsafe fn supported_capabilities(context: &EGLContext) -> Result<Vec<Capability>, GlesError> {
GlesRenderer::supported_capabilities(context)
}
/// Creates a new OpenGL ES 2 + Glow renderer from a given [`EGLContext`]
/// with all [`supported capabilities`](Self::supported_capabilities).
///
/// # Safety
///
/// This operation will cause undefined behavior if the given EGLContext is active in another thread.
///
/// See: [`with_capabilities`](Self::with_capabilities) for more information
pub unsafe fn new(context: EGLContext) -> Result<GlowRenderer, GlesError> {
let supported_capabilities = Self::supported_capabilities(&context)?;
Self::with_capabilities(context, supported_capabilities)
}
/// Creates a new OpenGL ES 2 + Glow renderer from a given [`EGLContext`]
/// with the specified [`Capabilities`](Capability). If a requested [`Capability`] is not supported an
/// error will be returned.
///
/// # Safety
///
/// This operation will cause undefined behavior if the given EGLContext is active in another thread.
///
/// # Implementation details
///
/// - Texture handles created by the resulting renderer are valid for every rendered created with an
/// `EGLContext` shared with the given one (see `EGLContext::new_shared`) and can be used on
/// any of these renderers.
/// - This renderer has no default framebuffer, use `Bind::bind` before rendering.
/// - Binding a new target, while another one is already bound, will replace the current target.
/// - Shm buffers can be released after a successful import, without the texture handle becoming invalid.
/// - Texture filtering starts with Linear-downscaling and Linear-upscaling
pub unsafe fn with_capabilities(
context: EGLContext,
capabilities: impl IntoIterator<Item = Capability>,
) -> Result<GlowRenderer, GlesError> {
let glow = {
context.make_current()?;
Context::from_loader_function(|s| crate::backend::egl::get_proc_address(s) as *const _)
};
let gl = GlesRenderer::with_capabilities(context, capabilities)?;
Ok(GlowRenderer {
gl,
glow: Arc::new(glow),
})
}
/// Get access to the underlying [`EGLContext`].
///
/// *Note*: Modifying the context state, might result in rendering issues.
/// The context state is considerd an implementation detail
/// and no guarantee is made about what can or cannot be changed.
/// To make sure a certain modification does not interfere with
/// the renderer's behaviour, check the source.
pub fn egl_context(&self) -> &EGLContext {
self.gl.egl_context()
}
/// Run custom code in the GL context owned by this renderer.
///
/// The OpenGL state of the renderer is considered an implementation detail
/// and no guarantee is made about what can or cannot be changed,
/// as such you should reset everything you change back to its previous value
/// or check the source code of the version of Smithay you are using to ensure
/// your changes don't interfere with the renderer's behavior.
/// Doing otherwise can lead to rendering errors while using other functions of this renderer.
pub fn with_context<F, R>(&mut self, func: F) -> Result<R, GlesError>
where
F: FnOnce(&Arc<Context>) -> R,
{
unsafe {
self.gl.egl_context().make_current()?;
}
Ok(func(&self.glow))
}
}
impl GlowFrame<'_, '_> {
/// Run custom code in the GL context owned by this renderer.
///
/// The OpenGL state of the renderer is considered an implementation detail
/// and no guarantee is made about what can or cannot be changed,
/// as such you should reset everything you change back to its previous value
/// or check the source code of the version of Smithay you are using to ensure
/// your changes don't interfere with the renderer's behavior.
/// Doing otherwise can lead to rendering errors while using other functions of this renderer.
pub fn with_context<F, R>(&mut self, func: F) -> Result<R, GlesError>
where
F: FnOnce(&Arc<Context>) -> R,
{
Ok(func(&self.glow))
}
}
// TODO: When GAT, use TryFrom and be generic over the Error,
// so `TryFrom<GlesRenderer, Error=Infaillable> for GlesRenderer` qualifies
// just as `TryFrom<GlesRenderer, Error=GlesError> for GlowRenderer`
impl From<GlesRenderer> for GlowRenderer {
#[inline]
fn from(renderer: GlesRenderer) -> GlowRenderer {
let glow = unsafe {
renderer.egl_context().make_current().unwrap();
Context::from_loader_function(|s| crate::backend::egl::get_proc_address(s) as *const _)
};
GlowRenderer {
gl: renderer,
glow: Arc::new(glow),
}
}
}
impl Borrow<GlesRenderer> for GlowRenderer {
#[inline]
fn borrow(&self) -> &GlesRenderer {
&self.gl
}
}
impl BorrowMut<GlesRenderer> for GlowRenderer {
#[inline]
fn borrow_mut(&mut self) -> &mut GlesRenderer {
&mut self.gl
}
}
impl<'frame, 'buffer> Borrow<GlesFrame<'frame, 'buffer>> for GlowFrame<'frame, 'buffer> {
#[inline]
fn borrow(&self) -> &GlesFrame<'frame, 'buffer> {
self.frame.as_ref().unwrap()
}
}
impl<'frame, 'buffer> BorrowMut<GlesFrame<'frame, 'buffer>> for GlowFrame<'frame, 'buffer> {
#[inline]
fn borrow_mut(&mut self) -> &mut GlesFrame<'frame, 'buffer> {
self.frame.as_mut().unwrap()
}
}
impl RendererSuper for GlowRenderer {
type Error = GlesError;
type TextureId = GlesTexture;
type Framebuffer<'buffer> = GlesTarget<'buffer>;
type Frame<'frame, 'buffer>
= GlowFrame<'frame, 'buffer>
where
'buffer: 'frame,
Self: 'frame;
}
impl Renderer for GlowRenderer {
fn context_id(&self) -> ContextId<GlesTexture> {
self.gl.context_id()
}
fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> {
self.gl.downscale_filter(filter)
}
fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> {
self.gl.upscale_filter(filter)
}
fn set_debug_flags(&mut self, flags: DebugFlags) {
self.gl.set_debug_flags(flags)
}
fn debug_flags(&self) -> DebugFlags {
self.gl.debug_flags()
}
#[profiling::function]
fn render<'frame, 'buffer>(
&'frame mut self,
target: &'frame mut GlesTarget<'buffer>,
output_size: Size<i32, Physical>,
transform: Transform,
) -> Result<GlowFrame<'frame, 'buffer>, Self::Error>
where
'buffer: 'frame,
{
let glow = self.glow.clone();
let frame = self.gl.render(target, output_size, transform)?;
Ok(GlowFrame {
frame: Some(frame),
glow,
})
}
#[profiling::function]
fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error> {
self.gl.wait(sync)
}
#[profiling::function]
fn cleanup_texture_cache(&mut self) -> Result<(), Self::Error> {
self.gl.cleanup_texture_cache()
}
}
impl Frame for GlowFrame<'_, '_> {
type Error = GlesError;
type TextureId = GlesTexture;
fn context_id(&self) -> ContextId<GlesTexture> {
self.frame.as_ref().unwrap().context_id()
}
#[profiling::function]
fn clear(&mut self, color: Color32F, at: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
self.frame.as_mut().unwrap().clear(color, at)
}
#[profiling::function]
fn draw_solid(
&mut self,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
color: Color32F,
) -> Result<(), Self::Error> {
self.frame.as_mut().unwrap().draw_solid(dst, damage, color)
}
#[profiling::function]
fn render_texture_from_to(
&mut self,
texture: &Self::TextureId,
src: Rectangle<f64, BufferCoord>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
src_transform: Transform,
alpha: f32,
) -> Result<(), Self::Error> {
Frame::render_texture_from_to(
self.frame.as_mut().unwrap(),
texture,
src,
dst,
damage,
opaque_regions,
src_transform,
alpha,
)
}
fn transformation(&self) -> Transform {
self.frame.as_ref().unwrap().transformation()
}
#[profiling::function]
fn render_texture_at(
&mut self,
texture: &Self::TextureId,
pos: crate::utils::Point<i32, Physical>,
texture_scale: i32,
output_scale: impl Into<crate::utils::Scale<f64>>,
src_transform: Transform,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
alpha: f32,
) -> Result<(), Self::Error> {
self.frame.as_mut().unwrap().render_texture_at(
texture,
pos,
texture_scale,
output_scale,
src_transform,
damage,
opaque_regions,
alpha,
)
}
#[profiling::function]
fn wait(&mut self, sync: &sync::SyncPoint) -> Result<(), Self::Error> {
self.frame.as_mut().unwrap().wait(sync)
}
#[profiling::function]
fn finish(mut self) -> Result<sync::SyncPoint, Self::Error> {
self.finish_internal()
}
}
impl GlowFrame<'_, '_> {
#[profiling::function]
fn finish_internal(&mut self) -> Result<sync::SyncPoint, GlesError> {
if let Some(frame) = self.frame.take() {
frame.finish()
} else {
Ok(sync::SyncPoint::default())
}
}
}
impl Drop for GlowFrame<'_, '_> {
fn drop(&mut self) {
if let Err(err) = self.finish_internal() {
warn!("Ignored error finishing GlowFrame on drop: {}", err);
}
}
}
#[cfg(feature = "wayland_frontend")]
impl ImportMemWl for GlowRenderer {
#[profiling::function]
fn import_shm_buffer(
&mut self,
buffer: &wl_buffer::WlBuffer,
surface: Option<&crate::wayland::compositor::SurfaceData>,
damage: &[Rectangle<i32, BufferCoord>],
) -> Result<GlesTexture, GlesError> {
self.gl.import_shm_buffer(buffer, surface, damage)
}
fn shm_formats(&self) -> Box<dyn Iterator<Item = wl_shm::Format>> {
self.gl.shm_formats()
}
}
impl ImportMem for GlowRenderer {
#[profiling::function]
fn import_memory(
&mut self,
data: &[u8],
format: Fourcc,
size: Size<i32, BufferCoord>,
flipped: bool,
) -> Result<GlesTexture, GlesError> {
self.gl.import_memory(data, format, size, flipped)
}
#[profiling::function]
fn update_memory(
&mut self,
texture: &Self::TextureId,
data: &[u8],
region: Rectangle<i32, BufferCoord>,
) -> Result<(), Self::Error> {
self.gl.update_memory(texture, data, region)
}
fn mem_formats(&self) -> Box<dyn Iterator<Item = Fourcc>> {
self.gl.mem_formats()
}
}
#[cfg(all(
feature = "wayland_frontend",
feature = "backend_egl",
feature = "use_system_lib"
))]
impl ImportEgl for GlowRenderer {
fn bind_wl_display(
&mut self,
display: &wayland_server::DisplayHandle,
) -> Result<(), crate::backend::egl::Error> {
self.gl.bind_wl_display(display)
}
fn unbind_wl_display(&mut self) {
self.gl.unbind_wl_display()
}
fn egl_reader(&self) -> Option<&EGLBufferReader> {
self.gl.egl_reader()
}
#[profiling::function]
fn import_egl_buffer(
&mut self,
buffer: &wl_buffer::WlBuffer,
surface: Option<&crate::wayland::compositor::SurfaceData>,
damage: &[Rectangle<i32, BufferCoord>],
) -> Result<GlesTexture, GlesError> {
self.gl.import_egl_buffer(buffer, surface, damage)
}
}
impl ImportDma for GlowRenderer {
#[profiling::function]
fn import_dmabuf(
&mut self,
buffer: &Dmabuf,
damage: Option<&[Rectangle<i32, BufferCoord>]>,
) -> Result<GlesTexture, GlesError> {
self.gl.import_dmabuf(buffer, damage)
}
fn dmabuf_formats(&self) -> FormatSet {
self.gl.dmabuf_formats()
}
fn has_dmabuf_format(&self, format: Format) -> bool {
self.gl.has_dmabuf_format(format)
}
}
#[cfg(feature = "wayland_frontend")]
impl ImportDmaWl for GlowRenderer {}
impl ExportMem for GlowRenderer {
type TextureMapping = GlesMapping;
#[profiling::function]
fn copy_framebuffer(
&mut self,
from: &GlesTarget<'_>,
region: Rectangle<i32, BufferCoord>,
format: Fourcc,
) -> Result<Self::TextureMapping, Self::Error> {
self.gl.copy_framebuffer(from, region, format)
}
#[profiling::function]
fn copy_texture(
&mut self,
texture: &Self::TextureId,
region: Rectangle<i32, BufferCoord>,
format: Fourcc,
) -> Result<Self::TextureMapping, Self::Error> {
self.gl.copy_texture(texture, region, format)
}
fn can_read_texture(&mut self, texture: &Self::TextureId) -> Result<bool, Self::Error> {
self.gl.can_read_texture(texture)
}
#[profiling::function]
fn map_texture<'a>(
&mut self,
texture_mapping: &'a Self::TextureMapping,
) -> Result<&'a [u8], Self::Error> {
self.gl.map_texture(texture_mapping)
}
}
impl<T> Bind<T> for GlowRenderer
where
GlesRenderer: Bind<T>,
{
#[profiling::function]
fn bind<'a>(&mut self, target: &'a mut T) -> Result<GlesTarget<'a>, GlesError> {
self.gl.bind(target)
}
fn supported_formats(&self) -> Option<FormatSet> {
self.gl.supported_formats()
}
}
impl<T> Offscreen<T> for GlowRenderer
where
GlesRenderer: Offscreen<T>,
{
#[profiling::function]
fn create_buffer(&mut self, format: Fourcc, size: Size<i32, BufferCoord>) -> Result<T, GlesError> {
self.gl.create_buffer(format, size)
}
}
impl<'buffer> BlitFrame<GlesTarget<'buffer>> for GlowFrame<'_, 'buffer> {
fn blit_to(
&mut self,
to: &mut GlesTarget<'buffer>,
src: Rectangle<i32, Physical>,
dst: Rectangle<i32, Physical>,
filter: TextureFilter,
) -> Result<(), Self::Error> {
self.frame.as_mut().unwrap().blit_to(to, src, dst, filter)
}
fn blit_from(
&mut self,
from: &GlesTarget<'buffer>,
src: Rectangle<i32, Physical>,
dst: Rectangle<i32, Physical>,
filter: TextureFilter,
) -> Result<(), Self::Error> {
self.frame.as_mut().unwrap().blit_from(from, src, dst, filter)
}
}
impl Blit for GlowRenderer {
#[profiling::function]
fn blit(
&mut self,
from: &GlesTarget<'_>,
to: &mut GlesTarget<'_>,
src: Rectangle<i32, Physical>,
dst: Rectangle<i32, Physical>,
filter: TextureFilter,
) -> Result<(), GlesError> {
self.gl.blit(from, to, src, dst, filter)
}
}
impl RenderElement<GlowRenderer> for PixelShaderElement {
#[profiling::function]
fn draw(
&self,
frame: &mut GlowFrame<'_, '_>,
src: Rectangle<f64, BufferCoord>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
) -> Result<(), GlesError> {
RenderElement::<GlesRenderer>::draw(self, frame.borrow_mut(), src, dst, damage, opaque_regions)
}
fn underlying_storage(&self, renderer: &mut GlowRenderer) -> Option<UnderlyingStorage<'_>> {
RenderElement::<GlesRenderer>::underlying_storage(self, renderer.borrow_mut())
}
}
impl RenderElement<GlowRenderer> for TextureShaderElement {
#[profiling::function]
fn draw(
&self,
frame: &mut GlowFrame<'_, '_>,
src: Rectangle<f64, BufferCoord>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
) -> Result<(), GlesError> {
RenderElement::<GlesRenderer>::draw(self, frame.borrow_mut(), src, dst, damage, opaque_regions)
}
fn underlying_storage(&self, renderer: &mut GlowRenderer) -> Option<UnderlyingStorage<'_>> {
RenderElement::<GlesRenderer>::underlying_storage(self, renderer.borrow_mut())
}
}