Skip to content
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
241 changes: 2 additions & 239 deletions src/mesa_surfaceless/context.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
//! OpenGL rendering contexts on surfaceless Mesa.

use euclid::default::Size2D;

use super::device::Device;
use super::surface::Surface;
use crate::base::egl::context::{self, CurrentContextGuard, EGLBackedContext};
use crate::context::ContextID;
use crate::egl;
use crate::egl::types::EGLint;
use crate::{ContextAttributes, Error, Gl, SurfaceInfo};

use std::os::raw::c_void;

use crate::base::egl::context::EGLBackedContext;
pub use crate::base::egl::context::{ContextDescriptor, NativeContext};
use crate::Gl;

/// Represents an OpenGL rendering context.
///
Expand All @@ -31,230 +21,3 @@ pub use crate::base::egl::context::{ContextDescriptor, NativeContext};
///
/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur.
pub struct Context(pub(crate) EGLBackedContext, pub(crate) Gl);

impl Device {
/// Creates a context descriptor with the given attributes.
///
/// Context descriptors are local to this device.
#[inline]
pub fn create_context_descriptor(
&self,
attributes: &ContextAttributes,
) -> Result<ContextDescriptor, Error> {
// Set environment variables as appropriate.
self.adapter.set_environment_variables();

unsafe {
ContextDescriptor::new(
self.native_connection.egl_display,
attributes,
&[
egl::SURFACE_TYPE as EGLint,
egl::PBUFFER_BIT as EGLint,
egl::RENDERABLE_TYPE as EGLint,
egl::OPENGL_BIT as EGLint,
egl::COLOR_BUFFER_TYPE as EGLint,
egl::RGB_BUFFER as EGLint,
],
)
}
}

/// Creates a new OpenGL context.
///
/// The context initially has no surface attached. Until a surface is bound to it, rendering
/// commands will fail or have no effect.
#[inline]
pub fn create_context(
&self,
descriptor: &ContextDescriptor,
share_with: Option<&Context>,
) -> Result<Context, Error> {
unsafe {
let context = EGLBackedContext::new(
self.native_connection.egl_display,
descriptor,
share_with.map(|ctx| &ctx.0),
self.gl_api(),
)?;
context.make_current(self.native_connection.egl_display)?;
Ok(Context(
context,
Gl::from_loader_function(context::get_proc_address),
))
}
}

/// Wraps an `EGLContext` in a native context and returns it. The context must be current.
///
/// The context is not retained, as there is no way to do this in the EGL API. Therefore,
/// it is the caller's responsibility to ensure that the returned `Context` object remains
/// alive as long as the `EGLContext` is.
#[inline]
pub unsafe fn create_context_from_native_context(
&self,
native_context: NativeContext,
) -> Result<Context, Error> {
Ok(Context(
EGLBackedContext::from_native_context(native_context),
Gl::from_loader_function(context::get_proc_address),
))
}

/// Destroys a context.
///
/// The context must have been created on this device.
pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> {
if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) {
self.destroy_surface(context, &mut surface)?;
}

unsafe {
context.0.destroy(self.native_connection.egl_display);
Ok(())
}
}

/// Given a context, returns its underlying EGL context and attached surfaces.
#[inline]
pub fn native_context(&self, context: &Context) -> NativeContext {
context.0.native_context()
}

/// Returns the descriptor that this context was created with.
#[inline]
pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor {
unsafe {
ContextDescriptor::from_egl_context(
&context.1,
self.native_connection.egl_display,
context.0.egl_context,
)
}
}

/// Makes the context the current OpenGL context for this thread.
///
/// After calling this function, it is valid to use OpenGL rendering commands.
#[inline]
pub fn make_context_current(&self, context: &Context) -> Result<(), Error> {
unsafe { context.0.make_current(self.native_connection.egl_display) }
}

/// Removes the current OpenGL context from this thread.
///
/// After calling this function, OpenGL rendering commands will fail until a new context is
/// made current.
#[inline]
pub fn make_no_context_current(&self) -> Result<(), Error> {
unsafe { context::make_no_context_current(self.native_connection.egl_display) }
}

#[inline]
pub(crate) fn temporarily_make_context_current(
&self,
context: &Context,
) -> Result<CurrentContextGuard, Error> {
let guard = CurrentContextGuard::new();
self.make_context_current(context)?;
Ok(guard)
}

/// Returns the attributes that the context descriptor was created with.
#[inline]
pub fn context_descriptor_attributes(
&self,
context_descriptor: &ContextDescriptor,
) -> ContextAttributes {
unsafe { context_descriptor.attributes(self.native_connection.egl_display) }
}

/// Fetches the address of an OpenGL function associated with this context.
///
/// OpenGL functions are local to a context. You should not use OpenGL functions on one context
/// with any other context.
///
/// This method is typically used with a function like `gl::load_with()` from the `gl` crate to
/// load OpenGL function pointers.
#[inline]
pub fn get_proc_address(&self, _: &Context, symbol_name: &str) -> *const c_void {
context::get_proc_address(symbol_name)
}

/// Attaches a surface to a context for rendering.
///
/// This function takes ownership of the surface. The surface must have been created with this
/// context, or an `IncompatibleSurface` error is returned.
///
/// If this function is called with a surface already bound, a `SurfaceAlreadyBound` error is
/// returned. To avoid this error, first unbind the existing surface with
/// `unbind_surface_from_context`.
///
/// If an error is returned, the surface is returned alongside it.
#[inline]
pub fn bind_surface_to_context(
&self,
context: &mut Context,
surface: Surface,
) -> Result<(), (Error, Surface)> {
unsafe {
context
.0
.bind_surface(self.native_connection.egl_display, surface.0)
.map_err(|(err, surface)| (err, Surface(surface)))
}
}

/// Removes and returns any attached surface from this context.
///
/// Any pending OpenGL commands targeting this surface will be automatically flushed, so the
/// surface is safe to read from immediately when this function returns.
pub fn unbind_surface_from_context(
&self,
context: &mut Context,
) -> Result<Option<Surface>, Error> {
unsafe {
context
.0
.unbind_surface(&context.1, self.native_connection.egl_display)
.map(|maybe_surface| maybe_surface.map(Surface))
}
}

/// Displays the contents of the currently bound surface to the screen, if
/// it is a widget surface.
///
/// Widget surfaces are internally double-buffered, so changes to them don't
/// show up in their associated widgets until this method is called.
pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> {
context
.0
.present_bound_surface(self.native_connection.egl_display)
}

/// Resizes a widget surface.
pub fn resize_bound_surface(
&self,
context: &mut Context,
size: Size2D<i32>,
) -> Result<(), Error> {
context.0.resize_bound_surface(size)
}

/// Returns a unique ID representing a context.
///
/// This ID is unique to all currently-allocated contexts. If you destroy a context and create
/// a new one, the new context might have the same ID as the destroyed one.
#[inline]
pub fn context_id(&self, context: &Context) -> ContextID {
context.0.id
}

/// Returns various information about the surface attached to a context.
///
/// This includes, most notably, the OpenGL framebuffer object needed to render to the surface.
#[inline]
pub fn context_surface_info(&self, context: &Context) -> Result<Option<SurfaceInfo>, Error> {
context.0.surface_info()
}
}
Loading
Loading