Skip to content

Commit 690e162

Browse files
authored
Add Device::present_bound_surface API (#361)
In order to present a widget surface that is bound to a context, a user of `surfman` must currently: - Unbind the surface - Present the surface - Rebind the surface On EGL platforms unbinding the surface calls: ``` eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context); ``` The expected resize story for Wayland is: 1. The server calls the application's `configure` callback with the new window size. 2. The application calls `wl_egl_window_resize`. 3. The application waits for the next frame callback. 4. The application redraws into the surface at the new size. Notably, `wl_egl_window_resize` *does not* resize the surface immediately. Resizing happens later in a driver-defined way. In Servo we handle this by: 1. Receiving the `winit` resize signal. 2. Unbinding the surface (calls `eglMakeCurrent` with `NO_SURFACE` values). 3. Resizing the surface (calls `wl_egl_window_resize`) 4. Rebinding the surface. 5. Waiting for the redraw signal from `winit` (presumably triggered by the Wayland frame callback). 6. Repainting the contents at the new size using OpenGL. 7. Unbinding the surface 8. Presenting the surface (calls `eglSwapBuffers`) 9. Rebinding the surface Either calling `eglMakeCurrent` before calling `eglSwapBuffers` or calling `eglSwapBuffers` with no surface boudn, means that the driver is failing to properly resize the surface after `wl_egl_window_resize`. In general, constantly having to call `eglMakeCurrent` multiple times in order to call `eglSwapBuffers` is both disruptive and not how typical OpenGL applications work. So this change adds a way to present the surface without having to first unbind it from the context. A followup change will address resizing. It does make any changes to existing APIs. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
1 parent ad7285a commit 690e162

18 files changed

Lines changed: 203 additions & 82 deletions

File tree

src/device.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,13 @@ where
204204
/// This will be `GL_TEXTURE_2D` or `GL_TEXTURE_RECTANGLE`, depending on platform.
205205
fn surface_gl_texture_target(&self) -> u32;
206206

207+
/// Displays the contents of the currently bound surface to the screen, if
208+
/// it is a widget surface.
209+
///
210+
/// Widget surfaces are internally double-buffered, so changes to them don't
211+
/// show up in their associated widgets until this method is called.
212+
fn present_bound_surface(&self, context: &mut Self::Context) -> Result<(), Error>;
213+
207214
/// Displays the contents of a widget surface on screen.
208215
///
209216
/// Widget surfaces are internally double-buffered, so changes to them don't show up in their

src/macros.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,11 @@ macro_rules! implement_interfaces {
322322
Device::surface_gl_texture_target(self)
323323
}
324324

325+
#[inline]
326+
fn present_bound_surface(&self, context: &mut Self::Context) -> Result<(), Error> {
327+
Device::present_bound_surface(self, context)
328+
}
329+
325330
#[inline]
326331
fn present_surface(
327332
&self,

src/platform/egl/context.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,18 @@ impl Device {
305305
}
306306
}
307307

308+
/// Displays the contents of the currently bound surface to the screen, if
309+
/// it is a widget surface.
310+
///
311+
/// Widget surfaces are internally double-buffered, so changes to them don't
312+
/// show up in their associated widgets until this method is called.
313+
pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> {
314+
match &context.framebuffer {
315+
Framebuffer::Surface(surface) => self.present_surface_inner(context, surface),
316+
_ => Ok(()),
317+
}
318+
}
319+
308320
/// Returns the attributes that the context descriptor was created with.
309321
pub fn context_descriptor_attributes(
310322
&self,

src/platform/egl/surface/android_surface.rs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -209,29 +209,6 @@ impl Device {
209209
}
210210
}
211211

212-
/// Displays the contents of a widget surface on screen.
213-
///
214-
/// Widget surfaces are internally double-buffered, so changes to them don't show up in their
215-
/// associated widgets until this method is called.
216-
///
217-
/// The supplied context must match the context the surface was created with, or an
218-
/// `IncompatibleSurface` error is returned.
219-
pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> {
220-
if context.id != surface.context_id {
221-
return Err(Error::IncompatibleSurface);
222-
}
223-
224-
EGL_FUNCTIONS.with(|egl| unsafe {
225-
match surface.objects {
226-
SurfaceObjects::Window { egl_surface } => {
227-
egl.SwapBuffers(self.egl_display, egl_surface);
228-
Ok(())
229-
}
230-
SurfaceObjects::HardwareBuffer { .. } => Err(Error::NoWidgetAttached),
231-
}
232-
})
233-
}
234-
235212
/// Resizes a widget surface.
236213
pub fn resize_surface(
237214
&self,

src/platform/egl/surface/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
55
use crate::context::ContextID;
66
use crate::platform::generic::egl::ffi::EGLImageKHR;
7+
use crate::{Context, Device, Error};
78

9+
use crate::platform::generic::egl::device::EGL_FUNCTIONS;
810
use euclid::default::Size2D;
911
use glow::Texture;
1012
use std::fmt::{self, Debug, Formatter};
@@ -85,3 +87,36 @@ impl Debug for SurfaceTexture {
8587
write!(f, "SurfaceTexture({:?})", self.surface)
8688
}
8789
}
90+
91+
impl Device {
92+
/// Displays the contents of a widget surface on screen.
93+
///
94+
/// Widget surfaces are internally double-buffered, so changes to them don't show up in their
95+
/// associated widgets until this method is called.
96+
///
97+
/// The supplied context must match the context the surface was created with, or an
98+
/// `IncompatibleSurface` error is returned.
99+
pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> {
100+
self.present_surface_inner(context, surface)
101+
}
102+
103+
pub(crate) fn present_surface_inner(
104+
&self,
105+
context: &Context,
106+
surface: &Surface,
107+
) -> Result<(), Error> {
108+
if context.id != surface.context_id {
109+
return Err(Error::IncompatibleSurface);
110+
}
111+
112+
EGL_FUNCTIONS.with(|egl| unsafe {
113+
match surface.objects {
114+
SurfaceObjects::Window { egl_surface } => {
115+
egl.SwapBuffers(self.egl_display, egl_surface);
116+
Ok(())
117+
}
118+
_ => Err(Error::NoWidgetAttached),
119+
}
120+
})
121+
}
122+
}

src/platform/egl/surface/ohos_surface.rs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -211,29 +211,6 @@ impl Device {
211211
}
212212
}
213213

214-
/// Displays the contents of a widget surface on screen.
215-
///
216-
/// Widget surfaces are internally double-buffered, so changes to them don't show up in their
217-
/// associated widgets until this method is called.
218-
///
219-
/// The supplied context must match the context the surface was created with, or an
220-
/// `IncompatibleSurface` error is returned.
221-
pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> {
222-
if context.id != surface.context_id {
223-
return Err(Error::IncompatibleSurface);
224-
}
225-
226-
EGL_FUNCTIONS.with(|egl| unsafe {
227-
match surface.objects {
228-
SurfaceObjects::Window { egl_surface } => {
229-
egl.SwapBuffers(self.egl_display, egl_surface);
230-
Ok(())
231-
}
232-
SurfaceObjects::HardwareBuffer { .. } => Err(Error::NoWidgetAttached),
233-
}
234-
})
235-
}
236-
237214
/// Resizes a widget surface.
238215
pub fn resize_surface(
239216
&self,

src/platform/generic/egl/context.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,13 @@ impl EGLBackedContext {
274274
Ok(Some(surface))
275275
}
276276

277+
pub fn present_bound_surface(&self, egl_display: EGLDisplay) -> Result<(), Error> {
278+
match &self.framebuffer {
279+
Framebuffer::Surface(surface) => surface.present(egl_display, self.egl_context),
280+
Framebuffer::None | Framebuffer::External(_) => Ok(()),
281+
}
282+
}
283+
277284
pub(crate) fn surface_info(&self) -> Result<Option<SurfaceInfo>, Error> {
278285
match self.framebuffer {
279286
Framebuffer::None => Ok(None),

src/platform/generic/multi/context.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,23 @@ where
264264
}
265265
}
266266

267+
/// Displays the contents of the currently bound surface to the screen, if
268+
/// it is a widget surface.
269+
///
270+
/// Widget surfaces are internally double-buffered, so changes to them don't
271+
/// show up in their associated widgets until this method is called.
272+
pub fn present_bound_surface(&self, context: &mut Context<Def, Alt>) -> Result<(), Error> {
273+
match (self, context) {
274+
(Device::Default(device), Context::Default(context)) => {
275+
device.present_bound_surface(context)
276+
}
277+
(Device::Alternate(device), Context::Alternate(context)) => {
278+
device.present_bound_surface(context)
279+
}
280+
_ => Err(Error::IncompatibleContext),
281+
}
282+
}
283+
267284
/// Returns the attributes that the context descriptor was created with.
268285
pub fn context_descriptor_attributes(
269286
&self,

src/platform/generic/multi/device.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,11 @@ where
284284
Device::surface_gl_texture_target(self)
285285
}
286286

287+
#[inline]
288+
fn present_bound_surface(&self, context: &mut Context<Def, Alt>) -> Result<(), Error> {
289+
Device::present_bound_surface(self, context)
290+
}
291+
287292
#[inline]
288293
fn present_surface(
289294
&self,

src/platform/macos/cgl/context.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,19 @@ impl Device {
394394
}
395395
}
396396

397+
/// Displays the contents of the currently bound surface to the screen, if
398+
/// it is a widget surface.
399+
///
400+
/// Widget surfaces are internally double-buffered, so changes to them don't
401+
/// show up in their associated widgets until this method is called.
402+
pub fn present_bound_surface(&self, context: &mut Context) -> Result<(), Error> {
403+
if let Framebuffer::Surface(surface) = &mut context.framebuffer {
404+
self.0.present_surface(&mut surface.system_surface)?;
405+
surface.bind_to_texture(&context.gl);
406+
}
407+
Ok(())
408+
}
409+
397410
/// Returns the attributes that the context descriptor was created with.
398411
pub fn context_descriptor_attributes(
399412
&self,

0 commit comments

Comments
 (0)