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
2 changes: 1 addition & 1 deletion blade-egui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl GuiPainter {
let valid_pos = self
.textures_to_delete
.iter()
.position(|&(_, ref sp)| !context.wait_for(sp, 0))
.position(|&(_, ref sp)| !context.wait_for(sp, 0).unwrap_or(true))
.unwrap_or_default();
for (texture, _) in self.textures_to_delete.drain(..valid_pos) {
context.destroy_texture_view(texture.view);
Expand Down
24 changes: 10 additions & 14 deletions blade-graphics/src/gles/egl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,6 @@ const GBM_BO_USE_LINEAR: u32 = 1 << 4;
type GlEglImageTargetTexture2dOesFun =
unsafe extern "system" fn(target: u32, image: *mut ffi::c_void);

#[derive(Debug)]
pub enum PlatformError {
Loading(egl::LoadError<libloading::Error>),
Init(egl::Error),
}

#[derive(Clone, Copy, Debug)]
enum SrgbFrameBufferKind {
/// No support for SRGB surface
Expand Down Expand Up @@ -279,7 +273,7 @@ impl super::Context {
} else {
egl::DynamicInstance::<egl::EGL1_4>::load_required()
};
egl_result.map_err(PlatformError::Loading)?
egl_result.map_err(crate::PlatformError::loading)?
};

let client_extensions = match egl.query_string(None, egl::EXTENSIONS) {
Expand Down Expand Up @@ -828,15 +822,15 @@ impl super::Context {
&[egl::ATTRIB_NONE],
)
}
.map_err(PlatformError::Init)?,
.map_err(crate::PlatformError::init)?,
Rdh::Xlib(handle) => unsafe {
let display_ptr = match handle.display {
Some(d) => d.as_ptr(),
None => ptr::null_mut(),
};
egl1_5.get_platform_display(EGL_PLATFORM_X11_KHR, display_ptr, &[egl::ATTRIB_NONE])
}
.map_err(PlatformError::Init)?,
.map_err(crate::PlatformError::init)?,
_ => {
return Err(crate::NotSupportedError::NoSupportedDeviceFound);
}
Expand All @@ -845,7 +839,7 @@ impl super::Context {
// Load a separate EGL instance for the presentation context so it
// has its own library handle (EglContext takes ownership).
let pres_egl_instance = unsafe { egl::DynamicInstance::<egl::EGL1_4>::load_required() }
.map_err(PlatformError::Loading)?;
.map_err(crate::PlatformError::loading)?;

let desc = crate::ContextDesc {
presentation: true,
Expand Down Expand Up @@ -914,7 +908,7 @@ impl super::Context {
"Failed to create window surface on presentation display: {:?}",
e
);
PlatformError::Init(e)
crate::PlatformError::init(e)
})?
};

Expand Down Expand Up @@ -1367,7 +1361,9 @@ impl EglContext {
egl: EglInstance,
display: egl::Display,
) -> Result<Self, crate::NotSupportedError> {
let version = egl.initialize(display).map_err(PlatformError::Init)?;
let version = egl
.initialize(display)
.map_err(crate::PlatformError::init)?;
let vendor = egl.query_string(Some(display), egl::VENDOR).unwrap();
let display_extensions = egl
.query_string(Some(display), egl::EXTENSIONS)
Expand Down Expand Up @@ -1438,7 +1434,7 @@ impl EglContext {
Ok(context) => context,
Err(e) => {
log::warn!("unable to create GLES 3.x context: {:?}", e);
return Err(PlatformError::Init(e).into());
return Err(crate::PlatformError::init(e).into());
}
};

Expand All @@ -1454,7 +1450,7 @@ impl EglContext {
.map(Some)
.map_err(|e| {
log::warn!("Error in create_pbuffer_surface: {:?}", e);
PlatformError::Init(e)
crate::PlatformError::init(e)
})?
};

Expand Down
23 changes: 16 additions & 7 deletions blade-graphics/src/gles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ const DEBUG_ID: u32 = 0;
const MAX_TIMEOUT: u64 = 1_000_000_000; // MAX_CLIENT_WAIT_TIMEOUT_WEBGL;
const MAX_QUERIES: usize = crate::limits::PASS_COUNT + 1;

pub use platform::PlatformError;

bitflags::bitflags! {
struct Capabilities: u32 {
const BUFFER_STORAGE = 1 << 0;
Expand Down Expand Up @@ -61,6 +59,10 @@ impl Buffer {
pub fn data(&self) -> *mut u8 {
self.data
}

pub fn size(&self) -> u64 {
self.size
}
}

#[derive(Clone, Copy, Debug, Hash, PartialEq)]
Expand Down Expand Up @@ -132,8 +134,9 @@ pub struct ComputePipeline {
wg_size: [u32; 3],
}

impl ComputePipeline {
pub fn get_workgroup_size(&self) -> [u32; 3] {
#[hidden_trait::expose]
impl crate::traits::ComputePipelineBase for ComputePipeline {
fn get_workgroup_size(&self) -> [u32; 3] {
self.wg_size
}
}
Expand Down Expand Up @@ -457,6 +460,10 @@ impl Context {
pub fn device_information(&self) -> &crate::DeviceInformation {
&self.device_information
}

pub fn memory_stats(&self) -> crate::MemoryStats {
crate::MemoryStats::default()
}
}

#[hidden_trait::expose]
Expand Down Expand Up @@ -559,7 +566,7 @@ impl crate::traits::CommandDevice for Context {
SyncPoint { fence }
}

fn wait_for(&self, sp: &SyncPoint, timeout_ms: u32) -> bool {
fn wait_for(&self, sp: &SyncPoint, timeout_ms: u32) -> Result<bool, crate::DeviceError> {
use glow::HasContext as _;

let gl = self.lock();
Expand All @@ -574,8 +581,10 @@ impl crate::traits::CommandDevice for Context {
let status =
unsafe { gl.client_wait_sync(sp.fence, glow::SYNC_FLUSH_COMMANDS_BIT, timeout_ns_i32) };
match status {
glow::ALREADY_SIGNALED | glow::CONDITION_SATISFIED => true,
_ => false,
glow::ALREADY_SIGNALED | glow::CONDITION_SATISFIED => Ok(true),
glow::TIMEOUT_EXPIRED => Ok(false),
glow::WAIT_FAILED => Err(crate::DeviceError::DeviceLost),
_ => Ok(false),
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions blade-graphics/src/gles/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ pub struct PlatformFrame {
extent: crate::Extent,
}

pub type PlatformError = ();

impl super::Surface {
pub fn info(&self) -> crate::SurfaceInfo {
self.platform.info
Expand Down
70 changes: 70 additions & 0 deletions blade-graphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,27 @@ pub const CANVAS_ID: &str = "blade";

use std::{fmt, num::NonZeroU32};

/// Error from the underlying graphics platform during initialization.
#[derive(Debug)]
pub struct PlatformError(String);

impl PlatformError {
pub(crate) fn loading(err: impl fmt::Debug) -> Self {
Self(format!("failed to load: {:?}", err))
}
pub(crate) fn init(err: impl fmt::Debug) -> Self {
Self(format!("failed to initialize: {:?}", err))
}
}

impl fmt::Display for PlatformError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}

impl std::error::Error for PlatformError {}

#[cfg(not(any(
vulkan,
windows,
Expand Down Expand Up @@ -146,12 +167,55 @@ pub enum NotSupportedError {
PlatformNotSupported,
}

impl fmt::Display for NotSupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Platform(e) => write!(f, "platform error: {}", e),
Self::NoSupportedDeviceFound => f.write_str("no supported device found"),
Self::PlatformNotSupported => f.write_str("platform not supported"),
}
}
}

impl std::error::Error for NotSupportedError {}

impl From<PlatformError> for NotSupportedError {
fn from(error: PlatformError) -> Self {
Self::Platform(error)
}
}

/// Error indicating a GPU device failure.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DeviceError {
/// The GPU device has been lost and can no longer be used.
DeviceLost,
/// The GPU ran out of memory.
OutOfMemory,
}

impl fmt::Display for DeviceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DeviceLost => f.write_str("device lost"),
Self::OutOfMemory => f.write_str("out of memory"),
}
}
}

impl std::error::Error for DeviceError {}

/// GPU memory usage statistics.
#[derive(Clone, Copy, Debug, Default)]
pub struct MemoryStats {
/// Total memory budget across all device-local heaps (bytes).
/// Zero if the backend doesn't support memory budget queries.
pub budget: u64,
/// Current memory usage across all device-local heaps (bytes).
/// Zero if the backend doesn't support memory budget queries.
pub usage: u64,
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct Capabilities {
/// Support binding arrays of handles.
Expand Down Expand Up @@ -258,6 +322,12 @@ impl BufferPiece {
pub fn data(&self) -> *mut u8 {
let base = self.buffer.data();
assert!(!base.is_null());
debug_assert!(
self.offset <= self.buffer.size(),
"BufferPiece offset {} exceeds buffer size {}",
self.offset,
self.buffer.size(),
);
unsafe { base.offset(self.offset as isize) }
}
}
Expand Down
31 changes: 23 additions & 8 deletions blade-graphics/src/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ mod surface;

const MAX_TIMESTAMPS: usize = crate::limits::PASS_COUNT * 2;

pub type PlatformError = ();

pub struct Surface {
view: Option<objc2::rc::Retained<objc2::runtime::NSObject>>,
render_layer: Retained<objc2_quartz_core::CAMetalLayer>,
Expand Down Expand Up @@ -90,6 +88,11 @@ impl Buffer {
use metal::MTLBuffer as _;
self.as_ref().contents().as_ptr() as *mut u8
}

pub fn size(&self) -> u64 {
use metal::MTLResource as _;
self.as_ref().allocatedSize() as u64
}
}

#[derive(Clone, Copy, Debug, Hash, PartialEq)]
Expand Down Expand Up @@ -248,8 +251,9 @@ pub struct ComputePipeline {

unsafe impl Send for ComputePipeline {}
unsafe impl Sync for ComputePipeline {}
impl ComputePipeline {
pub fn get_workgroup_size(&self) -> [u32; 3] {
#[hidden_trait::expose]
impl crate::traits::ComputePipelineBase for ComputePipeline {
fn get_workgroup_size(&self) -> [u32; 3] {
[
self.wg_size.width as u32,
self.wg_size.height as u32,
Expand Down Expand Up @@ -549,6 +553,15 @@ impl Context {
pub fn metal_device(&self) -> Retained<ProtocolObject<dyn metal::MTLDevice>> {
self.device.lock().unwrap().clone()
}

pub fn memory_stats(&self) -> crate::MemoryStats {
use metal::MTLDevice as _;
let device = self.device.lock().unwrap();
crate::MemoryStats {
budget: device.recommendedMaxWorkingSetSize() as u64,
usage: device.currentAllocatedSize() as u64,
}
}
}

#[hidden_trait::expose]
Expand Down Expand Up @@ -609,15 +622,17 @@ impl crate::traits::CommandDevice for Context {
SyncPoint { cmd_buf }
}

fn wait_for(&self, sp: &SyncPoint, timeout_ms: u32) -> bool {
fn wait_for(&self, sp: &SyncPoint, timeout_ms: u32) -> Result<bool, crate::DeviceError> {
use metal::MTLCommandBuffer as _;
let start = time::Instant::now();
loop {
if let metal::MTLCommandBufferStatus::Completed = sp.cmd_buf.status() {
return true;
match sp.cmd_buf.status() {
metal::MTLCommandBufferStatus::Completed => return Ok(true),
metal::MTLCommandBufferStatus::Error => return Err(crate::DeviceError::DeviceLost),
_ => {}
}
if start.elapsed().as_millis() >= timeout_ms as u128 {
return false;
return Ok(false);
}
thread::sleep(time::Duration::from_millis(1));
}
Expand Down
8 changes: 6 additions & 2 deletions blade-graphics/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ pub trait ResourceDevice {
fn destroy_acceleration_structure(&self, acceleration_structure: Self::AccelerationStructure);
}

pub trait ComputePipelineBase {
fn get_workgroup_size(&self) -> [u32; 3];
}

pub trait ShaderDevice {
type ComputePipeline: Send + Sync;
type ComputePipeline: Send + Sync + ComputePipelineBase;
type RenderPipeline: Send + Sync;

fn create_compute_pipeline(&self, desc: super::ComputePipelineDesc) -> Self::ComputePipeline;
Expand All @@ -44,7 +48,7 @@ pub trait CommandDevice {
fn create_command_encoder(&self, desc: super::CommandEncoderDesc) -> Self::CommandEncoder;
fn destroy_command_encoder(&self, encoder: &mut Self::CommandEncoder);
fn submit(&self, encoder: &mut Self::CommandEncoder) -> Self::SyncPoint;
fn wait_for(&self, sp: &Self::SyncPoint, timeout_ms: u32) -> bool;
fn wait_for(&self, sp: &Self::SyncPoint, timeout_ms: u32) -> Result<bool, super::DeviceError>;
}

pub trait CommandEncoder {
Expand Down
Loading
Loading