use crate::hal::window::Extent2D;
use crate::hal::{self, format as f, image, memory, CompositeAlpha};
use crate::{native, Backend as B, Device, PhysicalDevice, QueueFamily, Starc};
use glutin::{self, ContextTrait};
fn get_window_extent(window: &glutin::WindowedContext) -> image::Extent {
let px = window
.get_inner_size()
.unwrap()
.to_physical(window.get_hidpi_factor());
image::Extent {
width: px.width as image::Size,
height: px.height as image::Size,
depth: 1,
}
}
#[derive(Debug)]
pub struct Swapchain {
pub(crate) window: Starc<glutin::WindowedContext>,
pub(crate) extent: Extent2D,
}
impl hal::Swapchain<B> for Swapchain {
unsafe fn acquire_image(
&mut self,
_timeout_ns: u64,
_semaphore: Option<&native::Semaphore>,
_fence: Option<&native::Fence>,
) -> Result<(hal::SwapImageIndex, Option<hal::window::Suboptimal>), hal::AcquireError> {
Ok((0, None))
}
}
#[derive(Debug)]
pub struct Surface {
window: Starc<glutin::WindowedContext>,
}
impl Surface {
pub fn from_window(window: glutin::WindowedContext) -> Self {
Surface {
window: Starc::new(window),
}
}
pub fn get_window(&self) -> &glutin::WindowedContext {
&*self.window
}
pub fn window(&self) -> &glutin::WindowedContext {
&self.window
}
fn swapchain_formats(&self) -> Vec<f::Format> {
let pixel_format = self.window.get_pixel_format();
let color_bits = pixel_format.color_bits;
let alpha_bits = pixel_format.alpha_bits;
let srgb = pixel_format.srgb;
match (color_bits, alpha_bits, srgb) {
(24, 8, true) => vec![f::Format::Rgba8Srgb, f::Format::Bgra8Srgb],
(24, 8, false) => vec![f::Format::Rgba8Unorm, f::Format::Bgra8Unorm],
_ => vec![],
}
}
}
impl hal::Surface<B> for Surface {
fn kind(&self) -> hal::image::Kind {
let ex = get_window_extent(&self.window);
let samples = self.window.get_pixel_format().multisampling.unwrap_or(1);
hal::image::Kind::D2(ex.width, ex.height, 1, samples as _)
}
fn compatibility(
&self,
_: &PhysicalDevice,
) -> (
hal::SurfaceCapabilities,
Option<Vec<f::Format>>,
Vec<hal::PresentMode>,
) {
let ex = get_window_extent(&self.window);
let extent = hal::window::Extent2D::from(ex);
let caps = hal::SurfaceCapabilities {
image_count: if self.window.get_pixel_format().double_buffer {
2..3
} else {
1..2
},
current_extent: Some(extent),
extents: extent..hal::window::Extent2D {
width: ex.width + 1,
height: ex.height + 1,
},
max_image_layers: 1,
usage: image::Usage::COLOR_ATTACHMENT | image::Usage::TRANSFER_SRC,
composite_alpha: CompositeAlpha::OPAQUE,
};
let present_modes = vec![
hal::PresentMode::Fifo,
];
(caps, Some(self.swapchain_formats()), present_modes)
}
fn supports_queue_family(&self, _: &QueueFamily) -> bool {
true
}
}
impl Device {
pub(crate) fn create_swapchain_impl(
&self,
surface: &mut Surface,
config: hal::SwapchainConfig,
) -> (Swapchain, Vec<native::Image>) {
let swapchain = Swapchain {
extent: config.extent,
window: surface.window.clone(),
};
let gl = &self.share.context;
let (int_format, iformat, itype) = match config.format {
f::Format::Rgba8Unorm => (gl::RGBA8, gl::RGBA, gl::UNSIGNED_BYTE),
f::Format::Rgba8Srgb => (gl::SRGB8_ALPHA8, gl::RGBA, gl::UNSIGNED_BYTE),
_ => unimplemented!(),
};
let channel = config.format.base_format().1;
let images = (0..config.image_count)
.map(|_| unsafe {
let image = if config.image_layers > 1
|| config.image_usage.contains(image::Usage::STORAGE)
|| config.image_usage.contains(image::Usage::SAMPLED)
{
let mut name = 0;
gl.GenTextures(1, &mut name);
match config.extent {
Extent2D {
width: w,
height: h,
} => {
gl.BindTexture(gl::TEXTURE_2D, name);
if self.share.private_caps.image_storage {
gl.TexStorage2D(
gl::TEXTURE_2D,
config.image_layers as _,
int_format,
w as _,
h as _,
);
} else {
gl.TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_MAX_LEVEL,
(config.image_layers - 1) as _,
);
let mut w = w;
let mut h = h;
for i in 0..config.image_layers {
gl.TexImage2D(
gl::TEXTURE_2D,
i as _,
int_format as _,
w as _,
h as _,
0,
iformat,
itype,
std::ptr::null(),
);
w = std::cmp::max(w / 2, 1);
h = std::cmp::max(h / 2, 1);
}
}
}
};
native::ImageKind::Texture(name)
} else {
let mut name = 0;
gl.GenRenderbuffers(1, &mut name);
match config.extent {
Extent2D {
width: w,
height: h,
} => {
gl.BindRenderbuffer(gl::RENDERBUFFER, name);
gl.RenderbufferStorage(gl::RENDERBUFFER, int_format, w as _, h as _);
}
};
native::ImageKind::Surface(name)
};
let surface_desc = config.format.base_format().0.desc();
let bytes_per_texel = surface_desc.bits / 8;
let ext = config.extent;
let size = (ext.width * ext.height) as u64 * bytes_per_texel as u64;
if let Err(err) = self.share.check() {
panic!(
"Error creating swapchain image: {:?} with {:?} format",
err, config.format
);
}
native::Image {
kind: image,
channel,
requirements: memory::Requirements {
size,
alignment: 1,
type_mask: 0x7,
},
}
})
.collect::<Vec<_>>();
(swapchain, images)
}
}
impl hal::Instance for Surface {
type Backend = B;
fn enumerate_adapters(&self) -> Vec<hal::Adapter<B>> {
unsafe { self.window.make_current().unwrap() };
let adapter = PhysicalDevice::new_adapter(|s| self.window.get_proc_address(s) as *const _);
vec![adapter]
}
}
pub fn config_context(
builder: glutin::ContextBuilder,
color_format: f::Format,
ds_format: Option<f::Format>,
) -> glutin::ContextBuilder {
let color_base = color_format.base_format();
let color_bits = color_base.0.describe_bits();
let depth_bits = match ds_format {
Some(fm) => fm.base_format().0.describe_bits(),
None => f::BITS_ZERO,
};
builder
.with_depth_buffer(depth_bits.depth)
.with_stencil_buffer(depth_bits.stencil)
.with_pixel_format(color_bits.color, color_bits.alpha)
.with_srgb(color_base.1 == f::ChannelType::Srgb)
}
pub struct Headless(pub glutin::Context);
unsafe impl Send for Headless {}
unsafe impl Sync for Headless {}
impl hal::Instance for Headless {
type Backend = B;
fn enumerate_adapters(&self) -> Vec<hal::Adapter<B>> {
unsafe { self.0.make_current().unwrap() };
let adapter = PhysicalDevice::new_adapter(|s| self.0.get_proc_address(s) as *const _);
vec![adapter]
}
}