#[macro_use]
extern crate log;
extern crate sdl2;
extern crate gfx_core as core;
extern crate gfx_device_gl;
use core::handle;
use core::format::{ChannelType, DepthFormat, Format, RenderFormat};
pub use gfx_device_gl::{Device, Factory, Resources};
use sdl2::video::{DisplayMode, GLContext, Window, WindowBuilder, WindowBuildError};
use sdl2::pixels::PixelFormatEnum;
use core::{format, texture};
use core::memory::Typed;
use gfx_device_gl::Resources as R;
use std::error::Error;
use std::fmt;
#[derive(Debug)]
pub enum InitError {
PixelFormatUnsupportedError,
WindowBuildError(WindowBuildError),
SdlError(String),
}
impl fmt::Display for InitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
InitError::WindowBuildError(ref e) => write!(f, "{}: {:?}", self.description(), e),
InitError::SdlError(ref e) => write!(f, "{}: {}", self.description(), e),
_ => f.write_str(self.description()),
}
}
}
impl Error for InitError {
fn description(&self) -> &str {
match *self {
InitError::PixelFormatUnsupportedError => "pixel format unsupported",
InitError::WindowBuildError(_) => "unable to build a window",
InitError::SdlError(_) => "SDL error",
}
}
}
impl From<String> for InitError {
fn from(e: String) -> Self {
InitError::SdlError(e)
}
}
impl From<WindowBuildError> for InitError {
fn from(e: WindowBuildError) -> Self {
InitError::WindowBuildError(e)
}
}
fn sdl2_pixel_format_from_gfx(format: Format) -> Option<PixelFormatEnum> {
use core::format::SurfaceType::*;
use sdl2::pixels::PixelFormatEnum as SdlFmt;
let Format(surface, _) = format;
match surface {
R4_G4_B4_A4 => Some(SdlFmt::RGBA4444),
R5_G5_B5_A1 => Some(SdlFmt::RGBA5551),
R5_G6_B5 => Some(SdlFmt::RGB565),
R8_G8_B8_A8 => Some(SdlFmt::RGBA8888),
B8_G8_R8_A8 => Some(SdlFmt::BGRA8888),
R10_G10_B10_A2 => {
warn!("The transfer operations with this format may produce different results on SDL \
compared to Glutin/GLFW, beware!");
Some(SdlFmt::ARGB2101010)
}
R4_G4 | R8 | R8_G8 | R11_G11_B10 | R16 | R16_G16 | R16_G16_B16 |
R16_G16_B16_A16 | R32 | R32_G32 | R32_G32_B32 | R32_G32_B32_A32 | D16 | D24 |
D24_S8 | D32 | BC1_R8_G8_B8 | BC3_R8_G8_B8_A8 => None,
}
}
pub type InitRawOk = (Window, GLContext, Device, Factory,
handle::RawRenderTargetView<Resources>, handle::RawDepthStencilView<Resources>);
pub type InitOk<Cf, Df> =
(Window, GLContext, Device, Factory,
handle::RenderTargetView<Resources, Cf>,
handle::DepthStencilView<Resources, Df>);
pub fn init<Cf, Df>(video_subsystem: &sdl2::VideoSubsystem, builder: WindowBuilder)
-> Result<InitOk<Cf, Df>, InitError>
where
Cf: RenderFormat,
Df: DepthFormat,
{
use core::memory::Typed;
init_raw(video_subsystem, builder, Cf::get_format(), Df::get_format())
.map(|(w, gl, d, f, color_view, ds_view)|
(w, gl, d, f, Typed::new(color_view), Typed::new(ds_view)))
}
pub fn init_raw(video_subsystem: &sdl2::VideoSubsystem, mut builder: WindowBuilder, cf: Format, df: Format)
-> Result<InitRawOk, InitError> {
use core::texture::{AaMode, Size};
{
let depth_total_bits = df.0.get_total_bits();
let stencil_bits = df.0.get_alpha_stencil_bits();
let attr = video_subsystem.gl_attr();
attr.set_framebuffer_srgb_compatible(cf.1 == ChannelType::Srgb);
attr.set_alpha_size(cf.0.get_alpha_stencil_bits());
attr.set_depth_size(depth_total_bits - stencil_bits);
attr.set_stencil_size(stencil_bits);
attr.set_context_flags().set();
}
let mut window = builder.opengl().build()?;
let display_mode = DisplayMode {
format: sdl2_pixel_format_from_gfx(cf)
.ok_or(InitError::PixelFormatUnsupportedError)?,
..window.display_mode()?
};
window.set_display_mode(Some(display_mode))?;
let context = window.gl_create_context()?;
let (device, factory) = gfx_device_gl::create(|s| {
window.subsystem().gl_get_proc_address(s) as *const std::os::raw::c_void
});
let (width, height) = window.drawable_size();
let dim = (width as Size, height as Size, 1, AaMode::Single);
let (color_view, ds_view) = gfx_device_gl::create_main_targets_raw(dim, cf.0, df.0);
Ok((window, context, device, factory, color_view, ds_view))
}
fn get_window_dimensions(window: &sdl2::video::Window) -> texture::Dimensions {
let (width, height) = window.size();
let aa = window.subsystem().gl_attr().multisample_samples() as texture::NumSamples;
(width as texture::Size, height as texture::Size, 1, aa.into())
}
pub fn update_views<Cf, Df>(window: &sdl2::video::Window, color_view: &mut handle::RenderTargetView<R, Cf>,
ds_view: &mut handle::DepthStencilView<R, Df>)
where
Cf: format::RenderFormat,
Df: format::DepthFormat
{
let dim = color_view.get_dimensions();
assert_eq!(dim, ds_view.get_dimensions());
if let Some((cv, dv)) = update_views_raw(window, dim, Cf::get_format(), Df::get_format()) {
*color_view = Typed::new(cv);
*ds_view = Typed::new(dv);
}
}
pub fn update_views_raw(window: &sdl2::video::Window, old_dimensions: texture::Dimensions,
color_format: format::Format, ds_format: format::Format)
-> Option<(handle::RawRenderTargetView<R>, handle::RawDepthStencilView<R>)>
{
let dim = get_window_dimensions(window);
if dim != old_dimensions {
Some(gfx_device_gl::create_main_targets_raw(dim, color_format.0, ds_format.0))
} else {
None
}
}