mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
reclaim wasm vertex/buffer memory, consolidate glow backends (#290)
* Complete wasm backend memory mgmt, extract common glow code
Similar to 3e8d2d7d
, but for the wasm backend.
Also takes a pass at consolidating some of the common code between the
glow-native and glow-wasm backends. At this point, the backends just
diverge in their window handling, and the data types of their OpenGL
data types (e.g. vertex_buffer.id is WebBufferId vs. uint32 on native)
* glow impls share prerender innards
* coalesce vertex/buffer types
* minimize diff
* clarify module naming
This commit is contained in:
parent
3a10416e26
commit
5db0a7ad9d
@ -5,7 +5,7 @@ authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = ["glium-backend"]
|
||||
default = ["glow-backend"]
|
||||
glium-backend = ["glium", "glutin", "usvg/text"]
|
||||
glow-backend = ["glow", "glutin", "usvg/text"]
|
||||
wasm-backend = ["glow/stdweb", "instant/stdweb", "stdweb", "webgl_stdweb", "winit/stdweb"]
|
||||
|
@ -32,10 +32,10 @@ works in the browser using WebAssembly and
|
||||
|
||||
Why OpenGL? My requirements are super simple; I don't need the power of Vulkan
|
||||
or other new stuff. I want something simple that runs everywhere. If you want to
|
||||
make this work with WGPU or something else, it should be easy. The 3 backend
|
||||
implementations ([glium](src/backend_glium.rs),
|
||||
[glow on native](src/backend_glow.rs), [glow on wasm](src/backend_wasm.rs)) are
|
||||
each about 300 lines.
|
||||
make this work with WGPU or something else, it should be easy. The backends are
|
||||
each about 300 lines — [Glium](src/backend_glium.rs) running on native OpenGL,
|
||||
and [Glow](src/backend_glow.rs) running either [on
|
||||
native](src/backend_glow_native.rs) or [on wasm](src/backend_glow_wasm.rs).
|
||||
|
||||
### 2D drawing
|
||||
|
||||
|
@ -298,4 +298,8 @@ impl PrerenderInnards {
|
||||
pub fn monitor_scale_factor(&self) -> f64 {
|
||||
self.display.gl_window().window().scale_factor()
|
||||
}
|
||||
|
||||
pub fn draw_finished(&self, gfx_ctx_innards: GfxCtxInnards) {
|
||||
gfx_ctx_innards.finish()
|
||||
}
|
||||
}
|
||||
|
@ -4,90 +4,31 @@ use glow::HasContext;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
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);
|
||||
// TODO Need the same fallback as backend_glium
|
||||
// multisampling: 2 looks bad, 4 looks fine
|
||||
let context = glutin::ContextBuilder::new()
|
||||
.with_multisampling(4)
|
||||
.with_depth_buffer(2)
|
||||
.build_windowed(window, &event_loop)
|
||||
.unwrap();
|
||||
let windowed_context = unsafe { context.make_current().unwrap() };
|
||||
let gl =
|
||||
glow::Context::from_loader_function(|s| windowed_context.get_proc_address(s) as *const _);
|
||||
let program = unsafe { gl.create_program().expect("Cannot create program") };
|
||||
#[cfg(feature = "glow-backend")]
|
||||
pub use crate::backend_glow_native::setup;
|
||||
|
||||
unsafe {
|
||||
let shaders = [
|
||||
(glow::VERTEX_SHADER, include_str!("shaders/vertex_140.glsl")),
|
||||
(
|
||||
glow::FRAGMENT_SHADER,
|
||||
include_str!("shaders/fragment_140.glsl"),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.map(|(shader_type, source)| {
|
||||
let shader = gl
|
||||
.create_shader(*shader_type)
|
||||
.expect("Cannot create shader");
|
||||
gl.shader_source(shader, source);
|
||||
gl.compile_shader(shader);
|
||||
if !gl.get_shader_compile_status(shader) {
|
||||
panic!(gl.get_shader_info_log(shader));
|
||||
}
|
||||
gl.attach_shader(program, shader);
|
||||
shader
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gl.link_program(program);
|
||||
if !gl.get_program_link_status(program) {
|
||||
panic!(gl.get_program_info_log(program));
|
||||
}
|
||||
for shader in shaders {
|
||||
gl.detach_shader(program, shader);
|
||||
gl.delete_shader(shader);
|
||||
}
|
||||
gl.use_program(Some(program));
|
||||
|
||||
gl.enable(glow::SCISSOR_TEST);
|
||||
|
||||
gl.enable(glow::DEPTH_TEST);
|
||||
gl.depth_func(glow::LEQUAL);
|
||||
|
||||
gl.enable(glow::BLEND);
|
||||
gl.blend_func_separate(
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
);
|
||||
}
|
||||
|
||||
(
|
||||
PrerenderInnards {
|
||||
gl: Rc::new(gl),
|
||||
program,
|
||||
windowed_context,
|
||||
total_bytes_uploaded: Cell::new(0),
|
||||
},
|
||||
event_loop,
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "wasm-backend")]
|
||||
pub use crate::backend_glow_wasm::setup;
|
||||
|
||||
// Represents one frame that's gonna be drawn
|
||||
pub struct GfxCtxInnards<'a> {
|
||||
gl: Rc<glow::Context>,
|
||||
windowed_context: &'a glutin::WindowedContext<glutin::PossiblyCurrent>,
|
||||
gl: &'a glow::Context,
|
||||
program: &'a <glow::Context as glow::HasContext>::Program,
|
||||
|
||||
current_clip: Option<[i32; 4]>,
|
||||
}
|
||||
|
||||
impl<'a> GfxCtxInnards<'a> {
|
||||
pub fn new(
|
||||
gl: &'a glow::Context,
|
||||
program: &'a <glow::Context as glow::HasContext>::Program,
|
||||
) -> Self {
|
||||
GfxCtxInnards {
|
||||
gl,
|
||||
program,
|
||||
current_clip: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, color: Color) {
|
||||
unsafe {
|
||||
self.gl.clear_color(color.r, color.g, color.b, color.a);
|
||||
@ -150,6 +91,7 @@ impl<'a> GfxCtxInnards<'a> {
|
||||
pub fn take_clip(&mut self) -> Option<[i32; 4]> {
|
||||
self.current_clip.take()
|
||||
}
|
||||
|
||||
pub fn restore_clip(&mut self, clip: Option<[i32; 4]>) {
|
||||
self.current_clip = clip;
|
||||
if let Some(c) = clip {
|
||||
@ -158,10 +100,6 @@ impl<'a> GfxCtxInnards<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) {
|
||||
self.windowed_context.swap_buffers().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Something that's been sent to the GPU already.
|
||||
@ -183,7 +121,7 @@ impl Drop for Drawable {
|
||||
}
|
||||
|
||||
struct VertexArray {
|
||||
id: u32,
|
||||
id: <glow::Context as glow::HasContext>::VertexArray,
|
||||
was_destroyed: bool,
|
||||
}
|
||||
|
||||
@ -215,7 +153,7 @@ impl Drop for VertexArray {
|
||||
}
|
||||
|
||||
struct Buffer {
|
||||
id: u32,
|
||||
id: <glow::Context as glow::HasContext>::Buffer,
|
||||
was_destroyed: bool,
|
||||
}
|
||||
|
||||
@ -244,9 +182,15 @@ impl Drop for Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm-backend")]
|
||||
type WindowAdapter = crate::backend_glow_wasm::WindowAdapter;
|
||||
|
||||
#[cfg(feature = "glow-backend")]
|
||||
type WindowAdapter = crate::backend_glow_native::WindowAdapter;
|
||||
|
||||
pub struct PrerenderInnards {
|
||||
gl: Rc<glow::Context>,
|
||||
windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>,
|
||||
window_adapter: WindowAdapter,
|
||||
program: <glow::Context as glow::HasContext>::Program,
|
||||
|
||||
// TODO Prerender doesn't know what things are temporary and permanent. Could make the API more
|
||||
@ -255,6 +199,19 @@ pub struct PrerenderInnards {
|
||||
}
|
||||
|
||||
impl PrerenderInnards {
|
||||
pub fn new(
|
||||
gl: glow::Context,
|
||||
program: <glow::Context as glow::HasContext>::Program,
|
||||
window_adapter: WindowAdapter,
|
||||
) -> PrerenderInnards {
|
||||
PrerenderInnards {
|
||||
gl: Rc::new(gl),
|
||||
program,
|
||||
window_adapter,
|
||||
total_bytes_uploaded: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn actually_upload(&self, permanent: bool, batch: GeomBatch) -> Drawable {
|
||||
let mut vertices: Vec<[f32; 6]> = Vec::new();
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
@ -346,56 +303,47 @@ impl PrerenderInnards {
|
||||
}
|
||||
}
|
||||
|
||||
fn window(&self) -> &winit::window::Window {
|
||||
self.window_adapter.window()
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.windowed_context.window().request_redraw();
|
||||
self.window().request_redraw();
|
||||
}
|
||||
|
||||
pub fn set_cursor_icon(&self, icon: winit::window::CursorIcon) {
|
||||
self.windowed_context.window().set_cursor_icon(icon);
|
||||
self.window().set_cursor_icon(icon);
|
||||
}
|
||||
|
||||
pub fn draw_new_frame(&self) -> GfxCtxInnards {
|
||||
GfxCtxInnards {
|
||||
gl: self.gl.clone(),
|
||||
windowed_context: &self.windowed_context,
|
||||
program: &self.program,
|
||||
current_clip: None,
|
||||
}
|
||||
GfxCtxInnards::new(&self.gl, &self.program)
|
||||
}
|
||||
|
||||
pub fn window_resized(&self, new_size: ScreenDims, scale_factor: f64) {
|
||||
let physical_size = winit::dpi::LogicalSize::from(new_size).to_physical(scale_factor);
|
||||
self.windowed_context.resize(physical_size);
|
||||
self.window_adapter.window_resized(new_size, scale_factor);
|
||||
unsafe {
|
||||
self.gl.viewport(
|
||||
0,
|
||||
0,
|
||||
physical_size.width as i32,
|
||||
physical_size.height as i32,
|
||||
);
|
||||
self.gl
|
||||
.viewport(0, 0, physical_size.width, physical_size.height);
|
||||
// I think it's safe to assume there's not a clip right now.
|
||||
self.gl.scissor(
|
||||
0,
|
||||
0,
|
||||
physical_size.width as i32,
|
||||
physical_size.height as i32,
|
||||
);
|
||||
self.gl
|
||||
.scissor(0, 0, physical_size.width, physical_size.height);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_size(&self, scale_factor: f64) -> ScreenDims {
|
||||
self.windowed_context
|
||||
.window()
|
||||
.inner_size()
|
||||
.to_logical(scale_factor)
|
||||
.into()
|
||||
self.window().inner_size().to_logical(scale_factor).into()
|
||||
}
|
||||
|
||||
pub fn set_window_icon(&self, icon: winit::window::Icon) {
|
||||
self.windowed_context.window().set_window_icon(Some(icon));
|
||||
self.window().set_window_icon(Some(icon));
|
||||
}
|
||||
|
||||
pub fn monitor_scale_factor(&self) -> f64 {
|
||||
self.windowed_context.window().scale_factor()
|
||||
self.window().scale_factor()
|
||||
}
|
||||
|
||||
pub fn draw_finished(&self, gfc_ctx_innards: GfxCtxInnards) {
|
||||
self.window_adapter.draw_finished(gfc_ctx_innards)
|
||||
}
|
||||
}
|
||||
|
90
ezgui/src/backend_glow_native.rs
Normal file
90
ezgui/src/backend_glow_native.rs
Normal file
@ -0,0 +1,90 @@
|
||||
pub use crate::backend_glow::Drawable;
|
||||
use crate::backend_glow::{GfxCtxInnards, PrerenderInnards};
|
||||
use crate::ScreenDims;
|
||||
use glow::HasContext;
|
||||
|
||||
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);
|
||||
// TODO Need the same fallback as backend_glium
|
||||
// multisampling: 2 looks bad, 4 looks fine
|
||||
let context = glutin::ContextBuilder::new()
|
||||
.with_multisampling(4)
|
||||
.with_depth_buffer(2)
|
||||
.build_windowed(window, &event_loop)
|
||||
.unwrap();
|
||||
let windowed_context = unsafe { context.make_current().unwrap() };
|
||||
let gl =
|
||||
glow::Context::from_loader_function(|s| windowed_context.get_proc_address(s) as *const _);
|
||||
let program = unsafe { gl.create_program().expect("Cannot create program") };
|
||||
|
||||
unsafe {
|
||||
let shaders = [
|
||||
(glow::VERTEX_SHADER, include_str!("shaders/vertex_140.glsl")),
|
||||
(
|
||||
glow::FRAGMENT_SHADER,
|
||||
include_str!("shaders/fragment_140.glsl"),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.map(|(shader_type, source)| {
|
||||
let shader = gl
|
||||
.create_shader(*shader_type)
|
||||
.expect("Cannot create shader");
|
||||
gl.shader_source(shader, source);
|
||||
gl.compile_shader(shader);
|
||||
if !gl.get_shader_compile_status(shader) {
|
||||
panic!(gl.get_shader_info_log(shader));
|
||||
}
|
||||
gl.attach_shader(program, shader);
|
||||
shader
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gl.link_program(program);
|
||||
if !gl.get_program_link_status(program) {
|
||||
panic!(gl.get_program_info_log(program));
|
||||
}
|
||||
for shader in shaders {
|
||||
gl.detach_shader(program, shader);
|
||||
gl.delete_shader(shader);
|
||||
}
|
||||
gl.use_program(Some(program));
|
||||
|
||||
gl.enable(glow::SCISSOR_TEST);
|
||||
|
||||
gl.enable(glow::DEPTH_TEST);
|
||||
gl.depth_func(glow::LEQUAL);
|
||||
|
||||
gl.enable(glow::BLEND);
|
||||
gl.blend_func_separate(
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
);
|
||||
}
|
||||
|
||||
(
|
||||
PrerenderInnards::new(gl, program, WindowAdapter(windowed_context)),
|
||||
event_loop,
|
||||
)
|
||||
}
|
||||
|
||||
pub struct WindowAdapter(glutin::WindowedContext<glutin::PossiblyCurrent>);
|
||||
|
||||
impl WindowAdapter {
|
||||
pub fn window(&self) -> &winit::window::Window {
|
||||
&self.0.window()
|
||||
}
|
||||
|
||||
pub fn window_resized(&self, new_size: ScreenDims, scale_factor: f64) {
|
||||
let physical_size = winit::dpi::LogicalSize::from(new_size).to_physical(scale_factor);
|
||||
self.0.resize(physical_size);
|
||||
}
|
||||
|
||||
pub fn draw_finished(&self, _gfc_ctx_innards: GfxCtxInnards) {
|
||||
self.0.swap_buffers().unwrap();
|
||||
}
|
||||
}
|
107
ezgui/src/backend_glow_wasm.rs
Normal file
107
ezgui/src/backend_glow_wasm.rs
Normal file
@ -0,0 +1,107 @@
|
||||
pub use crate::backend_glow::Drawable;
|
||||
use crate::backend_glow::{GfxCtxInnards, PrerenderInnards};
|
||||
use crate::ScreenDims;
|
||||
use glow::HasContext;
|
||||
use stdweb::traits::INode;
|
||||
use webgl_stdweb::WebGL2RenderingContext;
|
||||
use winit::platform::web::WindowExtStdweb;
|
||||
|
||||
pub fn setup(window_title: &str) -> (PrerenderInnards, winit::event_loop::EventLoop<()>) {
|
||||
stdweb::console!(log, "Setting up ezgui");
|
||||
|
||||
// This doesn't seem to work for the shader panics here, but later it does work. Huh.
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
stdweb::console!(log, "panicked: %s", format!("{}", info));
|
||||
}));
|
||||
|
||||
let event_loop = winit::event_loop::EventLoop::new();
|
||||
let size = {
|
||||
// TODO Not sure how to get scrollbar dims
|
||||
let scrollbars = 30;
|
||||
let win = stdweb::web::window();
|
||||
// `inner_width` corresponds to the browser's `self.innerWidth` function, which are in
|
||||
// Logical, not Physical, pixels
|
||||
winit::dpi::LogicalSize::new(
|
||||
win.inner_width() - scrollbars,
|
||||
win.inner_height() - scrollbars,
|
||||
)
|
||||
};
|
||||
let window = winit::window::WindowBuilder::new()
|
||||
.with_title(window_title)
|
||||
.with_inner_size(size)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
let canvas = window.canvas();
|
||||
let document = stdweb::web::document();
|
||||
let body: stdweb::web::Node = document.body().expect("Get HTML body").into();
|
||||
body.append_child(&canvas);
|
||||
|
||||
let webgl2_context: WebGL2RenderingContext = canvas.get_context().unwrap();
|
||||
let gl = glow::Context::from_webgl2_context(webgl2_context);
|
||||
|
||||
let program = unsafe { gl.create_program().expect("Cannot create program") };
|
||||
|
||||
unsafe {
|
||||
let shaders = [
|
||||
(glow::VERTEX_SHADER, include_str!("shaders/vertex_300.glsl")),
|
||||
(
|
||||
glow::FRAGMENT_SHADER,
|
||||
include_str!("shaders/fragment_300.glsl"),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.map(|(shader_type, source)| {
|
||||
let shader = gl
|
||||
.create_shader(*shader_type)
|
||||
.expect("Cannot create shader");
|
||||
gl.shader_source(shader, source);
|
||||
gl.compile_shader(shader);
|
||||
if !gl.get_shader_compile_status(shader) {
|
||||
stdweb::console!(log, "Shader error: %s", gl.get_shader_info_log(shader));
|
||||
panic!(gl.get_shader_info_log(shader));
|
||||
}
|
||||
gl.attach_shader(program, shader);
|
||||
shader
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gl.link_program(program);
|
||||
if !gl.get_program_link_status(program) {
|
||||
stdweb::console!(log, "Linking error: %s", gl.get_program_info_log(program));
|
||||
panic!(gl.get_program_info_log(program));
|
||||
}
|
||||
for shader in shaders {
|
||||
gl.detach_shader(program, shader);
|
||||
gl.delete_shader(shader);
|
||||
}
|
||||
gl.use_program(Some(program));
|
||||
|
||||
gl.enable(glow::SCISSOR_TEST);
|
||||
|
||||
gl.enable(glow::DEPTH_TEST);
|
||||
gl.depth_func(glow::LEQUAL);
|
||||
|
||||
gl.enable(glow::BLEND);
|
||||
gl.blend_func_separate(
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
);
|
||||
}
|
||||
|
||||
(
|
||||
PrerenderInnards::new(gl, program, WindowAdapter(window)),
|
||||
event_loop,
|
||||
)
|
||||
}
|
||||
|
||||
pub struct WindowAdapter(winit::window::Window);
|
||||
|
||||
impl WindowAdapter {
|
||||
pub fn window(&self) -> &winit::window::Window {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn window_resized(&self, new_size: ScreenDims, scale_factor: f64) {}
|
||||
pub fn draw_finished(&self, _gfc_ctx_innards: GfxCtxInnards) {}
|
||||
}
|
@ -1,342 +0,0 @@
|
||||
use crate::drawing::Uniforms;
|
||||
use crate::{Canvas, Color, GeomBatch, ScreenDims, ScreenRectangle};
|
||||
use glow::HasContext;
|
||||
use std::cell::Cell;
|
||||
use stdweb::traits::INode;
|
||||
use webgl_stdweb::WebGL2RenderingContext;
|
||||
use winit::platform::web::WindowExtStdweb;
|
||||
|
||||
pub fn setup(window_title: &str) -> (PrerenderInnards, winit::event_loop::EventLoop<()>) {
|
||||
stdweb::console!(log, "Setting up ezgui");
|
||||
|
||||
// This doesn't seem to work for the shader panics here, but later it does work. Huh.
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
stdweb::console!(log, "panicked: %s", format!("{}", info));
|
||||
}));
|
||||
|
||||
let event_loop = winit::event_loop::EventLoop::new();
|
||||
let size = {
|
||||
// TODO Not sure how to get scrollbar dims
|
||||
let scrollbars = 30;
|
||||
let win = stdweb::web::window();
|
||||
// `inner_width` corresponds to the browser's `self.innerWidth` function, which are in
|
||||
// Logical, not Physical, pixels
|
||||
winit::dpi::LogicalSize::new(
|
||||
win.inner_width() - scrollbars,
|
||||
win.inner_height() - scrollbars,
|
||||
)
|
||||
};
|
||||
let window = winit::window::WindowBuilder::new()
|
||||
.with_title(window_title)
|
||||
.with_inner_size(size)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
let canvas = window.canvas();
|
||||
let document = stdweb::web::document();
|
||||
let body: stdweb::web::Node = document.body().expect("Get HTML body").into();
|
||||
body.append_child(&canvas);
|
||||
|
||||
let webgl2_context: WebGL2RenderingContext = canvas.get_context().unwrap();
|
||||
let gl = glow::Context::from_webgl2_context(webgl2_context);
|
||||
|
||||
let program = unsafe { gl.create_program().expect("Cannot create program") };
|
||||
|
||||
unsafe {
|
||||
let shaders = [
|
||||
(glow::VERTEX_SHADER, include_str!("shaders/vertex_300.glsl")),
|
||||
(
|
||||
glow::FRAGMENT_SHADER,
|
||||
include_str!("shaders/fragment_300.glsl"),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.map(|(shader_type, source)| {
|
||||
let shader = gl
|
||||
.create_shader(*shader_type)
|
||||
.expect("Cannot create shader");
|
||||
gl.shader_source(shader, source);
|
||||
gl.compile_shader(shader);
|
||||
if !gl.get_shader_compile_status(shader) {
|
||||
stdweb::console!(log, "Shader error: %s", gl.get_shader_info_log(shader));
|
||||
panic!(gl.get_shader_info_log(shader));
|
||||
}
|
||||
gl.attach_shader(program, shader);
|
||||
shader
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
gl.link_program(program);
|
||||
if !gl.get_program_link_status(program) {
|
||||
stdweb::console!(log, "Linking error: %s", gl.get_program_info_log(program));
|
||||
panic!(gl.get_program_info_log(program));
|
||||
}
|
||||
for shader in shaders {
|
||||
gl.detach_shader(program, shader);
|
||||
gl.delete_shader(shader);
|
||||
}
|
||||
gl.use_program(Some(program));
|
||||
|
||||
gl.enable(glow::SCISSOR_TEST);
|
||||
|
||||
gl.enable(glow::DEPTH_TEST);
|
||||
gl.depth_func(glow::LEQUAL);
|
||||
|
||||
gl.enable(glow::BLEND);
|
||||
gl.blend_func_separate(
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
glow::SRC_ALPHA,
|
||||
glow::ONE_MINUS_SRC_ALPHA,
|
||||
);
|
||||
}
|
||||
|
||||
(
|
||||
PrerenderInnards {
|
||||
gl,
|
||||
program,
|
||||
window,
|
||||
total_bytes_uploaded: Cell::new(0),
|
||||
},
|
||||
event_loop,
|
||||
)
|
||||
}
|
||||
|
||||
// Represents one frame that's gonna be drawn
|
||||
pub struct GfxCtxInnards<'a> {
|
||||
gl: &'a glow::Context,
|
||||
program: &'a <glow::Context as glow::HasContext>::Program,
|
||||
|
||||
current_clip: Option<[i32; 4]>,
|
||||
}
|
||||
|
||||
impl<'a> GfxCtxInnards<'a> {
|
||||
pub fn clear(&mut self, color: Color) {
|
||||
unsafe {
|
||||
self.gl.clear_color(color.r, color.g, color.b, color.a);
|
||||
self.gl.clear(glow::COLOR_BUFFER_BIT);
|
||||
|
||||
self.gl.clear_depth_f32(1.0);
|
||||
self.gl.clear(glow::DEPTH_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self, obj: &Drawable, uniforms: &Uniforms, _: &PrerenderInnards) {
|
||||
unsafe {
|
||||
let transform_loc = self
|
||||
.gl
|
||||
.get_uniform_location(*self.program, "transform")
|
||||
.unwrap();
|
||||
self.gl
|
||||
.uniform_3_f32_slice(Some(&transform_loc), &uniforms.transform);
|
||||
let window_loc = self
|
||||
.gl
|
||||
.get_uniform_location(*self.program, "window")
|
||||
.unwrap();
|
||||
self.gl
|
||||
.uniform_3_f32_slice(Some(&window_loc), &uniforms.window);
|
||||
|
||||
self.gl.bind_vertex_array(Some(obj.vert_array));
|
||||
self.gl
|
||||
.draw_elements(glow::TRIANGLES, obj.num_indices, glow::UNSIGNED_INT, 0);
|
||||
self.gl.bind_vertex_array(None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_clipping(&mut self, rect: ScreenRectangle, scale_factor: f64, canvas: &Canvas) {
|
||||
assert!(self.current_clip.is_none());
|
||||
// The scissor rectangle is in units of physical pixles, as opposed to logical pixels
|
||||
let left = (rect.x1 * scale_factor) as i32;
|
||||
// Y-inversion
|
||||
let bottom = ((canvas.window_height - rect.y2) * scale_factor) as i32;
|
||||
let width = ((rect.x2 - rect.x1) * scale_factor) as i32;
|
||||
let height = ((rect.y2 - rect.y1) * scale_factor) as i32;
|
||||
unsafe {
|
||||
self.gl.scissor(left, bottom, width, height);
|
||||
}
|
||||
self.current_clip = Some([left, bottom, width, height]);
|
||||
}
|
||||
|
||||
pub fn disable_clipping(&mut self, scale_factor: f64, canvas: &Canvas) {
|
||||
assert!(self.current_clip.is_some());
|
||||
self.current_clip = None;
|
||||
unsafe {
|
||||
self.gl.scissor(
|
||||
0,
|
||||
0,
|
||||
(canvas.window_width * scale_factor) as i32,
|
||||
(canvas.window_height * scale_factor) as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_clip(&mut self) -> Option<[i32; 4]> {
|
||||
self.current_clip.take()
|
||||
}
|
||||
pub fn restore_clip(&mut self, clip: Option<[i32; 4]>) {
|
||||
self.current_clip = clip;
|
||||
if let Some(c) = clip {
|
||||
unsafe {
|
||||
self.gl.scissor(c[0], c[1], c[2], c[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) {}
|
||||
}
|
||||
|
||||
// Something that's been sent to the GPU already.
|
||||
// TODO Implement Drop; have to keep a reference to gl.
|
||||
pub struct Drawable {
|
||||
_vert_buffer: glow::WebBufferKey,
|
||||
vert_array: glow::WebVertexArrayKey,
|
||||
_elem_buffer: glow::WebBufferKey,
|
||||
num_indices: i32,
|
||||
}
|
||||
|
||||
pub struct PrerenderInnards {
|
||||
gl: glow::Context,
|
||||
window: winit::window::Window,
|
||||
program: <glow::Context as glow::HasContext>::Program,
|
||||
|
||||
// TODO Prerender doesn't know what things are temporary and permanent. Could make the API more
|
||||
// detailed.
|
||||
pub total_bytes_uploaded: Cell<usize>,
|
||||
}
|
||||
|
||||
impl PrerenderInnards {
|
||||
pub fn actually_upload(&self, permanent: bool, batch: GeomBatch) -> Drawable {
|
||||
let mut vertices: Vec<[f32; 6]> = Vec::new();
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
|
||||
for (color, poly) in batch.consume() {
|
||||
let idx_offset = vertices.len();
|
||||
let (pts, raw_indices) = poly.raw_for_rendering();
|
||||
for pt in pts {
|
||||
let style = color.style(*pt);
|
||||
vertices.push([
|
||||
pt.x() as f32,
|
||||
pt.y() as f32,
|
||||
style[0],
|
||||
style[1],
|
||||
style[2],
|
||||
style[3],
|
||||
]);
|
||||
}
|
||||
for idx in raw_indices {
|
||||
indices.push((idx_offset + *idx) as u32);
|
||||
}
|
||||
}
|
||||
|
||||
let (vert_buffer, vert_array, elem_buffer) = unsafe {
|
||||
let vert_array = self.gl.create_vertex_array().unwrap();
|
||||
let vert_buffer = self.gl.create_buffer().unwrap();
|
||||
let elem_buffer = self.gl.create_buffer().unwrap();
|
||||
|
||||
self.gl.bind_vertex_array(Some(vert_array));
|
||||
|
||||
self.gl.bind_buffer(glow::ARRAY_BUFFER, Some(vert_buffer));
|
||||
self.gl.buffer_data_u8_slice(
|
||||
glow::ARRAY_BUFFER,
|
||||
&vertices.align_to::<u8>().1,
|
||||
// TODO Use permanent
|
||||
glow::STATIC_DRAW,
|
||||
);
|
||||
|
||||
self.gl
|
||||
.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(elem_buffer));
|
||||
self.gl.buffer_data_u8_slice(
|
||||
glow::ELEMENT_ARRAY_BUFFER,
|
||||
&indices.align_to::<u8>().1,
|
||||
glow::STATIC_DRAW,
|
||||
);
|
||||
|
||||
// TODO Can we have a single vertex array for everything, since there's an uber shader?
|
||||
|
||||
let stride = 6 * std::mem::size_of::<f32>() as i32;
|
||||
// position is vec2
|
||||
self.gl.enable_vertex_attrib_array(0);
|
||||
self.gl
|
||||
.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
|
||||
// style is vec4
|
||||
self.gl.enable_vertex_attrib_array(1);
|
||||
self.gl.vertex_attrib_pointer_f32(
|
||||
1,
|
||||
4,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
2 * std::mem::size_of::<f32>() as i32,
|
||||
);
|
||||
|
||||
// Safety?
|
||||
self.gl.bind_vertex_array(None);
|
||||
self.gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||
self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
|
||||
|
||||
(vert_buffer, vert_array, elem_buffer)
|
||||
};
|
||||
let num_indices = indices.len() as i32;
|
||||
|
||||
if permanent {
|
||||
/*self.total_bytes_uploaded.set(
|
||||
self.total_bytes_uploaded.get()
|
||||
+ vertex_buffer.get_size()
|
||||
+ index_buffer.get_size(),
|
||||
);*/
|
||||
}
|
||||
|
||||
Drawable {
|
||||
_vert_buffer: vert_buffer,
|
||||
vert_array,
|
||||
_elem_buffer: elem_buffer,
|
||||
num_indices,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.window.request_redraw();
|
||||
}
|
||||
|
||||
pub fn set_cursor_icon(&self, icon: winit::window::CursorIcon) {
|
||||
self.window.set_cursor_icon(icon);
|
||||
}
|
||||
|
||||
pub fn draw_new_frame(&self) -> GfxCtxInnards {
|
||||
GfxCtxInnards {
|
||||
gl: &self.gl,
|
||||
program: &self.program,
|
||||
current_clip: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_resized(&self, new_size: ScreenDims, scale_factor: f64) {
|
||||
let physical_size =
|
||||
winit::dpi::LogicalSize::from(new_size).to_physical::<f64>(scale_factor);
|
||||
unsafe {
|
||||
self.gl.viewport(
|
||||
0,
|
||||
0,
|
||||
physical_size.width as i32,
|
||||
physical_size.height as i32,
|
||||
);
|
||||
// I think it's safe to assume there's not a clip right now.
|
||||
self.gl.scissor(
|
||||
0,
|
||||
0,
|
||||
physical_size.width as i32,
|
||||
physical_size.height as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_size(&self, scale_factor: f64) -> ScreenDims {
|
||||
self.window.inner_size().to_logical(scale_factor).into()
|
||||
}
|
||||
|
||||
pub fn set_window_icon(&self, icon: winit::window::Icon) {
|
||||
self.window.set_window_icon(Some(icon));
|
||||
}
|
||||
|
||||
pub fn monitor_scale_factor(&self) -> f64 {
|
||||
self.window.scale_factor()
|
||||
}
|
||||
}
|
@ -184,7 +184,7 @@ impl<'a> LoadingScreen<'a> {
|
||||
&draw,
|
||||
);
|
||||
|
||||
g.inner.finish();
|
||||
g.prerender.inner.draw_finished(g.inner);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,12 @@
|
||||
mod assets;
|
||||
#[cfg(feature = "glium-backend")]
|
||||
mod backend_glium;
|
||||
#[cfg(feature = "glow-backend")]
|
||||
#[cfg(any(feature = "glow-backend", feature = "wasm-backend"))]
|
||||
mod backend_glow;
|
||||
#[cfg(feature = "glow-backend")]
|
||||
mod backend_glow_native;
|
||||
#[cfg(feature = "wasm-backend")]
|
||||
mod backend_wasm;
|
||||
mod backend_glow_wasm;
|
||||
mod canvas;
|
||||
mod color;
|
||||
mod drawing;
|
||||
@ -51,11 +53,8 @@ mod backend {
|
||||
#[cfg(feature = "glium-backend")]
|
||||
pub use crate::backend_glium::*;
|
||||
|
||||
#[cfg(feature = "glow-backend")]
|
||||
#[cfg(any(feature = "glow-backend", feature = "wasm-backend"))]
|
||||
pub use crate::backend_glow::*;
|
||||
|
||||
#[cfg(feature = "wasm-backend")]
|
||||
pub use crate::backend_wasm::*;
|
||||
}
|
||||
|
||||
pub use crate::backend::Drawable;
|
||||
|
@ -146,7 +146,7 @@ impl<G: GUI> State<G> {
|
||||
);
|
||||
}
|
||||
|
||||
g.inner.finish();
|
||||
prerender.inner.draw_finished(g.inner);
|
||||
naming_hint
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user