WIP: Factor out GL code
This commit is contained in:
parent
64b480fe32
commit
ae450b063e
|
@ -511,6 +511,7 @@ dependencies = [
|
||||||
"nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pathfinder_geometry 0.3.0",
|
"pathfinder_geometry 0.3.0",
|
||||||
"pathfinder_gl 0.1.0",
|
"pathfinder_gl 0.1.0",
|
||||||
|
"pathfinder_gpu 0.1.0",
|
||||||
"pathfinder_renderer 0.1.0",
|
"pathfinder_renderer 0.1.0",
|
||||||
"pathfinder_svg 0.1.0",
|
"pathfinder_svg 0.1.0",
|
||||||
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -537,12 +538,23 @@ dependencies = [
|
||||||
"gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pathfinder_geometry 0.3.0",
|
"pathfinder_geometry 0.3.0",
|
||||||
|
"pathfinder_gpu 0.1.0",
|
||||||
"pathfinder_renderer 0.1.0",
|
"pathfinder_renderer 0.1.0",
|
||||||
|
"pathfinder_simd 0.3.0",
|
||||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathfinder_gpu"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pathfinder_geometry 0.3.0",
|
||||||
|
"pathfinder_simd 0.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pathfinder_renderer"
|
name = "pathfinder_renderer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -570,6 +582,10 @@ dependencies = [
|
||||||
"usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathfinder_ui"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.7.24"
|
version = "0.7.24"
|
||||||
|
@ -1045,10 +1061,6 @@ name = "ucd-util"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ui"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
|
|
@ -4,6 +4,7 @@ members = [
|
||||||
"demo/native",
|
"demo/native",
|
||||||
"geometry",
|
"geometry",
|
||||||
"gl",
|
"gl",
|
||||||
|
"gpu",
|
||||||
"renderer",
|
"renderer",
|
||||||
"simd",
|
"simd",
|
||||||
"svg",
|
"svg",
|
||||||
|
|
|
@ -24,6 +24,9 @@ path = "../../geometry"
|
||||||
[dependencies.pathfinder_gl]
|
[dependencies.pathfinder_gl]
|
||||||
path = "../../gl"
|
path = "../../gl"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_gpu]
|
||||||
|
path = "../../gpu"
|
||||||
|
|
||||||
[dependencies.pathfinder_renderer]
|
[dependencies.pathfinder_renderer]
|
||||||
path = "../../renderer"
|
path = "../../renderer"
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,9 @@
|
||||||
//! GPU rendering code specifically for the demo.
|
//! GPU rendering code specifically for the demo.
|
||||||
|
|
||||||
use crate::GRIDLINE_COUNT;
|
use crate::GRIDLINE_COUNT;
|
||||||
use gl::types::{GLsizei, GLvoid};
|
use pathfinder_gpu::{BufferTarget, BufferUploadMode, Device, Resources, VertexAttrType};
|
||||||
use pathfinder_gl::device::{Buffer, BufferTarget, BufferUploadMode, Device, Program, Uniform};
|
|
||||||
use pathfinder_gl::device::{VertexArray, VertexAttr};
|
|
||||||
use pathfinder_renderer::paint::ColorU;
|
|
||||||
|
|
||||||
|
/*
|
||||||
pub struct DemoDevice {
|
pub struct DemoDevice {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
device: Device,
|
device: Device,
|
||||||
|
@ -62,65 +60,67 @@ impl DemoDevice {
|
||||||
pixels
|
pixels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub struct GroundProgram {
|
pub struct GroundProgram<D> where D: Device {
|
||||||
pub program: Program,
|
pub program: D::Program,
|
||||||
pub transform_uniform: Uniform,
|
pub transform_uniform: D::Uniform,
|
||||||
pub color_uniform: Uniform,
|
pub color_uniform: D::Uniform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GroundProgram {
|
impl<D> GroundProgram<D> where D: Device {
|
||||||
pub fn new(device: &Device) -> GroundProgram {
|
pub fn new(device: &D, resources: &Resources) -> GroundProgram<D> {
|
||||||
let program = device.create_program("demo_ground");
|
let program = device.create_program(resources, "demo_ground");
|
||||||
let transform_uniform = Uniform::new(&program, "Transform");
|
let transform_uniform = device.get_uniform(&program, "Transform");
|
||||||
let color_uniform = Uniform::new(&program, "Color");
|
let color_uniform = device.get_uniform(&program, "Color");
|
||||||
GroundProgram { program, transform_uniform, color_uniform }
|
GroundProgram { program, transform_uniform, color_uniform }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GroundSolidVertexArray {
|
pub struct GroundSolidVertexArray<D> where D: Device {
|
||||||
pub vertex_array: VertexArray,
|
pub vertex_array: D::VertexArray,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GroundSolidVertexArray {
|
impl<D> GroundSolidVertexArray<D> where D: Device {
|
||||||
pub fn new(ground_program: &GroundProgram, quad_vertex_positions_buffer: &Buffer)
|
pub fn new(device: &D,
|
||||||
-> GroundSolidVertexArray {
|
ground_program: &GroundProgram<D>,
|
||||||
let vertex_array = VertexArray::new();
|
quad_vertex_positions_buffer: &D::Buffer)
|
||||||
unsafe {
|
-> GroundSolidVertexArray<D> {
|
||||||
let position_attr = VertexAttr::new(&ground_program.program, "Position");
|
let vertex_array = device.create_vertex_array();
|
||||||
|
|
||||||
gl::BindVertexArray(vertex_array.gl_vertex_array);
|
let position_attr = device.get_vertex_attr(&ground_program.program, "Position");
|
||||||
gl::UseProgram(ground_program.program.gl_program);
|
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer);
|
device.bind_vertex_array(&vertex_array);
|
||||||
position_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0);
|
device.use_program(&ground_program.program);
|
||||||
}
|
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
|
device.configure_float_vertex_attr(&position_attr, 2, VertexAttrType::U8, false, 0, 0, 0);
|
||||||
|
|
||||||
GroundSolidVertexArray { vertex_array }
|
GroundSolidVertexArray { vertex_array }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GroundLineVertexArray {
|
pub struct GroundLineVertexArray<D> where D: Device {
|
||||||
pub vertex_array: VertexArray,
|
pub vertex_array: D::VertexArray,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
grid_vertex_positions_buffer: Buffer,
|
grid_vertex_positions_buffer: D::Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GroundLineVertexArray {
|
impl<D> GroundLineVertexArray<D> where D: Device {
|
||||||
pub fn new(ground_program: &GroundProgram) -> GroundLineVertexArray {
|
pub fn new(device: &D, ground_program: &GroundProgram<D>) -> GroundLineVertexArray<D> {
|
||||||
let grid_vertex_positions_buffer = Buffer::new();
|
let grid_vertex_positions_buffer = device.create_buffer();
|
||||||
grid_vertex_positions_buffer.upload(&create_grid_vertex_positions(),
|
device.upload_to_buffer(&grid_vertex_positions_buffer,
|
||||||
BufferTarget::Vertex,
|
&create_grid_vertex_positions(),
|
||||||
BufferUploadMode::Static);
|
BufferTarget::Vertex,
|
||||||
|
BufferUploadMode::Static);
|
||||||
|
|
||||||
let vertex_array = VertexArray::new();
|
let vertex_array = device.create_vertex_array();
|
||||||
unsafe {
|
|
||||||
let position_attr = VertexAttr::new(&ground_program.program, "Position");
|
|
||||||
|
|
||||||
gl::BindVertexArray(vertex_array.gl_vertex_array);
|
let position_attr = device.get_vertex_attr(&ground_program.program, "Position");
|
||||||
gl::UseProgram(ground_program.program.gl_program);
|
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, grid_vertex_positions_buffer.gl_buffer);
|
device.bind_vertex_array(&vertex_array);
|
||||||
position_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0);
|
device.use_program(&ground_program.program);
|
||||||
}
|
device.bind_buffer(&grid_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
|
device.configure_float_vertex_attr(&position_attr, 2, VertexAttrType::U8, false, 0, 0, 0);
|
||||||
|
|
||||||
GroundLineVertexArray { vertex_array, grid_vertex_positions_buffer }
|
GroundLineVertexArray { vertex_array, grid_vertex_positions_buffer }
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
//! A demo app for Pathfinder.
|
//! A demo app for Pathfinder.
|
||||||
|
|
||||||
use crate::device::{DemoDevice, GroundLineVertexArray, GroundProgram, GroundSolidVertexArray};
|
use crate::device::{GroundLineVertexArray, GroundProgram, GroundSolidVertexArray};
|
||||||
use crate::ui::{DemoUI, UIAction, UIEvent};
|
use crate::ui::{DemoUI, UIAction, UIEvent};
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use gl::types::GLsizei;
|
use gl::types::GLsizei;
|
||||||
|
@ -20,8 +20,9 @@ use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
||||||
use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32};
|
use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32};
|
||||||
use pathfinder_gl::device::Device;
|
use pathfinder_gl::device::GLDevice;
|
||||||
use pathfinder_gl::renderer::Renderer;
|
use pathfinder_gl::renderer::Renderer;
|
||||||
|
use pathfinder_gpu::{Device, Resources};
|
||||||
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
|
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
|
||||||
use pathfinder_renderer::gpu_data::BuiltScene;
|
use pathfinder_renderer::gpu_data::BuiltScene;
|
||||||
use pathfinder_renderer::paint::ColorU;
|
use pathfinder_renderer::paint::ColorU;
|
||||||
|
@ -96,14 +97,13 @@ pub struct DemoApp {
|
||||||
mouselook_enabled: bool,
|
mouselook_enabled: bool,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
|
|
||||||
ui: DemoUI,
|
ui: DemoUI<GLDevice>,
|
||||||
scene_thread_proxy: SceneThreadProxy,
|
scene_thread_proxy: SceneThreadProxy,
|
||||||
renderer: Renderer,
|
renderer: Renderer<GLDevice>,
|
||||||
|
|
||||||
device: DemoDevice,
|
ground_program: GroundProgram<GLDevice>,
|
||||||
ground_program: GroundProgram,
|
ground_solid_vertex_array: GroundSolidVertexArray<GLDevice>,
|
||||||
ground_solid_vertex_array: GroundSolidVertexArray,
|
ground_line_vertex_array: GroundLineVertexArray<GLDevice>,
|
||||||
ground_line_vertex_array: GroundLineVertexArray,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DemoApp {
|
impl DemoApp {
|
||||||
|
@ -130,8 +130,9 @@ impl DemoApp {
|
||||||
|
|
||||||
let sdl_event_pump = sdl_context.event_pump().unwrap();
|
let sdl_event_pump = sdl_context.event_pump().unwrap();
|
||||||
|
|
||||||
let device = Device::new();
|
let device = GLDevice::new();
|
||||||
let options = Options::get(&device);
|
let resources = Resources::locate();
|
||||||
|
let options = Options::get(&resources);
|
||||||
|
|
||||||
let (window_width, _) = window.size();
|
let (window_width, _) = window.size();
|
||||||
let (drawable_width, drawable_height) = window.drawable_size();
|
let (drawable_width, drawable_height) = window.drawable_size();
|
||||||
|
@ -139,7 +140,7 @@ impl DemoApp {
|
||||||
|
|
||||||
let base_scene = load_scene(&options.input_path);
|
let base_scene = load_scene(&options.input_path);
|
||||||
let scene_view_box = base_scene.view_box;
|
let scene_view_box = base_scene.view_box;
|
||||||
let renderer = Renderer::new(&device, drawable_size);
|
let renderer = Renderer::new(device, &resources, drawable_size);
|
||||||
let scene_thread_proxy = SceneThreadProxy::new(base_scene, options.clone());
|
let scene_thread_proxy = SceneThreadProxy::new(base_scene, options.clone());
|
||||||
update_drawable_size(&window, &scene_thread_proxy);
|
update_drawable_size(&window, &scene_thread_proxy);
|
||||||
|
|
||||||
|
@ -149,10 +150,15 @@ impl DemoApp {
|
||||||
Camera::new_2d(scene_view_box, drawable_size)
|
Camera::new_2d(scene_view_box, drawable_size)
|
||||||
};
|
};
|
||||||
|
|
||||||
let ground_program = GroundProgram::new(&device);
|
let ground_program = GroundProgram::new(&renderer.device, &resources);
|
||||||
let ground_solid_vertex_array =
|
let ground_solid_vertex_array =
|
||||||
GroundSolidVertexArray::new(&ground_program, &renderer.quad_vertex_positions_buffer());
|
GroundSolidVertexArray::new(&renderer.device,
|
||||||
let ground_line_vertex_array = GroundLineVertexArray::new(&ground_program);
|
&ground_program,
|
||||||
|
&renderer.quad_vertex_positions_buffer());
|
||||||
|
let ground_line_vertex_array = GroundLineVertexArray::new(&renderer.device,
|
||||||
|
&ground_program);
|
||||||
|
|
||||||
|
let ui = DemoUI::new(&renderer.device, &resources, options);
|
||||||
|
|
||||||
DemoApp {
|
DemoApp {
|
||||||
window,
|
window,
|
||||||
|
@ -173,11 +179,10 @@ impl DemoApp {
|
||||||
mouselook_enabled: false,
|
mouselook_enabled: false,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
|
|
||||||
ui: DemoUI::new(&device, options),
|
ui,
|
||||||
scene_thread_proxy,
|
scene_thread_proxy,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
||||||
device: DemoDevice::new(device),
|
|
||||||
ground_program,
|
ground_program,
|
||||||
ground_solid_vertex_array,
|
ground_solid_vertex_array,
|
||||||
ground_line_vertex_array,
|
ground_line_vertex_array,
|
||||||
|
@ -342,7 +347,7 @@ impl DemoApp {
|
||||||
tile_time,
|
tile_time,
|
||||||
} = render_msg;
|
} = render_msg;
|
||||||
|
|
||||||
self.device.clear(self.background_color());
|
self.renderer.device.clear(Some(self.background_color().to_f32().0), Some(1.0), Some(0));
|
||||||
self.draw_environment(&render_transform);
|
self.draw_environment(&render_transform);
|
||||||
self.render_vector_scene(&built_scene);
|
self.render_vector_scene(&built_scene);
|
||||||
|
|
||||||
|
@ -353,14 +358,17 @@ impl DemoApp {
|
||||||
let rendering_time = self.renderer.shift_timer_query();
|
let rendering_time = self.renderer.shift_timer_query();
|
||||||
let stats = built_scene.stats();
|
let stats = built_scene.stats();
|
||||||
self.renderer.debug_ui.add_sample(stats, tile_time, rendering_time);
|
self.renderer.debug_ui.add_sample(stats, tile_time, rendering_time);
|
||||||
self.renderer.debug_ui.draw();
|
self.renderer.debug_ui.draw(&self.renderer.device);
|
||||||
|
|
||||||
if !ui_event.is_none() {
|
if !ui_event.is_none() {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ui_action = UIAction::None;
|
let mut ui_action = UIAction::None;
|
||||||
self.ui.update(&mut self.renderer.debug_ui, &mut ui_event, &mut ui_action);
|
self.ui.update(&self.renderer.device,
|
||||||
|
&mut self.renderer.debug_ui,
|
||||||
|
&mut ui_event,
|
||||||
|
&mut ui_action);
|
||||||
self.handle_ui_action(&mut ui_action);
|
self.handle_ui_action(&mut ui_action);
|
||||||
|
|
||||||
// Switch camera mode (2D/3D) if requested.
|
// Switch camera mode (2D/3D) if requested.
|
||||||
|
@ -548,7 +556,8 @@ impl DemoApp {
|
||||||
fn take_screenshot(&mut self) {
|
fn take_screenshot(&mut self) {
|
||||||
let screenshot_path = self.pending_screenshot_path.take().unwrap();
|
let screenshot_path = self.pending_screenshot_path.take().unwrap();
|
||||||
let (drawable_width, drawable_height) = self.window.drawable_size();
|
let (drawable_width, drawable_height) = self.window.drawable_size();
|
||||||
let pixels = self.device.readback_pixels(drawable_width, drawable_height);
|
let drawable_size = Point2DI32::new(drawable_width as i32, drawable_height as i32);
|
||||||
|
let pixels = self.renderer.device.read_pixels_from_default_framebuffer(drawable_size);
|
||||||
image::save_buffer(screenshot_path,
|
image::save_buffer(screenshot_path,
|
||||||
&pixels,
|
&pixels,
|
||||||
drawable_width,
|
drawable_width,
|
||||||
|
@ -644,7 +653,7 @@ pub struct Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
fn get(device: &Device) -> Options {
|
fn get(resources: &Resources) -> Options {
|
||||||
let matches = App::new("tile-svg")
|
let matches = App::new("tile-svg")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("jobs")
|
Arg::with_name("jobs")
|
||||||
|
@ -666,7 +675,7 @@ impl Options {
|
||||||
let input_path = match matches.value_of("INPUT") {
|
let input_path = match matches.value_of("INPUT") {
|
||||||
Some(path) => PathBuf::from(path),
|
Some(path) => PathBuf::from(path),
|
||||||
None => {
|
None => {
|
||||||
let mut path = device.resources_directory.clone();
|
let mut path = resources.resources_directory.clone();
|
||||||
path.push("svg");
|
path.push("svg");
|
||||||
path.push(DEFAULT_SVG_FILENAME);
|
path.push(DEFAULT_SVG_FILENAME);
|
||||||
path
|
path
|
||||||
|
|
|
@ -13,7 +13,7 @@ use nfd::Response;
|
||||||
use pathfinder_geometry::basic::point::Point2DI32;
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
use pathfinder_geometry::basic::rect::RectI32;
|
use pathfinder_geometry::basic::rect::RectI32;
|
||||||
use pathfinder_gl::debug::{DebugUI, PADDING, TEXT_COLOR, WINDOW_COLOR};
|
use pathfinder_gl::debug::{DebugUI, PADDING, TEXT_COLOR, WINDOW_COLOR};
|
||||||
use pathfinder_gl::device::{Device, Texture};
|
use pathfinder_gpu::{Device, Resources};
|
||||||
use pathfinder_renderer::paint::ColorU;
|
use pathfinder_renderer::paint::ColorU;
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -57,15 +57,15 @@ static BG_LIGHT_PNG_NAME: &'static str = "demo-bg-light";
|
||||||
static BG_DARK_PNG_NAME: &'static str = "demo-bg-dark";
|
static BG_DARK_PNG_NAME: &'static str = "demo-bg-dark";
|
||||||
static SCREENSHOT_PNG_NAME: &'static str = "demo-screenshot";
|
static SCREENSHOT_PNG_NAME: &'static str = "demo-screenshot";
|
||||||
|
|
||||||
pub struct DemoUI {
|
pub struct DemoUI<D> where D: Device {
|
||||||
effects_texture: Texture,
|
effects_texture: D::Texture,
|
||||||
open_texture: Texture,
|
open_texture: D::Texture,
|
||||||
rotate_texture: Texture,
|
rotate_texture: D::Texture,
|
||||||
zoom_in_texture: Texture,
|
zoom_in_texture: D::Texture,
|
||||||
zoom_out_texture: Texture,
|
zoom_out_texture: D::Texture,
|
||||||
bg_light_texture: Texture,
|
bg_light_texture: D::Texture,
|
||||||
bg_dark_texture: Texture,
|
bg_dark_texture: D::Texture,
|
||||||
screenshot_texture: Texture,
|
screenshot_texture: D::Texture,
|
||||||
|
|
||||||
effects_panel_visible: bool,
|
effects_panel_visible: bool,
|
||||||
rotate_panel_visible: bool,
|
rotate_panel_visible: bool,
|
||||||
|
@ -78,16 +78,16 @@ pub struct DemoUI {
|
||||||
pub rotation: i32,
|
pub rotation: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DemoUI {
|
impl<D> DemoUI<D> where D: Device {
|
||||||
pub fn new(device: &Device, options: Options) -> DemoUI {
|
pub fn new(device: &D, resources: &Resources, options: Options) -> DemoUI<D> {
|
||||||
let effects_texture = device.create_texture_from_png(EFFECTS_PNG_NAME);
|
let effects_texture = device.create_texture_from_png(resources, EFFECTS_PNG_NAME);
|
||||||
let open_texture = device.create_texture_from_png(OPEN_PNG_NAME);
|
let open_texture = device.create_texture_from_png(resources, OPEN_PNG_NAME);
|
||||||
let rotate_texture = device.create_texture_from_png(ROTATE_PNG_NAME);
|
let rotate_texture = device.create_texture_from_png(resources, ROTATE_PNG_NAME);
|
||||||
let zoom_in_texture = device.create_texture_from_png(ZOOM_IN_PNG_NAME);
|
let zoom_in_texture = device.create_texture_from_png(resources, ZOOM_IN_PNG_NAME);
|
||||||
let zoom_out_texture = device.create_texture_from_png(ZOOM_OUT_PNG_NAME);
|
let zoom_out_texture = device.create_texture_from_png(resources, ZOOM_OUT_PNG_NAME);
|
||||||
let bg_light_texture = device.create_texture_from_png(BG_LIGHT_PNG_NAME);
|
let bg_light_texture = device.create_texture_from_png(resources, BG_LIGHT_PNG_NAME);
|
||||||
let bg_dark_texture = device.create_texture_from_png(BG_DARK_PNG_NAME);
|
let bg_dark_texture = device.create_texture_from_png(resources, BG_DARK_PNG_NAME);
|
||||||
let screenshot_texture = device.create_texture_from_png(SCREENSHOT_PNG_NAME);
|
let screenshot_texture = device.create_texture_from_png(resources, SCREENSHOT_PNG_NAME);
|
||||||
|
|
||||||
DemoUI {
|
DemoUI {
|
||||||
effects_texture,
|
effects_texture,
|
||||||
|
@ -115,19 +115,27 @@ impl DemoUI {
|
||||||
(self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI
|
(self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, debug_ui: &mut DebugUI, event: &mut UIEvent, action: &mut UIAction) {
|
pub fn update(&mut self,
|
||||||
|
device: &D,
|
||||||
|
debug_ui: &mut DebugUI<D>,
|
||||||
|
event: &mut UIEvent,
|
||||||
|
action: &mut UIAction) {
|
||||||
let bottom = debug_ui.framebuffer_size().y() - PADDING;
|
let bottom = debug_ui.framebuffer_size().y() - PADDING;
|
||||||
|
|
||||||
// Draw effects button.
|
// Draw effects button.
|
||||||
let effects_button_position = Point2DI32::new(PADDING, bottom - BUTTON_HEIGHT);
|
let effects_button_position = Point2DI32::new(PADDING, bottom - BUTTON_HEIGHT);
|
||||||
if self.draw_button(debug_ui, event, effects_button_position, &self.effects_texture) {
|
if self.draw_button(device,
|
||||||
|
debug_ui,
|
||||||
|
event,
|
||||||
|
effects_button_position,
|
||||||
|
&self.effects_texture) {
|
||||||
self.effects_panel_visible = !self.effects_panel_visible;
|
self.effects_panel_visible = !self.effects_panel_visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw open button.
|
// Draw open button.
|
||||||
let lower_button_y = bottom - BUTTON_HEIGHT;
|
let lower_button_y = bottom - BUTTON_HEIGHT;
|
||||||
let open_button_position = Point2DI32::new(OPEN_BUTTON_X, lower_button_y);
|
let open_button_position = Point2DI32::new(OPEN_BUTTON_X, lower_button_y);
|
||||||
if self.draw_button(debug_ui, event, open_button_position, &self.open_texture) {
|
if self.draw_button(device, debug_ui, event, open_button_position, &self.open_texture) {
|
||||||
if let Ok(Response::Okay(file)) = nfd::open_file_dialog(Some("svg"), None) {
|
if let Ok(Response::Okay(file)) = nfd::open_file_dialog(Some("svg"), None) {
|
||||||
*action = UIAction::OpenFile(PathBuf::from(file));
|
*action = UIAction::OpenFile(PathBuf::from(file));
|
||||||
}
|
}
|
||||||
|
@ -135,7 +143,8 @@ impl DemoUI {
|
||||||
|
|
||||||
// Draw screenshot button.
|
// Draw screenshot button.
|
||||||
let screenshot_button_position = Point2DI32::new(SCREENSHOT_BUTTON_X, lower_button_y);
|
let screenshot_button_position = Point2DI32::new(SCREENSHOT_BUTTON_X, lower_button_y);
|
||||||
if self.draw_button(debug_ui,
|
if self.draw_button(device,
|
||||||
|
debug_ui,
|
||||||
event,
|
event,
|
||||||
screenshot_button_position,
|
screenshot_button_position,
|
||||||
&self.screenshot_texture) {
|
&self.screenshot_texture) {
|
||||||
|
@ -146,7 +155,8 @@ impl DemoUI {
|
||||||
|
|
||||||
// Draw 3D switch.
|
// Draw 3D switch.
|
||||||
let three_d_switch_origin = Point2DI32::new(THREE_D_SWITCH_X, lower_button_y);
|
let three_d_switch_origin = Point2DI32::new(THREE_D_SWITCH_X, lower_button_y);
|
||||||
self.three_d_enabled = self.draw_text_switch(debug_ui,
|
self.three_d_enabled = self.draw_text_switch(device,
|
||||||
|
debug_ui,
|
||||||
event,
|
event,
|
||||||
three_d_switch_origin,
|
three_d_switch_origin,
|
||||||
"2D",
|
"2D",
|
||||||
|
@ -155,7 +165,8 @@ impl DemoUI {
|
||||||
|
|
||||||
// Draw background switch.
|
// Draw background switch.
|
||||||
let background_switch_origin = Point2DI32::new(BACKGROUND_SWITCH_X, lower_button_y);
|
let background_switch_origin = Point2DI32::new(BACKGROUND_SWITCH_X, lower_button_y);
|
||||||
self.dark_background_enabled = self.draw_image_switch(debug_ui,
|
self.dark_background_enabled = self.draw_image_switch(device,
|
||||||
|
debug_ui,
|
||||||
event,
|
event,
|
||||||
background_switch_origin,
|
background_switch_origin,
|
||||||
&self.bg_light_texture,
|
&self.bg_light_texture,
|
||||||
|
@ -166,19 +177,28 @@ impl DemoUI {
|
||||||
if !self.three_d_enabled {
|
if !self.three_d_enabled {
|
||||||
let rotate_button_y = bottom - BUTTON_HEIGHT;
|
let rotate_button_y = bottom - BUTTON_HEIGHT;
|
||||||
let rotate_button_position = Point2DI32::new(ROTATE_PANEL_X, rotate_button_y);
|
let rotate_button_position = Point2DI32::new(ROTATE_PANEL_X, rotate_button_y);
|
||||||
if self.draw_button(debug_ui, event, rotate_button_position, &self.rotate_texture) {
|
if self.draw_button(device,
|
||||||
|
debug_ui,
|
||||||
|
event,
|
||||||
|
rotate_button_position,
|
||||||
|
&self.rotate_texture) {
|
||||||
self.rotate_panel_visible = !self.rotate_panel_visible;
|
self.rotate_panel_visible = !self.rotate_panel_visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
let zoom_in_button_x = ROTATE_PANEL_X + BUTTON_WIDTH + PADDING;
|
let zoom_in_button_x = ROTATE_PANEL_X + BUTTON_WIDTH + PADDING;
|
||||||
let zoom_in_button_position = Point2DI32::new(zoom_in_button_x, rotate_button_y);
|
let zoom_in_button_position = Point2DI32::new(zoom_in_button_x, rotate_button_y);
|
||||||
if self.draw_button(debug_ui, event, zoom_in_button_position, &self.zoom_in_texture) {
|
if self.draw_button(device,
|
||||||
|
debug_ui,
|
||||||
|
event,
|
||||||
|
zoom_in_button_position,
|
||||||
|
&self.zoom_in_texture) {
|
||||||
*action = UIAction::ZoomIn;
|
*action = UIAction::ZoomIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
let zoom_out_button_x = ROTATE_PANEL_X + (BUTTON_WIDTH + PADDING) * 2;
|
let zoom_out_button_x = ROTATE_PANEL_X + (BUTTON_WIDTH + PADDING) * 2;
|
||||||
let zoom_out_button_position = Point2DI32::new(zoom_out_button_x, rotate_button_y);
|
let zoom_out_button_position = Point2DI32::new(zoom_out_button_x, rotate_button_y);
|
||||||
if self.draw_button(debug_ui,
|
if self.draw_button(device,
|
||||||
|
debug_ui,
|
||||||
event,
|
event,
|
||||||
zoom_out_button_position,
|
zoom_out_button_position,
|
||||||
&self.zoom_out_texture) {
|
&self.zoom_out_texture) {
|
||||||
|
@ -187,40 +207,44 @@ impl DemoUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw effects panel, if necessary.
|
// Draw effects panel, if necessary.
|
||||||
self.draw_effects_panel(debug_ui, event);
|
self.draw_effects_panel(device, debug_ui, event);
|
||||||
|
|
||||||
// Draw rotate panel, if necessary.
|
// Draw rotate panel, if necessary.
|
||||||
self.draw_rotate_panel(debug_ui, event, action);
|
self.draw_rotate_panel(device, debug_ui, event, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_effects_panel(&mut self, debug_ui: &mut DebugUI, event: &mut UIEvent) {
|
fn draw_effects_panel(&mut self, device: &D, debug_ui: &mut DebugUI<D>, event: &mut UIEvent) {
|
||||||
if !self.effects_panel_visible {
|
if !self.effects_panel_visible {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bottom = debug_ui.framebuffer_size().y() - PADDING;
|
let bottom = debug_ui.framebuffer_size().y() - PADDING;
|
||||||
let effects_panel_y = bottom - (BUTTON_HEIGHT + PADDING + EFFECTS_PANEL_HEIGHT);
|
let effects_panel_y = bottom - (BUTTON_HEIGHT + PADDING + EFFECTS_PANEL_HEIGHT);
|
||||||
debug_ui.draw_solid_rounded_rect(RectI32::new(Point2DI32::new(PADDING, effects_panel_y),
|
debug_ui.draw_solid_rounded_rect(device,
|
||||||
|
RectI32::new(Point2DI32::new(PADDING, effects_panel_y),
|
||||||
Point2DI32::new(EFFECTS_PANEL_WIDTH,
|
Point2DI32::new(EFFECTS_PANEL_WIDTH,
|
||||||
EFFECTS_PANEL_HEIGHT)),
|
EFFECTS_PANEL_HEIGHT)),
|
||||||
WINDOW_COLOR);
|
WINDOW_COLOR);
|
||||||
|
|
||||||
self.gamma_correction_effect_enabled =
|
self.gamma_correction_effect_enabled =
|
||||||
self.draw_effects_switch(debug_ui,
|
self.draw_effects_switch(device,
|
||||||
|
debug_ui,
|
||||||
event,
|
event,
|
||||||
"Gamma Correction",
|
"Gamma Correction",
|
||||||
0,
|
0,
|
||||||
effects_panel_y,
|
effects_panel_y,
|
||||||
self.gamma_correction_effect_enabled);
|
self.gamma_correction_effect_enabled);
|
||||||
self.stem_darkening_effect_enabled =
|
self.stem_darkening_effect_enabled =
|
||||||
self.draw_effects_switch(debug_ui,
|
self.draw_effects_switch(device,
|
||||||
|
debug_ui,
|
||||||
event,
|
event,
|
||||||
"Stem Darkening",
|
"Stem Darkening",
|
||||||
1,
|
1,
|
||||||
effects_panel_y,
|
effects_panel_y,
|
||||||
self.stem_darkening_effect_enabled);
|
self.stem_darkening_effect_enabled);
|
||||||
self.subpixel_aa_effect_enabled =
|
self.subpixel_aa_effect_enabled =
|
||||||
self.draw_effects_switch(debug_ui,
|
self.draw_effects_switch(device,
|
||||||
|
debug_ui,
|
||||||
event,
|
event,
|
||||||
"Subpixel AA",
|
"Subpixel AA",
|
||||||
2,
|
2,
|
||||||
|
@ -230,7 +254,8 @@ impl DemoUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_rotate_panel(&mut self,
|
fn draw_rotate_panel(&mut self,
|
||||||
debug_ui: &mut DebugUI,
|
device: &D,
|
||||||
|
debug_ui: &mut DebugUI<D>,
|
||||||
event: &mut UIEvent,
|
event: &mut UIEvent,
|
||||||
action: &mut UIAction) {
|
action: &mut UIAction) {
|
||||||
if !self.rotate_panel_visible {
|
if !self.rotate_panel_visible {
|
||||||
|
@ -241,7 +266,8 @@ impl DemoUI {
|
||||||
let rotate_panel_y = bottom - (BUTTON_HEIGHT + PADDING + ROTATE_PANEL_HEIGHT);
|
let rotate_panel_y = bottom - (BUTTON_HEIGHT + PADDING + ROTATE_PANEL_HEIGHT);
|
||||||
let rotate_panel_origin = Point2DI32::new(ROTATE_PANEL_X, rotate_panel_y);
|
let rotate_panel_origin = Point2DI32::new(ROTATE_PANEL_X, rotate_panel_y);
|
||||||
let rotate_panel_size = Point2DI32::new(ROTATE_PANEL_WIDTH, ROTATE_PANEL_HEIGHT);
|
let rotate_panel_size = Point2DI32::new(ROTATE_PANEL_WIDTH, ROTATE_PANEL_HEIGHT);
|
||||||
debug_ui.draw_solid_rounded_rect(RectI32::new(rotate_panel_origin, rotate_panel_size),
|
debug_ui.draw_solid_rounded_rect(device,
|
||||||
|
RectI32::new(rotate_panel_origin, rotate_panel_size),
|
||||||
WINDOW_COLOR);
|
WINDOW_COLOR);
|
||||||
|
|
||||||
let (widget_x, widget_y) = (ROTATE_PANEL_X + PADDING, rotate_panel_y + PADDING);
|
let (widget_x, widget_y) = (ROTATE_PANEL_X + PADDING, rotate_panel_y + PADDING);
|
||||||
|
@ -257,32 +283,35 @@ impl DemoUI {
|
||||||
let slider_track_rect =
|
let slider_track_rect =
|
||||||
RectI32::new(Point2DI32::new(widget_x, slider_track_y),
|
RectI32::new(Point2DI32::new(widget_x, slider_track_y),
|
||||||
Point2DI32::new(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT));
|
Point2DI32::new(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT));
|
||||||
debug_ui.draw_rect_outline(slider_track_rect, TEXT_COLOR);
|
debug_ui.draw_rect_outline(device, slider_track_rect, TEXT_COLOR);
|
||||||
|
|
||||||
let slider_knob_x = widget_x + self.rotation - SLIDER_KNOB_WIDTH / 2;
|
let slider_knob_x = widget_x + self.rotation - SLIDER_KNOB_WIDTH / 2;
|
||||||
let slider_knob_rect =
|
let slider_knob_rect =
|
||||||
RectI32::new(Point2DI32::new(slider_knob_x, widget_y),
|
RectI32::new(Point2DI32::new(slider_knob_x, widget_y),
|
||||||
Point2DI32::new(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT));
|
Point2DI32::new(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT));
|
||||||
debug_ui.draw_solid_rect(slider_knob_rect, TEXT_COLOR);
|
debug_ui.draw_solid_rect(device, slider_knob_rect, TEXT_COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_button(&self,
|
fn draw_button(&self,
|
||||||
debug_ui: &mut DebugUI,
|
device: &D,
|
||||||
|
debug_ui: &mut DebugUI<D>,
|
||||||
event: &mut UIEvent,
|
event: &mut UIEvent,
|
||||||
origin: Point2DI32,
|
origin: Point2DI32,
|
||||||
texture: &Texture)
|
texture: &D::Texture)
|
||||||
-> bool {
|
-> bool {
|
||||||
let button_rect = RectI32::new(origin, Point2DI32::new(BUTTON_WIDTH, BUTTON_HEIGHT));
|
let button_rect = RectI32::new(origin, Point2DI32::new(BUTTON_WIDTH, BUTTON_HEIGHT));
|
||||||
debug_ui.draw_solid_rounded_rect(button_rect, WINDOW_COLOR);
|
debug_ui.draw_solid_rounded_rect(device, button_rect, WINDOW_COLOR);
|
||||||
debug_ui.draw_rounded_rect_outline(button_rect, OUTLINE_COLOR);
|
debug_ui.draw_rounded_rect_outline(device, button_rect, OUTLINE_COLOR);
|
||||||
debug_ui.draw_texture(origin + Point2DI32::new(PADDING, PADDING),
|
debug_ui.draw_texture(device,
|
||||||
|
origin + Point2DI32::new(PADDING, PADDING),
|
||||||
texture,
|
texture,
|
||||||
BUTTON_ICON_COLOR);
|
BUTTON_ICON_COLOR);
|
||||||
event.handle_mouse_down_in_rect(button_rect).is_some()
|
event.handle_mouse_down_in_rect(button_rect).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_effects_switch(&self,
|
fn draw_effects_switch(&self,
|
||||||
debug_ui: &mut DebugUI,
|
device: &D,
|
||||||
|
debug_ui: &mut DebugUI<D>,
|
||||||
event: &mut UIEvent,
|
event: &mut UIEvent,
|
||||||
text: &str,
|
text: &str,
|
||||||
index: i32,
|
index: i32,
|
||||||
|
@ -291,11 +320,12 @@ impl DemoUI {
|
||||||
-> bool {
|
-> bool {
|
||||||
let text_x = PADDING * 2;
|
let text_x = PADDING * 2;
|
||||||
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
|
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
|
||||||
debug_ui.draw_text(text, Point2DI32::new(text_x, text_y), false);
|
debug_ui.draw_text(device, text, Point2DI32::new(text_x, text_y), false);
|
||||||
|
|
||||||
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (SWITCH_SIZE + PADDING);
|
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (SWITCH_SIZE + PADDING);
|
||||||
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
|
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
|
||||||
self.draw_text_switch(debug_ui,
|
self.draw_text_switch(device,
|
||||||
|
debug_ui,
|
||||||
event,
|
event,
|
||||||
Point2DI32::new(switch_x, switch_y),
|
Point2DI32::new(switch_x, switch_y),
|
||||||
"Off",
|
"Off",
|
||||||
|
@ -304,14 +334,15 @@ impl DemoUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_text_switch(&self,
|
fn draw_text_switch(&self,
|
||||||
debug_ui: &mut DebugUI,
|
device: &D,
|
||||||
|
debug_ui: &mut DebugUI<D>,
|
||||||
event: &mut UIEvent,
|
event: &mut UIEvent,
|
||||||
origin: Point2DI32,
|
origin: Point2DI32,
|
||||||
off_text: &str,
|
off_text: &str,
|
||||||
on_text: &str,
|
on_text: &str,
|
||||||
mut value: bool)
|
mut value: bool)
|
||||||
-> bool {
|
-> bool {
|
||||||
value = self.draw_switch(debug_ui, event, origin, value);
|
value = self.draw_switch(device, debug_ui, event, origin, value);
|
||||||
|
|
||||||
let off_size = debug_ui.measure_text(off_text);
|
let off_size = debug_ui.measure_text(off_text);
|
||||||
let on_size = debug_ui.measure_text(on_text);
|
let on_size = debug_ui.measure_text(on_text);
|
||||||
|
@ -319,32 +350,39 @@ impl DemoUI {
|
||||||
let on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 - on_size / 2;
|
let on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 - on_size / 2;
|
||||||
let text_top = BUTTON_TEXT_OFFSET;
|
let text_top = BUTTON_TEXT_OFFSET;
|
||||||
|
|
||||||
debug_ui.draw_text(off_text, origin + Point2DI32::new(off_offset, text_top), !value);
|
debug_ui.draw_text(device,
|
||||||
debug_ui.draw_text(on_text, origin + Point2DI32::new(on_offset, text_top), value);
|
off_text,
|
||||||
|
origin + Point2DI32::new(off_offset, text_top),
|
||||||
|
!value);
|
||||||
|
debug_ui.draw_text(device, on_text, origin + Point2DI32::new(on_offset, text_top), value);
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_image_switch(&self,
|
fn draw_image_switch(&self,
|
||||||
debug_ui: &mut DebugUI,
|
device: &D,
|
||||||
|
debug_ui: &mut DebugUI<D>,
|
||||||
event: &mut UIEvent,
|
event: &mut UIEvent,
|
||||||
origin: Point2DI32,
|
origin: Point2DI32,
|
||||||
off_texture: &Texture,
|
off_texture: &D::Texture,
|
||||||
on_texture: &Texture,
|
on_texture: &D::Texture,
|
||||||
mut value: bool)
|
mut value: bool)
|
||||||
-> bool {
|
-> bool {
|
||||||
value = self.draw_switch(debug_ui, event, origin, value);
|
value = self.draw_switch(device, debug_ui, event, origin, value);
|
||||||
|
|
||||||
let off_offset = SWITCH_HALF_SIZE / 2 - off_texture.size.x() / 2;
|
let off_offset = SWITCH_HALF_SIZE / 2 - device.texture_size(off_texture).x() / 2;
|
||||||
let on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 - on_texture.size.x() / 2;
|
let on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 -
|
||||||
|
device.texture_size(on_texture).x() / 2;
|
||||||
|
|
||||||
let off_color = if !value { WINDOW_COLOR } else { TEXT_COLOR };
|
let off_color = if !value { WINDOW_COLOR } else { TEXT_COLOR };
|
||||||
let on_color = if value { WINDOW_COLOR } else { TEXT_COLOR };
|
let on_color = if value { WINDOW_COLOR } else { TEXT_COLOR };
|
||||||
|
|
||||||
debug_ui.draw_texture(origin + Point2DI32::new(off_offset, PADDING),
|
debug_ui.draw_texture(device,
|
||||||
|
origin + Point2DI32::new(off_offset, PADDING),
|
||||||
off_texture,
|
off_texture,
|
||||||
off_color);
|
off_color);
|
||||||
debug_ui.draw_texture(origin + Point2DI32::new(on_offset, PADDING),
|
debug_ui.draw_texture(device,
|
||||||
|
origin + Point2DI32::new(on_offset, PADDING),
|
||||||
on_texture,
|
on_texture,
|
||||||
on_color);
|
on_color);
|
||||||
|
|
||||||
|
@ -352,7 +390,8 @@ impl DemoUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_switch(&self,
|
fn draw_switch(&self,
|
||||||
debug_ui: &mut DebugUI,
|
device: &D,
|
||||||
|
debug_ui: &mut DebugUI<D>,
|
||||||
event: &mut UIEvent,
|
event: &mut UIEvent,
|
||||||
origin: Point2DI32,
|
origin: Point2DI32,
|
||||||
mut value: bool)
|
mut value: bool)
|
||||||
|
@ -362,15 +401,18 @@ impl DemoUI {
|
||||||
value = !value;
|
value = !value;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_ui.draw_solid_rounded_rect(widget_rect, WINDOW_COLOR);
|
debug_ui.draw_solid_rounded_rect(device, widget_rect, WINDOW_COLOR);
|
||||||
debug_ui.draw_rounded_rect_outline(widget_rect, OUTLINE_COLOR);
|
debug_ui.draw_rounded_rect_outline(device, widget_rect, OUTLINE_COLOR);
|
||||||
|
|
||||||
let highlight_size = Point2DI32::new(SWITCH_HALF_SIZE, BUTTON_HEIGHT);
|
let highlight_size = Point2DI32::new(SWITCH_HALF_SIZE, BUTTON_HEIGHT);
|
||||||
if !value {
|
if !value {
|
||||||
debug_ui.draw_solid_rounded_rect(RectI32::new(origin, highlight_size), TEXT_COLOR);
|
debug_ui.draw_solid_rounded_rect(device,
|
||||||
|
RectI32::new(origin, highlight_size),
|
||||||
|
TEXT_COLOR);
|
||||||
} else {
|
} else {
|
||||||
let x_offset = SWITCH_HALF_SIZE + 1;
|
let x_offset = SWITCH_HALF_SIZE + 1;
|
||||||
debug_ui.draw_solid_rounded_rect(RectI32::new(origin + Point2DI32::new(x_offset, 0),
|
debug_ui.draw_solid_rounded_rect(device,
|
||||||
|
RectI32::new(origin + Point2DI32::new(x_offset, 0),
|
||||||
highlight_size),
|
highlight_size),
|
||||||
TEXT_COLOR);
|
TEXT_COLOR);
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,13 +144,6 @@ impl Transform3DF32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn transpose(&self) -> Transform3DF32 {
|
|
||||||
let mut m = *self;
|
|
||||||
F32x4::transpose_4x4(&mut m.c0, &mut m.c1, &mut m.c2, &mut m.c3);
|
|
||||||
m
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(pcwalton): Is this right, due to transposition? I think we may have to reverse the
|
// FIXME(pcwalton): Is this right, due to transposition? I think we may have to reverse the
|
||||||
// two.
|
// two.
|
||||||
//
|
//
|
||||||
|
@ -384,19 +377,6 @@ mod test {
|
||||||
assert_eq!(a.transform_point(p), q);
|
assert_eq!(a.transform_point(p), q);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transpose() {
|
|
||||||
let a = Transform3DF32::row_major(3.0, 1.0, 4.0, 5.0,
|
|
||||||
9.0, 2.0, 6.0, 5.0,
|
|
||||||
3.0, 5.0, 8.0, 9.0,
|
|
||||||
7.0, 9.0, 3.0, 2.0);
|
|
||||||
let b = Transform3DF32::row_major(3.0, 9.0, 3.0, 7.0,
|
|
||||||
1.0, 2.0, 5.0, 9.0,
|
|
||||||
4.0, 6.0, 8.0, 3.0,
|
|
||||||
5.0, 5.0, 9.0, 2.0);
|
|
||||||
assert_eq!(a.transpose(), b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inverse() {
|
fn test_inverse() {
|
||||||
// Random matrix.
|
// Random matrix.
|
||||||
|
|
|
@ -18,5 +18,11 @@ features = ["png_codec"]
|
||||||
[dependencies.pathfinder_geometry]
|
[dependencies.pathfinder_geometry]
|
||||||
path = "../geometry"
|
path = "../geometry"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_gpu]
|
||||||
|
path = "../gpu"
|
||||||
|
|
||||||
[dependencies.pathfinder_renderer]
|
[dependencies.pathfinder_renderer]
|
||||||
path = "../renderer"
|
path = "../renderer"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_simd]
|
||||||
|
path = "../simd"
|
||||||
|
|
426
gl/src/debug.rs
426
gl/src/debug.rs
|
@ -15,26 +15,24 @@
|
||||||
//!
|
//!
|
||||||
//! The debug font atlas was generated using: https://evanw.github.io/font-texture-generator/
|
//! The debug font atlas was generated using: https://evanw.github.io/font-texture-generator/
|
||||||
|
|
||||||
use crate::device::{Buffer, BufferTarget, BufferUploadMode, Device, Program, Texture};
|
|
||||||
use crate::device::{Uniform, VertexAttr};
|
|
||||||
use gl::types::{GLfloat, GLint, GLsizei, GLuint};
|
|
||||||
use gl;
|
|
||||||
use pathfinder_geometry::basic::point::Point2DI32;
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
use pathfinder_geometry::basic::rect::RectI32;
|
use pathfinder_geometry::basic::rect::RectI32;
|
||||||
|
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, Device, Primitive, RenderState};
|
||||||
|
use pathfinder_gpu::{Resources, UniformData, VertexAttrType};
|
||||||
use pathfinder_renderer::gpu_data::Stats;
|
use pathfinder_renderer::gpu_data::Stats;
|
||||||
use pathfinder_renderer::paint::ColorU;
|
use pathfinder_renderer::paint::ColorU;
|
||||||
|
use pathfinder_simd::default::F32x4;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::ops::{Add, Div};
|
use std::ops::{Add, Div};
|
||||||
use std::ptr;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
const SAMPLE_BUFFER_SIZE: usize = 60;
|
const SAMPLE_BUFFER_SIZE: usize = 60;
|
||||||
|
|
||||||
const DEBUG_TEXTURE_VERTEX_SIZE: GLint = 8;
|
const DEBUG_TEXTURE_VERTEX_SIZE: usize = 8;
|
||||||
const DEBUG_SOLID_VERTEX_SIZE: GLint = 4;
|
const DEBUG_SOLID_VERTEX_SIZE: usize = 4;
|
||||||
|
|
||||||
pub const PADDING: i32 = 12;
|
pub const PADDING: i32 = 12;
|
||||||
|
|
||||||
|
@ -84,43 +82,44 @@ struct DebugCharacter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugFont {
|
impl DebugFont {
|
||||||
fn load(device: &Device) -> DebugFont {
|
fn load(resources: &Resources) -> DebugFont {
|
||||||
let mut path = device.resources_directory.clone();
|
let mut path = resources.resources_directory.clone();
|
||||||
path.push(FONT_JSON_FILENAME);
|
path.push(FONT_JSON_FILENAME);
|
||||||
|
|
||||||
serde_json::from_reader(BufReader::new(File::open(path).unwrap())).unwrap()
|
serde_json::from_reader(BufReader::new(File::open(path).unwrap())).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DebugUI {
|
pub struct DebugUI<D> where D: Device {
|
||||||
framebuffer_size: Point2DI32,
|
framebuffer_size: Point2DI32,
|
||||||
|
|
||||||
texture_program: DebugTextureProgram,
|
texture_program: DebugTextureProgram<D>,
|
||||||
texture_vertex_array: DebugTextureVertexArray,
|
texture_vertex_array: DebugTextureVertexArray<D>,
|
||||||
font: DebugFont,
|
font: DebugFont,
|
||||||
solid_program: DebugSolidProgram,
|
solid_program: DebugSolidProgram<D>,
|
||||||
solid_vertex_array: DebugSolidVertexArray,
|
solid_vertex_array: DebugSolidVertexArray<D>,
|
||||||
|
|
||||||
font_texture: Texture,
|
font_texture: D::Texture,
|
||||||
corner_fill_texture: Texture,
|
corner_fill_texture: D::Texture,
|
||||||
corner_outline_texture: Texture,
|
corner_outline_texture: D::Texture,
|
||||||
|
|
||||||
cpu_samples: SampleBuffer<CPUSample>,
|
cpu_samples: SampleBuffer<CPUSample>,
|
||||||
gpu_samples: SampleBuffer<GPUSample>,
|
gpu_samples: SampleBuffer<GPUSample>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugUI {
|
impl<D> DebugUI<D> where D: Device {
|
||||||
pub fn new(device: &Device, framebuffer_size: Point2DI32) -> DebugUI {
|
pub fn new(device: &D, resources: &Resources, framebuffer_size: Point2DI32) -> DebugUI<D> {
|
||||||
let texture_program = DebugTextureProgram::new(device);
|
let texture_program = DebugTextureProgram::new(device, resources);
|
||||||
let texture_vertex_array = DebugTextureVertexArray::new(&texture_program);
|
let texture_vertex_array = DebugTextureVertexArray::new(device, &texture_program);
|
||||||
let font = DebugFont::load(device);
|
let font = DebugFont::load(resources);
|
||||||
|
|
||||||
let solid_program = DebugSolidProgram::new(device);
|
let solid_program = DebugSolidProgram::new(device, resources);
|
||||||
let solid_vertex_array = DebugSolidVertexArray::new(&solid_program);
|
let solid_vertex_array = DebugSolidVertexArray::new(device, &solid_program);
|
||||||
|
|
||||||
let font_texture = device.create_texture_from_png(FONT_PNG_NAME);
|
let font_texture = device.create_texture_from_png(resources, FONT_PNG_NAME);
|
||||||
let corner_fill_texture = device.create_texture_from_png(CORNER_FILL_PNG_NAME);
|
let corner_fill_texture = device.create_texture_from_png(resources, CORNER_FILL_PNG_NAME);
|
||||||
let corner_outline_texture = device.create_texture_from_png(CORNER_OUTLINE_PNG_NAME);
|
let corner_outline_texture = device.create_texture_from_png(resources,
|
||||||
|
CORNER_OUTLINE_PNG_NAME);
|
||||||
|
|
||||||
DebugUI {
|
DebugUI {
|
||||||
framebuffer_size,
|
framebuffer_size,
|
||||||
|
@ -158,47 +157,55 @@ impl DebugUI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self) {
|
pub fn draw(&self, device: &D) {
|
||||||
// Draw performance window.
|
// Draw performance window.
|
||||||
let bottom = self.framebuffer_size.y() - PADDING;
|
let bottom = self.framebuffer_size.y() - PADDING;
|
||||||
let window_rect = RectI32::new(
|
let window_rect = RectI32::new(
|
||||||
Point2DI32::new(self.framebuffer_size.x() - PADDING - PERF_WINDOW_WIDTH,
|
Point2DI32::new(self.framebuffer_size.x() - PADDING - PERF_WINDOW_WIDTH,
|
||||||
bottom - PERF_WINDOW_HEIGHT),
|
bottom - PERF_WINDOW_HEIGHT),
|
||||||
Point2DI32::new(PERF_WINDOW_WIDTH, PERF_WINDOW_HEIGHT));
|
Point2DI32::new(PERF_WINDOW_WIDTH, PERF_WINDOW_HEIGHT));
|
||||||
self.draw_solid_rounded_rect(window_rect, WINDOW_COLOR);
|
self.draw_solid_rounded_rect(device, window_rect, WINDOW_COLOR);
|
||||||
let origin = window_rect.origin() + Point2DI32::new(PADDING, PADDING + FONT_ASCENT);
|
let origin = window_rect.origin() + Point2DI32::new(PADDING, PADDING + FONT_ASCENT);
|
||||||
|
|
||||||
let mean_cpu_sample = self.cpu_samples.mean();
|
let mean_cpu_sample = self.cpu_samples.mean();
|
||||||
self.draw_text(&format!("Objects: {}", mean_cpu_sample.stats.object_count), origin, false);
|
self.draw_text(device,
|
||||||
self.draw_text(&format!("Solid Tiles: {}", mean_cpu_sample.stats.solid_tile_count),
|
&format!("Objects: {}", mean_cpu_sample.stats.object_count),
|
||||||
|
origin,
|
||||||
|
false);
|
||||||
|
self.draw_text(device,
|
||||||
|
&format!("Solid Tiles: {}", mean_cpu_sample.stats.solid_tile_count),
|
||||||
origin + Point2DI32::new(0, LINE_HEIGHT * 1),
|
origin + Point2DI32::new(0, LINE_HEIGHT * 1),
|
||||||
false);
|
false);
|
||||||
self.draw_text(&format!("Mask Tiles: {}", mean_cpu_sample.stats.mask_tile_count),
|
self.draw_text(device,
|
||||||
|
&format!("Mask Tiles: {}", mean_cpu_sample.stats.mask_tile_count),
|
||||||
origin + Point2DI32::new(0, LINE_HEIGHT * 2),
|
origin + Point2DI32::new(0, LINE_HEIGHT * 2),
|
||||||
false);
|
false);
|
||||||
self.draw_text(&format!("Fills: {}", mean_cpu_sample.stats.fill_count),
|
self.draw_text(device,
|
||||||
|
&format!("Fills: {}", mean_cpu_sample.stats.fill_count),
|
||||||
origin + Point2DI32::new(0, LINE_HEIGHT * 3),
|
origin + Point2DI32::new(0, LINE_HEIGHT * 3),
|
||||||
false);
|
false);
|
||||||
|
|
||||||
self.draw_text(&format!("CPU Time: {:.3} ms", duration_to_ms(mean_cpu_sample.elapsed)),
|
self.draw_text(device,
|
||||||
|
&format!("CPU Time: {:.3} ms", duration_to_ms(mean_cpu_sample.elapsed)),
|
||||||
origin + Point2DI32::new(0, LINE_HEIGHT * 4),
|
origin + Point2DI32::new(0, LINE_HEIGHT * 4),
|
||||||
false);
|
false);
|
||||||
|
|
||||||
let mean_gpu_sample = self.gpu_samples.mean();
|
let mean_gpu_sample = self.gpu_samples.mean();
|
||||||
self.draw_text(&format!("GPU Time: {:.3} ms", duration_to_ms(mean_gpu_sample.elapsed)),
|
self.draw_text(device,
|
||||||
|
&format!("GPU Time: {:.3} ms", duration_to_ms(mean_gpu_sample.elapsed)),
|
||||||
origin + Point2DI32::new(0, LINE_HEIGHT * 5),
|
origin + Point2DI32::new(0, LINE_HEIGHT * 5),
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_solid_rect(&self, rect: RectI32, color: ColorU) {
|
pub fn draw_solid_rect(&self, device: &D, rect: RectI32, color: ColorU) {
|
||||||
self.draw_rect(rect, color, true);
|
self.draw_rect(device, rect, color, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_rect_outline(&self, rect: RectI32, color: ColorU) {
|
pub fn draw_rect_outline(&self, device: &D, rect: RectI32, color: ColorU) {
|
||||||
self.draw_rect(rect, color, false);
|
self.draw_rect(device, rect, color, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_rect(&self, rect: RectI32, color: ColorU, filled: bool) {
|
fn draw_rect(&self, device: &D, rect: RectI32, color: ColorU, filled: bool) {
|
||||||
let vertex_data = [
|
let vertex_data = [
|
||||||
DebugSolidVertex::new(rect.origin()),
|
DebugSolidVertex::new(rect.origin()),
|
||||||
DebugSolidVertex::new(rect.upper_right()),
|
DebugSolidVertex::new(rect.upper_right()),
|
||||||
|
@ -207,44 +214,50 @@ impl DebugUI {
|
||||||
];
|
];
|
||||||
|
|
||||||
if filled {
|
if filled {
|
||||||
self.draw_solid_rects_with_vertex_data(&vertex_data, &QUAD_INDICES, color, true);
|
self.draw_solid_rects_with_vertex_data(device,
|
||||||
|
&vertex_data,
|
||||||
|
&QUAD_INDICES,
|
||||||
|
color,
|
||||||
|
true);
|
||||||
} else {
|
} else {
|
||||||
self.draw_solid_rects_with_vertex_data(&vertex_data, &RECT_LINE_INDICES, color, false);
|
self.draw_solid_rects_with_vertex_data(device,
|
||||||
|
&vertex_data,
|
||||||
|
&RECT_LINE_INDICES,
|
||||||
|
color,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_solid_rects_with_vertex_data(&self,
|
fn draw_solid_rects_with_vertex_data(&self,
|
||||||
|
device: &D,
|
||||||
vertex_data: &[DebugSolidVertex],
|
vertex_data: &[DebugSolidVertex],
|
||||||
index_data: &[u32],
|
index_data: &[u32],
|
||||||
color: ColorU,
|
color: ColorU,
|
||||||
filled: bool) {
|
filled: bool) {
|
||||||
unsafe {
|
device.bind_vertex_array(&self.solid_vertex_array.vertex_array);
|
||||||
gl::BindVertexArray(self.solid_vertex_array.gl_vertex_array);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.solid_vertex_array
|
device.upload_to_buffer(&self.solid_vertex_array.vertex_buffer,
|
||||||
.vertex_buffer
|
vertex_data,
|
||||||
.upload(vertex_data, BufferTarget::Vertex, BufferUploadMode::Dynamic);
|
BufferTarget::Vertex,
|
||||||
self.solid_vertex_array
|
BufferUploadMode::Dynamic);
|
||||||
.index_buffer
|
device.upload_to_buffer(&self.solid_vertex_array.index_buffer,
|
||||||
.upload(index_data, BufferTarget::Index, BufferUploadMode::Dynamic);
|
index_data,
|
||||||
|
BufferTarget::Index,
|
||||||
|
BufferUploadMode::Dynamic);
|
||||||
|
|
||||||
unsafe {
|
device.use_program(&self.solid_program.program);
|
||||||
gl::UseProgram(self.solid_program.program.gl_program);
|
device.set_uniform(&self.solid_program.framebuffer_size_uniform,
|
||||||
gl::Uniform2f(self.solid_program.framebuffer_size_uniform.location,
|
UniformData::Vec2(self.framebuffer_size.0.to_f32x4()));
|
||||||
self.framebuffer_size.x() as GLfloat,
|
set_color_uniform(device, &self.solid_program.color_uniform, color);
|
||||||
self.framebuffer_size.y() as GLfloat);
|
|
||||||
set_color_uniform(&self.solid_program.color_uniform, color);
|
let primitive = if filled { Primitive::Triangles } else { Primitive::Lines };
|
||||||
gl::BlendEquation(gl::FUNC_ADD);
|
device.draw_elements(primitive, index_data.len() as u32, &RenderState {
|
||||||
gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
|
blend: BlendState::RGBOneAlphaOneMinusSrcAlpha,
|
||||||
gl::Enable(gl::BLEND);
|
..RenderState::default()
|
||||||
let primitive = if filled { gl::TRIANGLES } else { gl::LINES };
|
});
|
||||||
gl::DrawElements(primitive, index_data.len() as GLint, gl::UNSIGNED_INT, ptr::null());
|
|
||||||
gl::Disable(gl::BLEND);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_text(&self, string: &str, origin: Point2DI32, invert: bool) {
|
pub fn draw_text(&self, device: &D, string: &str, origin: Point2DI32, invert: bool) {
|
||||||
let mut next = origin;
|
let mut next = origin;
|
||||||
let char_count = string.chars().count();
|
let char_count = string.chars().count();
|
||||||
let mut vertex_data = Vec::with_capacity(char_count * 4);
|
let mut vertex_data = Vec::with_capacity(char_count * 4);
|
||||||
|
@ -274,12 +287,20 @@ impl DebugUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = if invert { INVERTED_TEXT_COLOR } else { TEXT_COLOR };
|
let color = if invert { INVERTED_TEXT_COLOR } else { TEXT_COLOR };
|
||||||
self.draw_texture_with_vertex_data(&vertex_data, &index_data, &self.font_texture, color);
|
self.draw_texture_with_vertex_data(device,
|
||||||
|
&vertex_data,
|
||||||
|
&index_data,
|
||||||
|
&self.font_texture,
|
||||||
|
color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_texture(&self, origin: Point2DI32, texture: &Texture, color: ColorU) {
|
pub fn draw_texture(&self,
|
||||||
let position_rect = RectI32::new(origin, texture.size);
|
device: &D,
|
||||||
let tex_coord_rect = RectI32::new(Point2DI32::default(), texture.size);
|
origin: Point2DI32,
|
||||||
|
texture: &D::Texture,
|
||||||
|
color: ColorU) {
|
||||||
|
let position_rect = RectI32::new(origin, device.texture_size(&texture));
|
||||||
|
let tex_coord_rect = RectI32::new(Point2DI32::default(), position_rect.size());
|
||||||
let vertex_data = [
|
let vertex_data = [
|
||||||
DebugTextureVertex::new(position_rect.origin(), tex_coord_rect.origin()),
|
DebugTextureVertex::new(position_rect.origin(), tex_coord_rect.origin()),
|
||||||
DebugTextureVertex::new(position_rect.upper_right(), tex_coord_rect.upper_right()),
|
DebugTextureVertex::new(position_rect.upper_right(), tex_coord_rect.upper_right()),
|
||||||
|
@ -287,7 +308,7 @@ impl DebugUI {
|
||||||
DebugTextureVertex::new(position_rect.lower_left(), tex_coord_rect.lower_left()),
|
DebugTextureVertex::new(position_rect.lower_left(), tex_coord_rect.lower_left()),
|
||||||
];
|
];
|
||||||
|
|
||||||
self.draw_texture_with_vertex_data(&vertex_data, &QUAD_INDICES, texture, color);
|
self.draw_texture_with_vertex_data(device, &vertex_data, &QUAD_INDICES, texture, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn measure_text(&self, string: &str) -> i32 {
|
pub fn measure_text(&self, string: &str) -> i32 {
|
||||||
|
@ -303,10 +324,10 @@ impl DebugUI {
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_solid_rounded_rect(&self, rect: RectI32, color: ColorU) {
|
pub fn draw_solid_rounded_rect(&self, device: &D, rect: RectI32, color: ColorU) {
|
||||||
let corner_texture = self.corner_texture(true);
|
let corner_texture = self.corner_texture(true);
|
||||||
let corner_rects = CornerRects::new(rect, corner_texture);
|
let corner_rects = CornerRects::new(device, rect, corner_texture);
|
||||||
self.draw_rounded_rect_corners(color, corner_texture, &corner_rects);
|
self.draw_rounded_rect_corners(device, color, corner_texture, &corner_rects);
|
||||||
|
|
||||||
let solid_rect_mid = RectI32::from_points(corner_rects.upper_left.upper_right(),
|
let solid_rect_mid = RectI32::from_points(corner_rects.upper_left.upper_right(),
|
||||||
corner_rects.lower_right.lower_left());
|
corner_rects.lower_right.lower_left());
|
||||||
|
@ -336,13 +357,17 @@ impl DebugUI {
|
||||||
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 4));
|
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 4));
|
||||||
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 8));
|
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 8));
|
||||||
|
|
||||||
self.draw_solid_rects_with_vertex_data(&vertex_data, &index_data[0..18], color, true);
|
self.draw_solid_rects_with_vertex_data(device,
|
||||||
|
&vertex_data,
|
||||||
|
&index_data[0..18],
|
||||||
|
color,
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_rounded_rect_outline(&self, rect: RectI32, color: ColorU) {
|
pub fn draw_rounded_rect_outline(&self, device: &D, rect: RectI32, color: ColorU) {
|
||||||
let corner_texture = self.corner_texture(false);
|
let corner_texture = self.corner_texture(false);
|
||||||
let corner_rects = CornerRects::new(rect, corner_texture);
|
let corner_rects = CornerRects::new(device, rect, corner_texture);
|
||||||
self.draw_rounded_rect_corners(color, corner_texture, &corner_rects);
|
self.draw_rounded_rect_corners(device, color, corner_texture, &corner_rects);
|
||||||
|
|
||||||
let vertex_data = vec![
|
let vertex_data = vec![
|
||||||
DebugSolidVertex::new(corner_rects.upper_left.upper_right()),
|
DebugSolidVertex::new(corner_rects.upper_left.upper_right()),
|
||||||
|
@ -356,14 +381,15 @@ impl DebugUI {
|
||||||
];
|
];
|
||||||
|
|
||||||
let index_data = &OUTLINE_RECT_LINE_INDICES;
|
let index_data = &OUTLINE_RECT_LINE_INDICES;
|
||||||
self.draw_solid_rects_with_vertex_data(&vertex_data, index_data, color, false);
|
self.draw_solid_rects_with_vertex_data(device, &vertex_data, index_data, color, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_rounded_rect_corners(&self,
|
fn draw_rounded_rect_corners(&self,
|
||||||
|
device: &D,
|
||||||
color: ColorU,
|
color: ColorU,
|
||||||
texture: &Texture,
|
texture: &D::Texture,
|
||||||
corner_rects: &CornerRects) {
|
corner_rects: &CornerRects) {
|
||||||
let corner_size = texture.size;
|
let corner_size = device.texture_size(&texture);
|
||||||
let tex_coord_rect = RectI32::new(Point2DI32::default(), corner_size);
|
let tex_coord_rect = RectI32::new(Point2DI32::default(), corner_size);
|
||||||
|
|
||||||
let vertex_data = vec![
|
let vertex_data = vec![
|
||||||
|
@ -410,151 +436,126 @@ impl DebugUI {
|
||||||
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 8));
|
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 8));
|
||||||
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 12));
|
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 12));
|
||||||
|
|
||||||
self.draw_texture_with_vertex_data(&vertex_data, &index_data, texture, color);
|
self.draw_texture_with_vertex_data(device, &vertex_data, &index_data, texture, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corner_texture(&self, filled: bool) -> &Texture {
|
fn corner_texture(&self, filled: bool) -> &D::Texture {
|
||||||
if filled { &self.corner_fill_texture } else { &self.corner_outline_texture }
|
if filled { &self.corner_fill_texture } else { &self.corner_outline_texture }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_texture_with_vertex_data(&self,
|
fn draw_texture_with_vertex_data(&self,
|
||||||
|
device: &D,
|
||||||
vertex_data: &[DebugTextureVertex],
|
vertex_data: &[DebugTextureVertex],
|
||||||
index_data: &[u32],
|
index_data: &[u32],
|
||||||
texture: &Texture,
|
texture: &D::Texture,
|
||||||
color: ColorU) {
|
color: ColorU) {
|
||||||
self.texture_vertex_array
|
device.upload_to_buffer(&self.texture_vertex_array.vertex_buffer,
|
||||||
.vertex_buffer
|
vertex_data,
|
||||||
.upload(&vertex_data, BufferTarget::Vertex, BufferUploadMode::Dynamic);
|
BufferTarget::Vertex,
|
||||||
self.texture_vertex_array
|
BufferUploadMode::Dynamic);
|
||||||
.index_buffer
|
device.upload_to_buffer(&self.texture_vertex_array.index_buffer,
|
||||||
.upload(&index_data, BufferTarget::Index, BufferUploadMode::Dynamic);
|
index_data,
|
||||||
|
BufferTarget::Index,
|
||||||
|
BufferUploadMode::Dynamic);
|
||||||
|
|
||||||
unsafe {
|
device.bind_vertex_array(&self.texture_vertex_array.vertex_array);
|
||||||
gl::BindVertexArray(self.texture_vertex_array.gl_vertex_array);
|
device.use_program(&self.texture_program.program);
|
||||||
gl::UseProgram(self.texture_program.program.gl_program);
|
device.set_uniform(&self.texture_program.framebuffer_size_uniform,
|
||||||
gl::Uniform2f(self.texture_program.framebuffer_size_uniform.location,
|
UniformData::Vec2(self.framebuffer_size.0.to_f32x4()));
|
||||||
self.framebuffer_size.x() as GLfloat,
|
device.set_uniform(&self.texture_program.texture_size_uniform,
|
||||||
self.framebuffer_size.y() as GLfloat);
|
UniformData::Vec2(device.texture_size(&texture).0.to_f32x4()));
|
||||||
gl::Uniform2f(self.texture_program.texture_size_uniform.location,
|
set_color_uniform(device, &self.texture_program.color_uniform, color);
|
||||||
texture.size.x() as GLfloat,
|
device.bind_texture(texture, 0);
|
||||||
texture.size.y() as GLfloat);
|
device.set_uniform(&self.texture_program.texture_uniform, UniformData::TextureUnit(0));
|
||||||
set_color_uniform(&self.texture_program.color_uniform, color);
|
|
||||||
texture.bind(0);
|
device.draw_elements(Primitive::Triangles, index_data.len() as u32, &RenderState {
|
||||||
gl::Uniform1i(self.texture_program.texture_uniform.location, 0);
|
blend: BlendState::RGBOneAlphaOneMinusSrcAlpha,
|
||||||
gl::BlendEquation(gl::FUNC_ADD);
|
..RenderState::default()
|
||||||
gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
|
});
|
||||||
gl::Enable(gl::BLEND);
|
|
||||||
gl::DrawElements(gl::TRIANGLES,
|
|
||||||
index_data.len() as GLsizei,
|
|
||||||
gl::UNSIGNED_INT,
|
|
||||||
ptr::null());
|
|
||||||
gl::Disable(gl::BLEND);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DebugTextureVertexArray {
|
struct DebugTextureVertexArray<D> where D: Device {
|
||||||
gl_vertex_array: GLuint,
|
vertex_array: D::VertexArray,
|
||||||
vertex_buffer: Buffer,
|
vertex_buffer: D::Buffer,
|
||||||
index_buffer: Buffer,
|
index_buffer: D::Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugTextureVertexArray {
|
impl<D> DebugTextureVertexArray<D> where D: Device {
|
||||||
fn new(debug_texture_program: &DebugTextureProgram) -> DebugTextureVertexArray {
|
fn new(device: &D, debug_texture_program: &DebugTextureProgram<D>)
|
||||||
let vertex_buffer = Buffer::new();
|
-> DebugTextureVertexArray<D> {
|
||||||
let index_buffer = Buffer::new();
|
let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer());
|
||||||
let mut gl_vertex_array = 0;
|
let vertex_array = device.create_vertex_array();
|
||||||
unsafe {
|
|
||||||
let position_attr = VertexAttr::new(&debug_texture_program.program, "Position");
|
|
||||||
let tex_coord_attr = VertexAttr::new(&debug_texture_program.program, "TexCoord");
|
|
||||||
|
|
||||||
gl::GenVertexArrays(1, &mut gl_vertex_array);
|
let position_attr = device.get_vertex_attr(&debug_texture_program.program, "Position");
|
||||||
gl::BindVertexArray(gl_vertex_array);
|
let tex_coord_attr = device.get_vertex_attr(&debug_texture_program.program, "TexCoord");
|
||||||
gl::UseProgram(debug_texture_program.program.gl_program);
|
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer);
|
device.bind_vertex_array(&vertex_array);
|
||||||
position_attr.configure_float(2,
|
device.use_program(&debug_texture_program.program);
|
||||||
gl::UNSIGNED_SHORT,
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
false,
|
device.bind_buffer(&index_buffer, BufferTarget::Index);
|
||||||
DEBUG_TEXTURE_VERTEX_SIZE,
|
device.configure_float_vertex_attr(&position_attr,
|
||||||
0,
|
2,
|
||||||
0);
|
VertexAttrType::U16,
|
||||||
tex_coord_attr.configure_float(2,
|
false,
|
||||||
gl::UNSIGNED_SHORT,
|
DEBUG_TEXTURE_VERTEX_SIZE,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
|
device.configure_float_vertex_attr(&tex_coord_attr,
|
||||||
|
2,
|
||||||
|
VertexAttrType::U16,
|
||||||
false,
|
false,
|
||||||
DEBUG_TEXTURE_VERTEX_SIZE,
|
DEBUG_TEXTURE_VERTEX_SIZE,
|
||||||
4,
|
4,
|
||||||
0);
|
0);
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer.gl_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
DebugTextureVertexArray { gl_vertex_array, vertex_buffer, index_buffer }
|
DebugTextureVertexArray { vertex_array, vertex_buffer, index_buffer }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for DebugTextureVertexArray {
|
struct DebugSolidVertexArray<D> where D: Device {
|
||||||
#[inline]
|
vertex_array: D::VertexArray,
|
||||||
fn drop(&mut self) {
|
vertex_buffer: D::Buffer,
|
||||||
unsafe {
|
index_buffer: D::Buffer,
|
||||||
gl::DeleteVertexArrays(1, &mut self.gl_vertex_array);
|
}
|
||||||
}
|
|
||||||
|
impl<D> DebugSolidVertexArray<D> where D: Device {
|
||||||
|
fn new(device: &D, debug_solid_program: &DebugSolidProgram<D>) -> DebugSolidVertexArray<D> {
|
||||||
|
let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer());
|
||||||
|
let vertex_array = device.create_vertex_array();
|
||||||
|
|
||||||
|
let position_attr = device.get_vertex_attr(&debug_solid_program.program, "Position");
|
||||||
|
device.bind_vertex_array(&vertex_array);
|
||||||
|
device.use_program(&debug_solid_program.program);
|
||||||
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
|
device.bind_buffer(&index_buffer, BufferTarget::Index);
|
||||||
|
device.configure_float_vertex_attr(&position_attr,
|
||||||
|
2,
|
||||||
|
VertexAttrType::U16,
|
||||||
|
false,
|
||||||
|
DEBUG_SOLID_VERTEX_SIZE,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
|
|
||||||
|
DebugSolidVertexArray { vertex_array, vertex_buffer, index_buffer }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DebugSolidVertexArray {
|
struct DebugTextureProgram<D> where D: Device {
|
||||||
gl_vertex_array: GLuint,
|
program: D::Program,
|
||||||
vertex_buffer: Buffer,
|
framebuffer_size_uniform: D::Uniform,
|
||||||
index_buffer: Buffer,
|
texture_size_uniform: D::Uniform,
|
||||||
|
texture_uniform: D::Uniform,
|
||||||
|
color_uniform: D::Uniform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugSolidVertexArray {
|
impl<D> DebugTextureProgram<D> where D: Device {
|
||||||
fn new(debug_solid_program: &DebugSolidProgram) -> DebugSolidVertexArray {
|
fn new(device: &D, resources: &Resources) -> DebugTextureProgram<D> {
|
||||||
let vertex_buffer = Buffer::new();
|
let program = device.create_program(resources, "debug_texture");
|
||||||
let index_buffer = Buffer::new();
|
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
|
||||||
let mut gl_vertex_array = 0;
|
let texture_size_uniform = device.get_uniform(&program, "TextureSize");
|
||||||
unsafe {
|
let texture_uniform = device.get_uniform(&program, "Texture");
|
||||||
let position_attr = VertexAttr::new(&debug_solid_program.program, "Position");
|
let color_uniform = device.get_uniform(&program, "Color");
|
||||||
|
|
||||||
gl::GenVertexArrays(1, &mut gl_vertex_array);
|
|
||||||
gl::BindVertexArray(gl_vertex_array);
|
|
||||||
gl::UseProgram(debug_solid_program.program.gl_program);
|
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer);
|
|
||||||
position_attr.configure_float(2,
|
|
||||||
gl::UNSIGNED_SHORT,
|
|
||||||
false,
|
|
||||||
DEBUG_SOLID_VERTEX_SIZE,
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer.gl_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
DebugSolidVertexArray { gl_vertex_array, vertex_buffer, index_buffer }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for DebugSolidVertexArray {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gl::DeleteVertexArrays(1, &mut self.gl_vertex_array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DebugTextureProgram {
|
|
||||||
program: Program,
|
|
||||||
framebuffer_size_uniform: Uniform,
|
|
||||||
texture_size_uniform: Uniform,
|
|
||||||
texture_uniform: Uniform,
|
|
||||||
color_uniform: Uniform,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DebugTextureProgram {
|
|
||||||
fn new(device: &Device) -> DebugTextureProgram {
|
|
||||||
let program = device.create_program("debug_texture");
|
|
||||||
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
|
|
||||||
let texture_size_uniform = Uniform::new(&program, "TextureSize");
|
|
||||||
let texture_uniform = Uniform::new(&program, "Texture");
|
|
||||||
let color_uniform = Uniform::new(&program, "Color");
|
|
||||||
DebugTextureProgram {
|
DebugTextureProgram {
|
||||||
program,
|
program,
|
||||||
framebuffer_size_uniform,
|
framebuffer_size_uniform,
|
||||||
|
@ -565,17 +566,17 @@ impl DebugTextureProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DebugSolidProgram {
|
struct DebugSolidProgram<D> where D: Device {
|
||||||
program: Program,
|
program: D::Program,
|
||||||
framebuffer_size_uniform: Uniform,
|
framebuffer_size_uniform: D::Uniform,
|
||||||
color_uniform: Uniform,
|
color_uniform: D::Uniform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugSolidProgram {
|
impl<D> DebugSolidProgram<D> where D: Device {
|
||||||
fn new(device: &Device) -> DebugSolidProgram {
|
fn new(device: &D, resources: &Resources) -> DebugSolidProgram<D> {
|
||||||
let program = device.create_program("debug_solid");
|
let program = device.create_program(resources, "debug_solid");
|
||||||
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
|
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
|
||||||
let color_uniform = Uniform::new(&program, "Color");
|
let color_uniform = device.get_uniform(&program, "Color");
|
||||||
DebugSolidProgram { program, framebuffer_size_uniform, color_uniform }
|
DebugSolidProgram { program, framebuffer_size_uniform, color_uniform }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -645,14 +646,9 @@ impl<S> SampleBuffer<S> where S: Add<S, Output=S> + Div<u32, Output=S> + Clone +
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_color_uniform(uniform: &Uniform, color: ColorU) {
|
fn set_color_uniform<D>(device: &D, uniform: &D::Uniform, color: ColorU) where D: Device {
|
||||||
unsafe {
|
let color = F32x4::new(color.r as f32, color.g as f32, color.b as f32, color.a as f32);
|
||||||
gl::Uniform4f(uniform.location,
|
device.set_uniform(uniform, UniformData::Vec4(color * F32x4::splat(1.0 / 255.0)));
|
||||||
color.r as f32 * (1.0 / 255.0),
|
|
||||||
color.g as f32 * (1.0 / 255.0),
|
|
||||||
color.b as f32 * (1.0 / 255.0),
|
|
||||||
color.a as f32 * (1.0 / 255.0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -722,8 +718,8 @@ struct CornerRects {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CornerRects {
|
impl CornerRects {
|
||||||
fn new(rect: RectI32, texture: &Texture) -> CornerRects {
|
fn new<D>(device: &D, rect: RectI32, texture: &D::Texture) -> CornerRects where D: Device {
|
||||||
let size = texture.size;
|
let size = device.texture_size(texture);
|
||||||
CornerRects {
|
CornerRects {
|
||||||
upper_left: RectI32::new(rect.origin(), size),
|
upper_left: RectI32::new(rect.origin(), size),
|
||||||
upper_right: RectI32::new(rect.upper_right() - Point2DI32::new(size.x(), 0), size),
|
upper_right: RectI32::new(rect.upper_right() - Point2DI32::new(size.x(), 0), size),
|
||||||
|
|
861
gl/src/device.rs
861
gl/src/device.rs
|
@ -1,4 +1,4 @@
|
||||||
// pathfinder/demo/src/device.rs
|
// pathfinder/gl/src/device.rs
|
||||||
//
|
//
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
//
|
//
|
||||||
|
@ -8,72 +8,175 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
//! Minimal abstractions over GPU device capabilities.
|
//! An OpenGL implementation of the device abstraction.
|
||||||
|
|
||||||
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
use gl::types::{GLboolean, GLchar, GLdouble, GLenum, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||||
use pathfinder_geometry::basic::point::Point2DI32;
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
use std::env;
|
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, Device, Primitive};
|
||||||
|
use pathfinder_gpu::{RenderState, ShaderKind, StencilFunc, TextureFormat};
|
||||||
|
use pathfinder_gpu::{UniformData, VertexAttrType};
|
||||||
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
pub struct Device {
|
pub struct GLDevice;
|
||||||
pub resources_directory: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Device {
|
impl GLDevice {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Device {
|
pub fn new() -> GLDevice { GLDevice }
|
||||||
Device { resources_directory: locate_resources_directory() }
|
|
||||||
|
fn set_texture_parameters(&self, texture: &GLTexture) {
|
||||||
|
self.bind_texture(texture, 0);
|
||||||
|
unsafe {
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn set_render_state(&self, render_state: &RenderState) {
|
||||||
pub fn create_texture_from_png(&self, name: &str) -> Texture {
|
|
||||||
let mut path = self.resources_directory.clone();
|
|
||||||
path.push("textures");
|
|
||||||
path.push(format!("{}.png", name));
|
|
||||||
|
|
||||||
let image = image::open(&path).unwrap().to_luma();
|
|
||||||
|
|
||||||
let mut texture = Texture {
|
|
||||||
gl_texture: 0,
|
|
||||||
size: Point2DI32::new(image.width() as i32, image.height() as i32),
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::GenTextures(1, &mut texture.gl_texture);
|
// Set blend.
|
||||||
texture.bind(0);
|
match render_state.blend {
|
||||||
gl::TexImage2D(gl::TEXTURE_2D,
|
BlendState::Off => gl::Disable(gl::BLEND),
|
||||||
0,
|
BlendState::RGBOneAlphaOneMinusSrcAlpha => {
|
||||||
gl::RED as GLint,
|
gl::BlendFuncSeparate(gl::ONE, gl::ONE_MINUS_SRC_ALPHA, gl::ONE, gl::ONE);
|
||||||
image.width() as GLsizei,
|
gl::Enable(gl::BLEND);
|
||||||
image.height() as GLsizei,
|
}
|
||||||
0,
|
BlendState::RGBOneAlphaOne => {
|
||||||
gl::RED,
|
gl::BlendFunc(gl::ONE, gl::ONE);
|
||||||
gl::UNSIGNED_BYTE,
|
gl::Enable(gl::BLEND);
|
||||||
image.as_ptr() as *const GLvoid);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set depth.
|
||||||
|
match render_state.depth {
|
||||||
|
None => gl::Disable(gl::DEPTH_TEST),
|
||||||
|
Some(ref state) => {
|
||||||
|
gl::Enable(gl::DEPTH_TEST);
|
||||||
|
gl::DepthFunc(state.func.to_gl_depth_func());
|
||||||
|
gl::DepthMask(state.write as GLboolean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stencil.
|
||||||
|
match render_state.stencil {
|
||||||
|
None => gl::Disable(gl::STENCIL_TEST),
|
||||||
|
Some(ref state) => {
|
||||||
|
gl::StencilFunc(state.func.to_gl_stencil_func(),
|
||||||
|
state.reference as GLint,
|
||||||
|
state.mask);
|
||||||
|
let pass_action = if state.pass_replace { gl::REPLACE } else { gl::KEEP };
|
||||||
|
gl::StencilOp(gl::KEEP, gl::KEEP, pass_action);
|
||||||
|
gl::Enable(gl::STENCIL_TEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set color mask.
|
||||||
|
let color_mask = render_state.color_mask as GLboolean;
|
||||||
|
gl::ColorMask(color_mask, color_mask, color_mask, color_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_render_state(&self, render_state: &RenderState) {
|
||||||
|
unsafe {
|
||||||
|
match render_state.blend {
|
||||||
|
BlendState::Off => {}
|
||||||
|
BlendState::RGBOneAlphaOneMinusSrcAlpha | BlendState::RGBOneAlphaOne => {
|
||||||
|
gl::Disable(gl::BLEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if render_state.depth.is_some() {
|
||||||
|
gl::Disable(gl::DEPTH_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if render_state.stencil.is_some() {
|
||||||
|
gl::Disable(gl::STENCIL_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for GLDevice {
|
||||||
|
type Buffer = GLBuffer;
|
||||||
|
type Framebuffer = GLFramebuffer;
|
||||||
|
type Program = GLProgram;
|
||||||
|
type Shader = GLShader;
|
||||||
|
type Texture = GLTexture;
|
||||||
|
type TimerQuery = GLTimerQuery;
|
||||||
|
type Uniform = GLUniform;
|
||||||
|
type VertexArray = GLVertexArray;
|
||||||
|
type VertexAttr = GLVertexAttr;
|
||||||
|
|
||||||
|
fn create_texture(&self, format: TextureFormat, size: Point2DI32) -> GLTexture {
|
||||||
|
let (gl_internal_format, gl_format, gl_type);
|
||||||
|
match format {
|
||||||
|
TextureFormat::R16F => {
|
||||||
|
gl_internal_format = gl::R16F as GLint;
|
||||||
|
gl_format = gl::RED;
|
||||||
|
gl_type = gl::HALF_FLOAT;
|
||||||
|
}
|
||||||
|
TextureFormat::RGBA8 => {
|
||||||
|
gl_internal_format = gl::RGBA as GLint;
|
||||||
|
gl_format = gl::RGBA;
|
||||||
|
gl_type = gl::UNSIGNED_BYTE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
texture.set_parameters();
|
let mut texture = GLTexture { gl_texture: 0, size };
|
||||||
|
unsafe {
|
||||||
|
gl::GenTextures(1, &mut texture.gl_texture);
|
||||||
|
self.bind_texture(&texture, 0);
|
||||||
|
gl::TexImage2D(gl::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl_internal_format,
|
||||||
|
size.x() as GLsizei,
|
||||||
|
size.y() as GLsizei,
|
||||||
|
0,
|
||||||
|
gl_format,
|
||||||
|
gl_type,
|
||||||
|
ptr::null());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_texture_parameters(&texture);
|
||||||
texture
|
texture
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_shader(&self, name: &str, kind: ShaderKind) -> Shader {
|
fn create_texture_from_data(&self, size: Point2DI32, data: &[u8]) -> GLTexture {
|
||||||
let suffix = match kind { ShaderKind::Vertex => 'v', ShaderKind::Fragment => 'f' };
|
assert!(data.len() >= size.x() as usize * size.y() as usize);
|
||||||
let mut path = self.resources_directory.clone();
|
|
||||||
path.push("shaders");
|
let mut texture = GLTexture { gl_texture: 0, size };
|
||||||
path.push(format!("{}.{}s.glsl", name, suffix));
|
unsafe {
|
||||||
|
gl::GenTextures(1, &mut texture.gl_texture);
|
||||||
|
self.bind_texture(&texture, 0);
|
||||||
|
gl::TexImage2D(gl::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl::RED as GLint,
|
||||||
|
size.x() as GLsizei,
|
||||||
|
size.y() as GLsizei,
|
||||||
|
0,
|
||||||
|
gl::RED,
|
||||||
|
gl::UNSIGNED_BYTE,
|
||||||
|
data.as_ptr() as *const GLvoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_texture_parameters(&texture);
|
||||||
|
texture
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind) -> GLShader {
|
||||||
|
let gl_shader_kind = match kind {
|
||||||
|
ShaderKind::Vertex => gl::VERTEX_SHADER,
|
||||||
|
ShaderKind::Fragment => gl::FRAGMENT_SHADER,
|
||||||
|
};
|
||||||
|
|
||||||
let mut source = vec![];
|
|
||||||
File::open(&path).unwrap().read_to_end(&mut source).unwrap();
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let gl_shader_kind = match kind {
|
|
||||||
ShaderKind::Vertex => gl::VERTEX_SHADER,
|
|
||||||
ShaderKind::Fragment => gl::FRAGMENT_SHADER,
|
|
||||||
};
|
|
||||||
let gl_shader = gl::CreateShader(gl_shader_kind);
|
let gl_shader = gl::CreateShader(gl_shader_kind);
|
||||||
gl::ShaderSource(gl_shader,
|
gl::ShaderSource(gl_shader,
|
||||||
1,
|
1,
|
||||||
|
@ -95,14 +198,15 @@ impl Device {
|
||||||
panic!("{:?} shader '{}' compilation failed", kind, name);
|
panic!("{:?} shader '{}' compilation failed", kind, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader { gl_shader }
|
GLShader { gl_shader }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_program(&self, name: &str) -> Program {
|
fn create_program_from_shaders(&self,
|
||||||
let vertex_shader = self.create_shader(name, ShaderKind::Vertex);
|
name: &str,
|
||||||
let fragment_shader = self.create_shader(name, ShaderKind::Fragment);
|
vertex_shader: GLShader,
|
||||||
|
fragment_shader: GLShader)
|
||||||
|
-> GLProgram {
|
||||||
let gl_program;
|
let gl_program;
|
||||||
unsafe {
|
unsafe {
|
||||||
gl_program = gl::CreateProgram();
|
gl_program = gl::CreateProgram();
|
||||||
|
@ -125,15 +229,332 @@ impl Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Program { gl_program, vertex_shader, fragment_shader }
|
GLProgram { gl_program, vertex_shader, fragment_shader }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn create_vertex_array(&self) -> GLVertexArray {
|
||||||
|
unsafe {
|
||||||
|
let mut array = GLVertexArray { gl_vertex_array: 0 };
|
||||||
|
gl::GenVertexArrays(1, &mut array.gl_vertex_array);
|
||||||
|
array
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> GLVertexAttr {
|
||||||
|
let name = CString::new(format!("a{}", name)).unwrap();
|
||||||
|
let attr = unsafe {
|
||||||
|
gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar) as GLuint
|
||||||
|
};
|
||||||
|
GLVertexAttr { attr }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_uniform(&self, program: &GLProgram, name: &str) -> GLUniform {
|
||||||
|
let name = CString::new(format!("u{}", name)).unwrap();
|
||||||
|
let location = unsafe {
|
||||||
|
gl::GetUniformLocation(program.gl_program, name.as_ptr() as *const GLchar)
|
||||||
|
};
|
||||||
|
GLUniform { location }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_program(&self, program: &Self::Program) {
|
||||||
|
unsafe {
|
||||||
|
gl::UseProgram(program.gl_program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_float_vertex_attr(&self,
|
||||||
|
attr: &GLVertexAttr,
|
||||||
|
size: usize,
|
||||||
|
attr_type: VertexAttrType,
|
||||||
|
normalized: bool,
|
||||||
|
stride: usize,
|
||||||
|
offset: usize,
|
||||||
|
divisor: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl::VertexAttribPointer(attr.attr,
|
||||||
|
size as GLint,
|
||||||
|
attr_type.to_gl_type(),
|
||||||
|
if normalized { gl::TRUE } else { gl::FALSE },
|
||||||
|
stride as GLint,
|
||||||
|
offset as *const GLvoid);
|
||||||
|
gl::VertexAttribDivisor(attr.attr, divisor);
|
||||||
|
gl::EnableVertexAttribArray(attr.attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_int_vertex_attr(&self,
|
||||||
|
attr: &GLVertexAttr,
|
||||||
|
size: usize,
|
||||||
|
attr_type: VertexAttrType,
|
||||||
|
stride: usize,
|
||||||
|
offset: usize,
|
||||||
|
divisor: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl::VertexAttribIPointer(attr.attr,
|
||||||
|
size as GLint,
|
||||||
|
attr_type.to_gl_type(),
|
||||||
|
stride as GLint,
|
||||||
|
offset as *const GLvoid);
|
||||||
|
gl::VertexAttribDivisor(attr.attr, divisor);
|
||||||
|
gl::EnableVertexAttribArray(attr.attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData) {
|
||||||
|
unsafe {
|
||||||
|
match data {
|
||||||
|
UniformData::Vec2(data) => gl::Uniform2f(uniform.location, data.x(), data.y()),
|
||||||
|
UniformData::Vec4(data) => {
|
||||||
|
gl::Uniform4f(uniform.location, data.x(), data.y(), data.z(), data.w());
|
||||||
|
}
|
||||||
|
UniformData::TextureUnit(unit) => gl::Uniform1i(uniform.location, unit as GLint),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_framebuffer(&self, texture: GLTexture) -> GLFramebuffer {
|
||||||
|
let mut gl_framebuffer = 0;
|
||||||
|
unsafe {
|
||||||
|
gl::GenFramebuffers(1, &mut gl_framebuffer);
|
||||||
|
assert_eq!(gl::GetError(), gl::NO_ERROR);
|
||||||
|
gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer);
|
||||||
|
self.bind_texture(&texture, 0);
|
||||||
|
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
|
||||||
|
gl::COLOR_ATTACHMENT0,
|
||||||
|
gl::TEXTURE_2D,
|
||||||
|
texture.gl_texture,
|
||||||
|
0);
|
||||||
|
assert_eq!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER), gl::FRAMEBUFFER_COMPLETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFramebuffer { gl_framebuffer, texture }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_buffer(&self) -> GLBuffer {
|
||||||
|
unsafe {
|
||||||
|
let mut gl_buffer = 0;
|
||||||
|
gl::GenBuffers(1, &mut gl_buffer);
|
||||||
|
GLBuffer { gl_buffer }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn upload_to_buffer<T>(&self,
|
||||||
|
buffer: &GLBuffer,
|
||||||
|
data: &[T],
|
||||||
|
target: BufferTarget,
|
||||||
|
mode: BufferUploadMode) {
|
||||||
|
let target = match target {
|
||||||
|
BufferTarget::Vertex => gl::ARRAY_BUFFER,
|
||||||
|
BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER,
|
||||||
|
};
|
||||||
|
let mode = match mode {
|
||||||
|
BufferUploadMode::Static => gl::STATIC_DRAW,
|
||||||
|
BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW,
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
gl::BindBuffer(target, buffer.gl_buffer);
|
||||||
|
gl::BufferData(target,
|
||||||
|
(data.len() * mem::size_of::<T>()) as GLsizeiptr,
|
||||||
|
data.as_ptr() as *const GLvoid,
|
||||||
|
mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture {
|
||||||
|
&framebuffer.texture
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn texture_size(&self, texture: &Self::Texture) -> Point2DI32 {
|
||||||
|
texture.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn upload_to_texture(&self, texture: &Self::Texture, size: Point2DI32, data: &[u8]) {
|
||||||
|
assert!(data.len() >= size.x() as usize * size.y() as usize * 4);
|
||||||
|
unsafe {
|
||||||
|
self.bind_texture(texture, 0);
|
||||||
|
gl::TexImage2D(gl::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl::RGBA as GLint,
|
||||||
|
size.x() as GLsizei,
|
||||||
|
size.y() as GLsizei,
|
||||||
|
0,
|
||||||
|
gl::RGBA,
|
||||||
|
gl::UNSIGNED_BYTE,
|
||||||
|
data.as_ptr() as *const GLvoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_texture_parameters(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_pixels_from_default_framebuffer(&self, size: Point2DI32) -> Vec<u8> {
|
||||||
|
let mut pixels = vec![0; size.x() as usize * size.y() as usize * 4];
|
||||||
|
unsafe {
|
||||||
|
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
|
||||||
|
gl::ReadPixels(0,
|
||||||
|
0,
|
||||||
|
size.x() as GLsizei,
|
||||||
|
size.y() as GLsizei,
|
||||||
|
gl::RGBA,
|
||||||
|
gl::UNSIGNED_BYTE,
|
||||||
|
pixels.as_mut_ptr() as *mut GLvoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip right-side-up.
|
||||||
|
let stride = size.x() as usize * 4;
|
||||||
|
for y in 0..(size.y() as usize / 2) {
|
||||||
|
let (index_a, index_b) = (y * stride, (size.y() as usize - y - 1) * stride);
|
||||||
|
for offset in 0..stride {
|
||||||
|
pixels.swap(index_a + offset, index_b + offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Switch to `ColorF`!
|
||||||
|
fn clear(&self, color: Option<F32x4>, depth: Option<f32>, stencil: Option<u8>) {
|
||||||
|
unsafe {
|
||||||
|
let mut flags = 0;
|
||||||
|
if let Some(color) = color {
|
||||||
|
gl::ClearColor(color.x(), color.y(), color.z(), color.w());
|
||||||
|
flags |= gl::COLOR_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
if let Some(depth) = depth {
|
||||||
|
gl::ClearDepth(depth as GLdouble);
|
||||||
|
flags |= gl::DEPTH_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
if let Some(stencil) = stencil {
|
||||||
|
gl::ClearStencil(stencil as GLint);
|
||||||
|
flags |= gl::STENCIL_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
if flags != 0 {
|
||||||
|
gl::Clear(flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_arrays(&self, primitive: Primitive, index_count: u32, render_state: &RenderState) {
|
||||||
|
self.set_render_state(render_state);
|
||||||
|
unsafe {
|
||||||
|
gl::DrawArrays(primitive.to_gl_primitive(), 0, index_count as GLsizei);
|
||||||
|
}
|
||||||
|
self.reset_render_state(render_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_elements(&self, primitive: Primitive, index_count: u32, render_state: &RenderState) {
|
||||||
|
self.set_render_state(render_state);
|
||||||
|
unsafe {
|
||||||
|
gl::DrawElements(primitive.to_gl_primitive(),
|
||||||
|
index_count as GLsizei,
|
||||||
|
gl::UNSIGNED_INT,
|
||||||
|
ptr::null());
|
||||||
|
}
|
||||||
|
self.reset_render_state(render_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_arrays_instanced(&self,
|
||||||
|
primitive: Primitive,
|
||||||
|
index_count: u32,
|
||||||
|
instance_count: u32,
|
||||||
|
render_state: &RenderState) {
|
||||||
|
self.set_render_state(render_state);
|
||||||
|
unsafe {
|
||||||
|
gl::DrawArraysInstanced(primitive.to_gl_primitive(),
|
||||||
|
0,
|
||||||
|
index_count as GLsizei,
|
||||||
|
instance_count as GLsizei);
|
||||||
|
}
|
||||||
|
self.reset_render_state(render_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn create_timer_query(&self) -> GLTimerQuery {
|
||||||
|
let mut query = GLTimerQuery { gl_query: 0 };
|
||||||
|
unsafe {
|
||||||
|
gl::GenQueries(1, &mut query.gl_query);
|
||||||
|
}
|
||||||
|
query
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn begin_timer_query(&self, query: &Self::TimerQuery) {
|
||||||
|
unsafe {
|
||||||
|
gl::BeginQuery(gl::TIME_ELAPSED, query.gl_query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn end_timer_query(&self, _: &Self::TimerQuery) {
|
||||||
|
unsafe {
|
||||||
|
gl::EndQuery(gl::TIME_ELAPSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn timer_query_is_available(&self, query: &Self::TimerQuery) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let mut result = 0;
|
||||||
|
gl::GetQueryObjectiv(query.gl_query, gl::QUERY_RESULT_AVAILABLE, &mut result);
|
||||||
|
result != gl::FALSE as GLint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_timer_query(&self, query: &Self::TimerQuery) -> Duration {
|
||||||
|
unsafe {
|
||||||
|
let mut result = 0;
|
||||||
|
gl::GetQueryObjectui64v(query.gl_query, gl::QUERY_RESULT, &mut result);
|
||||||
|
Duration::from_nanos(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bind_vertex_array(&self, vertex_array: &GLVertexArray) {
|
||||||
|
unsafe {
|
||||||
|
gl::BindVertexArray(vertex_array.gl_vertex_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bind_buffer(&self, buffer: &GLBuffer, target: BufferTarget) {
|
||||||
|
unsafe {
|
||||||
|
gl::BindBuffer(target.to_gl_target(), buffer.gl_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bind_default_framebuffer(&self, size: Point2DI32) {
|
||||||
|
unsafe {
|
||||||
|
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
|
||||||
|
gl::Viewport(0, 0, size.x(), size.y());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bind_framebuffer(&self, framebuffer: &GLFramebuffer) {
|
||||||
|
unsafe {
|
||||||
|
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.gl_framebuffer);
|
||||||
|
gl::Viewport(0, 0, framebuffer.texture.size.x(), framebuffer.texture.size.y());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bind_texture(&self, texture: &GLTexture, unit: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl::ActiveTexture(gl::TEXTURE0 + unit);
|
||||||
|
gl::BindTexture(gl::TEXTURE_2D, texture.gl_texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VertexArray {
|
pub struct GLVertexArray {
|
||||||
pub gl_vertex_array: GLuint,
|
pub gl_vertex_array: GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for VertexArray {
|
impl Drop for GLVertexArray {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -142,30 +563,11 @@ impl Drop for VertexArray {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VertexArray {
|
pub struct GLVertexAttr {
|
||||||
#[inline]
|
|
||||||
pub fn new() -> VertexArray {
|
|
||||||
unsafe {
|
|
||||||
let mut array = VertexArray { gl_vertex_array: 0 };
|
|
||||||
gl::GenVertexArrays(1, &mut array.gl_vertex_array);
|
|
||||||
array
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VertexAttr {
|
|
||||||
attr: GLuint,
|
attr: GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VertexAttr {
|
impl GLVertexAttr {
|
||||||
pub fn new(program: &Program, name: &str) -> VertexAttr {
|
|
||||||
let name = CString::new(format!("a{}", name)).unwrap();
|
|
||||||
let attr = unsafe {
|
|
||||||
gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar) as GLuint
|
|
||||||
};
|
|
||||||
VertexAttr { attr }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn configure_float(&self,
|
pub fn configure_float(&self,
|
||||||
size: GLint,
|
size: GLint,
|
||||||
gl_type: GLuint,
|
gl_type: GLuint,
|
||||||
|
@ -199,37 +601,12 @@ impl VertexAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Framebuffer {
|
pub struct GLFramebuffer {
|
||||||
pub gl_framebuffer: GLuint,
|
pub gl_framebuffer: GLuint,
|
||||||
pub texture: Texture,
|
pub texture: GLTexture,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Framebuffer {
|
impl Drop for GLFramebuffer {
|
||||||
pub fn new(texture: Texture) -> Framebuffer {
|
|
||||||
let mut gl_framebuffer = 0;
|
|
||||||
unsafe {
|
|
||||||
gl::GenFramebuffers(1, &mut gl_framebuffer);
|
|
||||||
assert_eq!(gl::GetError(), gl::NO_ERROR);
|
|
||||||
gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer);
|
|
||||||
texture.bind(0);
|
|
||||||
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
|
|
||||||
gl::COLOR_ATTACHMENT0,
|
|
||||||
gl::TEXTURE_2D,
|
|
||||||
texture.gl_texture,
|
|
||||||
0);
|
|
||||||
assert_eq!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER), gl::FRAMEBUFFER_COMPLETE);
|
|
||||||
}
|
|
||||||
Framebuffer { gl_framebuffer, texture }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bind(&self) {
|
|
||||||
unsafe {
|
|
||||||
gl::BindFramebuffer(gl::FRAMEBUFFER, self.gl_framebuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Framebuffer {
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::DeleteFramebuffers(1, &mut self.gl_framebuffer)
|
gl::DeleteFramebuffers(1, &mut self.gl_framebuffer)
|
||||||
|
@ -237,39 +614,11 @@ impl Drop for Framebuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Buffer {
|
pub struct GLBuffer {
|
||||||
pub gl_buffer: GLuint,
|
pub gl_buffer: GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Buffer {
|
impl Drop for GLBuffer {
|
||||||
pub fn new() -> Buffer {
|
|
||||||
unsafe {
|
|
||||||
let mut gl_buffer = 0;
|
|
||||||
gl::GenBuffers(1, &mut gl_buffer);
|
|
||||||
Buffer { gl_buffer }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn upload<T>(&self, data: &[T], target: BufferTarget, mode: BufferUploadMode) {
|
|
||||||
let target = match target {
|
|
||||||
BufferTarget::Vertex => gl::ARRAY_BUFFER,
|
|
||||||
BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER,
|
|
||||||
};
|
|
||||||
let mode = match mode {
|
|
||||||
BufferUploadMode::Static => gl::STATIC_DRAW,
|
|
||||||
BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
gl::BindBuffer(target, self.gl_buffer);
|
|
||||||
gl::BufferData(target,
|
|
||||||
(data.len() * mem::size_of::<T>()) as GLsizeiptr,
|
|
||||||
data.as_ptr() as *const GLvoid,
|
|
||||||
mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Buffer {
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::DeleteBuffers(1, &mut self.gl_buffer)
|
gl::DeleteBuffers(1, &mut self.gl_buffer)
|
||||||
|
@ -277,40 +626,20 @@ impl Drop for Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BufferTarget {
|
|
||||||
Vertex,
|
|
||||||
Index,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum BufferUploadMode {
|
|
||||||
Static,
|
|
||||||
Dynamic,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Uniform {
|
pub struct GLUniform {
|
||||||
pub location: GLint,
|
pub location: GLint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Uniform {
|
pub struct GLProgram {
|
||||||
pub fn new(program: &Program, name: &str) -> Uniform {
|
|
||||||
let name = CString::new(format!("u{}", name)).unwrap();
|
|
||||||
let location = unsafe {
|
|
||||||
gl::GetUniformLocation(program.gl_program, name.as_ptr() as *const GLchar)
|
|
||||||
};
|
|
||||||
Uniform { location }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Program {
|
|
||||||
pub gl_program: GLuint,
|
pub gl_program: GLuint,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
vertex_shader: Shader,
|
vertex_shader: GLShader,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fragment_shader: Shader,
|
fragment_shader: GLShader,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Program {
|
impl Drop for GLProgram {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::DeleteProgram(self.gl_program)
|
gl::DeleteProgram(self.gl_program)
|
||||||
|
@ -318,11 +647,11 @@ impl Drop for Program {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Shader {
|
pub struct GLShader {
|
||||||
gl_shader: GLuint,
|
gl_shader: GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Shader {
|
impl Drop for GLShader {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::DeleteShader(self.gl_shader)
|
gl::DeleteShader(self.gl_shader)
|
||||||
|
@ -330,99 +659,16 @@ impl Drop for Shader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
pub struct GLTexture {
|
||||||
enum ShaderKind {
|
|
||||||
Vertex,
|
|
||||||
Fragment,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Texture {
|
|
||||||
gl_texture: GLuint,
|
gl_texture: GLuint,
|
||||||
pub size: Point2DI32,
|
pub size: Point2DI32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texture {
|
pub struct GLTimerQuery {
|
||||||
pub fn new_r16f(size: Point2DI32) -> Texture {
|
|
||||||
let mut texture = Texture { gl_texture: 0, size };
|
|
||||||
unsafe {
|
|
||||||
gl::GenTextures(1, &mut texture.gl_texture);
|
|
||||||
texture.bind(0);
|
|
||||||
gl::TexImage2D(gl::TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
gl::R16F as GLint,
|
|
||||||
size.x() as GLsizei,
|
|
||||||
size.y() as GLsizei,
|
|
||||||
0,
|
|
||||||
gl::RED,
|
|
||||||
gl::HALF_FLOAT,
|
|
||||||
ptr::null());
|
|
||||||
}
|
|
||||||
|
|
||||||
texture.set_parameters();
|
|
||||||
texture
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_rgba(size: Point2DI32) -> Texture {
|
|
||||||
let mut texture = Texture { gl_texture: 0, size };
|
|
||||||
unsafe {
|
|
||||||
gl::GenTextures(1, &mut texture.gl_texture);
|
|
||||||
texture.bind(0);
|
|
||||||
gl::TexImage2D(gl::TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
gl::RGBA as GLint,
|
|
||||||
size.x() as GLsizei,
|
|
||||||
size.y() as GLsizei,
|
|
||||||
0,
|
|
||||||
gl::RGBA,
|
|
||||||
gl::UNSIGNED_BYTE,
|
|
||||||
ptr::null());
|
|
||||||
}
|
|
||||||
|
|
||||||
texture.set_parameters();
|
|
||||||
texture
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bind(&self, unit: u32) {
|
|
||||||
unsafe {
|
|
||||||
gl::ActiveTexture(gl::TEXTURE0 + unit);
|
|
||||||
gl::BindTexture(gl::TEXTURE_2D, self.gl_texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn upload_rgba(&self, size: Point2DI32, data: &[u8]) {
|
|
||||||
assert!(data.len() >= size.x() as usize * size.y() as usize * 4);
|
|
||||||
unsafe {
|
|
||||||
self.bind(0);
|
|
||||||
gl::TexImage2D(gl::TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
gl::RGBA as GLint,
|
|
||||||
size.x() as GLsizei,
|
|
||||||
size.y() as GLsizei,
|
|
||||||
0,
|
|
||||||
gl::RGBA,
|
|
||||||
gl::UNSIGNED_BYTE,
|
|
||||||
data.as_ptr() as *const GLvoid);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.set_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_parameters(&self) {
|
|
||||||
self.bind(0);
|
|
||||||
unsafe {
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TimerQuery {
|
|
||||||
gl_query: GLuint,
|
gl_query: GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for TimerQuery {
|
impl Drop for GLTimerQuery {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -431,70 +677,71 @@ impl Drop for TimerQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimerQuery {
|
trait BufferTargetExt {
|
||||||
#[inline]
|
fn to_gl_target(self) -> GLuint;
|
||||||
pub fn new() -> TimerQuery {
|
}
|
||||||
let mut query = TimerQuery { gl_query: 0 };
|
|
||||||
unsafe {
|
|
||||||
gl::GenQueries(1, &mut query.gl_query);
|
|
||||||
}
|
|
||||||
query
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
impl BufferTargetExt for BufferTarget {
|
||||||
pub fn begin(&self) {
|
fn to_gl_target(self) -> GLuint {
|
||||||
unsafe {
|
match self {
|
||||||
gl::BeginQuery(gl::TIME_ELAPSED, self.gl_query);
|
BufferTarget::Vertex => gl::ARRAY_BUFFER,
|
||||||
}
|
BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER,
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn end(&self) {
|
|
||||||
unsafe {
|
|
||||||
gl::EndQuery(gl::TIME_ELAPSED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_available(&self) -> bool {
|
|
||||||
unsafe {
|
|
||||||
let mut result = 0;
|
|
||||||
gl::GetQueryObjectiv(self.gl_query, gl::QUERY_RESULT_AVAILABLE, &mut result);
|
|
||||||
result != gl::FALSE as GLint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get(&self) -> u64 {
|
|
||||||
unsafe {
|
|
||||||
let mut result = 0;
|
|
||||||
gl::GetQueryObjectui64v(self.gl_query, gl::QUERY_RESULT, &mut result);
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(pcwalton): Do something better!
|
trait DepthFuncExt {
|
||||||
fn locate_resources_directory() -> PathBuf {
|
fn to_gl_depth_func(self) -> GLenum;
|
||||||
let mut parent_directory = env::current_dir().unwrap();
|
}
|
||||||
loop {
|
|
||||||
// So ugly :(
|
|
||||||
let mut resources_directory = parent_directory.clone();
|
|
||||||
resources_directory.push("resources");
|
|
||||||
if resources_directory.is_dir() {
|
|
||||||
let mut shaders_directory = resources_directory.clone();
|
|
||||||
let mut textures_directory = resources_directory.clone();
|
|
||||||
shaders_directory.push("shaders");
|
|
||||||
textures_directory.push("textures");
|
|
||||||
if shaders_directory.is_dir() && textures_directory.is_dir() {
|
|
||||||
return resources_directory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !parent_directory.pop() {
|
impl DepthFuncExt for DepthFunc {
|
||||||
break;
|
fn to_gl_depth_func(self) -> GLenum {
|
||||||
|
match self {
|
||||||
|
DepthFunc::Less => gl::LESS,
|
||||||
|
DepthFunc::Always => gl::ALWAYS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait PrimitiveExt {
|
||||||
|
fn to_gl_primitive(self) -> GLuint;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimitiveExt for Primitive {
|
||||||
|
fn to_gl_primitive(self) -> GLuint {
|
||||||
|
match self {
|
||||||
|
Primitive::Triangles => gl::TRIANGLES,
|
||||||
|
Primitive::TriangleFan => gl::TRIANGLE_FAN,
|
||||||
|
Primitive::Lines => gl::LINES,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait StencilFuncExt {
|
||||||
|
fn to_gl_stencil_func(self) -> GLenum;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StencilFuncExt for StencilFunc {
|
||||||
|
fn to_gl_stencil_func(self) -> GLenum {
|
||||||
|
match self {
|
||||||
|
StencilFunc::Always => gl::ALWAYS,
|
||||||
|
StencilFunc::Equal => gl::EQUAL,
|
||||||
|
StencilFunc::NotEqual => gl::NOTEQUAL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait VertexAttrTypeExt {
|
||||||
|
fn to_gl_type(self) -> GLuint;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VertexAttrTypeExt for VertexAttrType {
|
||||||
|
fn to_gl_type(self) -> GLuint {
|
||||||
|
match self {
|
||||||
|
VertexAttrType::F32 => gl::FLOAT,
|
||||||
|
VertexAttrType::I16 => gl::SHORT,
|
||||||
|
VertexAttrType::U16 => gl::UNSIGNED_SHORT,
|
||||||
|
VertexAttrType::U8 => gl::UNSIGNED_BYTE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
panic!("No suitable `resources/` directory found!");
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "pathfinder_gpu"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[dependencies.image]
|
||||||
|
version = "0.21"
|
||||||
|
default-features = false
|
||||||
|
features = ["png_codec"]
|
||||||
|
|
||||||
|
[dependencies.pathfinder_geometry]
|
||||||
|
path = "../geometry"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_simd]
|
||||||
|
path = "../simd"
|
|
@ -0,0 +1,266 @@
|
||||||
|
// pathfinder/gpu/src/lib.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Minimal abstractions over GPU device capabilities.
|
||||||
|
|
||||||
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
|
use pathfinder_simd::default::F32x4;
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub trait Device {
|
||||||
|
type Buffer;
|
||||||
|
type Framebuffer;
|
||||||
|
type Program;
|
||||||
|
type Shader;
|
||||||
|
type Texture;
|
||||||
|
type TimerQuery;
|
||||||
|
type Uniform;
|
||||||
|
type VertexArray;
|
||||||
|
type VertexAttr;
|
||||||
|
|
||||||
|
fn create_texture(&self, format: TextureFormat, size: Point2DI32) -> Self::Texture;
|
||||||
|
fn create_texture_from_data(&self, size: Point2DI32, data: &[u8]) -> Self::Texture;
|
||||||
|
fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind)
|
||||||
|
-> Self::Shader;
|
||||||
|
fn create_vertex_array(&self) -> Self::VertexArray;
|
||||||
|
fn create_program_from_shaders(&self,
|
||||||
|
name: &str,
|
||||||
|
vertex_shader: Self::Shader,
|
||||||
|
fragment_shader: Self::Shader)
|
||||||
|
-> Self::Program;
|
||||||
|
fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Self::VertexAttr;
|
||||||
|
fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform;
|
||||||
|
fn use_program(&self, program: &Self::Program);
|
||||||
|
fn configure_float_vertex_attr(&self,
|
||||||
|
attr: &Self::VertexAttr,
|
||||||
|
size: usize,
|
||||||
|
attr_type: VertexAttrType,
|
||||||
|
normalized: bool,
|
||||||
|
stride: usize,
|
||||||
|
offset: usize,
|
||||||
|
divisor: u32);
|
||||||
|
fn configure_int_vertex_attr(&self,
|
||||||
|
attr: &Self::VertexAttr,
|
||||||
|
size: usize,
|
||||||
|
attr_type: VertexAttrType,
|
||||||
|
stride: usize,
|
||||||
|
offset: usize,
|
||||||
|
divisor: u32);
|
||||||
|
fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData);
|
||||||
|
fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer;
|
||||||
|
fn create_buffer(&self) -> Self::Buffer;
|
||||||
|
fn upload_to_buffer<T>(&self,
|
||||||
|
buffer: &Self::Buffer,
|
||||||
|
data: &[T],
|
||||||
|
target: BufferTarget,
|
||||||
|
mode: BufferUploadMode);
|
||||||
|
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
|
||||||
|
fn texture_size(&self, texture: &Self::Texture) -> Point2DI32;
|
||||||
|
fn upload_to_texture(&self, texture: &Self::Texture, size: Point2DI32, data: &[u8]);
|
||||||
|
fn read_pixels_from_default_framebuffer(&self, size: Point2DI32) -> Vec<u8>;
|
||||||
|
// TODO(pcwalton): Switch to `ColorF`!
|
||||||
|
fn clear(&self, color: Option<F32x4>, depth: Option<f32>, stencil: Option<u8>);
|
||||||
|
fn draw_arrays(&self, primitive: Primitive, index_count: u32, render_state: &RenderState);
|
||||||
|
fn draw_elements(&self, primitive: Primitive, index_count: u32, render_state: &RenderState);
|
||||||
|
fn draw_arrays_instanced(&self,
|
||||||
|
primitive: Primitive,
|
||||||
|
index_count: u32,
|
||||||
|
instance_count: u32,
|
||||||
|
render_state: &RenderState);
|
||||||
|
fn create_timer_query(&self) -> Self::TimerQuery;
|
||||||
|
fn begin_timer_query(&self, query: &Self::TimerQuery);
|
||||||
|
fn end_timer_query(&self, query: &Self::TimerQuery);
|
||||||
|
fn timer_query_is_available(&self, query: &Self::TimerQuery) -> bool;
|
||||||
|
fn get_timer_query(&self, query: &Self::TimerQuery) -> Duration;
|
||||||
|
|
||||||
|
// TODO(pcwalton): Go bindless...
|
||||||
|
fn bind_vertex_array(&self, vertex_array: &Self::VertexArray);
|
||||||
|
fn bind_buffer(&self, buffer: &Self::Buffer, target: BufferTarget);
|
||||||
|
fn bind_default_framebuffer(&self, size: Point2DI32);
|
||||||
|
fn bind_framebuffer(&self, framebuffer: &Self::Framebuffer);
|
||||||
|
fn bind_texture(&self, texture: &Self::Texture, unit: u32);
|
||||||
|
|
||||||
|
fn create_texture_from_png(&self, resources: &Resources, name: &str) -> Self::Texture {
|
||||||
|
let mut path = resources.resources_directory.clone();
|
||||||
|
path.push("textures");
|
||||||
|
path.push(format!("{}.png", name));
|
||||||
|
|
||||||
|
let image = image::open(&path).unwrap().to_luma();
|
||||||
|
let size = Point2DI32::new(image.width() as i32, image.height() as i32);
|
||||||
|
self.create_texture_from_data(size, &image)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_shader(&self, resources: &Resources, name: &str, kind: ShaderKind) -> Self::Shader {
|
||||||
|
let suffix = match kind { ShaderKind::Vertex => 'v', ShaderKind::Fragment => 'f' };
|
||||||
|
let mut path = resources.resources_directory.clone();
|
||||||
|
path.push("shaders");
|
||||||
|
path.push(format!("{}.{}s.glsl", name, suffix));
|
||||||
|
|
||||||
|
let mut source = vec![];
|
||||||
|
File::open(&path).unwrap().read_to_end(&mut source).unwrap();
|
||||||
|
self.create_shader_from_source(name, &source, kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_program(&self, resources: &Resources, name: &str) -> Self::Program {
|
||||||
|
let vertex_shader = self.create_shader(resources, name, ShaderKind::Vertex);
|
||||||
|
let fragment_shader = self.create_shader(resources, name, ShaderKind::Fragment);
|
||||||
|
self.create_program_from_shaders(name, vertex_shader, fragment_shader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum TextureFormat {
|
||||||
|
R16F,
|
||||||
|
RGBA8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum VertexAttrType {
|
||||||
|
F32,
|
||||||
|
I16,
|
||||||
|
U16,
|
||||||
|
U8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum BufferTarget {
|
||||||
|
Vertex,
|
||||||
|
Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum BufferUploadMode {
|
||||||
|
Static,
|
||||||
|
Dynamic,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum ShaderKind {
|
||||||
|
Vertex,
|
||||||
|
Fragment,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum UniformData {
|
||||||
|
Vec2(F32x4),
|
||||||
|
Vec4(F32x4),
|
||||||
|
TextureUnit(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Primitive {
|
||||||
|
Triangles,
|
||||||
|
TriangleFan,
|
||||||
|
Lines,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
|
pub struct RenderState {
|
||||||
|
pub blend: BlendState,
|
||||||
|
pub depth: Option<DepthState>,
|
||||||
|
pub stencil: Option<StencilState>,
|
||||||
|
pub color_mask: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum BlendState {
|
||||||
|
Off,
|
||||||
|
RGBOneAlphaOneMinusSrcAlpha,
|
||||||
|
RGBOneAlphaOne,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
|
pub struct DepthState {
|
||||||
|
pub func: DepthFunc,
|
||||||
|
pub write: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum DepthFunc {
|
||||||
|
Less,
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct StencilState {
|
||||||
|
pub func: StencilFunc,
|
||||||
|
pub reference: u32,
|
||||||
|
pub mask: u32,
|
||||||
|
pub pass_replace: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum StencilFunc {
|
||||||
|
Always,
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BlendState {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> BlendState {
|
||||||
|
BlendState::Off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StencilState {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> StencilState {
|
||||||
|
StencilState { func: StencilFunc::default(), reference: 0, mask: !0, pass_replace: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DepthFunc {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> DepthFunc {
|
||||||
|
DepthFunc::Less
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StencilFunc {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> StencilFunc {
|
||||||
|
StencilFunc::Always
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Resources {
|
||||||
|
pub resources_directory: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resources {
|
||||||
|
pub fn locate() -> Resources {
|
||||||
|
let mut parent_directory = env::current_dir().unwrap();
|
||||||
|
loop {
|
||||||
|
// So ugly :(
|
||||||
|
let mut resources_directory = parent_directory.clone();
|
||||||
|
resources_directory.push("resources");
|
||||||
|
if resources_directory.is_dir() {
|
||||||
|
let mut shaders_directory = resources_directory.clone();
|
||||||
|
let mut textures_directory = resources_directory.clone();
|
||||||
|
shaders_directory.push("shaders");
|
||||||
|
textures_directory.push("textures");
|
||||||
|
if shaders_directory.is_dir() && textures_directory.is_dir() {
|
||||||
|
return Resources { resources_directory };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !parent_directory.pop() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("No suitable `resources/` directory found!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,9 +8,16 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use crate::default::{F32x4, U32x4};
|
use crate::default::F32x4;
|
||||||
|
|
||||||
impl F32x4 {
|
impl F32x4 {
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_slice(slice: &[f32]) -> F32x4 {
|
||||||
|
F32x4::new(slice[0], slice[1], slice[2], slice[3])
|
||||||
|
}
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -131,11 +131,6 @@ impl F32x4 {
|
||||||
F32x4([self[2], other[2], self[3], other[3]]))
|
F32x4([self[2], other[2], self[3], other[3]]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn transpose4(a: &mut F32x4, b: &mut F32x4, c: &mut F32x4, d: &mut F32x4) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cross(&self, other: F32x4) -> F32x4 {
|
pub fn cross(&self, other: F32x4) -> F32x4 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|
|
@ -1427,11 +1427,6 @@ impl F32x4 {
|
||||||
unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, other.0, 0b0001_1011)) }
|
unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, other.0, 0b0001_1011)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn transpose_4x4(a: &mut F32x4, b: &mut F32x4, c: &mut F32x4, d: &mut F32x4) {
|
|
||||||
unsafe { x86_64::_MM_TRANSPOSE4_PS(&mut a.0, &mut b.0, &mut c.0, &mut d.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(pcwalton): Move to `Point3DF32`!
|
// FIXME(pcwalton): Move to `Point3DF32`!
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cross(&self, other: F32x4) -> F32x4 {
|
pub fn cross(&self, other: F32x4) -> F32x4 {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[package]
|
||||||
|
name = "pathfinder_ui"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
edition = "2018"
|
|
@ -0,0 +1 @@
|
||||||
|
|
Loading…
Reference in New Issue