mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 20:29:04 +03:00
rearrange ezgui internals to isolate all glium/glutin specifics into one
module
This commit is contained in:
parent
82f149db56
commit
c5a055cbb6
333
ezgui/src/backend_glium.rs
Normal file
333
ezgui/src/backend_glium.rs
Normal file
@ -0,0 +1,333 @@
|
||||
use crate::drawing::Uniforms;
|
||||
use crate::{Canvas, Color, ScreenDims, ScreenRectangle, TextureType};
|
||||
use geom::{Angle, Polygon, Pt2D};
|
||||
use glium::texture::{RawImage2d, Texture2dArray};
|
||||
use glium::uniforms::{SamplerBehavior, SamplerWrapFunction, UniformValue};
|
||||
use glium::Surface;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
pub fn setup(window_title: &str) -> (PrerenderInnards, winit::event_loop::EventLoop<()>) {
|
||||
let event_loop = winit::event_loop::EventLoop::new();
|
||||
let window = winit::window::WindowBuilder::new()
|
||||
.with_title(window_title)
|
||||
.with_maximized(true);
|
||||
// multisampling: 2 looks bad, 4 looks fine
|
||||
let context = glutin::ContextBuilder::new()
|
||||
.with_multisampling(4)
|
||||
.with_depth_buffer(2);
|
||||
// TODO This step got slow
|
||||
println!("Initializing OpenGL window");
|
||||
let display = glium::Display::new(window, context, &event_loop).unwrap();
|
||||
|
||||
let (vertex_shader, fragment_shader) =
|
||||
if display.is_glsl_version_supported(&glium::Version(glium::Api::Gl, 1, 4)) {
|
||||
(
|
||||
include_str!("assets/vertex_140.glsl"),
|
||||
include_str!("assets/fragment_140.glsl"),
|
||||
)
|
||||
} else {
|
||||
panic!(
|
||||
"GLSL 140 not supported. Try {:?} or {:?}",
|
||||
display.get_opengl_version(),
|
||||
display.get_supported_glsl_version()
|
||||
);
|
||||
};
|
||||
|
||||
// To quickly iterate on shaders without recompiling...
|
||||
/*let mut vert = String::new();
|
||||
let mut frag = String::new();
|
||||
let (vertex_shader, fragment_shader) = {
|
||||
use std::io::Read;
|
||||
|
||||
let mut f1 = std::fs::File:: open("../ezgui/src/assets/vertex_140.glsl").unwrap();
|
||||
f1.read_to_string(&mut vert).unwrap();
|
||||
|
||||
let mut f2 = std::fs::File:: open("../ezgui/src/assets/fragment_140.glsl").unwrap();
|
||||
f2.read_to_string(&mut frag).unwrap();
|
||||
|
||||
(&vert, &frag)
|
||||
};*/
|
||||
|
||||
let program = glium::Program::new(
|
||||
&display,
|
||||
glium::program::ProgramCreationInput::SourceCode {
|
||||
vertex_shader,
|
||||
tessellation_control_shader: None,
|
||||
tessellation_evaluation_shader: None,
|
||||
geometry_shader: None,
|
||||
fragment_shader,
|
||||
transform_feedback_varyings: None,
|
||||
// Without this, SRGB gets enabled and post-processes the color from the fragment
|
||||
// shader.
|
||||
outputs_srgb: true,
|
||||
uses_point_size: false,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(
|
||||
PrerenderInnards {
|
||||
display,
|
||||
program,
|
||||
total_bytes_uploaded: Cell::new(0),
|
||||
texture_arrays: RefCell::new(Vec::new()),
|
||||
texture_lookups: RefCell::new(HashMap::new()),
|
||||
},
|
||||
event_loop,
|
||||
)
|
||||
}
|
||||
|
||||
struct InnerUniforms<'a> {
|
||||
values: &'a Uniforms,
|
||||
arrays: &'a Vec<Texture2dArray>,
|
||||
}
|
||||
|
||||
impl<'b> glium::uniforms::Uniforms for InnerUniforms<'b> {
|
||||
fn visit_values<'a, F: FnMut(&str, UniformValue<'a>)>(&'a self, mut output: F) {
|
||||
output("transform", UniformValue::Vec3(self.values.transform));
|
||||
output("window", UniformValue::Vec3(self.values.window));
|
||||
|
||||
// This is fine to use for all of the texture styles; all but non-tiling textures clamp to
|
||||
// [0, 1] anyway.
|
||||
let tile = SamplerBehavior {
|
||||
wrap_function: (
|
||||
SamplerWrapFunction::Repeat,
|
||||
SamplerWrapFunction::Repeat,
|
||||
SamplerWrapFunction::Repeat,
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
for (idx, tex) in self.arrays.iter().enumerate() {
|
||||
output(
|
||||
&format!("tex{}", idx),
|
||||
UniformValue::Texture2dArray(tex, Some(tile)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Represents one frame that's gonna be drawn
|
||||
pub struct GfxCtxInnards<'a> {
|
||||
target: glium::Frame,
|
||||
params: glium::DrawParameters<'a>,
|
||||
}
|
||||
|
||||
impl<'a> GfxCtxInnards<'a> {
|
||||
pub fn clear(&mut self, color: Color) {
|
||||
match color {
|
||||
Color::RGBA(r, g, b, a) => {
|
||||
// Without this, SRGB gets enabled and post-processes the color from the fragment
|
||||
// shader.
|
||||
self.target.clear_color_srgb_and_depth((r, g, b, a), 1.0);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self, obj: &Drawable, uniforms: &Uniforms, prerender: &PrerenderInnards) {
|
||||
self.target
|
||||
.draw(
|
||||
&obj.vertex_buffer,
|
||||
&obj.index_buffer,
|
||||
&prerender.program,
|
||||
&InnerUniforms {
|
||||
values: uniforms,
|
||||
arrays: &prerender.texture_arrays.borrow(),
|
||||
},
|
||||
&self.params,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn enable_clipping(&mut self, rect: ScreenRectangle, canvas: &Canvas) {
|
||||
assert!(self.params.scissor.is_none());
|
||||
// The scissor rectangle has to be in device coordinates, so you would think some transform
|
||||
// by scale factor (previously called HiDPI factor) has to happen here. But actually,
|
||||
// window dimensions and the rectangle passed in are already scaled up. So don't do
|
||||
// anything here!
|
||||
self.params.scissor = Some(glium::Rect {
|
||||
left: rect.x1 as u32,
|
||||
// Y-inversion
|
||||
bottom: (canvas.window_height - rect.y2) as u32,
|
||||
width: (rect.x2 - rect.x1) as u32,
|
||||
height: (rect.y2 - rect.y1) as u32,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn disable_clipping(&mut self) {
|
||||
assert!(self.params.scissor.is_some());
|
||||
self.params.scissor = None;
|
||||
}
|
||||
|
||||
pub fn take_clip(&mut self) -> Option<glium::Rect> {
|
||||
self.params.scissor.take()
|
||||
}
|
||||
pub fn restore_clip(&mut self, clip: Option<glium::Rect>) {
|
||||
self.params.scissor = clip;
|
||||
}
|
||||
|
||||
pub fn finish(self) {
|
||||
self.target.finish().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Something that's been sent to the GPU already.
|
||||
pub struct Drawable {
|
||||
vertex_buffer: glium::VertexBuffer<Vertex>,
|
||||
index_buffer: glium::IndexBuffer<u32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Vertex {
|
||||
position: [f32; 2],
|
||||
// Each type of Color encodes something different here. See the actually_upload method and
|
||||
// fragment_140.glsl.
|
||||
// TODO Make this u8?
|
||||
style: [f32; 4],
|
||||
}
|
||||
|
||||
glium::implement_vertex!(Vertex, position, style);
|
||||
|
||||
pub struct PrerenderInnards {
|
||||
display: glium::Display,
|
||||
program: glium::Program,
|
||||
|
||||
// TODO Prerender doesn't know what things are temporary and permanent. Could make the API more
|
||||
// detailed (and use the corresponding persistent glium types).
|
||||
pub total_bytes_uploaded: Cell<usize>,
|
||||
|
||||
// Kind of a weird place for this, but ah well.
|
||||
texture_arrays: RefCell<Vec<Texture2dArray>>,
|
||||
pub(crate) texture_lookups: RefCell<HashMap<String, Color>>,
|
||||
}
|
||||
|
||||
impl PrerenderInnards {
|
||||
pub fn actually_upload(&self, permanent: bool, list: Vec<(Color, &Polygon)>) -> Drawable {
|
||||
let mut vertices: Vec<Vertex> = Vec::new();
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
|
||||
for (color, poly) in list {
|
||||
let idx_offset = vertices.len();
|
||||
let (pts, raw_indices) = poly.raw_for_rendering();
|
||||
for pt in pts {
|
||||
// For the three texture cases, pass [U coordinate, V coordinate, texture group ID,
|
||||
// 100 + texture offset ID] as the style. The last field is between 0 an 1 RGBA's
|
||||
// alpha values, so bump by 100 to distinguish from that.
|
||||
let style = match color {
|
||||
Color::RGBA(r, g, b, a) => [r, g, b, a],
|
||||
Color::TileTexture(id, tex_dims) => {
|
||||
// The texture uses SamplerWrapFunction::Repeat, so don't clamp to [0, 1].
|
||||
// Also don't offset based on the polygon's bounds -- even if there are
|
||||
// separate but adjacent polygons, we want seamless tiling.
|
||||
let tx = pt.x() / tex_dims.width;
|
||||
let ty = pt.y() / tex_dims.height;
|
||||
[tx as f32, ty as f32, id.0, 100.0 + id.1]
|
||||
}
|
||||
Color::StretchTexture(id, _, angle) => {
|
||||
// TODO Cache
|
||||
let b = poly.get_bounds();
|
||||
let center = poly.center();
|
||||
let origin_pt = Pt2D::new(pt.x() - center.x(), pt.y() - center.y());
|
||||
let (sin, cos) = angle.invert_y().normalized_radians().sin_cos();
|
||||
let rot_pt = Pt2D::new(
|
||||
center.x() + origin_pt.x() * cos - origin_pt.y() * sin,
|
||||
center.y() + origin_pt.y() * cos + origin_pt.x() * sin,
|
||||
);
|
||||
|
||||
let tx = (rot_pt.x() - b.min_x) / b.width();
|
||||
let ty = (rot_pt.y() - b.min_y) / b.height();
|
||||
[tx as f32, ty as f32, id.0, 100.0 + id.1]
|
||||
}
|
||||
// Two final special cases
|
||||
Color::HatchingStyle1 => [100.0, 0.0, 0.0, 0.0],
|
||||
Color::HatchingStyle2 => [101.0, 0.0, 0.0, 0.0],
|
||||
};
|
||||
vertices.push(Vertex {
|
||||
position: [pt.x() as f32, pt.y() as f32],
|
||||
style,
|
||||
});
|
||||
}
|
||||
for idx in raw_indices {
|
||||
indices.push((idx_offset + *idx) as u32);
|
||||
}
|
||||
}
|
||||
|
||||
let vertex_buffer = if permanent {
|
||||
glium::VertexBuffer::immutable(&self.display, &vertices).unwrap()
|
||||
} else {
|
||||
glium::VertexBuffer::new(&self.display, &vertices).unwrap()
|
||||
};
|
||||
let index_buffer = if permanent {
|
||||
glium::IndexBuffer::immutable(
|
||||
&self.display,
|
||||
glium::index::PrimitiveType::TrianglesList,
|
||||
&indices,
|
||||
)
|
||||
.unwrap()
|
||||
} else {
|
||||
glium::IndexBuffer::new(
|
||||
&self.display,
|
||||
glium::index::PrimitiveType::TrianglesList,
|
||||
&indices,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
if permanent {
|
||||
self.total_bytes_uploaded.set(
|
||||
self.total_bytes_uploaded.get()
|
||||
+ vertex_buffer.get_size()
|
||||
+ index_buffer.get_size(),
|
||||
);
|
||||
}
|
||||
|
||||
Drawable {
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.display.gl_window().window().request_redraw();
|
||||
}
|
||||
|
||||
pub(crate) fn draw_new_frame<'a>(&self) -> GfxCtxInnards<'a> {
|
||||
GfxCtxInnards {
|
||||
target: self.display.draw(),
|
||||
params: glium::DrawParameters {
|
||||
blend: glium::Blend::alpha_blending(),
|
||||
depth: glium::Depth {
|
||||
test: glium::DepthTest::IfLessOrEqual,
|
||||
write: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn upload_textures(
|
||||
&self,
|
||||
dims_to_textures: BTreeMap<(u32, u32), Vec<(String, Vec<u8>, TextureType)>>,
|
||||
) {
|
||||
for (group_idx, (raw_dims, list)) in dims_to_textures.into_iter().enumerate() {
|
||||
let mut raw_data = Vec::new();
|
||||
for (tex_idx, (filename, raw, tex_type)) in list.into_iter().enumerate() {
|
||||
let tex_id = (group_idx as f32, tex_idx as f32);
|
||||
let dims = ScreenDims::new(f64::from(raw_dims.0), f64::from(raw_dims.1));
|
||||
self.texture_lookups.borrow_mut().insert(
|
||||
filename,
|
||||
match tex_type {
|
||||
TextureType::Stretch => Color::StretchTexture(tex_id, dims, Angle::ZERO),
|
||||
TextureType::Tile => Color::TileTexture(tex_id, dims),
|
||||
},
|
||||
);
|
||||
raw_data.push(RawImage2d::from_raw_rgba(raw, raw_dims));
|
||||
}
|
||||
self.texture_arrays
|
||||
.borrow_mut()
|
||||
.push(Texture2dArray::new(&self.display, raw_data).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
use crate::{Color, ScreenDims, ScreenPt, ScreenRectangle, Text, UserInput};
|
||||
use crate::{ScreenDims, ScreenPt, ScreenRectangle, Text, UserInput};
|
||||
use abstutil::Timer;
|
||||
use geom::{Bounds, Polygon, Pt2D};
|
||||
use glium::texture::Texture2dArray;
|
||||
use geom::{Bounds, Pt2D};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Canvas {
|
||||
// All of these f64's are in screen-space, so do NOT use Pt2D.
|
||||
@ -34,10 +32,6 @@ pub struct Canvas {
|
||||
pub(crate) lctrl_held: bool,
|
||||
// This should be mutually exclusive among all buttons and things in map-space.
|
||||
pub(crate) button_tooltip: Option<Text>,
|
||||
|
||||
// TODO Definitely a weird place to stash this!
|
||||
pub(crate) texture_arrays: Vec<Texture2dArray>,
|
||||
pub(crate) texture_lookups: HashMap<String, Color>,
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
@ -63,9 +57,6 @@ impl Canvas {
|
||||
|
||||
lctrl_held: false,
|
||||
button_tooltip: None,
|
||||
|
||||
texture_arrays: Vec::new(),
|
||||
texture_lookups: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,19 +184,6 @@ impl Canvas {
|
||||
b
|
||||
}
|
||||
|
||||
pub fn texture(&self, filename: &str) -> Color {
|
||||
if let Some(c) = self.texture_lookups.get(filename) {
|
||||
return *c;
|
||||
}
|
||||
panic!("Don't know texture {}", filename);
|
||||
}
|
||||
|
||||
pub fn texture_rect(&self, filename: &str) -> (Color, Polygon) {
|
||||
let color = self.texture(filename);
|
||||
let dims = color.texture_dims();
|
||||
(color, Polygon::rectangle(dims.width, dims.height))
|
||||
}
|
||||
|
||||
pub fn save_camera_state(&self, map_name: &str) {
|
||||
let state = CameraState {
|
||||
cam_x: self.cam_x,
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::assets::Assets;
|
||||
use crate::backend_glium::{GfxCtxInnards, PrerenderInnards};
|
||||
use crate::svg;
|
||||
use crate::{
|
||||
Canvas, Color, EventCtx, HorizontalAlignment, ScreenDims, ScreenPt, ScreenRectangle, Text,
|
||||
VerticalAlignment,
|
||||
Canvas, Color, Drawable, EventCtx, HorizontalAlignment, ScreenDims, ScreenPt, ScreenRectangle,
|
||||
Text, VerticalAlignment,
|
||||
};
|
||||
use geom::{Angle, Bounds, Circle, Distance, Line, Polygon, Pt2D};
|
||||
use glium::uniforms::{SamplerBehavior, SamplerWrapFunction, UniformValue};
|
||||
use glium::Surface;
|
||||
use std::cell::Cell;
|
||||
|
||||
// Lower is more on top
|
||||
@ -14,16 +13,15 @@ const MAPSPACE_Z: f32 = 1.0;
|
||||
const SCREENSPACE_Z: f32 = 0.5;
|
||||
const TOOLTIP_Z: f32 = 0.0;
|
||||
|
||||
struct Uniforms<'a> {
|
||||
pub struct Uniforms {
|
||||
// (cam_x, cam_y, cam_zoom)
|
||||
transform: [f32; 3],
|
||||
pub transform: [f32; 3],
|
||||
// (window_width, window_height, 0.0 for mapspace or 1.0 for screenspace)
|
||||
window: [f32; 3],
|
||||
canvas: &'a Canvas,
|
||||
pub window: [f32; 3],
|
||||
}
|
||||
|
||||
impl<'a> Uniforms<'a> {
|
||||
fn new(canvas: &'a Canvas) -> Uniforms<'a> {
|
||||
impl Uniforms {
|
||||
pub fn new(canvas: &Canvas) -> Uniforms {
|
||||
Uniforms {
|
||||
transform: [
|
||||
canvas.cam_x as f32,
|
||||
@ -35,40 +33,13 @@ impl<'a> Uniforms<'a> {
|
||||
canvas.window_height as f32,
|
||||
MAPSPACE_Z,
|
||||
],
|
||||
canvas,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> glium::uniforms::Uniforms for Uniforms<'b> {
|
||||
fn visit_values<'a, F: FnMut(&str, UniformValue<'a>)>(&'a self, mut output: F) {
|
||||
output("transform", UniformValue::Vec3(self.transform));
|
||||
output("window", UniformValue::Vec3(self.window));
|
||||
|
||||
// This is fine to use for all of the texture styles; all but non-tiling textures clamp to
|
||||
// [0, 1] anyway.
|
||||
let tile = SamplerBehavior {
|
||||
wrap_function: (
|
||||
SamplerWrapFunction::Repeat,
|
||||
SamplerWrapFunction::Repeat,
|
||||
SamplerWrapFunction::Repeat,
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
for (idx, tex) in self.canvas.texture_arrays.iter().enumerate() {
|
||||
output(
|
||||
&format!("tex{}", idx),
|
||||
UniformValue::Texture2dArray(tex, Some(tile)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GfxCtx<'a> {
|
||||
pub(crate) target: &'a mut glium::Frame,
|
||||
program: &'a glium::Program,
|
||||
uniforms: Uniforms<'a>,
|
||||
pub(crate) params: glium::DrawParameters<'a>,
|
||||
pub(crate) inner: GfxCtxInnards<'a>,
|
||||
uniforms: Uniforms,
|
||||
|
||||
screencap_mode: bool,
|
||||
pub(crate) naming_hint: Option<String>,
|
||||
@ -83,31 +54,16 @@ pub struct GfxCtx<'a> {
|
||||
|
||||
impl<'a> GfxCtx<'a> {
|
||||
pub(crate) fn new(
|
||||
canvas: &'a Canvas,
|
||||
prerender: &'a Prerender,
|
||||
target: &'a mut glium::Frame,
|
||||
program: &'a glium::Program,
|
||||
canvas: &'a Canvas,
|
||||
screencap_mode: bool,
|
||||
) -> GfxCtx<'a> {
|
||||
let params = glium::DrawParameters {
|
||||
blend: glium::Blend::alpha_blending(),
|
||||
depth: glium::Depth {
|
||||
test: glium::DepthTest::IfLessOrEqual,
|
||||
write: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let uniforms = Uniforms::new(&canvas);
|
||||
|
||||
let uniforms = Uniforms::new(canvas);
|
||||
GfxCtx {
|
||||
inner: prerender.inner.draw_new_frame(),
|
||||
uniforms,
|
||||
canvas,
|
||||
prerender,
|
||||
target,
|
||||
program,
|
||||
uniforms,
|
||||
params,
|
||||
num_draw_calls: 0,
|
||||
num_forks: 0,
|
||||
screencap_mode,
|
||||
@ -148,14 +104,7 @@ impl<'a> GfxCtx<'a> {
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, color: Color) {
|
||||
match color {
|
||||
Color::RGBA(r, g, b, a) => {
|
||||
// Without this, SRGB gets enabled and post-processes the color from the fragment
|
||||
// shader.
|
||||
self.target.clear_color_srgb_and_depth((r, g, b, a), 1.0);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
self.inner.clear(color);
|
||||
}
|
||||
|
||||
pub fn draw_line(&mut self, color: Color, thickness: Distance, line: &Line) {
|
||||
@ -194,15 +143,8 @@ impl<'a> GfxCtx<'a> {
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self, obj: &Drawable) {
|
||||
self.target
|
||||
.draw(
|
||||
&obj.vertex_buffer,
|
||||
&obj.index_buffer,
|
||||
&self.program,
|
||||
&self.uniforms,
|
||||
&self.params,
|
||||
)
|
||||
.unwrap();
|
||||
self.inner
|
||||
.redraw(obj, &self.uniforms, &self.prerender.inner);
|
||||
self.num_draw_calls += 1;
|
||||
|
||||
// println!("{:?}", backtrace::Backtrace::new());
|
||||
@ -216,23 +158,11 @@ impl<'a> GfxCtx<'a> {
|
||||
|
||||
// TODO Stateful API :(
|
||||
pub fn enable_clipping(&mut self, rect: ScreenRectangle) {
|
||||
assert!(self.params.scissor.is_none());
|
||||
// The scissor rectangle has to be in device coordinates, so you would think some transform
|
||||
// by scale factor (previously called HiDPI factor) has to happen here. But actually,
|
||||
// window dimensions and the rectangle passed in are already scaled up. So don't do
|
||||
// anything here!
|
||||
self.params.scissor = Some(glium::Rect {
|
||||
left: rect.x1 as u32,
|
||||
// Y-inversion
|
||||
bottom: (self.canvas.window_height - rect.y2) as u32,
|
||||
width: (rect.x2 - rect.x1) as u32,
|
||||
height: (rect.y2 - rect.y1) as u32,
|
||||
});
|
||||
self.inner.enable_clipping(rect, self.canvas);
|
||||
}
|
||||
|
||||
pub fn disable_clipping(&mut self) {
|
||||
assert!(self.params.scissor.is_some());
|
||||
self.params.scissor = None;
|
||||
self.inner.disable_clipping();
|
||||
}
|
||||
|
||||
// Canvas stuff.
|
||||
@ -293,10 +223,10 @@ impl<'a> GfxCtx<'a> {
|
||||
];
|
||||
self.num_forks += 1;
|
||||
// Temporarily disable clipping if needed.
|
||||
let clip = self.params.scissor.take();
|
||||
let clip = self.inner.take_clip();
|
||||
batch.draw(self);
|
||||
self.unfork();
|
||||
self.params.scissor = clip;
|
||||
self.inner.restore_clip(clip);
|
||||
}
|
||||
|
||||
pub fn get_screen_bounds(&self) -> Bounds {
|
||||
@ -485,31 +415,12 @@ pub enum RewriteColor {
|
||||
ChangeAll(Color),
|
||||
}
|
||||
|
||||
// Something that's been sent to the GPU already.
|
||||
pub struct Drawable {
|
||||
vertex_buffer: glium::VertexBuffer<Vertex>,
|
||||
index_buffer: glium::IndexBuffer<u32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct Vertex {
|
||||
position: [f32; 2],
|
||||
// Each type of Color encodes something different here. See the actually_upload method and
|
||||
// fragment_140.glsl.
|
||||
// TODO Make this u8?
|
||||
style: [f32; 4],
|
||||
}
|
||||
|
||||
glium::implement_vertex!(Vertex, position, style);
|
||||
|
||||
// TODO Don't expose this directly
|
||||
// TODO Rename or something maybe. This actually owns all the permanent state of everything.
|
||||
pub struct Prerender {
|
||||
pub(crate) inner: PrerenderInnards,
|
||||
pub(crate) assets: Assets,
|
||||
pub(crate) display: glium::Display,
|
||||
pub(crate) num_uploads: Cell<usize>,
|
||||
// TODO Prerender doesn't know what things are temporary and permanent. Could make the API more
|
||||
// detailed (and use the corresponding persistent glium types).
|
||||
pub(crate) total_bytes_uploaded: Cell<usize>,
|
||||
}
|
||||
|
||||
impl Prerender {
|
||||
@ -523,7 +434,7 @@ impl Prerender {
|
||||
}
|
||||
|
||||
pub fn get_total_bytes_uploaded(&self) -> usize {
|
||||
self.total_bytes_uploaded.get()
|
||||
self.inner.total_bytes_uploaded.get()
|
||||
}
|
||||
|
||||
pub(crate) fn upload_temporary(&self, list: Vec<(Color, &Polygon)>) -> Drawable {
|
||||
@ -532,90 +443,24 @@ impl Prerender {
|
||||
|
||||
fn actually_upload(&self, permanent: bool, list: Vec<(Color, &Polygon)>) -> Drawable {
|
||||
// println!("{:?}", backtrace::Backtrace::new());
|
||||
|
||||
self.num_uploads.set(self.num_uploads.get() + 1);
|
||||
self.inner.actually_upload(permanent, list)
|
||||
}
|
||||
|
||||
let mut vertices: Vec<Vertex> = Vec::new();
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
pub fn request_redraw(&self) {
|
||||
self.inner.request_redraw()
|
||||
}
|
||||
|
||||
for (color, poly) in list {
|
||||
let idx_offset = vertices.len();
|
||||
let (pts, raw_indices) = poly.raw_for_rendering();
|
||||
for pt in pts {
|
||||
// For the three texture cases, pass [U coordinate, V coordinate, texture group ID,
|
||||
// 100 + texture offset ID] as the style. The last field is between 0 an 1 RGBA's
|
||||
// alpha values, so bump by 100 to distinguish from that.
|
||||
let style = match color {
|
||||
Color::RGBA(r, g, b, a) => [r, g, b, a],
|
||||
Color::TileTexture(id, tex_dims) => {
|
||||
// The texture uses SamplerWrapFunction::Repeat, so don't clamp to [0, 1].
|
||||
// Also don't offset based on the polygon's bounds -- even if there are
|
||||
// separate but adjacent polygons, we want seamless tiling.
|
||||
let tx = pt.x() / tex_dims.width;
|
||||
let ty = pt.y() / tex_dims.height;
|
||||
[tx as f32, ty as f32, id.0, 100.0 + id.1]
|
||||
}
|
||||
Color::StretchTexture(id, _, angle) => {
|
||||
// TODO Cache
|
||||
let b = poly.get_bounds();
|
||||
let center = poly.center();
|
||||
let origin_pt = Pt2D::new(pt.x() - center.x(), pt.y() - center.y());
|
||||
let (sin, cos) = angle.invert_y().normalized_radians().sin_cos();
|
||||
let rot_pt = Pt2D::new(
|
||||
center.x() + origin_pt.x() * cos - origin_pt.y() * sin,
|
||||
center.y() + origin_pt.y() * cos + origin_pt.x() * sin,
|
||||
);
|
||||
|
||||
let tx = (rot_pt.x() - b.min_x) / b.width();
|
||||
let ty = (rot_pt.y() - b.min_y) / b.height();
|
||||
[tx as f32, ty as f32, id.0, 100.0 + id.1]
|
||||
}
|
||||
// Two final special cases
|
||||
Color::HatchingStyle1 => [100.0, 0.0, 0.0, 0.0],
|
||||
Color::HatchingStyle2 => [101.0, 0.0, 0.0, 0.0],
|
||||
};
|
||||
vertices.push(Vertex {
|
||||
position: [pt.x() as f32, pt.y() as f32],
|
||||
style,
|
||||
});
|
||||
}
|
||||
for idx in raw_indices {
|
||||
indices.push((idx_offset + *idx) as u32);
|
||||
}
|
||||
pub(crate) fn texture(&self, filename: &str) -> Color {
|
||||
if let Some(c) = self.inner.texture_lookups.borrow().get(filename) {
|
||||
return *c;
|
||||
}
|
||||
panic!("Don't know texture {}", filename);
|
||||
}
|
||||
|
||||
let vertex_buffer = if permanent {
|
||||
glium::VertexBuffer::immutable(&self.display, &vertices).unwrap()
|
||||
} else {
|
||||
glium::VertexBuffer::new(&self.display, &vertices).unwrap()
|
||||
};
|
||||
let index_buffer = if permanent {
|
||||
glium::IndexBuffer::immutable(
|
||||
&self.display,
|
||||
glium::index::PrimitiveType::TrianglesList,
|
||||
&indices,
|
||||
)
|
||||
.unwrap()
|
||||
} else {
|
||||
glium::IndexBuffer::new(
|
||||
&self.display,
|
||||
glium::index::PrimitiveType::TrianglesList,
|
||||
&indices,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
if permanent {
|
||||
self.total_bytes_uploaded.set(
|
||||
self.total_bytes_uploaded.get()
|
||||
+ vertex_buffer.get_size()
|
||||
+ index_buffer.get_size(),
|
||||
);
|
||||
}
|
||||
|
||||
Drawable {
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
}
|
||||
pub(crate) fn texture_rect(&self, filename: &str) -> (Color, Polygon) {
|
||||
let color = self.texture(filename);
|
||||
let dims = color.texture_dims();
|
||||
(color, Polygon::rectangle(dims.width, dims.height))
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
use crate::{
|
||||
svg, text, Canvas, Color, Drawable, Event, GeomBatch, GfxCtx, Line, Prerender, ScreenDims,
|
||||
ScreenPt, Text, UserInput,
|
||||
svg, text, Canvas, Color, Drawable, Event, GeomBatch, GfxCtx, Line, Prerender, ScreenPt, Text,
|
||||
UserInput,
|
||||
};
|
||||
use abstutil::{elapsed_seconds, Timer, TimerSink};
|
||||
use geom::{Angle, Polygon};
|
||||
use glium::texture::{RawImage2d, Texture2dArray};
|
||||
use geom::Polygon;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::time::Instant;
|
||||
@ -15,8 +14,6 @@ pub struct EventCtx<'a> {
|
||||
// TODO These two probably shouldn't be public
|
||||
pub canvas: &'a mut Canvas,
|
||||
pub prerender: &'a Prerender,
|
||||
|
||||
pub(crate) program: &'a glium::Program,
|
||||
}
|
||||
|
||||
impl<'a> EventCtx<'a> {
|
||||
@ -30,7 +27,6 @@ impl<'a> EventCtx<'a> {
|
||||
&timer_name,
|
||||
Box::new(LoadingScreen::new(
|
||||
self.prerender,
|
||||
self.program,
|
||||
self.canvas.window_width,
|
||||
self.canvas.window_height,
|
||||
timer_name.clone(),
|
||||
@ -54,7 +50,6 @@ impl<'a> EventCtx<'a> {
|
||||
input: UserInput::new(Event::NoOp, self.canvas),
|
||||
canvas: self.canvas,
|
||||
prerender: self.prerender,
|
||||
program: self.program,
|
||||
};
|
||||
cb(&mut tmp)
|
||||
}
|
||||
@ -86,8 +81,8 @@ impl<'a> EventCtx<'a> {
|
||||
}
|
||||
|
||||
pub fn set_textures(&mut self, textures: Vec<(&str, TextureType)>, timer: &mut Timer) {
|
||||
self.canvas.texture_arrays.clear();
|
||||
self.canvas.texture_lookups.clear();
|
||||
// TODO Only allow setting once. Trying to get rid of this entirely.
|
||||
assert!(self.prerender.inner.texture_lookups.borrow().is_empty());
|
||||
|
||||
// Group textures with the same dimensions and create a texture array. Videocards have a
|
||||
// limit on the number of textures that can be uploaded.
|
||||
@ -131,24 +126,7 @@ impl<'a> EventCtx<'a> {
|
||||
using the same image dimensions."
|
||||
);
|
||||
}
|
||||
for (group_idx, (raw_dims, list)) in dims_to_textures.into_iter().enumerate() {
|
||||
let mut raw_data = Vec::new();
|
||||
for (tex_idx, (filename, raw, tex_type)) in list.into_iter().enumerate() {
|
||||
let tex_id = (group_idx as f32, tex_idx as f32);
|
||||
let dims = ScreenDims::new(f64::from(raw_dims.0), f64::from(raw_dims.1));
|
||||
self.canvas.texture_lookups.insert(
|
||||
filename,
|
||||
match tex_type {
|
||||
TextureType::Stretch => Color::StretchTexture(tex_id, dims, Angle::ZERO),
|
||||
TextureType::Tile => Color::TileTexture(tex_id, dims),
|
||||
},
|
||||
);
|
||||
raw_data.push(RawImage2d::from_raw_rgba(raw, raw_dims));
|
||||
}
|
||||
self.canvas
|
||||
.texture_arrays
|
||||
.push(Texture2dArray::new(&self.prerender.display, raw_data).unwrap());
|
||||
}
|
||||
self.prerender.inner.upload_textures(dims_to_textures)
|
||||
}
|
||||
|
||||
// Delegation to assets
|
||||
@ -165,7 +143,6 @@ impl<'a> EventCtx<'a> {
|
||||
pub struct LoadingScreen<'a> {
|
||||
canvas: Canvas,
|
||||
prerender: &'a Prerender,
|
||||
program: &'a glium::Program,
|
||||
lines: VecDeque<String>,
|
||||
max_capacity: usize,
|
||||
last_drawn: Instant,
|
||||
@ -175,18 +152,16 @@ pub struct LoadingScreen<'a> {
|
||||
impl<'a> LoadingScreen<'a> {
|
||||
pub fn new(
|
||||
prerender: &'a Prerender,
|
||||
program: &'a glium::Program,
|
||||
initial_width: f64,
|
||||
initial_height: f64,
|
||||
title: String,
|
||||
) -> LoadingScreen<'a> {
|
||||
let canvas = Canvas::new(initial_width, initial_height);
|
||||
|
||||
let max_capacity = (0.8 * initial_height / prerender.assets.default_line_height) as usize;
|
||||
LoadingScreen {
|
||||
prerender,
|
||||
program,
|
||||
lines: VecDeque::new(),
|
||||
max_capacity: (0.8 * initial_height / prerender.assets.default_line_height) as usize,
|
||||
max_capacity,
|
||||
// If the loading callback takes less than 0.5s, we don't redraw at all.
|
||||
last_drawn: Instant::now(),
|
||||
title,
|
||||
@ -195,7 +170,7 @@ impl<'a> LoadingScreen<'a> {
|
||||
}
|
||||
|
||||
fn redraw(&mut self) {
|
||||
// TODO Ideally we wouldn't have to dothis, but text rendering is still slow. :)
|
||||
// TODO Ideally we wouldn't have to do this, but text rendering is still slow. :)
|
||||
if elapsed_seconds(self.last_drawn) < 0.5 {
|
||||
return;
|
||||
}
|
||||
@ -207,14 +182,7 @@ impl<'a> LoadingScreen<'a> {
|
||||
txt.add(Line(l));
|
||||
}
|
||||
|
||||
let mut target = self.prerender.display.draw();
|
||||
let mut g = GfxCtx::new(
|
||||
&self.canvas,
|
||||
self.prerender,
|
||||
&mut target,
|
||||
self.program,
|
||||
false,
|
||||
);
|
||||
let mut g = GfxCtx::new(self.prerender, &self.canvas, false);
|
||||
g.clear(Color::BLACK);
|
||||
|
||||
let mut batch = GeomBatch::from(vec![(
|
||||
@ -232,7 +200,7 @@ impl<'a> LoadingScreen<'a> {
|
||||
&draw,
|
||||
);
|
||||
|
||||
target.finish().unwrap();
|
||||
g.inner.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod assets;
|
||||
mod backend_glium;
|
||||
mod canvas;
|
||||
mod color;
|
||||
mod drawing;
|
||||
@ -13,9 +14,10 @@ mod svg;
|
||||
mod text;
|
||||
mod widgets;
|
||||
|
||||
pub use crate::backend_glium::Drawable;
|
||||
pub use crate::canvas::{Canvas, HorizontalAlignment, VerticalAlignment};
|
||||
pub use crate::color::Color;
|
||||
pub use crate::drawing::{Drawable, GeomBatch, GfxCtx, Prerender, RewriteColor};
|
||||
pub use crate::drawing::{GeomBatch, GfxCtx, Prerender, RewriteColor};
|
||||
pub use crate::event::{hotkey, lctrl, Event, Key, MultiKey};
|
||||
pub use crate::event_ctx::{EventCtx, TextureType};
|
||||
pub use crate::input::UserInput;
|
||||
|
@ -36,12 +36,7 @@ pub(crate) struct State<G: GUI> {
|
||||
|
||||
impl<G: GUI> State<G> {
|
||||
// The bool indicates if the input was actually used.
|
||||
fn event(
|
||||
&mut self,
|
||||
ev: Event,
|
||||
prerender: &Prerender,
|
||||
program: &glium::Program,
|
||||
) -> (EventLoopMode, bool) {
|
||||
fn event(&mut self, ev: Event, prerender: &Prerender) -> (EventLoopMode, bool) {
|
||||
// It's impossible / very unlikey we'll grab the cursor in map space before the very first
|
||||
// start_drawing call.
|
||||
let input = UserInput::new(ev, &self.canvas);
|
||||
@ -81,7 +76,6 @@ impl<G: GUI> State<G> {
|
||||
input: input,
|
||||
canvas: &mut self.canvas,
|
||||
prerender,
|
||||
program,
|
||||
};
|
||||
let evloop = self.gui.event(&mut ctx);
|
||||
// TODO We should always do has_been_consumed, but various hacks prevent this from being
|
||||
@ -102,15 +96,8 @@ impl<G: GUI> State<G> {
|
||||
}
|
||||
|
||||
// Returns naming hint. Logically consumes the number of uploads.
|
||||
pub(crate) fn draw(
|
||||
&mut self,
|
||||
display: &glium::Display,
|
||||
program: &glium::Program,
|
||||
prerender: &Prerender,
|
||||
screenshot: bool,
|
||||
) -> Option<String> {
|
||||
let mut target = display.draw();
|
||||
let mut g = GfxCtx::new(&self.canvas, &prerender, &mut target, program, screenshot);
|
||||
pub(crate) fn draw(&mut self, prerender: &Prerender, screenshot: bool) -> Option<String> {
|
||||
let mut g = GfxCtx::new(prerender, &self.canvas, screenshot);
|
||||
|
||||
self.canvas.start_drawing();
|
||||
|
||||
@ -131,7 +118,7 @@ impl<G: GUI> State<G> {
|
||||
);
|
||||
}
|
||||
|
||||
target.finish().unwrap();
|
||||
g.inner.finish();
|
||||
naming_hint
|
||||
}
|
||||
}
|
||||
@ -171,71 +158,14 @@ impl Settings {
|
||||
}
|
||||
|
||||
pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings, make_gui: F) -> ! {
|
||||
let event_loop = winit::event_loop::EventLoop::new();
|
||||
let window = winit::window::WindowBuilder::new()
|
||||
.with_title(settings.window_title)
|
||||
.with_maximized(true);
|
||||
// multisampling: 2 looks bad, 4 looks fine
|
||||
let context = glutin::ContextBuilder::new()
|
||||
.with_multisampling(4)
|
||||
.with_depth_buffer(2);
|
||||
// TODO This step got slow
|
||||
println!("Initializing OpenGL window");
|
||||
let display = glium::Display::new(window, context, &event_loop).unwrap();
|
||||
|
||||
let (vertex_shader, fragment_shader) =
|
||||
if display.is_glsl_version_supported(&glium::Version(glium::Api::Gl, 1, 4)) {
|
||||
(
|
||||
include_str!("assets/vertex_140.glsl"),
|
||||
include_str!("assets/fragment_140.glsl"),
|
||||
)
|
||||
} else {
|
||||
panic!(
|
||||
"GLSL 140 not supported. Try {:?} or {:?}",
|
||||
display.get_opengl_version(),
|
||||
display.get_supported_glsl_version()
|
||||
);
|
||||
};
|
||||
|
||||
// To quickly iterate on shaders without recompiling...
|
||||
/*let mut vert = String::new();
|
||||
let mut frag = String::new();
|
||||
let (vertex_shader, fragment_shader) = {
|
||||
use std::io::Read;
|
||||
|
||||
let mut f1 = std::fs::File:: open("../ezgui/src/assets/vertex_140.glsl").unwrap();
|
||||
f1.read_to_string(&mut vert).unwrap();
|
||||
|
||||
let mut f2 = std::fs::File:: open("../ezgui/src/assets/fragment_140.glsl").unwrap();
|
||||
f2.read_to_string(&mut frag).unwrap();
|
||||
|
||||
(&vert, &frag)
|
||||
};*/
|
||||
|
||||
let program = glium::Program::new(
|
||||
&display,
|
||||
glium::program::ProgramCreationInput::SourceCode {
|
||||
vertex_shader,
|
||||
tessellation_control_shader: None,
|
||||
tessellation_evaluation_shader: None,
|
||||
geometry_shader: None,
|
||||
fragment_shader,
|
||||
transform_feedback_varyings: None,
|
||||
// Without this, SRGB gets enabled and post-processes the color from the fragment
|
||||
// shader.
|
||||
outputs_srgb: true,
|
||||
uses_point_size: false,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let (prerender_innards, event_loop) = crate::backend_glium::setup(&settings.window_title);
|
||||
|
||||
let window_size = event_loop.primary_monitor().size();
|
||||
let mut canvas = Canvas::new(window_size.width.into(), window_size.height.into());
|
||||
let prerender = Prerender {
|
||||
assets: Assets::new(settings.default_font_size, settings.font_dir),
|
||||
display,
|
||||
num_uploads: Cell::new(0),
|
||||
total_bytes_uploaded: Cell::new(0),
|
||||
inner: prerender_innards,
|
||||
};
|
||||
|
||||
let gui = make_gui(&mut EventCtx {
|
||||
@ -243,7 +173,6 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
||||
input: UserInput::new(Event::NoOp, &canvas),
|
||||
canvas: &mut canvas,
|
||||
prerender: &prerender,
|
||||
program: &program,
|
||||
});
|
||||
|
||||
let mut state = State { canvas, gui };
|
||||
@ -294,7 +223,7 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
||||
}
|
||||
}
|
||||
winit::event::Event::RedrawRequested(_) => {
|
||||
state.draw(&prerender.display, &program, &prerender, false);
|
||||
state.draw(&prerender, false);
|
||||
prerender.num_uploads.set(0);
|
||||
return;
|
||||
}
|
||||
@ -319,9 +248,9 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
||||
winit::event_loop::ControlFlow::WaitUntil(Instant::now() + UPDATE_FREQUENCY);
|
||||
}
|
||||
|
||||
let (mode, input_used) = state.event(ev, &prerender, &program);
|
||||
let (mode, input_used) = state.event(ev, &prerender);
|
||||
if input_used {
|
||||
prerender.display.gl_window().window().request_redraw();
|
||||
prerender.request_redraw();
|
||||
}
|
||||
|
||||
match mode {
|
||||
@ -346,19 +275,10 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
||||
max_x,
|
||||
max_y,
|
||||
} => {
|
||||
widgets::screenshot_everything(
|
||||
&mut state,
|
||||
&dir,
|
||||
&prerender.display,
|
||||
&program,
|
||||
&prerender,
|
||||
zoom,
|
||||
max_x,
|
||||
max_y,
|
||||
);
|
||||
widgets::screenshot_everything(&mut state, &dir, &prerender, zoom, max_x, max_y);
|
||||
}
|
||||
EventLoopMode::ScreenCaptureCurrentShot => {
|
||||
widgets::screenshot_current(&mut state, &prerender.display, &program, &prerender);
|
||||
widgets::screenshot_current(&mut state, &prerender);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -135,7 +135,7 @@ impl Button {
|
||||
const HORIZ_PADDING: f64 = 30.0;
|
||||
const VERT_PADDING: f64 = 10.0;
|
||||
|
||||
let img_color = ctx.canvas.texture(filename);
|
||||
let img_color = ctx.prerender.texture(filename);
|
||||
let dims = img_color.texture_dims();
|
||||
let img_rect =
|
||||
Polygon::rectangle(dims.width, dims.height).translate(HORIZ_PADDING, VERT_PADDING);
|
||||
|
@ -22,7 +22,7 @@ impl JustDraw {
|
||||
}
|
||||
|
||||
pub fn image(ctx: &EventCtx, filename: &str) -> ManagedWidget {
|
||||
let (color, rect) = ctx.canvas.texture_rect(filename);
|
||||
let (color, rect) = ctx.prerender.texture_rect(filename);
|
||||
let batch = GeomBatch::from(vec![(color, rect)]);
|
||||
JustDraw::wrap(ctx, batch)
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ use std::{fs, process, thread, time};
|
||||
pub(crate) fn screenshot_everything<G: GUI>(
|
||||
state: &mut State<G>,
|
||||
dir_path: &str,
|
||||
display: &glium::Display,
|
||||
program: &glium::Program,
|
||||
prerender: &Prerender,
|
||||
zoom: f64,
|
||||
max_x: f64,
|
||||
@ -32,9 +30,7 @@ pub(crate) fn screenshot_everything<G: GUI>(
|
||||
state.canvas.cam_x = (tile_x as f64) * state.canvas.window_width;
|
||||
state.canvas.cam_y = (tile_y as f64) * state.canvas.window_height;
|
||||
|
||||
let suffix = state
|
||||
.draw(display, program, prerender, true)
|
||||
.unwrap_or_else(String::new);
|
||||
let suffix = state.draw(prerender, true).unwrap_or_else(String::new);
|
||||
let filename = format!("{:02}x{:02}{}.png", tile_x + 1, tile_y + 1, suffix);
|
||||
|
||||
// TODO Is vsync or something else causing the above redraw to not actually show up in
|
||||
@ -56,13 +52,8 @@ pub(crate) fn screenshot_everything<G: GUI>(
|
||||
finish(dir_path, filenames, num_tiles_x, num_tiles_y);
|
||||
}
|
||||
|
||||
pub(crate) fn screenshot_current<G: GUI>(
|
||||
state: &mut State<G>,
|
||||
display: &glium::Display,
|
||||
program: &glium::Program,
|
||||
prerender: &Prerender,
|
||||
) {
|
||||
state.draw(display, program, prerender, true);
|
||||
pub(crate) fn screenshot_current<G: GUI>(state: &mut State<G>, prerender: &Prerender) {
|
||||
state.draw(prerender, true);
|
||||
thread::sleep(time::Duration::from_millis(100));
|
||||
screencap("../screenshot.png");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user