#[cfg(target_os = "android")]
pub(super) mod android;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(super) mod angle;
#[cfg(target_os = "linux")]
pub(super) mod linux;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(super) mod macos;
#[cfg(target_os = "windows")]
pub(super) mod windows;
use super::EglDisplayKind;
use edgefirst_tensor::{PixelFormat, Tensor, TensorDyn};
#[derive(Debug, Clone, Copy)]
pub(crate) struct PlatformCaps {
pub(crate) transfer_backend: super::TransferBackend,
pub(crate) render_dtypes: crate::RenderDtypeSupport,
pub(crate) serialize_gl: bool,
#[allow(dead_code)]
pub(crate) external_oes: bool,
pub(crate) native_fence_sync: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PackedImportFormat {
Rgba8888,
Rgba16161616F,
Rgba32323232F,
}
impl PackedImportFormat {
pub(crate) fn bytes_per_pixel(self) -> usize {
match self {
PackedImportFormat::Rgba8888 => 4,
PackedImportFormat::Rgba16161616F => 8,
PackedImportFormat::Rgba32323232F => 16,
}
}
}
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "ios"))]
pub(super) fn unplaced_destination<T>(img: &Tensor<T>) -> Option<usize>
where
T: num_traits::Num + Clone + std::fmt::Debug + Send + Sync + edgefirst_tensor::Element,
{
match (img.plane_offset(), img.view_origin()) {
(Some(offset), None) if offset != 0 => Some(offset),
_ => None,
}
}
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "ios"))]
pub(super) fn refuse_offset_source<T>(
img: &Tensor<T>,
what: &str,
binding: &str,
) -> crate::Result<()>
where
T: num_traits::Num + Clone + std::fmt::Debug + Send + Sync + edgefirst_tensor::Element,
{
match img.plane_offset() {
None | Some(0) => Ok(()),
Some(offset) => Err(crate::Error::NotSupported(format!(
"GL convert: {what} starts {offset} bytes into its buffer, and {binding} \
can only bind the whole buffer from its origin; uploading the window \
instead"
))),
}
}
pub(super) trait GlPlatform {
type Display;
type Import;
type ImportHandle: Copy;
const PERSISTENT_TEX_BINDINGS: bool;
const ZERO_COPY_FLOAT: super::float_dispatch::ZeroCopyFloatSet;
const EXTERNAL_OES: bool;
fn load_gl_once(display: &Self::Display);
fn init_display(kind: Option<EglDisplayKind>) -> crate::Result<Self::Display>;
fn import_buffer(
display: &Self::Display,
img: &Tensor<u8>,
fmt: PixelFormat,
for_dst: bool,
) -> crate::Result<Self::Import>;
fn validate_import_identity<T>(_img: &Tensor<T>, _what: &str) -> crate::Result<()>
where
T: num_traits::Num + Clone + std::fmt::Debug + Send + Sync + edgefirst_tensor::Element,
{
Ok(())
}
fn dst_import_places<T>(_img: &Tensor<T>) -> bool
where
T: num_traits::Num + Clone + std::fmt::Debug + Send + Sync + edgefirst_tensor::Element,
{
true
}
fn import_buffer_nv_r8(
display: &Self::Display,
img: &Tensor<u8>,
fmt: PixelFormat,
) -> crate::Result<Self::Import>;
const NV_CHROMA_IMPORT: bool = false;
fn import_buffer_nv_chroma(
_display: &Self::Display,
_img: &Tensor<u8>,
fmt: PixelFormat,
) -> crate::Result<Self::Import> {
Err(crate::Error::NotSupported(format!(
"no {fmt:?} chroma-plane import on this platform"
)))
}
fn import_buffer_packed<T>(
display: &Self::Display,
img: &Tensor<T>,
width: usize,
height: usize,
fmt: PackedImportFormat,
) -> crate::Result<Self::Import>
where
T: num_traits::Num + Clone + std::fmt::Debug + Send + Sync + edgefirst_tensor::Element;
fn import_handle(import: &Self::Import) -> Self::ImportHandle;
fn import_extent(import: &Self::Import) -> Option<(u32, u32)>;
fn import_origin(_import: &Self::Import) -> (u32, u32) {
(0, 0)
}
unsafe fn attach_tex_image_2d(
display: &Self::Display,
handle: Self::ImportHandle,
) -> crate::Result<()>;
unsafe fn attach_tex_image_external(
display: &Self::Display,
handle: Self::ImportHandle,
) -> crate::Result<()>;
unsafe fn attach_renderbuffer_storage(
display: &Self::Display,
handle: Self::ImportHandle,
) -> crate::Result<()>;
fn begin_gpu_pass(display: &Self::Display);
fn end_gpu_pass(display: &Self::Display);
fn native_fence_sync(display: &Self::Display) -> bool;
fn export_completion_fence(
display: &Self::Display,
recorded: Option<u64>,
) -> crate::Result<Option<super::CompletionFence>>;
fn record_completion(display: &Self::Display, dst: &mut TensorDyn, submit: bool)
-> Option<u64>;
fn submit_recorded(display: &Self::Display);
}
#[cfg(target_os = "linux")]
pub(super) type Platform = linux::LinuxEgl;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(super) type Platform = angle::AngleClientBuffer;
#[cfg(target_os = "android")]
pub(super) type Platform = android::AndroidEgl;
#[cfg(target_os = "windows")]
pub(super) type Platform = windows::AngleD3d11;
const _: fn() = || {
fn assert_platform<P: GlPlatform>() {}
assert_platform::<Platform>();
};
#[cfg(test)]
mod tests {
use super::PackedImportFormat;
#[test]
fn rgba32323232f_is_16_bytes_per_pixel() {
assert_eq!(PackedImportFormat::Rgba32323232F.bytes_per_pixel(), 16);
}
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "ios"))]
#[test]
fn an_offset_source_is_refused_and_an_origin_source_is_not() {
use edgefirst_tensor::{Tensor, TensorMemory};
let mut t = Tensor::<u8>::new(&[4, 4, 4], Some(TensorMemory::Mem), None).expect("alloc");
assert!(
super::refuse_offset_source(&t, "source", "ext").is_ok(),
"no offset recorded: attachable"
);
t.set_plane_offset(0);
assert!(
super::refuse_offset_source(&t, "source", "ext").is_ok(),
"a zero offset is the origin: attachable"
);
t.set_plane_offset(16);
let err = super::refuse_offset_source(&t, "source", "ext")
.expect_err("a source 16 bytes in cannot be bound from the origin");
assert!(
matches!(err, crate::Error::NotSupported(_)),
"NotSupported is what sends the engine to the upload path; got {err}"
);
}
}