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
21 changes: 3 additions & 18 deletions src/angle/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,16 @@ use crate::surface::Framebuffer;
use crate::Gl;
use std::thread;

/// Represents an OpenGL rendering context.
///
/// A context allows you to issue rendering commands to a surface. When initially created, a
/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you
/// attach a surface to the context before rendering.
///
/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any
/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it
/// must first be detached from its context. Each surface is associated with a single context upon
/// creation and may not be rendered to from any other context. However, you can wrap a surface in
/// a surface texture, which allows the surface to be read from another context.
///
/// OpenGL objects may not be shared across contexts directly, but surface textures effectively
/// allow for sharing of texture data. Contexts are local to a single thread and device.
///
/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur.
pub struct Context {
/// An implementation of [`crate::Context`] for ANGLE on Windows.
pub struct AngleContext {
pub(crate) egl_context: EGLContext,
pub(crate) id: ContextID,
pub(crate) framebuffer: Framebuffer<Surface, ExternalEGLSurfaces>,
pub(crate) context_is_owned: bool,
pub(crate) gl: Gl,
}

impl Drop for Context {
impl Drop for AngleContext {
#[inline]
fn drop(&mut self) {
if self.egl_context != egl::NO_CONTEXT && !thread::panicking() {
Expand Down
97 changes: 63 additions & 34 deletions src/angle/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use super::adapter::AngleAdapter;
use crate::angle::connection::Connection;
use crate::angle::context::{Context, NativeContext};
use crate::angle::context::{AngleContext, NativeContext};
use crate::angle::surface::{
Surface, SurfaceDataGuard, SurfaceTexture, Synchronization, Win32Objects,
};
Expand All @@ -20,10 +20,9 @@ use crate::egl::types::{
EGLAttrib, EGLConfig, EGLDeviceEXT, EGLDisplay, EGLNativeWindowType, EGLSurface, EGLint,
};
use crate::surface::Framebuffer;
use crate::EglContextDescriptor;
use crate::{
egl, gl, Adapter, ContextAttributes, ContextDescriptor, Error, GLApi, Gl, SurfaceAccess,
SurfaceInfo, SurfaceType,
egl, gl, Adapter, Context, ContextAttributes, ContextDescriptor, EglContextDescriptor, Error,
GLApi, Gl, SurfaceAccess, SurfaceInfo, SurfaceType,
};
use euclid::default::Size2D;
use glow::HasContext;
Expand Down Expand Up @@ -271,6 +270,7 @@ impl Device {
descriptor: &ContextDescriptor,
share_with: Option<&Context>,
) -> Result<Context, Error> {
let share_with = share_with.map(Context::angle).transpose()?;
let (egl_context, id) = {
let mut next_context_id_lock = CREATE_CONTEXT_MUTEX.lock().unwrap();
let egl_context = unsafe {
Expand Down Expand Up @@ -302,14 +302,14 @@ impl Device {
})?;
}

let context = Context {
Ok(AngleContext {
egl_context,
id,
framebuffer: Framebuffer::None,
context_is_owned: true,
gl: unsafe { Gl::from_loader_function(context::get_proc_address) },
};
Ok(context)
}
.into())
}

/// Wraps a native `EGLContext` in a context object.
Expand All @@ -324,7 +324,7 @@ impl Device {
let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap();

// Create the context.
let context = Context {
let context = AngleContext {
egl_context: native_context.egl_context,
id: *next_context_id,
framebuffer: Framebuffer::External(ExternalEGLSurfaces {
Expand All @@ -336,21 +336,22 @@ impl Device {
};
next_context_id.0 += 1;

Ok(context)
Ok(context.into())
}

/// Destroys a context.
///
/// The context must have been created on this device.
pub fn destroy_context(&self, context: &mut Context) -> Result<(), Error> {
if context.egl_context == egl::NO_CONTEXT {
if context.angle()?.egl_context == egl::NO_CONTEXT {
return Ok(());
}

if let Ok(Some(mut surface)) = self.unbind_surface_from_context(context) {
self.destroy_surface(context, &mut surface)?;
}

let context: &mut AngleContext = context.try_into()?;
EGL_FUNCTIONS.with(|egl| unsafe {
egl.MakeCurrent(
self.egl_display,
Expand All @@ -372,6 +373,7 @@ impl Device {

/// Returns the descriptor that this context was created with.
pub fn context_descriptor(&self, context: &Context) -> ContextDescriptor {
let context = context.angle().expect("Passed incorrect context type");
unsafe {
EglContextDescriptor::from_egl_context(
&context.gl,
Expand All @@ -382,31 +384,35 @@ impl Device {
}
}

/// Makes the context the current OpenGL context for this thread.
///
/// After calling this function, it is valid to use OpenGL rendering commands.
pub fn make_context_current(&self, context: &Context) -> Result<(), Error> {
unsafe {
let (egl_draw_surface, egl_read_surface) = match context.framebuffer {
Framebuffer::Surface(ref surface) => (surface.egl_surface, surface.egl_surface),
Framebuffer::None => (egl::NO_SURFACE, egl::NO_SURFACE),
Framebuffer::External(ref surfaces) => (surfaces.draw, surfaces.read),
};
fn make_context_current_inner(&self, context: &AngleContext) -> Result<(), Error> {
let (egl_draw_surface, egl_read_surface) = match context.framebuffer {
Framebuffer::Surface(ref surface) => (surface.egl_surface, surface.egl_surface),
Framebuffer::None => (egl::NO_SURFACE, egl::NO_SURFACE),
Framebuffer::External(ref surfaces) => (surfaces.draw, surfaces.read),
};

EGL_FUNCTIONS.with(|egl| {
let result = egl.MakeCurrent(
EGL_FUNCTIONS.with(|egl| {
let result = unsafe {
egl.MakeCurrent(
self.egl_display,
egl_draw_surface,
egl_read_surface,
context.egl_context,
);
if result == egl::FALSE {
let err = egl.GetError().to_windowing_api_error();
return Err(Error::MakeCurrentFailed(err));
}
Ok(())
})
}
)
};
if result == egl::FALSE {
let err = unsafe { egl.GetError() }.to_windowing_api_error();
return Err(Error::MakeCurrentFailed(err));
}
Ok(())
})
}

/// Makes the context the current OpenGL context for this thread.
///
/// After calling this function, it is valid to use OpenGL rendering commands.
pub fn make_context_current(&self, context: &Context) -> Result<(), Error> {
self.make_context_current_inner(context.angle()?)
}

/// Removes the current OpenGL context from this thread.
Expand All @@ -419,14 +425,14 @@ impl Device {

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

pub(crate) fn context_is_current(&self, context: &Context) -> bool {
pub(crate) fn context_is_current(&self, context: &AngleContext) -> bool {
EGL_FUNCTIONS.with(|egl| unsafe { egl.GetCurrentContext() == context.egl_context })
}

Expand Down Expand Up @@ -477,6 +483,10 @@ impl Device {
context: &mut Context,
surface: Surface,
) -> Result<(), (Error, Surface)> {
let context: &mut AngleContext = match context.try_into() {
Ok(context) => context,
Err(error) => return Err((error, surface)),
};
if context.id != surface.context_id {
return Err((Error::IncompatibleSurface, surface));
}
Expand Down Expand Up @@ -514,7 +524,7 @@ impl Device {

if is_current {
// We need to make ourselves current again, because the surface changed.
drop(self.make_context_current(context));
drop(self.make_context_current_inner(context));
}

Ok(())
Expand All @@ -528,6 +538,7 @@ impl Device {
&self,
context: &mut Context,
) -> Result<Option<Surface>, Error> {
let context: &mut AngleContext = context.try_into()?;
match context.framebuffer {
Framebuffer::None => return Ok(None),
Framebuffer::External(_) => return Err(Error::ExternalRenderTarget),
Expand Down Expand Up @@ -559,6 +570,7 @@ impl Device {
/// 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> {
let context: &mut AngleContext = context.try_into()?;
match &context.framebuffer {
Framebuffer::Surface(surface) => surface.present(self),
_ => Ok(()),
Expand All @@ -571,6 +583,7 @@ impl Device {
context: &mut Context,
size: Size2D<i32>,
) -> Result<(), Error> {
let context: &mut AngleContext = context.try_into()?;
if let Framebuffer::Surface(surface) = &mut context.framebuffer {
surface.resize(size);
}
Expand All @@ -583,13 +596,15 @@ impl Device {
/// a new one, the new context might have the same ID as the destroyed one.
#[inline]
pub fn context_id(&self, context: &Context) -> ContextID {
let context = context.angle().expect("Passed incorrect context type");
context.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.
pub fn context_surface_info(&self, context: &Context) -> Result<Option<SurfaceInfo>, Error> {
let context = context.angle()?;
match context.framebuffer {
Framebuffer::None => Ok(None),
Framebuffer::External(_) => Err(Error::ExternalRenderTarget),
Expand All @@ -599,6 +614,7 @@ impl Device {

/// Given a context, returns its underlying EGL context and attached surfaces.
pub fn native_context(&self, context: &Context) -> NativeContext {
let context = context.angle().expect("Passed incorrect context type");
let (egl_draw_surface, egl_read_surface) = match context.framebuffer {
Framebuffer::Surface(Surface { egl_surface, .. }) => (egl_surface, egl_surface),
Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read),
Expand Down Expand Up @@ -643,6 +659,7 @@ impl Device {
let context_descriptor = self.context_descriptor(context);
let context_descriptor = context_descriptor.egl()?.clone();
let egl_config = self.context_descriptor_to_egl_config(&context_descriptor);
let context = context.angle()?;

unsafe {
let attributes = [
Expand Down Expand Up @@ -746,6 +763,7 @@ impl Device {
) -> Result<Surface, Error> {
let context_descriptor = self.context_descriptor(context).egl()?.clone();
let egl_config = self.context_descriptor_to_egl_config(&context_descriptor);
let context = context.angle()?;

unsafe {
EGL_FUNCTIONS.with(|egl| {
Expand Down Expand Up @@ -875,6 +893,11 @@ impl Device {
local_egl_surface: EGLSurface,
local_keyed_mutex: Option<ComPtr<IDXGIKeyedMutex>>,
) -> Result<SurfaceTexture, (Error, Surface)> {
let context = match context.angle() {
Ok(context) => context,
Err(error) => return Err((error, surface)),
};

EGL_FUNCTIONS.with(|egl| {
unsafe {
let _guard = self.temporarily_make_context_current(context);
Expand Down Expand Up @@ -944,6 +967,7 @@ impl Device {
context: &mut Context,
surface: &mut Surface,
) -> Result<(), Error> {
let context = context.angle()?;
if context.id != surface.context_id {
return Err(Error::IncompatibleSurface);
}
Expand Down Expand Up @@ -982,6 +1006,11 @@ impl Device {
context: &mut Context,
mut surface_texture: SurfaceTexture,
) -> Result<Surface, (Error, SurfaceTexture)> {
let context = match context.angle() {
Ok(context) => context,
Err(error) => return Err((error, surface_texture)),
};

unsafe {
if let Some(texture) = surface_texture.gl_texture.take() {
context.gl.delete_texture(texture);
Expand Down
21 changes: 3 additions & 18 deletions src/cgl/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,8 @@ use std::ptr;
use std::rc::Rc;
use std::thread;

/// Represents an OpenGL rendering context.
///
/// A context allows you to issue rendering commands to a surface. When initially created, a
/// context has no attached surface, so rendering commands will fail or be ignored. Typically, you
/// attach a surface to the context before rendering.
///
/// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in any
/// way other than rendering to it (e.g. presenting it to a window, which causes a buffer swap), it
/// must first be detached from its context. Each surface is associated with a single context upon
/// creation and may not be rendered to from any other context. However, you can wrap a surface in
/// a surface texture, which allows the surface to be read from another context.
///
/// OpenGL objects may not be shared across contexts directly, but surface textures effectively
/// allow for sharing of texture data. Contexts are local to a single thread and device.
///
/// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur.
pub struct Context {
/// An implementation of [`crate::Context`] for CGL.
pub struct CglContext {
pub(crate) cgl_context: CGLContextObj,
pub(crate) id: ContextID,
pub(crate) framebuffer: Framebuffer<Surface, ()>,
Expand All @@ -39,7 +24,7 @@ pub struct Context {
/// Wraps a native CGL context object.
pub struct NativeContext(pub CGLContextObj);

impl Drop for Context {
impl Drop for CglContext {
#[inline]
fn drop(&mut self) {
if !self.cgl_context.is_null() && !thread::panicking() {
Expand Down
Loading
Loading