use gfx_core::{
format::{ChannelType, DepthFormat, Format, RenderFormat},
handle::{DepthStencilView, RawDepthStencilView, RawRenderTargetView, RenderTargetView},
memory::Typed,
texture,
};
use gfx_device_gl::Resources as R;
use glutin::{
config::{ColorBufferType, ConfigTemplateBuilder},
context::ContextAttributesBuilder,
display::GetGlDisplay,
prelude::{GlConfig, GlDisplay, NotCurrentGlContext},
surface::{SurfaceAttributesBuilder, WindowSurface},
};
use glutin_winit::GlWindow;
use raw_window_handle::HasRawWindowHandle;
use std::{error::Error, ffi::CString};
pub fn window_builder<T: 'static>(
event_loop: &winit::event_loop::EventLoop<T>,
winit: winit::window::WindowBuilder,
) -> Builder<'_, T> {
Builder {
event_loop,
winit,
surface_attrs: <_>::default(),
ctx_attrs: <_>::default(),
config_attrs: <_>::default(),
sample_number_pref: <_>::default(),
}
}
#[derive(Debug, Clone)]
pub struct Builder<'a, T: 'static> {
event_loop: &'a winit::event_loop::EventLoop<T>,
winit: winit::window::WindowBuilder,
surface_attrs: Option<SurfaceAttributesBuilder<WindowSurface>>,
ctx_attrs: ContextAttributesBuilder,
config_attrs: ConfigTemplateBuilder,
sample_number_pref: NumberOfSamples,
}
impl<T> Builder<'_, T> {
pub fn surface_attributes(
mut self,
surface_attrs: SurfaceAttributesBuilder<WindowSurface>,
) -> Self {
self.surface_attrs = Some(surface_attrs);
self
}
pub fn context_attributes(mut self, ctx_attrs: ContextAttributesBuilder) -> Self {
self.ctx_attrs = ctx_attrs;
self
}
pub fn config_template(mut self, conf: ConfigTemplateBuilder) -> Self {
self.config_attrs = conf;
self
}
pub fn number_of_samples(mut self, pref: impl Into<NumberOfSamples>) -> Self {
self.sample_number_pref = pref.into();
self
}
pub fn build<Color, Depth>(self) -> Result<Init<Color, Depth>, Box<dyn Error>>
where
Color: RenderFormat,
Depth: DepthFormat,
{
self.build_raw(Color::get_format(), Depth::get_format())
.map(|i| i.into_typed())
}
pub fn build_raw(
self,
color_format: Format,
depth_format: Format,
) -> Result<RawInit, Box<dyn Error>> {
let Format(color_surface, color_channel) = color_format;
let color_total_bits = color_surface.get_total_bits();
let alpha_bits = color_surface.get_alpha_stencil_bits();
let depth_total_bits = depth_format.0.get_total_bits();
let stencil_bits = depth_format.0.get_alpha_stencil_bits();
let srgb = color_channel == ChannelType::Srgb;
let surface_attrs = self
.surface_attrs
.unwrap_or_else(|| SurfaceAttributesBuilder::new().with_srgb(srgb.then_some(true)));
let config_attrs = self
.config_attrs
.with_alpha_size(alpha_bits)
.with_depth_size(depth_total_bits - stencil_bits)
.with_stencil_size(stencil_bits);
let mut no_suitable_config = false;
let (window, gl_config) = glutin_winit::DisplayBuilder::new()
.with_window_builder(Some(self.winit))
.build(self.event_loop, config_attrs, |configs| {
let mut configs: Vec<_> = configs.collect();
assert!(!configs.is_empty(), "no gl configs?");
let best = self
.sample_number_pref
.find(configs.iter().enumerate().filter(|(_, c)| {
let color_bits = match c.color_buffer_type() {
None => 0,
Some(ColorBufferType::Luminance(s)) => s,
Some(ColorBufferType::Rgb {
r_size,
g_size,
b_size,
}) => r_size + g_size + b_size,
};
(!srgb || c.srgb_capable())
&& color_bits == color_total_bits - alpha_bits
&& c.alpha_size() == alpha_bits
&& (c.depth_size() == depth_total_bits - stencil_bits
|| c.depth_size() == depth_total_bits)
&& c.stencil_size() == stencil_bits
}));
match best {
Some((idx, _)) => configs.swap_remove(idx),
None => {
no_suitable_config = true;
configs.swap_remove(0)
}
}
})?;
if no_suitable_config {
return Err("no suitable gl config found, color+depth not supported?".into());
}
let window = window.unwrap(); let raw_window_handle = window.raw_window_handle();
let gl_display = gl_config.display();
let (gl_surface, gl_context) = {
let ctx_attrs = self.ctx_attrs.build(Some(raw_window_handle));
let surface_attrs = window.build_surface_attributes(surface_attrs);
let surface = unsafe { gl_display.create_window_surface(&gl_config, &surface_attrs)? };
let context = unsafe { gl_display.create_context(&gl_config, &ctx_attrs)? }
.make_current(&surface)?;
(surface, context)
};
let (device, factory) =
gfx_device_gl::create(|s| gl_display.get_proc_address(&CString::new(s).unwrap()) as _);
let window_size = window.inner_size();
let tex_dimensions = (
window_size.width as _,
window_size.height as _,
1,
gl_config.num_samples().into(),
);
let (color_view, depth_view) =
gfx_device_gl::create_main_targets_raw(tex_dimensions, color_surface, depth_format.0);
Ok(RawInit {
window,
gl_config,
gl_surface,
gl_context,
device,
factory,
color_view,
depth_view,
})
}
}
#[non_exhaustive]
pub struct InitState<ColorView, DepthView> {
pub window: winit::window::Window,
pub gl_config: glutin::config::Config,
pub gl_surface: glutin::surface::Surface<WindowSurface>,
pub gl_context: glutin::context::PossiblyCurrentContext,
pub device: gfx_device_gl::Device,
pub factory: gfx_device_gl::Factory,
pub color_view: ColorView,
pub depth_view: DepthView,
}
pub type RawInit = InitState<RawRenderTargetView<R>, RawDepthStencilView<R>>;
pub type Init<Color, Depth> = InitState<RenderTargetView<R, Color>, DepthStencilView<R, Depth>>;
impl RawInit {
fn into_typed<Color: RenderFormat, Depth: DepthFormat>(self) -> Init<Color, Depth> {
Init {
window: self.window,
gl_config: self.gl_config,
gl_surface: self.gl_surface,
gl_context: self.gl_context,
device: self.device,
factory: self.factory,
color_view: Typed::new(self.color_view),
depth_view: Typed::new(self.depth_view),
}
}
}
pub fn resize_views<Color: RenderFormat, Depth: DepthFormat>(
new_size: winit::dpi::PhysicalSize<u32>,
color_view: &mut RenderTargetView<R, Color>,
depth_view: &mut DepthStencilView<R, Depth>,
) {
if let Some((cv, dv)) = resized_views(new_size, color_view, depth_view) {
*color_view = cv;
*depth_view = dv;
}
}
#[must_use]
pub fn resized_views<Color: RenderFormat, Depth: DepthFormat>(
new_size: winit::dpi::PhysicalSize<u32>,
color_view: &RenderTargetView<R, Color>,
depth_view: &DepthStencilView<R, Depth>,
) -> Option<(RenderTargetView<R, Color>, DepthStencilView<R, Depth>)> {
let old_dimensions = color_view.get_dimensions();
debug_assert_eq!(old_dimensions, depth_view.get_dimensions());
let (cv, dv) = resized_views_raw(
new_size,
old_dimensions,
Color::get_format(),
Depth::get_format(),
)?;
Some((Typed::new(cv), Typed::new(dv)))
}
#[must_use]
pub fn resized_views_raw(
new_size: winit::dpi::PhysicalSize<u32>,
old_dimensions: texture::Dimensions,
color_fmt: Format,
ds_fmt: Format,
) -> Option<(RawRenderTargetView<R>, RawDepthStencilView<R>)> {
let new_dimensions = (
new_size.width as _,
new_size.height as _,
old_dimensions.2,
old_dimensions.3,
);
if old_dimensions == new_dimensions {
return None;
}
Some(gfx_device_gl::create_main_targets_raw(
new_dimensions,
color_fmt.0,
ds_fmt.0,
))
}
#[derive(Debug, Clone, Copy)]
pub enum NumberOfSamples {
Max,
Specific(u8),
}
impl Default for NumberOfSamples {
fn default() -> Self {
Self::Specific(0)
}
}
impl From<u8> for NumberOfSamples {
fn from(val: u8) -> Self {
Self::Specific(val)
}
}
impl NumberOfSamples {
fn find<'a>(
self,
mut configs: impl Iterator<Item = (usize, &'a glutin::config::Config)>,
) -> Option<(usize, &'a glutin::config::Config)> {
match self {
Self::Max => configs.max_by_key(|(_, c)| c.num_samples()),
Self::Specific(n) => configs.find(|(_, c)| c.num_samples() == n),
}
}
}