mod ref_values;
pub mod stats_renderer;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use gfx::handle::ShaderResourceView;
use gfx::texture::{AaMode, Kind, Mipmap};
use gfx::traits::FactoryExt;
use gfx_glutin::{ContextBuilderExt, WindowInitExt, WindowUpdateExt};
use glutin::dpi::LogicalSize;
use glutin::event::{Event, VirtualKeyCode, WindowEvent};
use glutin::{ContextBuilder, GlProfile, GlRequest, Robustness};
use gfx::{self, *};
use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::window::{Fullscreen, WindowBuilder};
use old_school_gfx_glutin_ext as gfx_glutin;
use crate::fps_counter::FpsCounter;
use crate::pixmap::Pixmap;
use crate::primitive::create_quad_max;
use crate::vertex::Vertex;
use stats_renderer::{Corner, StatsRenderer};
pub(crate) type ColorFormat = gfx::format::Rgba8;
pub(crate) type DepthFormat = gfx::format::DepthStencil;
type F = gfx_device_gl::Factory;
pub(crate) type R = gfx_device_gl::Resources;
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
gfx_defines! {
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
image: gfx::TextureSampler<[f32; 4]> = "t_Image",
out: gfx::RenderTarget<ColorFormat> = "Target0",
}
}
pub struct Renderer<'a> {
title: &'a str,
pixmap: Arc<Pixmap>,
stats: StatsRenderer<F>,
events_loop: EventLoop<()>,
#[allow(unused)]
fps: FpsCounter,
}
impl<'a> Renderer<'a> {
pub fn new(title: &'a str, pixmap: Arc<Pixmap>) -> Renderer<'a> {
Renderer {
title,
pixmap,
stats: StatsRenderer::new(Corner::TopLeft),
events_loop: EventLoop::new(),
fps: FpsCounter::new(),
}
}
pub fn run(
mut self,
fullscreen: bool,
stats_size: u8,
stats_offset: (u32, u32),
stats_padding: i32,
stats_col_spacing: i32,
keep_running: Arc<AtomicBool>,
) {
let size = self.pixmap.dimensions();
let monitor = if fullscreen {
Some(Fullscreen::Borderless(self.events_loop.primary_monitor()))
} else {
None
};
let builder = WindowBuilder::new()
.with_title(self.title.to_string())
.with_fullscreen(monitor)
.with_inner_size(LogicalSize {
width: size.0 as f64,
height: size.1 as f64,
});
let (window, mut device, mut factory, mut main_color, mut main_depth) =
ContextBuilder::new()
.with_srgb(true)
.with_gl(GlRequest::Latest)
.with_gl_robustness(Robustness::TryRobustNoResetNotification)
.with_gl_profile(GlProfile::Core)
.with_multisampling(1)
.with_gfx_color_depth::<ColorFormat, DepthFormat>()
.with_vsync(true)
.build_windowed(builder, &self.events_loop)
.unwrap()
.init_gfx();
let my_window_id = window.window().id();
let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
let pso = factory
.create_pipeline_simple(
include_bytes!("../../shaders/screen.glslv"),
include_bytes!("../../shaders/screen.glslf"),
pipe::new(),
)
.unwrap();
let plane = create_quad_max();
let (vertex_buffer, slice) = plane.create_vertex_buffer(&mut factory);
let texture_kind = Kind::D2(size.0 as u16, size.1 as u16, AaMode::Single);
let base_image = (
Renderer::create_texture(&mut factory, self.pixmap.as_bytes(), texture_kind),
factory.create_sampler_linear(),
);
let mut data_depth = main_depth.clone();
let mut data = pipe::Data {
vbuf: vertex_buffer,
image: base_image,
out: main_color.clone(),
};
let dimensions = (size.0 as f32, size.1 as f32);
self.stats
.init(
factory.clone(),
dimensions,
main_color.clone(),
main_depth.clone(),
stats_size,
stats_offset,
stats_padding,
stats_col_spacing,
)
.expect("failed to initialize stats text renderer");
let mut next_frame_time = Instant::now();
self.events_loop.run(move |event, _target, control_flow| {
if !keep_running.load(Ordering::SeqCst) {
*control_flow = ControlFlow::Exit;
return;
}
let keycode = if let Event::WindowEvent { window_id, event } = &event {
if window_id == &my_window_id {
if let WindowEvent::KeyboardInput { input, .. } = event {
input.virtual_keycode
} else {
None
}
} else {
None
}
} else {
None
};
let is_close_request = if let Event::WindowEvent {
window_id,
event: WindowEvent::CloseRequested,
} = &event
{
window_id == &my_window_id
} else {
false
};
let exit = keycode == Some(VirtualKeyCode::Escape) || is_close_request;
if let Event::WindowEvent {
event: WindowEvent::Resized(s),
..
} = event
{
let dimensions = (s.width as f32, s.height as f32);
window.update_gfx(&mut main_color, &mut main_depth);
window.update_gfx(&mut data.out, &mut data_depth);
self.stats.update_views(&window, dimensions);
}
if Instant::now() > next_frame_time || event == Event::MainEventsCleared {
data.image = (
Renderer::create_texture(&mut factory, self.pixmap.as_bytes(), texture_kind),
factory.create_sampler_linear(),
);
encoder.clear(&data.out, BLACK);
encoder.draw(&slice, &pso, &data);
self.stats.draw(&mut encoder, &main_color).unwrap();
encoder.flush(&mut device);
window.swap_buffers().unwrap();
device.cleanup();
next_frame_time = Instant::now() + Duration::from_millis(1);
}
if exit || !keep_running.load(Ordering::SeqCst) {
keep_running.store(false, Ordering::SeqCst);
} else {
*control_flow = ControlFlow::WaitUntil(next_frame_time);
}
});
}
pub fn run_default(self) {
self.run(false, 20, (10, 10), 12, 20, Arc::new(AtomicBool::new(true)));
}
pub fn stats(&self) -> &StatsRenderer<F> {
&self.stats
}
fn create_texture(factory: &mut F, data: &[u8], kind: Kind) -> ShaderResourceView<R, [f32; 4]> {
let (_, view) = factory
.create_texture_immutable_u8::<ColorFormat>(kind, Mipmap::Provided, &[data])
.unwrap();
view
}
}