use crate::backend::{no_backend, Backend, BackendPreference};
use impeller_core::Recording;
use impeller_hal::{
Capabilities, Error, Extent2D, HalContext, PixelFormat, Result, RuntimeProgram,
};
#[allow(clippy::large_enum_variant)]
pub enum Context {
#[cfg(feature = "vulkan")]
Vulkan(impeller_hal_vulkan::VulkanContext),
#[cfg(feature = "gles")]
Gles(impeller_hal_gles::GlesContext),
}
impl Context {
pub fn new(preference: BackendPreference) -> Result<Self> {
let mut attempts = Vec::new();
#[cfg(feature = "vulkan")]
if matches!(
preference,
BackendPreference::Auto | BackendPreference::Vulkan
) {
match impeller_hal_vulkan::VulkanContext::new(
impeller_hal_vulkan::DevicePreference::Auto,
) {
Ok(ctx) => return Ok(Self::Vulkan(ctx)),
Err(e) => attempts.push((Backend::Vulkan, e)),
}
}
#[cfg(feature = "gles")]
if matches!(
preference,
BackendPreference::Auto | BackendPreference::Gles
) {
match impeller_hal_gles::GlesContext::new(impeller_hal_gles::DisplayTarget::Surfaceless)
{
Ok(ctx) => return Ok(Self::Gles(ctx)),
Err(e) => attempts.push((Backend::Gles, e)),
}
}
let _ = preference;
Err(no_backend(attempts))
}
pub fn backend(&self) -> Backend {
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(_) => Backend::Vulkan,
#[cfg(feature = "gles")]
Self::Gles(_) => Backend::Gles,
}
}
pub fn capabilities(&self) -> &Capabilities {
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(ctx) => HalContext::capabilities(ctx),
#[cfg(feature = "gles")]
Self::Gles(ctx) => HalContext::capabilities(ctx),
}
}
pub fn create_surface(&mut self, extent: Extent2D, format: PixelFormat) -> Result<Surface> {
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(ctx) => Ok(Surface::Vulkan(HalContext::create_texture(
ctx,
&impeller_hal::TextureDescriptor::offscreen(extent, format),
)?)),
#[cfg(feature = "gles")]
Self::Gles(ctx) => Ok(Surface::Gles(HalContext::create_texture(
ctx,
&impeller_hal::TextureDescriptor::offscreen(extent, format),
)?)),
}
}
pub fn draw(&mut self, surface: &mut Surface, recording: &Recording) -> Result<()> {
self.draw_with_images(surface, recording, &[])
}
pub fn draw_with_images(
&mut self,
surface: &mut Surface,
recording: &Recording,
images: &[&Image],
) -> Result<()> {
match (self, surface) {
#[cfg(feature = "vulkan")]
(Self::Vulkan(ctx), Surface::Vulkan(texture)) => {
let images = vulkan_textures(images)?;
impeller_core::execute::<impeller_hal_vulkan::VulkanHal>(
ctx, texture, recording, &images,
)
}
#[cfg(feature = "gles")]
(Self::Gles(ctx), Surface::Gles(texture)) => {
let images = gles_textures(images)?;
impeller_core::execute::<impeller_hal_gles::GlesHal>(
ctx, texture, recording, &images,
)
}
#[allow(unreachable_patterns)]
_ => Err(Error::Unsupported(
"this surface was created by a different context",
)),
}
}
pub fn register_program(&mut self, program: &RuntimeProgram) -> Result<u32> {
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(ctx) => ctx.register_program(program),
#[cfg(feature = "gles")]
Self::Gles(ctx) => ctx.register_program(program),
}
}
pub fn create_image(&mut self, extent: Extent2D, format: PixelFormat) -> Result<Image> {
let descriptor = impeller_hal::TextureDescriptor::offscreen(extent, format);
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(ctx) => HalContext::create_texture(ctx, &descriptor).map(Image::Vulkan),
#[cfg(feature = "gles")]
Self::Gles(ctx) => HalContext::create_texture(ctx, &descriptor).map(Image::Gles),
}
}
pub fn create_mipmapped_image(
&mut self,
extent: Extent2D,
format: PixelFormat,
) -> Result<Image> {
let descriptor = impeller_hal::TextureDescriptor::mipmapped(extent, format);
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(ctx) => HalContext::create_texture(ctx, &descriptor).map(Image::Vulkan),
#[cfg(feature = "gles")]
Self::Gles(ctx) => HalContext::create_texture(ctx, &descriptor).map(Image::Gles),
}
}
pub fn write_image(&mut self, image: &mut Image, pixels: &[u8]) -> Result<()> {
match (self, image) {
#[cfg(feature = "vulkan")]
(Self::Vulkan(ctx), Image::Vulkan(texture)) => {
HalContext::write_texture(ctx, texture, pixels)
}
#[cfg(feature = "gles")]
(Self::Gles(ctx), Image::Gles(texture)) => {
HalContext::write_texture(ctx, texture, pixels)
}
#[allow(unreachable_patterns)]
_ => Err(Error::Unsupported(
"this image was created by a different context",
)),
}
}
pub fn destroy_image(&mut self, image: Image) {
match (self, image) {
#[cfg(feature = "vulkan")]
(Self::Vulkan(ctx), Image::Vulkan(texture)) => {
HalContext::destroy_texture(ctx, texture)
}
#[cfg(feature = "gles")]
(Self::Gles(ctx), Image::Gles(texture)) => HalContext::destroy_texture(ctx, texture),
#[allow(unreachable_patterns)]
_ => {}
}
}
pub fn read(&mut self, surface: &mut Surface) -> Result<Vec<u8>> {
match (self, surface) {
#[cfg(feature = "vulkan")]
(Self::Vulkan(ctx), Surface::Vulkan(texture)) => HalContext::read_texture(ctx, texture),
#[cfg(feature = "gles")]
(Self::Gles(ctx), Surface::Gles(texture)) => HalContext::read_texture(ctx, texture),
#[allow(unreachable_patterns)]
_ => Err(Error::Unsupported(
"this surface was created by a different context",
)),
}
}
pub fn destroy_surface(&mut self, surface: Surface) {
match (self, surface) {
#[cfg(feature = "vulkan")]
(Self::Vulkan(ctx), Surface::Vulkan(texture)) => {
HalContext::destroy_texture(ctx, texture)
}
#[cfg(feature = "gles")]
(Self::Gles(ctx), Surface::Gles(texture)) => HalContext::destroy_texture(ctx, texture),
#[allow(unreachable_patterns)]
_ => {}
}
}
}
pub enum Image {
#[cfg(feature = "vulkan")]
Vulkan(impeller_hal_vulkan::VulkanTexture),
#[cfg(feature = "gles")]
Gles(impeller_hal_gles::GlesTexture),
}
impl Image {
pub fn extent(&self) -> Extent2D {
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(texture) => texture.extent(),
#[cfg(feature = "gles")]
Self::Gles(texture) => texture.extent(),
}
}
}
#[cfg(feature = "vulkan")]
fn vulkan_textures<'a>(
images: &[&'a Image],
) -> Result<Vec<&'a impeller_hal_vulkan::VulkanTexture>> {
images
.iter()
.map(|image| match image {
Image::Vulkan(texture) => Ok(texture),
#[allow(unreachable_patterns)]
_ => Err(Error::Unsupported(
"this image was created by a different context",
)),
})
.collect()
}
#[cfg(feature = "gles")]
fn gles_textures<'a>(images: &[&'a Image]) -> Result<Vec<&'a impeller_hal_gles::GlesTexture>> {
images
.iter()
.map(|image| match image {
Image::Gles(texture) => Ok(texture),
#[allow(unreachable_patterns)]
_ => Err(Error::Unsupported(
"this image was created by a different context",
)),
})
.collect()
}
pub enum Surface {
#[cfg(feature = "vulkan")]
Vulkan(impeller_hal_vulkan::VulkanTexture),
#[cfg(feature = "gles")]
Gles(impeller_hal_gles::GlesTexture),
}
impl Surface {
pub fn extent(&self) -> Extent2D {
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(texture) => texture.extent(),
#[cfg(feature = "gles")]
Self::Gles(texture) => texture.extent(),
}
}
pub fn format(&self) -> PixelFormat {
match self {
#[cfg(feature = "vulkan")]
Self::Vulkan(texture) => texture.format(),
#[cfg(feature = "gles")]
Self::Gles(texture) => texture.format(),
}
}
}