mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b8326b131 |
No files matched your search
+5
-2
@@ -19,8 +19,11 @@ large_enum_variant = "allow"
|
||||
# wgpu-hal 30.0.1 with gfx-rs/wgpu#10124: wgpu 30 creates a Vulkan descriptor
|
||||
# pool with no pool sizes while it creates the device, which Vulkan allows only
|
||||
# since 1.3.215, and Intel's Windows driver for 6th-10th gen iGPUs (HD 520/530,
|
||||
# UHD 620/630) crashes on it with an access violation (#746). Drop this and the
|
||||
# folder once a wgpu release has the fix.
|
||||
# UHD 620/630) crashes on it with an access violation (#746). Also, on macOS
|
||||
# with display sync off, acquiring times out instead of waiting while the last
|
||||
# drawable presented is not on screen yet, so a window with VSync off is not
|
||||
# held to the display's refresh rate (#1333; gfx.rs draws that frame
|
||||
# offscreen). Drop this and the folder once wgpu has both.
|
||||
[patch.crates-io]
|
||||
wgpu-hal = { path = "third_party/wgpu-hal" }
|
||||
|
||||
|
||||
@@ -288,8 +288,11 @@ pub struct Renderer {
|
||||
adapter: wgpu::Adapter,
|
||||
/// The window's swapchain; `None` when rendering offscreen (`--offscreen`).
|
||||
surface: Option<wgpu::Surface<'static>>,
|
||||
/// The offscreen colour target, sized like `config`.
|
||||
/// The offscreen colour target, sized like `config`; with a window, for
|
||||
/// the frames the display has no room for (Metal with VSync off).
|
||||
offscreen: Option<wgpu::Texture>,
|
||||
/// Rendering offscreen only (`--offscreen`): no surface to recreate.
|
||||
windowless: bool,
|
||||
/// Offscreen submissions still on the GPU: at most two frames run ahead.
|
||||
in_flight: std::collections::VecDeque<wgpu::SubmissionIndex>,
|
||||
/// Which steps of the first frame on this device still go to the log
|
||||
@@ -2126,6 +2129,7 @@ impl Renderer {
|
||||
instance,
|
||||
adapter,
|
||||
surface,
|
||||
windowless: offscreen.is_some(),
|
||||
offscreen,
|
||||
in_flight: std::collections::VecDeque::new(),
|
||||
first_frame: if previous.is_none() {
|
||||
@@ -2301,7 +2305,7 @@ impl Renderer {
|
||||
}
|
||||
/// Reattach a window after surface loss or a failed renderer replacement.
|
||||
pub fn recreate_surface(&mut self) -> Result<()> {
|
||||
if self.offscreen.is_some() {
|
||||
if self.windowless {
|
||||
return Ok(());
|
||||
}
|
||||
// Wayland allows only one tearing-control object per window surface.
|
||||
@@ -2372,6 +2376,8 @@ impl Renderer {
|
||||
self.config.height = height;
|
||||
if let Some(surface) = &self.surface {
|
||||
surface.configure(&self.device, &self.config);
|
||||
// Made again at the new size when next needed.
|
||||
self.offscreen = None;
|
||||
} else {
|
||||
self.offscreen = Some(Self::offscreen_target(&self.device, &self.config));
|
||||
}
|
||||
@@ -2826,6 +2832,21 @@ impl Renderer {
|
||||
None => (None, false),
|
||||
Some(wgpu::CurrentSurfaceTexture::Success(f)) => (Some(f), false),
|
||||
Some(wgpu::CurrentSurfaceTexture::Suboptimal(f)) => (Some(f), true),
|
||||
// Metal with VSync off (the vendored wgpu-hal): the display has yet
|
||||
// to show the last frame presented. A window's compositor takes one
|
||||
// drawable a refresh, so waiting for one would hold the frame rate
|
||||
// to the display's, VSync or not (#1333). This frame is drawn
|
||||
// offscreen instead, as vanilla's OpenGL window drops the frames
|
||||
// the display has no time for: it shows the newest each refresh.
|
||||
Some(wgpu::CurrentSurfaceTexture::Timeout)
|
||||
if cfg!(target_os = "macos")
|
||||
&& self.config.present_mode != wgpu::PresentMode::Fifo =>
|
||||
{
|
||||
if self.offscreen.is_none() {
|
||||
self.offscreen = Some(Self::offscreen_target(&self.device, &self.config));
|
||||
}
|
||||
(None, false)
|
||||
}
|
||||
Some(wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded) => {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
+19
-1
@@ -644,6 +644,10 @@ pub struct Surface {
|
||||
render_layer: Mutex<Retained<CAMetalLayer>>,
|
||||
swapchain_format: RwLock<Option<wgt::TextureFormat>>,
|
||||
extent: RwLock<wgt::Extent3d>,
|
||||
/// Horizon: drawables presented and not yet on screen (or dropped).
|
||||
queued: Arc<atomic::AtomicUsize>,
|
||||
/// Horizon: since when `acquire_texture` has found `queued` above zero.
|
||||
busy_since: Mutex<Option<std::time::Instant>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Surface {}
|
||||
@@ -801,9 +805,23 @@ impl crate::Queue for Queue {
|
||||
}
|
||||
unsafe fn present(
|
||||
&self,
|
||||
_surface: &Surface,
|
||||
surface: &Surface,
|
||||
texture: SurfaceTexture,
|
||||
) -> Result<(), crate::SurfaceError> {
|
||||
// Horizon: count the drawable until the display shows it (or drops it).
|
||||
let queued = surface.queued.clone();
|
||||
queued.fetch_add(1, atomic::Ordering::AcqRel);
|
||||
let block = block2::RcBlock::new(move |_drawable| {
|
||||
// Saturating: the count may have been reset since.
|
||||
let _ = queued.try_update(atomic::Ordering::AcqRel, atomic::Ordering::Acquire, |n| {
|
||||
n.checked_sub(1)
|
||||
});
|
||||
});
|
||||
unsafe {
|
||||
texture
|
||||
.drawable
|
||||
.addPresentedHandler(block2::RcBlock::as_ptr(&block))
|
||||
};
|
||||
autoreleasepool(|_| {
|
||||
// We do not bother adjusting `command_buffer_created_not_submitted`
|
||||
// because we immediately commit this buffer.
|
||||
|
||||
+24
@@ -40,6 +40,8 @@ impl super::Surface {
|
||||
render_layer: Mutex::new(layer),
|
||||
swapchain_format: RwLock::new(None),
|
||||
extent: RwLock::new(wgt::Extent3d::default()),
|
||||
queued: Default::default(),
|
||||
busy_since: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,6 +372,28 @@ impl crate::Surface for super::Surface {
|
||||
}
|
||||
}
|
||||
|
||||
// Horizon: with display sync off, don't wait for a drawable. A
|
||||
// window's compositor takes one a refresh, so `nextDrawable` would
|
||||
// wait for the display whenever the last one presented is not on
|
||||
// screen yet, and the frame rate would follow the display. Report a
|
||||
// timeout instead; the caller draws that frame elsewhere.
|
||||
// A drawable whose handler never comes (none should) stops it after
|
||||
// a moment.
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let mut busy = self.busy_since.lock();
|
||||
if !render_layer.displaySyncEnabled()
|
||||
&& self.queued.load(core::sync::atomic::Ordering::Acquire) > 0
|
||||
{
|
||||
let since = *busy.get_or_insert_with(std::time::Instant::now);
|
||||
if since.elapsed() < core::time::Duration::from_millis(100) {
|
||||
return Err(crate::SurfaceError::Timeout);
|
||||
}
|
||||
self.queued.store(0, core::sync::atomic::Ordering::Release);
|
||||
}
|
||||
*busy = None;
|
||||
}
|
||||
|
||||
let (drawable, texture) = match autoreleasepool(|_| {
|
||||
render_layer
|
||||
.nextDrawable()
|
||||
|
||||
Reference in new issue
Block a user