Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/egl context priority #1133

Merged
merged 4 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,11 @@ jobs:
restore-keys: ${{ runner.os }}-cargo-registry-

- name: Rust toolchain
uses: dtolnay/rust-toolchain@nightly
# nightly changed no_coverage to coverage(off) which breaks the build
# see: https://github.com/rust-lang/rust/pull/114656
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2023-06-17
components: llvm-tools-preview

- name: grcov cache
Expand Down
4 changes: 2 additions & 2 deletions anvil/src/udev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use smithay::{
compositor::DrmCompositor, CreateDrmNodeError, DrmDevice, DrmDeviceFd, DrmError, DrmEvent,
DrmEventMetadata, DrmNode, DrmSurface, GbmBufferedSurface, NodeType,
},
egl::{self, EGLDevice, EGLDisplay},
egl::{self, context::ContextPriority, EGLDevice, EGLDisplay},
libinput::{LibinputInputBackend, LibinputSessionInterface},
renderer::{
damage::{Error as OutputDamageTrackerError, OutputDamageTracker},
Expand Down Expand Up @@ -228,7 +228,7 @@ pub fn run_udev() {
};
info!("Using {} as primary gpu.", primary_gpu);

let gpus = GpuManager::new(GbmGlesBackend::default()).unwrap();
let gpus = GpuManager::new(GbmGlesBackend::with_context_priority(ContextPriority::High)).unwrap();

let data = UdevData {
dh: display.handle(),
Expand Down
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ fn gl_generate() {
"EGL_KHR_swap_buffers_with_damage",
"EGL_KHR_fence_sync",
"EGL_ANDROID_native_fence_sync",
"EGL_IMG_context_priority",
],
)
.write_bindings(gl_generator::GlobalGenerator, &mut file)
Expand Down
157 changes: 149 additions & 8 deletions src/backend/egl/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{

use libc::c_void;

use super::{ffi, wrap_egl_call_bool, wrap_egl_call_ptr, Error, MakeCurrentError};
use super::{ffi, wrap_egl_call_bool, wrap_egl_call_ptr, EGLError, Error, MakeCurrentError};
use crate::{
backend::{
allocator::Format as DrmFormat,
Expand All @@ -19,7 +19,7 @@ use crate::{
utils::user_data::UserDataMap,
};

use tracing::{info, info_span, instrument, trace};
use tracing::{info, info_span, instrument, trace, warn};

/// EGL context for rendering
#[derive(Debug)]
Expand All @@ -33,6 +33,48 @@ pub struct EGLContext {
pub(crate) span: tracing::Span,
}

/// Defines the priority for an [`EGLContext`]
///
/// see: https://registry.khronos.org/EGL/extensions/IMG/EGL_IMG_context_priority.txt
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ContextPriority {
/// High priority
///
/// Note: This might require special system privileges like `CAP_SYS_NICE` to succeed.
High,
/// Medium priority
///
/// Default if not specified
#[default]
Medium,
/// Low priority
Low,
}

impl TryFrom<ffi::egl::types::EGLenum> for ContextPriority {
type Error = EGLError;

fn try_from(value: ffi::egl::types::EGLenum) -> Result<Self, Self::Error> {
let priority = match value {
ffi::egl::CONTEXT_PRIORITY_HIGH_IMG => ContextPriority::High,
ffi::egl::CONTEXT_PRIORITY_MEDIUM_IMG => ContextPriority::Medium,
ffi::egl::CONTEXT_PRIORITY_LOW_IMG => ContextPriority::Low,
_ => return Err(EGLError::BadParameter),
};
Ok(priority)
}
}

impl From<ContextPriority> for ffi::egl::types::EGLenum {
fn from(value: ContextPriority) -> Self {
match value {
ContextPriority::High => ffi::egl::CONTEXT_PRIORITY_HIGH_IMG,
ContextPriority::Medium => ffi::egl::CONTEXT_PRIORITY_MEDIUM_IMG,
ContextPriority::Low => ffi::egl::CONTEXT_PRIORITY_LOW_IMG,
}
}
}

// SAFETY: A context can be sent to another thread when this context is not current.
//
// If the context is made current, the safety requirements for calling `EGLDisplay::make_current` must be
Expand Down Expand Up @@ -74,7 +116,16 @@ impl EGLContext {

/// Creates a new configless `EGLContext` from a given `EGLDisplay`
pub fn new(display: &EGLDisplay) -> Result<EGLContext, Error> {
Self::new_internal(display, None, None)
Self::new_internal(display, None, None, None)
}

/// Creates a new configless `EGLContext` with the specified priority from a given `EGLDisplay`
///
/// Note: The priority is a hint that might be ignored by the underlying platform.
/// It also requires `EGL_IMG_context_priority` to be available, otherwise the priority will be
/// ignored.
pub fn new_with_priority(display: &EGLDisplay, priority: ContextPriority) -> Result<EGLContext, Error> {
Self::new_internal(display, None, None, Some(priority))
}

/// Create a new [`EGLContext`] from a given `EGLDisplay` and configuration requirements
Expand All @@ -83,12 +134,39 @@ impl EGLContext {
attributes: GlAttributes,
reqs: PixelFormatRequirements,
) -> Result<EGLContext, Error> {
Self::new_internal(display, None, Some((attributes, reqs)))
Self::new_internal(display, None, Some((attributes, reqs)), None)
}

/// Create a new [`EGLContext`] from a given `EGLDisplay`, configuration requirements and priority
///
/// Note: The priority is a hint that might be ignored by the underlying platform.
/// It also requires `EGL_IMG_context_priority` to be available, otherwise the priority will be
/// ignored.
pub fn new_with_config_and_priority(
display: &EGLDisplay,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
priority: ContextPriority,
) -> Result<EGLContext, Error> {
Self::new_internal(display, None, Some((attributes, reqs)), Some(priority))
}

/// Create a new configless `EGLContext` from a given `EGLDisplay` sharing resources with another context
pub fn new_shared(display: &EGLDisplay, share: &EGLContext) -> Result<EGLContext, Error> {
Self::new_internal(display, Some(share), None)
Self::new_internal(display, Some(share), None, None)
}

/// Create a new configless `EGLContext` with the specified priority from a given `EGLDisplay` sharing resources with another context
///
/// Note: The priority is a hint that might be ignored by the underlying platform.
/// It also requires `EGL_IMG_context_priority` to be available, otherwise the priority will be
/// ignored.
pub fn new_shared_with_priority(
display: &EGLDisplay,
share: &EGLContext,
priority: ContextPriority,
) -> Result<EGLContext, Error> {
Self::new_internal(display, Some(share), None, Some(priority))
}

/// Create a new `EGLContext` from a given `EGLDisplay` and configuration requirements sharing resources with another context
Expand All @@ -98,13 +176,29 @@ impl EGLContext {
attributes: GlAttributes,
reqs: PixelFormatRequirements,
) -> Result<EGLContext, Error> {
Self::new_internal(display, Some(share), Some((attributes, reqs)))
Self::new_internal(display, Some(share), Some((attributes, reqs)), None)
}

/// Create a new `EGLContext` with the specified priority from a given `EGLDisplay` and configuration requirements sharing resources with another context
///
/// Note: The priority is a hint that might be ignored by the underlying platform.
/// It also requires `EGL_IMG_context_priority` to be available, otherwise the priority will be
/// ignored.
pub fn new_shared_with_config_and_priority(
display: &EGLDisplay,
share: &EGLContext,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
priority: ContextPriority,
) -> Result<EGLContext, Error> {
Self::new_internal(display, Some(share), Some((attributes, reqs)), Some(priority))
}

fn new_internal(
display: &EGLDisplay,
shared: Option<&EGLContext>,
config: Option<(GlAttributes, PixelFormatRequirements)>,
priority: Option<ContextPriority>,
) -> Result<EGLContext, Error> {
let span = info_span!(parent: &display.span, "egl_context", ptr = tracing::field::Empty, shared = tracing::field::Empty);
let _guard = span.enter();
Expand Down Expand Up @@ -142,7 +236,7 @@ impl EGLContext {
}
};

let mut context_attributes = Vec::with_capacity(10);
let mut context_attributes = Vec::with_capacity(12);

if let Some((attributes, _)) = config {
let version = attributes.version;
Expand Down Expand Up @@ -176,6 +270,22 @@ impl EGLContext {
context_attributes.push(2);
}

let has_context_priority = display
.extensions()
.iter()
.any(|x| x == "EGL_IMG_context_priority");
if let Some(priority) = priority {
if !has_context_priority {
warn!(
?priority,
"ignoring requested context priority, EGL_IMG_context_priority not supported"
);
} else {
context_attributes.push(ffi::egl::CONTEXT_PRIORITY_LEVEL_IMG as i32);
context_attributes.push(Into::<ffi::egl::types::EGLenum>::into(priority) as i32);
}
}

context_attributes.push(ffi::egl::NONE as i32);

trace!("Creating EGL context...");
Expand All @@ -192,7 +302,38 @@ impl EGLContext {
.map_err(Error::CreationFailed)?;
span.record("ptr", context as usize);

info!("EGL context created");
let context_priority = if has_context_priority {
let mut context_priority = 0;
let res = wrap_egl_call_bool(|| unsafe {
ffi::egl::QueryContext(
**display.get_display_handle(),
context,
ffi::egl::CONTEXT_PRIORITY_LEVEL_IMG as ffi::egl::types::EGLint,
&mut context_priority,
)
});

if res.is_ok() {
match ContextPriority::try_from(context_priority as ffi::egl::types::EGLenum) {
Ok(context_priority) => Some(context_priority),
Err(_) => {
warn!(context_priority, "failed to parse context priority");
None
}
}
} else {
warn!("failed to query context priority");
None
}
} else {
None
};

if priority.is_some() && has_context_priority && priority != context_priority {
warn!(requested = ?priority, got = ?context_priority, "failed to set context priority");
}

info!(priority = ?context_priority, "EGL context created");

drop(_guard);
Ok(EGLContext {
Expand Down
20 changes: 18 additions & 2 deletions src/backend/renderer/multigpu/egl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use wayland_server::protocol::wl_buffer;

use crate::backend::{
drm::{CreateDrmNodeError, DrmNode},
egl::{EGLContext, EGLDevice, EGLDisplay, Error as EGLError},
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay, Error as EGLError},
renderer::{
gles::{GlesError, GlesRenderer},
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
Expand Down Expand Up @@ -58,6 +58,7 @@ type Factory = Box<dyn Fn(&EGLDisplay) -> Result<GlesRenderer, Error>>;
/// A [`GraphicsApi`] utilizing EGL for device enumeration and OpenGL ES for rendering.
pub struct EglGlesBackend<R> {
factory: Option<Factory>,
context_priority: Option<ContextPriority>,
_renderer: std::marker::PhantomData<R>,
}

Expand All @@ -71,6 +72,7 @@ impl<R> Default for EglGlesBackend<R> {
fn default() -> Self {
EglGlesBackend {
factory: None,
context_priority: None,
_renderer: std::marker::PhantomData,
}
}
Expand All @@ -87,6 +89,18 @@ impl<R> EglGlesBackend<R> {
..Default::default()
}
}

/// Initialize a new [`EglGlesBackend`] with a [`ContextPriority`] for instantiating [`GlesRenderer`]s
///
/// Note: This is mutually exclusive with [`EglGlesBackend::with_factory`](EglGlesBackend::with_factory),
/// but you can create an [`EGLContext`] with a specific [`ContextPriority`] within your factory.
/// See [`EGLContext::new_with_priority`] for more information.
pub fn with_context_priority(priority: ContextPriority) -> Self {
Self {
context_priority: Some(priority),
..Default::default()
}
}
}

impl<R: From<GlesRenderer> + Renderer<Error = GlesError>> GraphicsApi for EglGlesBackend<R> {
Expand All @@ -113,7 +127,9 @@ impl<R: From<GlesRenderer> + Renderer<Error = GlesError>> GraphicsApi for EglGle
let renderer = if let Some(factory) = self.factory.as_ref() {
factory(&display)?.into()
} else {
let context = EGLContext::new(&display).map_err(Error::Egl)?;
let context =
EGLContext::new_with_priority(&display, self.context_priority.unwrap_or_default())
.map_err(Error::Egl)?;
unsafe { GlesRenderer::new(context).map_err(Error::Gl)? }.into()
};

Expand Down
20 changes: 18 additions & 2 deletions src/backend/renderer/multigpu/gbm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use wayland_server::protocol::wl_buffer;

use crate::backend::{
drm::{CreateDrmNodeError, DrmNode},
egl::{EGLContext, EGLDisplay, Error as EGLError},
egl::{context::ContextPriority, EGLContext, EGLDisplay, Error as EGLError},
renderer::{
gles::{GlesError, GlesRenderer},
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
Expand Down Expand Up @@ -61,6 +61,7 @@ type Factory = Box<dyn Fn(&EGLDisplay) -> Result<GlesRenderer, Error>>;
pub struct GbmGlesBackend<R> {
devices: HashMap<DrmNode, EGLDisplay>,
factory: Option<Factory>,
context_priority: Option<ContextPriority>,
_renderer: std::marker::PhantomData<R>,
}

Expand All @@ -77,6 +78,7 @@ impl<R> Default for GbmGlesBackend<R> {
GbmGlesBackend {
devices: HashMap::new(),
factory: None,
context_priority: None,
_renderer: std::marker::PhantomData,
}
}
Expand All @@ -94,6 +96,18 @@ impl<R> GbmGlesBackend<R> {
}
}

/// Initialize a new [`GbmGlesBackend`] with a [`ContextPriority`] for instantiating [`GlesRenderer`]s
///
/// Note: This is mutually exclusive with [`GbmGlesBackend::with_factory`](GbmGlesBackend::with_factory),
/// but you can create an [`EGLContext`] with a specific [`ContextPriority`] within your factory.
/// See [`EGLContext::new_with_priority`] for more information.
pub fn with_context_priority(priority: ContextPriority) -> Self {
Self {
context_priority: Some(priority),
..Default::default()
}
}

/// Add a new GBM device for a given node to the api
pub fn add_node<T: AsFd + Send + 'static>(
&mut self,
Expand Down Expand Up @@ -135,7 +149,9 @@ impl<R: From<GlesRenderer> + Renderer<Error = GlesError>> GraphicsApi for GbmGle
let renderer = if let Some(factory) = self.factory.as_ref() {
factory(display)?.into()
} else {
let context = EGLContext::new(display).map_err(Error::Egl)?;
let context =
EGLContext::new_with_priority(display, self.context_priority.unwrap_or_default())
.map_err(Error::Egl)?;
unsafe { GlesRenderer::new(context).map_err(Error::Gl)? }.into()
};

Expand Down
Loading