WIP: Factor out GL code

This commit is contained in:
Patrick Walton 2019-03-04 14:55:32 -08:00
parent 64b480fe32
commit ae450b063e
18 changed files with 1765 additions and 1095 deletions

20
Cargo.lock generated
View File

@ -511,6 +511,7 @@ dependencies = [
"nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_geometry 0.3.0",
"pathfinder_gl 0.1.0",
"pathfinder_gpu 0.1.0",
"pathfinder_renderer 0.1.0",
"pathfinder_svg 0.1.0",
"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)",
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_geometry 0.3.0",
"pathfinder_gpu 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_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)",
]
[[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]]
name = "pathfinder_renderer"
version = "0.1.0"
@ -570,6 +582,10 @@ dependencies = [
"usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pathfinder_ui"
version = "0.1.0"
[[package]]
name = "phf"
version = "0.7.24"
@ -1045,10 +1061,6 @@ name = "ucd-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ui"
version = "0.1.0"
[[package]]
name = "unicode-segmentation"
version = "1.2.1"

View File

@ -4,6 +4,7 @@ members = [
"demo/native",
"geometry",
"gl",
"gpu",
"renderer",
"simd",
"svg",

View File

@ -24,6 +24,9 @@ path = "../../geometry"
[dependencies.pathfinder_gl]
path = "../../gl"
[dependencies.pathfinder_gpu]
path = "../../gpu"
[dependencies.pathfinder_renderer]
path = "../../renderer"

View File

@ -11,11 +11,9 @@
//! GPU rendering code specifically for the demo.
use crate::GRIDLINE_COUNT;
use gl::types::{GLsizei, GLvoid};
use pathfinder_gl::device::{Buffer, BufferTarget, BufferUploadMode, Device, Program, Uniform};
use pathfinder_gl::device::{VertexArray, VertexAttr};
use pathfinder_renderer::paint::ColorU;
use pathfinder_gpu::{BufferTarget, BufferUploadMode, Device, Resources, VertexAttrType};
/*
pub struct DemoDevice {
#[allow(dead_code)]
device: Device,
@ -62,65 +60,67 @@ impl DemoDevice {
pixels
}
}
*/
pub struct GroundProgram {
pub program: Program,
pub transform_uniform: Uniform,
pub color_uniform: Uniform,
pub struct GroundProgram<D> where D: Device {
pub program: D::Program,
pub transform_uniform: D::Uniform,
pub color_uniform: D::Uniform,
}
impl GroundProgram {
pub fn new(device: &Device) -> GroundProgram {
let program = device.create_program("demo_ground");
let transform_uniform = Uniform::new(&program, "Transform");
let color_uniform = Uniform::new(&program, "Color");
impl<D> GroundProgram<D> where D: Device {
pub fn new(device: &D, resources: &Resources) -> GroundProgram<D> {
let program = device.create_program(resources, "demo_ground");
let transform_uniform = device.get_uniform(&program, "Transform");
let color_uniform = device.get_uniform(&program, "Color");
GroundProgram { program, transform_uniform, color_uniform }
}
}
pub struct GroundSolidVertexArray {
pub vertex_array: VertexArray,
pub struct GroundSolidVertexArray<D> where D: Device {
pub vertex_array: D::VertexArray,
}
impl GroundSolidVertexArray {
pub fn new(ground_program: &GroundProgram, quad_vertex_positions_buffer: &Buffer)
-> GroundSolidVertexArray {
let vertex_array = VertexArray::new();
unsafe {
let position_attr = VertexAttr::new(&ground_program.program, "Position");
impl<D> GroundSolidVertexArray<D> where D: Device {
pub fn new(device: &D,
ground_program: &GroundProgram<D>,
quad_vertex_positions_buffer: &D::Buffer)
-> GroundSolidVertexArray<D> {
let vertex_array = device.create_vertex_array();
gl::BindVertexArray(vertex_array.gl_vertex_array);
gl::UseProgram(ground_program.program.gl_program);
gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer);
position_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0);
}
let position_attr = device.get_vertex_attr(&ground_program.program, "Position");
device.bind_vertex_array(&vertex_array);
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 }
}
}
pub struct GroundLineVertexArray {
pub vertex_array: VertexArray,
pub struct GroundLineVertexArray<D> where D: Device {
pub vertex_array: D::VertexArray,
#[allow(dead_code)]
grid_vertex_positions_buffer: Buffer,
grid_vertex_positions_buffer: D::Buffer,
}
impl GroundLineVertexArray {
pub fn new(ground_program: &GroundProgram) -> GroundLineVertexArray {
let grid_vertex_positions_buffer = Buffer::new();
grid_vertex_positions_buffer.upload(&create_grid_vertex_positions(),
impl<D> GroundLineVertexArray<D> where D: Device {
pub fn new(device: &D, ground_program: &GroundProgram<D>) -> GroundLineVertexArray<D> {
let grid_vertex_positions_buffer = device.create_buffer();
device.upload_to_buffer(&grid_vertex_positions_buffer,
&create_grid_vertex_positions(),
BufferTarget::Vertex,
BufferUploadMode::Static);
let vertex_array = VertexArray::new();
unsafe {
let position_attr = VertexAttr::new(&ground_program.program, "Position");
let vertex_array = device.create_vertex_array();
gl::BindVertexArray(vertex_array.gl_vertex_array);
gl::UseProgram(ground_program.program.gl_program);
gl::BindBuffer(gl::ARRAY_BUFFER, grid_vertex_positions_buffer.gl_buffer);
position_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0);
}
let position_attr = device.get_vertex_attr(&ground_program.program, "Position");
device.bind_vertex_array(&vertex_array);
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 }
}

View File

@ -10,7 +10,7 @@
//! 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 clap::{App, Arg};
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::transform2d::Transform2DF32;
use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32};
use pathfinder_gl::device::Device;
use pathfinder_gl::device::GLDevice;
use pathfinder_gl::renderer::Renderer;
use pathfinder_gpu::{Device, Resources};
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
use pathfinder_renderer::gpu_data::BuiltScene;
use pathfinder_renderer::paint::ColorU;
@ -96,14 +97,13 @@ pub struct DemoApp {
mouselook_enabled: bool,
dirty: bool,
ui: DemoUI,
ui: DemoUI<GLDevice>,
scene_thread_proxy: SceneThreadProxy,
renderer: Renderer,
renderer: Renderer<GLDevice>,
device: DemoDevice,
ground_program: GroundProgram,
ground_solid_vertex_array: GroundSolidVertexArray,
ground_line_vertex_array: GroundLineVertexArray,
ground_program: GroundProgram<GLDevice>,
ground_solid_vertex_array: GroundSolidVertexArray<GLDevice>,
ground_line_vertex_array: GroundLineVertexArray<GLDevice>,
}
impl DemoApp {
@ -130,8 +130,9 @@ impl DemoApp {
let sdl_event_pump = sdl_context.event_pump().unwrap();
let device = Device::new();
let options = Options::get(&device);
let device = GLDevice::new();
let resources = Resources::locate();
let options = Options::get(&resources);
let (window_width, _) = window.size();
let (drawable_width, drawable_height) = window.drawable_size();
@ -139,7 +140,7 @@ impl DemoApp {
let base_scene = load_scene(&options.input_path);
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());
update_drawable_size(&window, &scene_thread_proxy);
@ -149,10 +150,15 @@ impl DemoApp {
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 =
GroundSolidVertexArray::new(&ground_program, &renderer.quad_vertex_positions_buffer());
let ground_line_vertex_array = GroundLineVertexArray::new(&ground_program);
GroundSolidVertexArray::new(&renderer.device,
&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 {
window,
@ -173,11 +179,10 @@ impl DemoApp {
mouselook_enabled: false,
dirty: true,
ui: DemoUI::new(&device, options),
ui,
scene_thread_proxy,
renderer,
device: DemoDevice::new(device),
ground_program,
ground_solid_vertex_array,
ground_line_vertex_array,
@ -342,7 +347,7 @@ impl DemoApp {
tile_time,
} = 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.render_vector_scene(&built_scene);
@ -353,14 +358,17 @@ impl DemoApp {
let rendering_time = self.renderer.shift_timer_query();
let stats = built_scene.stats();
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() {
self.dirty = true;
}
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);
// Switch camera mode (2D/3D) if requested.
@ -548,7 +556,8 @@ impl DemoApp {
fn take_screenshot(&mut self) {
let screenshot_path = self.pending_screenshot_path.take().unwrap();
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,
&pixels,
drawable_width,
@ -644,7 +653,7 @@ pub struct Options {
}
impl Options {
fn get(device: &Device) -> Options {
fn get(resources: &Resources) -> Options {
let matches = App::new("tile-svg")
.arg(
Arg::with_name("jobs")
@ -666,7 +675,7 @@ impl Options {
let input_path = match matches.value_of("INPUT") {
Some(path) => PathBuf::from(path),
None => {
let mut path = device.resources_directory.clone();
let mut path = resources.resources_directory.clone();
path.push("svg");
path.push(DEFAULT_SVG_FILENAME);
path

View File

@ -13,7 +13,7 @@ use nfd::Response;
use pathfinder_geometry::basic::point::Point2DI32;
use pathfinder_geometry::basic::rect::RectI32;
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 std::f32::consts::PI;
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 SCREENSHOT_PNG_NAME: &'static str = "demo-screenshot";
pub struct DemoUI {
effects_texture: Texture,
open_texture: Texture,
rotate_texture: Texture,
zoom_in_texture: Texture,
zoom_out_texture: Texture,
bg_light_texture: Texture,
bg_dark_texture: Texture,
screenshot_texture: Texture,
pub struct DemoUI<D> where D: Device {
effects_texture: D::Texture,
open_texture: D::Texture,
rotate_texture: D::Texture,
zoom_in_texture: D::Texture,
zoom_out_texture: D::Texture,
bg_light_texture: D::Texture,
bg_dark_texture: D::Texture,
screenshot_texture: D::Texture,
effects_panel_visible: bool,
rotate_panel_visible: bool,
@ -78,16 +78,16 @@ pub struct DemoUI {
pub rotation: i32,
}
impl DemoUI {
pub fn new(device: &Device, options: Options) -> DemoUI {
let effects_texture = device.create_texture_from_png(EFFECTS_PNG_NAME);
let open_texture = device.create_texture_from_png(OPEN_PNG_NAME);
let rotate_texture = device.create_texture_from_png(ROTATE_PNG_NAME);
let zoom_in_texture = device.create_texture_from_png(ZOOM_IN_PNG_NAME);
let zoom_out_texture = device.create_texture_from_png(ZOOM_OUT_PNG_NAME);
let bg_light_texture = device.create_texture_from_png(BG_LIGHT_PNG_NAME);
let bg_dark_texture = device.create_texture_from_png(BG_DARK_PNG_NAME);
let screenshot_texture = device.create_texture_from_png(SCREENSHOT_PNG_NAME);
impl<D> DemoUI<D> where D: Device {
pub fn new(device: &D, resources: &Resources, options: Options) -> DemoUI<D> {
let effects_texture = device.create_texture_from_png(resources, EFFECTS_PNG_NAME);
let open_texture = device.create_texture_from_png(resources, OPEN_PNG_NAME);
let rotate_texture = device.create_texture_from_png(resources, ROTATE_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(resources, ZOOM_OUT_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(resources, BG_DARK_PNG_NAME);
let screenshot_texture = device.create_texture_from_png(resources, SCREENSHOT_PNG_NAME);
DemoUI {
effects_texture,
@ -115,19 +115,27 @@ impl DemoUI {
(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;
// Draw effects button.
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;
}
// Draw open button.
let lower_button_y = bottom - BUTTON_HEIGHT;
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) {
*action = UIAction::OpenFile(PathBuf::from(file));
}
@ -135,7 +143,8 @@ impl DemoUI {
// Draw screenshot button.
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,
screenshot_button_position,
&self.screenshot_texture) {
@ -146,7 +155,8 @@ impl DemoUI {
// Draw 3D switch.
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,
three_d_switch_origin,
"2D",
@ -155,7 +165,8 @@ impl DemoUI {
// Draw background switch.
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,
background_switch_origin,
&self.bg_light_texture,
@ -166,19 +177,28 @@ impl DemoUI {
if !self.three_d_enabled {
let rotate_button_y = bottom - BUTTON_HEIGHT;
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;
}
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);
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;
}
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);
if self.draw_button(debug_ui,
if self.draw_button(device,
debug_ui,
event,
zoom_out_button_position,
&self.zoom_out_texture) {
@ -187,40 +207,44 @@ impl DemoUI {
}
// 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.
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 {
return;
}
let bottom = debug_ui.framebuffer_size().y() - PADDING;
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,
EFFECTS_PANEL_HEIGHT)),
WINDOW_COLOR);
self.gamma_correction_effect_enabled =
self.draw_effects_switch(debug_ui,
self.draw_effects_switch(device,
debug_ui,
event,
"Gamma Correction",
0,
effects_panel_y,
self.gamma_correction_effect_enabled);
self.stem_darkening_effect_enabled =
self.draw_effects_switch(debug_ui,
self.draw_effects_switch(device,
debug_ui,
event,
"Stem Darkening",
1,
effects_panel_y,
self.stem_darkening_effect_enabled);
self.subpixel_aa_effect_enabled =
self.draw_effects_switch(debug_ui,
self.draw_effects_switch(device,
debug_ui,
event,
"Subpixel AA",
2,
@ -230,7 +254,8 @@ impl DemoUI {
}
fn draw_rotate_panel(&mut self,
debug_ui: &mut DebugUI,
device: &D,
debug_ui: &mut DebugUI<D>,
event: &mut UIEvent,
action: &mut UIAction) {
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_origin = Point2DI32::new(ROTATE_PANEL_X, rotate_panel_y);
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);
let (widget_x, widget_y) = (ROTATE_PANEL_X + PADDING, rotate_panel_y + PADDING);
@ -257,32 +283,35 @@ impl DemoUI {
let slider_track_rect =
RectI32::new(Point2DI32::new(widget_x, slider_track_y),
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_rect =
RectI32::new(Point2DI32::new(slider_knob_x, widget_y),
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,
debug_ui: &mut DebugUI,
device: &D,
debug_ui: &mut DebugUI<D>,
event: &mut UIEvent,
origin: Point2DI32,
texture: &Texture)
texture: &D::Texture)
-> bool {
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_rounded_rect_outline(button_rect, OUTLINE_COLOR);
debug_ui.draw_texture(origin + Point2DI32::new(PADDING, PADDING),
debug_ui.draw_solid_rounded_rect(device, button_rect, WINDOW_COLOR);
debug_ui.draw_rounded_rect_outline(device, button_rect, OUTLINE_COLOR);
debug_ui.draw_texture(device,
origin + Point2DI32::new(PADDING, PADDING),
texture,
BUTTON_ICON_COLOR);
event.handle_mouse_down_in_rect(button_rect).is_some()
}
fn draw_effects_switch(&self,
debug_ui: &mut DebugUI,
device: &D,
debug_ui: &mut DebugUI<D>,
event: &mut UIEvent,
text: &str,
index: i32,
@ -291,11 +320,12 @@ impl DemoUI {
-> bool {
let text_x = PADDING * 2;
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_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
self.draw_text_switch(debug_ui,
self.draw_text_switch(device,
debug_ui,
event,
Point2DI32::new(switch_x, switch_y),
"Off",
@ -304,14 +334,15 @@ impl DemoUI {
}
fn draw_text_switch(&self,
debug_ui: &mut DebugUI,
device: &D,
debug_ui: &mut DebugUI<D>,
event: &mut UIEvent,
origin: Point2DI32,
off_text: &str,
on_text: &str,
mut value: 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 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 text_top = BUTTON_TEXT_OFFSET;
debug_ui.draw_text(off_text, origin + Point2DI32::new(off_offset, text_top), !value);
debug_ui.draw_text(on_text, origin + Point2DI32::new(on_offset, text_top), value);
debug_ui.draw_text(device,
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
}
fn draw_image_switch(&self,
debug_ui: &mut DebugUI,
device: &D,
debug_ui: &mut DebugUI<D>,
event: &mut UIEvent,
origin: Point2DI32,
off_texture: &Texture,
on_texture: &Texture,
off_texture: &D::Texture,
on_texture: &D::Texture,
mut value: 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 on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 - on_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 -
device.texture_size(on_texture).x() / 2;
let off_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_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_color);
@ -352,7 +390,8 @@ impl DemoUI {
}
fn draw_switch(&self,
debug_ui: &mut DebugUI,
device: &D,
debug_ui: &mut DebugUI<D>,
event: &mut UIEvent,
origin: Point2DI32,
mut value: bool)
@ -362,15 +401,18 @@ impl DemoUI {
value = !value;
}
debug_ui.draw_solid_rounded_rect(widget_rect, WINDOW_COLOR);
debug_ui.draw_rounded_rect_outline(widget_rect, OUTLINE_COLOR);
debug_ui.draw_solid_rounded_rect(device, widget_rect, WINDOW_COLOR);
debug_ui.draw_rounded_rect_outline(device, widget_rect, OUTLINE_COLOR);
let highlight_size = Point2DI32::new(SWITCH_HALF_SIZE, BUTTON_HEIGHT);
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 {
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),
TEXT_COLOR);
}

View File

@ -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
// two.
//
@ -384,19 +377,6 @@ mod test {
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]
fn test_inverse() {
// Random matrix.

View File

@ -18,5 +18,11 @@ features = ["png_codec"]
[dependencies.pathfinder_geometry]
path = "../geometry"
[dependencies.pathfinder_gpu]
path = "../gpu"
[dependencies.pathfinder_renderer]
path = "../renderer"
[dependencies.pathfinder_simd]
path = "../simd"

View File

@ -15,26 +15,24 @@
//!
//! 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::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::paint::ColorU;
use pathfinder_simd::default::F32x4;
use serde_json;
use std::collections::{HashMap, VecDeque};
use std::fs::File;
use std::io::BufReader;
use std::ops::{Add, Div};
use std::ptr;
use std::time::Duration;
const SAMPLE_BUFFER_SIZE: usize = 60;
const DEBUG_TEXTURE_VERTEX_SIZE: GLint = 8;
const DEBUG_SOLID_VERTEX_SIZE: GLint = 4;
const DEBUG_TEXTURE_VERTEX_SIZE: usize = 8;
const DEBUG_SOLID_VERTEX_SIZE: usize = 4;
pub const PADDING: i32 = 12;
@ -84,43 +82,44 @@ struct DebugCharacter {
}
impl DebugFont {
fn load(device: &Device) -> DebugFont {
let mut path = device.resources_directory.clone();
fn load(resources: &Resources) -> DebugFont {
let mut path = resources.resources_directory.clone();
path.push(FONT_JSON_FILENAME);
serde_json::from_reader(BufReader::new(File::open(path).unwrap())).unwrap()
}
}
pub struct DebugUI {
pub struct DebugUI<D> where D: Device {
framebuffer_size: Point2DI32,
texture_program: DebugTextureProgram,
texture_vertex_array: DebugTextureVertexArray,
texture_program: DebugTextureProgram<D>,
texture_vertex_array: DebugTextureVertexArray<D>,
font: DebugFont,
solid_program: DebugSolidProgram,
solid_vertex_array: DebugSolidVertexArray,
solid_program: DebugSolidProgram<D>,
solid_vertex_array: DebugSolidVertexArray<D>,
font_texture: Texture,
corner_fill_texture: Texture,
corner_outline_texture: Texture,
font_texture: D::Texture,
corner_fill_texture: D::Texture,
corner_outline_texture: D::Texture,
cpu_samples: SampleBuffer<CPUSample>,
gpu_samples: SampleBuffer<GPUSample>,
}
impl DebugUI {
pub fn new(device: &Device, framebuffer_size: Point2DI32) -> DebugUI {
let texture_program = DebugTextureProgram::new(device);
let texture_vertex_array = DebugTextureVertexArray::new(&texture_program);
let font = DebugFont::load(device);
impl<D> DebugUI<D> where D: Device {
pub fn new(device: &D, resources: &Resources, framebuffer_size: Point2DI32) -> DebugUI<D> {
let texture_program = DebugTextureProgram::new(device, resources);
let texture_vertex_array = DebugTextureVertexArray::new(device, &texture_program);
let font = DebugFont::load(resources);
let solid_program = DebugSolidProgram::new(device);
let solid_vertex_array = DebugSolidVertexArray::new(&solid_program);
let solid_program = DebugSolidProgram::new(device, resources);
let solid_vertex_array = DebugSolidVertexArray::new(device, &solid_program);
let font_texture = device.create_texture_from_png(FONT_PNG_NAME);
let corner_fill_texture = device.create_texture_from_png(CORNER_FILL_PNG_NAME);
let corner_outline_texture = device.create_texture_from_png(CORNER_OUTLINE_PNG_NAME);
let font_texture = device.create_texture_from_png(resources, FONT_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(resources,
CORNER_OUTLINE_PNG_NAME);
DebugUI {
framebuffer_size,
@ -158,47 +157,55 @@ impl DebugUI {
}
}
pub fn draw(&self) {
pub fn draw(&self, device: &D) {
// Draw performance window.
let bottom = self.framebuffer_size.y() - PADDING;
let window_rect = RectI32::new(
Point2DI32::new(self.framebuffer_size.x() - PADDING - PERF_WINDOW_WIDTH,
bottom - 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 mean_cpu_sample = self.cpu_samples.mean();
self.draw_text(&format!("Objects: {}", mean_cpu_sample.stats.object_count), origin, false);
self.draw_text(&format!("Solid Tiles: {}", mean_cpu_sample.stats.solid_tile_count),
self.draw_text(device,
&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),
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),
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),
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),
false);
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),
false);
}
pub fn draw_solid_rect(&self, rect: RectI32, color: ColorU) {
self.draw_rect(rect, color, true);
pub fn draw_solid_rect(&self, device: &D, rect: RectI32, color: ColorU) {
self.draw_rect(device, rect, color, true);
}
pub fn draw_rect_outline(&self, rect: RectI32, color: ColorU) {
self.draw_rect(rect, color, false);
pub fn draw_rect_outline(&self, device: &D, rect: RectI32, color: ColorU) {
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 = [
DebugSolidVertex::new(rect.origin()),
DebugSolidVertex::new(rect.upper_right()),
@ -207,44 +214,50 @@ impl DebugUI {
];
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 {
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,
device: &D,
vertex_data: &[DebugSolidVertex],
index_data: &[u32],
color: ColorU,
filled: bool) {
unsafe {
gl::BindVertexArray(self.solid_vertex_array.gl_vertex_array);
device.bind_vertex_array(&self.solid_vertex_array.vertex_array);
device.upload_to_buffer(&self.solid_vertex_array.vertex_buffer,
vertex_data,
BufferTarget::Vertex,
BufferUploadMode::Dynamic);
device.upload_to_buffer(&self.solid_vertex_array.index_buffer,
index_data,
BufferTarget::Index,
BufferUploadMode::Dynamic);
device.use_program(&self.solid_program.program);
device.set_uniform(&self.solid_program.framebuffer_size_uniform,
UniformData::Vec2(self.framebuffer_size.0.to_f32x4()));
set_color_uniform(device, &self.solid_program.color_uniform, color);
let primitive = if filled { Primitive::Triangles } else { Primitive::Lines };
device.draw_elements(primitive, index_data.len() as u32, &RenderState {
blend: BlendState::RGBOneAlphaOneMinusSrcAlpha,
..RenderState::default()
});
}
self.solid_vertex_array
.vertex_buffer
.upload(vertex_data, BufferTarget::Vertex, BufferUploadMode::Dynamic);
self.solid_vertex_array
.index_buffer
.upload(index_data, BufferTarget::Index, BufferUploadMode::Dynamic);
unsafe {
gl::UseProgram(self.solid_program.program.gl_program);
gl::Uniform2f(self.solid_program.framebuffer_size_uniform.location,
self.framebuffer_size.x() as GLfloat,
self.framebuffer_size.y() as GLfloat);
set_color_uniform(&self.solid_program.color_uniform, color);
gl::BlendEquation(gl::FUNC_ADD);
gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
gl::Enable(gl::BLEND);
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 char_count = string.chars().count();
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 };
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) {
let position_rect = RectI32::new(origin, texture.size);
let tex_coord_rect = RectI32::new(Point2DI32::default(), texture.size);
pub fn draw_texture(&self,
device: &D,
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 = [
DebugTextureVertex::new(position_rect.origin(), tex_coord_rect.origin()),
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()),
];
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 {
@ -303,10 +324,10 @@ impl DebugUI {
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_rects = CornerRects::new(rect, corner_texture);
self.draw_rounded_rect_corners(color, corner_texture, &corner_rects);
let corner_rects = CornerRects::new(device, rect, corner_texture);
self.draw_rounded_rect_corners(device, color, corner_texture, &corner_rects);
let solid_rect_mid = RectI32::from_points(corner_rects.upper_left.upper_right(),
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 + 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_rects = CornerRects::new(rect, corner_texture);
self.draw_rounded_rect_corners(color, corner_texture, &corner_rects);
let corner_rects = CornerRects::new(device, rect, corner_texture);
self.draw_rounded_rect_corners(device, color, corner_texture, &corner_rects);
let vertex_data = vec![
DebugSolidVertex::new(corner_rects.upper_left.upper_right()),
@ -356,14 +381,15 @@ impl DebugUI {
];
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,
device: &D,
color: ColorU,
texture: &Texture,
texture: &D::Texture,
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 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 + 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 }
}
fn draw_texture_with_vertex_data(&self,
device: &D,
vertex_data: &[DebugTextureVertex],
index_data: &[u32],
texture: &Texture,
texture: &D::Texture,
color: ColorU) {
self.texture_vertex_array
.vertex_buffer
.upload(&vertex_data, BufferTarget::Vertex, BufferUploadMode::Dynamic);
self.texture_vertex_array
.index_buffer
.upload(&index_data, BufferTarget::Index, BufferUploadMode::Dynamic);
device.upload_to_buffer(&self.texture_vertex_array.vertex_buffer,
vertex_data,
BufferTarget::Vertex,
BufferUploadMode::Dynamic);
device.upload_to_buffer(&self.texture_vertex_array.index_buffer,
index_data,
BufferTarget::Index,
BufferUploadMode::Dynamic);
unsafe {
gl::BindVertexArray(self.texture_vertex_array.gl_vertex_array);
gl::UseProgram(self.texture_program.program.gl_program);
gl::Uniform2f(self.texture_program.framebuffer_size_uniform.location,
self.framebuffer_size.x() as GLfloat,
self.framebuffer_size.y() as GLfloat);
gl::Uniform2f(self.texture_program.texture_size_uniform.location,
texture.size.x() as GLfloat,
texture.size.y() as GLfloat);
set_color_uniform(&self.texture_program.color_uniform, color);
texture.bind(0);
gl::Uniform1i(self.texture_program.texture_uniform.location, 0);
gl::BlendEquation(gl::FUNC_ADD);
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);
}
device.bind_vertex_array(&self.texture_vertex_array.vertex_array);
device.use_program(&self.texture_program.program);
device.set_uniform(&self.texture_program.framebuffer_size_uniform,
UniformData::Vec2(self.framebuffer_size.0.to_f32x4()));
device.set_uniform(&self.texture_program.texture_size_uniform,
UniformData::Vec2(device.texture_size(&texture).0.to_f32x4()));
set_color_uniform(device, &self.texture_program.color_uniform, color);
device.bind_texture(texture, 0);
device.set_uniform(&self.texture_program.texture_uniform, UniformData::TextureUnit(0));
device.draw_elements(Primitive::Triangles, index_data.len() as u32, &RenderState {
blend: BlendState::RGBOneAlphaOneMinusSrcAlpha,
..RenderState::default()
});
}
}
struct DebugTextureVertexArray {
gl_vertex_array: GLuint,
vertex_buffer: Buffer,
index_buffer: Buffer,
struct DebugTextureVertexArray<D> where D: Device {
vertex_array: D::VertexArray,
vertex_buffer: D::Buffer,
index_buffer: D::Buffer,
}
impl DebugTextureVertexArray {
fn new(debug_texture_program: &DebugTextureProgram) -> DebugTextureVertexArray {
let vertex_buffer = Buffer::new();
let index_buffer = Buffer::new();
let mut gl_vertex_array = 0;
unsafe {
let position_attr = VertexAttr::new(&debug_texture_program.program, "Position");
let tex_coord_attr = VertexAttr::new(&debug_texture_program.program, "TexCoord");
impl<D> DebugTextureVertexArray<D> where D: Device {
fn new(device: &D, debug_texture_program: &DebugTextureProgram<D>)
-> DebugTextureVertexArray<D> {
let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer());
let vertex_array = device.create_vertex_array();
gl::GenVertexArrays(1, &mut gl_vertex_array);
gl::BindVertexArray(gl_vertex_array);
gl::UseProgram(debug_texture_program.program.gl_program);
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer);
position_attr.configure_float(2,
gl::UNSIGNED_SHORT,
let position_attr = device.get_vertex_attr(&debug_texture_program.program, "Position");
let tex_coord_attr = device.get_vertex_attr(&debug_texture_program.program, "TexCoord");
device.bind_vertex_array(&vertex_array);
device.use_program(&debug_texture_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_TEXTURE_VERTEX_SIZE,
0,
0);
tex_coord_attr.configure_float(2,
gl::UNSIGNED_SHORT,
device.configure_float_vertex_attr(&tex_coord_attr,
2,
VertexAttrType::U16,
false,
DEBUG_TEXTURE_VERTEX_SIZE,
4,
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 {
#[inline]
fn drop(&mut self) {
unsafe {
gl::DeleteVertexArrays(1, &mut self.gl_vertex_array);
}
}
struct DebugSolidVertexArray<D> where D: Device {
vertex_array: D::VertexArray,
vertex_buffer: D::Buffer,
index_buffer: D::Buffer,
}
struct DebugSolidVertexArray {
gl_vertex_array: GLuint,
vertex_buffer: Buffer,
index_buffer: Buffer,
}
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();
impl DebugSolidVertexArray {
fn new(debug_solid_program: &DebugSolidProgram) -> DebugSolidVertexArray {
let vertex_buffer = Buffer::new();
let index_buffer = Buffer::new();
let mut gl_vertex_array = 0;
unsafe {
let position_attr = VertexAttr::new(&debug_solid_program.program, "Position");
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,
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);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer.gl_buffer);
}
DebugSolidVertexArray { gl_vertex_array, vertex_buffer, index_buffer }
DebugSolidVertexArray { 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<D> where D: Device {
program: D::Program,
framebuffer_size_uniform: D::Uniform,
texture_size_uniform: D::Uniform,
texture_uniform: D::Uniform,
color_uniform: D::Uniform,
}
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");
impl<D> DebugTextureProgram<D> where D: Device {
fn new(device: &D, resources: &Resources) -> DebugTextureProgram<D> {
let program = device.create_program(resources, "debug_texture");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let texture_size_uniform = device.get_uniform(&program, "TextureSize");
let texture_uniform = device.get_uniform(&program, "Texture");
let color_uniform = device.get_uniform(&program, "Color");
DebugTextureProgram {
program,
framebuffer_size_uniform,
@ -565,17 +566,17 @@ impl DebugTextureProgram {
}
}
struct DebugSolidProgram {
program: Program,
framebuffer_size_uniform: Uniform,
color_uniform: Uniform,
struct DebugSolidProgram<D> where D: Device {
program: D::Program,
framebuffer_size_uniform: D::Uniform,
color_uniform: D::Uniform,
}
impl DebugSolidProgram {
fn new(device: &Device) -> DebugSolidProgram {
let program = device.create_program("debug_solid");
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
let color_uniform = Uniform::new(&program, "Color");
impl<D> DebugSolidProgram<D> where D: Device {
fn new(device: &D, resources: &Resources) -> DebugSolidProgram<D> {
let program = device.create_program(resources, "debug_solid");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let color_uniform = device.get_uniform(&program, "Color");
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) {
unsafe {
gl::Uniform4f(uniform.location,
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));
}
fn set_color_uniform<D>(device: &D, uniform: &D::Uniform, color: ColorU) where D: Device {
let color = F32x4::new(color.r as f32, color.g as f32, color.b as f32, color.a as f32);
device.set_uniform(uniform, UniformData::Vec4(color * F32x4::splat(1.0 / 255.0)));
}
#[derive(Clone, Default)]
@ -722,8 +718,8 @@ struct CornerRects {
}
impl CornerRects {
fn new(rect: RectI32, texture: &Texture) -> CornerRects {
let size = texture.size;
fn new<D>(device: &D, rect: RectI32, texture: &D::Texture) -> CornerRects where D: Device {
let size = device.texture_size(texture);
CornerRects {
upper_left: RectI32::new(rect.origin(), size),
upper_right: RectI32::new(rect.upper_right() - Point2DI32::new(size.x(), 0), size),

View File

@ -1,4 +1,4 @@
// pathfinder/demo/src/device.rs
// pathfinder/gl/src/device.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
@ -8,72 +8,175 @@
// option. This file may not be copied, modified, or distributed
// 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 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::fs::File;
use std::io::Read;
use std::mem;
use std::path::PathBuf;
use std::ptr;
use std::time::Duration;
pub struct Device {
pub resources_directory: PathBuf,
pub struct GLDevice;
impl GLDevice {
#[inline]
pub fn new() -> GLDevice { GLDevice }
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);
}
}
fn set_render_state(&self, render_state: &RenderState) {
unsafe {
// Set blend.
match render_state.blend {
BlendState::Off => gl::Disable(gl::BLEND),
BlendState::RGBOneAlphaOneMinusSrcAlpha => {
gl::BlendFuncSeparate(gl::ONE, gl::ONE_MINUS_SRC_ALPHA, gl::ONE, gl::ONE);
gl::Enable(gl::BLEND);
}
BlendState::RGBOneAlphaOne => {
gl::BlendFunc(gl::ONE, gl::ONE);
gl::Enable(gl::BLEND);
}
}
// 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 {
#[inline]
pub fn new() -> Device {
Device { resources_directory: locate_resources_directory() }
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;
}
}
#[inline]
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),
};
let mut texture = GLTexture { gl_texture: 0, size };
unsafe {
gl::GenTextures(1, &mut texture.gl_texture);
texture.bind(0);
self.bind_texture(&texture, 0);
gl::TexImage2D(gl::TEXTURE_2D,
0,
gl::RED as GLint,
image.width() as GLsizei,
image.height() as GLsizei,
gl_internal_format,
size.x() as GLsizei,
size.y() as GLsizei,
0,
gl::RED,
gl::UNSIGNED_BYTE,
image.as_ptr() as *const GLvoid);
gl_format,
gl_type,
ptr::null());
}
texture.set_parameters();
self.set_texture_parameters(&texture);
texture
}
fn create_shader(&self, name: &str, kind: ShaderKind) -> Shader {
let suffix = match kind { ShaderKind::Vertex => 'v', ShaderKind::Fragment => 'f' };
let mut path = self.resources_directory.clone();
path.push("shaders");
path.push(format!("{}.{}s.glsl", name, suffix));
fn create_texture_from_data(&self, size: Point2DI32, data: &[u8]) -> GLTexture {
assert!(data.len() >= size.x() as usize * size.y() as usize);
let mut source = vec![];
File::open(&path).unwrap().read_to_end(&mut source).unwrap();
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::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,
};
unsafe {
let gl_shader = gl::CreateShader(gl_shader_kind);
gl::ShaderSource(gl_shader,
1,
@ -95,14 +198,15 @@ impl Device {
panic!("{:?} shader '{}' compilation failed", kind, name);
}
Shader { gl_shader }
GLShader { gl_shader }
}
}
pub fn create_program(&self, name: &str) -> Program {
let vertex_shader = self.create_shader(name, ShaderKind::Vertex);
let fragment_shader = self.create_shader(name, ShaderKind::Fragment);
fn create_program_from_shaders(&self,
name: &str,
vertex_shader: GLShader,
fragment_shader: GLShader)
-> GLProgram {
let gl_program;
unsafe {
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,
}
impl Drop for VertexArray {
impl Drop for GLVertexArray {
#[inline]
fn drop(&mut self) {
unsafe {
@ -142,30 +563,11 @@ impl Drop for VertexArray {
}
}
impl VertexArray {
#[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 {
pub struct GLVertexAttr {
attr: GLuint,
}
impl VertexAttr {
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 }
}
impl GLVertexAttr {
pub fn configure_float(&self,
size: GLint,
gl_type: GLuint,
@ -199,37 +601,12 @@ impl VertexAttr {
}
}
pub struct Framebuffer {
pub struct GLFramebuffer {
pub gl_framebuffer: GLuint,
pub texture: Texture,
pub texture: GLTexture,
}
impl Framebuffer {
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 {
impl Drop for GLFramebuffer {
fn drop(&mut self) {
unsafe {
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,
}
impl Buffer {
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 {
impl Drop for GLBuffer {
fn drop(&mut self) {
unsafe {
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)]
pub struct Uniform {
pub struct GLUniform {
pub location: GLint,
}
impl Uniform {
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 struct GLProgram {
pub gl_program: GLuint,
#[allow(dead_code)]
vertex_shader: Shader,
vertex_shader: GLShader,
#[allow(dead_code)]
fragment_shader: Shader,
fragment_shader: GLShader,
}
impl Drop for Program {
impl Drop for GLProgram {
fn drop(&mut self) {
unsafe {
gl::DeleteProgram(self.gl_program)
@ -318,11 +647,11 @@ impl Drop for Program {
}
}
struct Shader {
pub struct GLShader {
gl_shader: GLuint,
}
impl Drop for Shader {
impl Drop for GLShader {
fn drop(&mut self) {
unsafe {
gl::DeleteShader(self.gl_shader)
@ -330,99 +659,16 @@ impl Drop for Shader {
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum ShaderKind {
Vertex,
Fragment,
}
pub struct Texture {
pub struct GLTexture {
gl_texture: GLuint,
pub size: Point2DI32,
}
impl Texture {
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 {
pub struct GLTimerQuery {
gl_query: GLuint,
}
impl Drop for TimerQuery {
impl Drop for GLTimerQuery {
#[inline]
fn drop(&mut self) {
unsafe {
@ -431,70 +677,71 @@ impl Drop for TimerQuery {
}
}
impl TimerQuery {
#[inline]
pub fn new() -> TimerQuery {
let mut query = TimerQuery { gl_query: 0 };
unsafe {
gl::GenQueries(1, &mut query.gl_query);
}
query
}
trait BufferTargetExt {
fn to_gl_target(self) -> GLuint;
}
#[inline]
pub fn begin(&self) {
unsafe {
gl::BeginQuery(gl::TIME_ELAPSED, self.gl_query);
}
}
#[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
impl BufferTargetExt for BufferTarget {
fn to_gl_target(self) -> GLuint {
match self {
BufferTarget::Vertex => gl::ARRAY_BUFFER,
BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER,
}
}
}
// FIXME(pcwalton): Do something better!
fn locate_resources_directory() -> PathBuf {
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() {
break;
}
}
panic!("No suitable `resources/` directory found!");
trait DepthFuncExt {
fn to_gl_depth_func(self) -> GLenum;
}
impl DepthFuncExt for DepthFunc {
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,
}
}
}

File diff suppressed because it is too large Load Diff

18
gpu/Cargo.toml Normal file
View File

@ -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"

266
gpu/src/lib.rs Normal file
View File

@ -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!");
}
}

View File

@ -8,9 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::default::{F32x4, U32x4};
use crate::default::F32x4;
impl F32x4 {
// Constructors
#[inline]
pub fn from_slice(slice: &[f32]) -> F32x4 {
F32x4::new(slice[0], slice[1], slice[2], slice[3])
}
// Accessors
#[inline]

View File

@ -131,11 +131,6 @@ impl F32x4 {
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]
pub fn cross(&self, other: F32x4) -> F32x4 {
unimplemented!()

View File

@ -1427,11 +1427,6 @@ impl F32x4 {
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`!
#[inline]
pub fn cross(&self, other: F32x4) -> F32x4 {

5
ui/Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[package]
name = "pathfinder_ui"
version = "0.1.0"
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018"

1
ui/src/lib.rs Normal file
View File

@ -0,0 +1 @@