Skip to content

Commit

Permalink
Implemented GPU line draw commands
Browse files Browse the repository at this point in the history
Missing support for poly-lines.
  • Loading branch information
simias committed Nov 18, 2015
1 parent 148cbdc commit f6514ed
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 20 deletions.
48 changes: 48 additions & 0 deletions src/gpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,30 @@ impl Gpu {
semi_transparent: true,
texture: TextureMethod::Blended,
}),
0x40 =>
(3, Gp0Attributes {
callback: Gpu::gp0_monochrome_line,
semi_transparent: false,
texture: TextureMethod::None,
}),
0x42 =>
(3, Gp0Attributes {
callback: Gpu::gp0_monochrome_line,
semi_transparent: true,
texture: TextureMethod::None,
}),
0x50 =>
(4, Gp0Attributes {
callback: Gpu::gp0_shaded_line,
semi_transparent: false,
texture: TextureMethod::None,
}),
0x52 =>
(4, Gp0Attributes {
callback: Gpu::gp0_shaded_line,
semi_transparent: true,
texture: TextureMethod::None,
}),
0x60 =>
(3, Gp0Attributes {
callback: Gpu::gp0_monochrome_rect,
Expand Down Expand Up @@ -856,6 +880,18 @@ impl Gpu {
self.renderer.push_quad(&vertices);
}

/// Draw a monochrome line
fn gp0_monochrome_line(&mut self) {
let vertices = [
self.gp0_attributes.vertex(gp0_position(self.gp0_command[1]),
gp0_color(self.gp0_command[0])),
self.gp0_attributes.vertex(gp0_position(self.gp0_command[2]),
gp0_color(self.gp0_command[0])),
];

self.renderer.push_line(&vertices);
}

/// Draw a textured unshaded triangle
fn gp0_textured_triangle(&mut self) {
let color = gp0_color(self.gp0_command[0]);
Expand Down Expand Up @@ -920,6 +956,18 @@ impl Gpu {
self.renderer.push_quad(&vertices);
}

/// Draw a shaded line
fn gp0_shaded_line(&mut self) {
let vertices = [
self.gp0_attributes.vertex(gp0_position(self.gp0_command[1]),
gp0_color(self.gp0_command[0])),
self.gp0_attributes.vertex(gp0_position(self.gp0_command[3]),
gp0_color(self.gp0_command[2])),
];

self.renderer.push_line(&vertices);
}

/// Draw a textured shaded triangle
fn gp0_textured_shaded_triangle(&mut self) {
let vertices = [
Expand Down
111 changes: 91 additions & 20 deletions src/gpu/opengl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use sdl2::video::GLProfile;
use glium_sdl2;

use glium::{Program, VertexBuffer, Frame, Surface, DrawParameters, Rect, Blend};
use glium::index;
use glium::uniforms::{UniformsStorage, EmptyUniforms};
use glium::program::ProgramCreationInput;

Expand Down Expand Up @@ -60,6 +61,13 @@ pub struct Renderer {
program: Program,
/// Permanent vertex buffer
vertex_buffer: VertexBuffer<Vertex>,
/// List of queued draw commands. Each command contains a
/// primitive type (triangle or line) and a number of *vertices*
/// to be drawn from the `vertex_buffer`.
command_queue: Vec<(index::PrimitiveType, u32)>,
/// Current draw command. Will be pushed onto the `command_queue`
/// if a new command needs to be started.
current_command: (index::PrimitiveType, u32),
/// GLSL uniforms
uniforms: UniformsStorage<'static, [i32; 2], EmptyUniforms>,
/// Current draw offset
Expand Down Expand Up @@ -126,6 +134,15 @@ impl Renderer {
offset: [0; 2],
};

// In order to have the line size scale with the internal
// resolution upscale we need to compute the upscaling ratio.
//
// XXX I only use the y scaling factor since I assume that
// both dimensions are scaled by the same ratio. Otherwise
// we'd have to change the line thickness depending on its
// angle and that would be tricky.
let scaling_factor = fb_y_res as f32 / 512.;

let params = DrawParameters {
// Default to full screen
scissor: Some(Rect {
Expand All @@ -134,6 +151,7 @@ impl Renderer {
width: fb_x_res as u32,
height: fb_y_res as u32
}),
line_width: Some(scaling_factor),
// XXX temporary hack for semi-transparency, use basic
// alpha blending.
blend: Blend::alpha_blending(),
Expand All @@ -147,6 +165,8 @@ impl Renderer {
fb_y_res: fb_y_res,
program: program,
vertex_buffer: vertex_buffer,
command_queue: Vec::new(),
current_command: (index::PrimitiveType::TrianglesList, 0),
uniforms: uniforms,
offset: (0, 0),
params: params,
Expand All @@ -156,24 +176,58 @@ impl Renderer {

/// Add a triangle to the draw buffer
pub fn push_triangle(&mut self, vertices: &[Vertex; 3]) {
self.push_primitive(index::PrimitiveType::TrianglesList,
vertices);
}

/// Add a quad to the draw buffer
pub fn push_quad(&mut self, vertices: &[Vertex; 4]) {
self.push_triangle(&[vertices[0], vertices[1], vertices[2]]);
self.push_triangle(&[vertices[1], vertices[2], vertices[3]]);
}

/// Add a line to the draw buffer
pub fn push_line(&mut self, vertices: &[Vertex; 2]) {
self.push_primitive(index::PrimitiveType::LinesList,
vertices);
}

/// Add a primitive to the draw buffer
fn push_primitive(&mut self,
primitive_type: index::PrimitiveType,
vertices: &[Vertex]) {
let primitive_vertices = vertices.len() as u32;

// Make sure we have enough room left to queue the vertex. We
// need to push two triangles to draw a quad, so 6 vertex
if self.nvertices + 3 > VERTEX_BUFFER_LEN {
if self.nvertices + primitive_vertices > VERTEX_BUFFER_LEN {
// The vertex attribute buffers are full, force an early
// draw
self.draw();
}

let slice = self.vertex_buffer.slice(self.nvertices as usize..
(self.nvertices + 3) as usize).unwrap();
let (mut cmd_type, mut cmd_len) = self.current_command;

if primitive_type != cmd_type {
// We have to change the primitive type. Push the current
// command onto the queue and start a new one.
if cmd_len > 0 {
self.command_queue.push(self.current_command);
}

cmd_type = primitive_type;
cmd_len = 0;
}

// Copy the vertices into the vertex buffer
let start = self.nvertices as usize;
let end = start + primitive_vertices as usize;

let slice = self.vertex_buffer.slice(start..end).unwrap();
slice.write(vertices);
self.nvertices += 3;
}

/// Add a quad to the draw buffer
pub fn push_quad(&mut self, vertices: &[Vertex; 4]) {
self.push_triangle(&[vertices[0], vertices[1], vertices[2]]);
self.push_triangle(&[vertices[1], vertices[2], vertices[3]]);
self.nvertices += primitive_vertices;
self.current_command = (cmd_type, cmd_len + primitive_vertices);
}

/// Fill a rectangle in memory with the given color. This method
Expand Down Expand Up @@ -269,20 +323,37 @@ impl Renderer {

/// Draw the buffered commands and reset the buffers
pub fn draw(&mut self) {
use glium::index;

self.target
.as_mut()
.unwrap()
.draw(self.vertex_buffer.slice(0..self.nvertices as usize).unwrap(),
&index::NoIndices(index::PrimitiveType::TrianglesList),
&self.program,
&self.uniforms,
&self.params)
.unwrap();
let target = self.target.as_mut().unwrap();

// Push the last pending command if needed
let (_, cmd_len) = self.current_command;

if cmd_len > 0 {
self.command_queue.push(self.current_command);
}

let mut vertex_pos = 0;

for &(cmd_type, cmd_len) in &self.command_queue {
let start = vertex_pos;
let end = start + cmd_len as usize;

let vertices = self.vertex_buffer.slice(start..end).unwrap();

target.draw(vertices,
&index::NoIndices(cmd_type),
&self.program,
&self.uniforms,
&self.params)
.unwrap();

vertex_pos = end;
}

// Reset the buffers
self.nvertices = 0;
self.command_queue.clear();
self.current_command = (index::PrimitiveType::TrianglesList, 0);
}

/// Draw the buffered commands and display them
Expand Down

0 comments on commit f6514ed

Please sign in to comment.