WIP: get demo compiling again

This commit is contained in:
Patrick Walton 2018-12-30 09:38:57 -08:00
parent 28d948a36e
commit 6e0e621d19
4 changed files with 159 additions and 66 deletions

View File

@ -15,12 +15,14 @@ precision highp float;
uniform vec2 uFramebufferSize; uniform vec2 uFramebufferSize;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uStencilTextureSize; uniform vec2 uStencilTextureSize;
uniform sampler2D uFillColorsTexture;
uniform vec2 uFillColorsTextureSize;
uniform vec2 uViewBoxOrigin; uniform vec2 uViewBoxOrigin;
in vec2 aTessCoord; in vec2 aTessCoord;
in vec2 aTileOrigin; in vec2 aTileOrigin;
in float aBackdrop; in float aBackdrop;
in vec4 aColor; in int aObject;
out vec2 vTexCoord; out vec2 vTexCoord;
out float vBackdrop; out float vBackdrop;
@ -38,6 +40,6 @@ void main() {
vec2 texCoord = computeTileOffset(tileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize; vec2 texCoord = computeTileOffset(tileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize;
vTexCoord = texCoord / uStencilTextureSize; vTexCoord = texCoord / uStencilTextureSize;
vBackdrop = aBackdrop; vBackdrop = aBackdrop;
vColor = aColor; vColor = texture(uFillColorsTexture, vec2(float(aObject) / uFillColorsTextureSize.x, 0.0));
gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0); gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);
} }

View File

@ -14,17 +14,18 @@ precision highp float;
uniform vec2 uFramebufferSize; uniform vec2 uFramebufferSize;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform sampler2D uFillColorsTexture;
uniform vec2 uFillColorsTextureSize;
uniform vec2 uViewBoxOrigin; uniform vec2 uViewBoxOrigin;
in vec2 aTessCoord; in vec2 aTessCoord;
in vec2 aTileOrigin; in vec2 aTileOrigin;
in vec4 aColor; in int aObject;
out vec4 vColor; out vec4 vColor;
void main() { void main() {
vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin; vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin;
vColor = aColor; vColor = texture(uFillColorsTexture, vec2(float(aObject) / uFillColorsTextureSize.x, 0.0));
gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0); gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);
} }

View File

@ -54,16 +54,23 @@ class App {
private gl: WebGL2RenderingContext; private gl: WebGL2RenderingContext;
private disjointTimerQueryExt: any; private disjointTimerQueryExt: any;
private areaLUTTexture: WebGLTexture; private areaLUTTexture: WebGLTexture;
private fillColorsTexture: WebGLTexture;
private stencilTexture: WebGLTexture; private stencilTexture: WebGLTexture;
private stencilFramebuffer: WebGLFramebuffer; private stencilFramebuffer: WebGLFramebuffer;
private fillProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT', private fillProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT',
'TessCoord' | 'From' | 'To' | 'TileIndex'>; 'TessCoord' | 'From' | 'To' | 'TileIndex'>;
private solidTileProgram: Program<'FramebufferSize' | 'TileSize' | 'ViewBoxOrigin', private solidTileProgram: Program<'FramebufferSize' |
'TessCoord' | 'TileOrigin' | 'Color'>; 'TileSize' |
'FillColorsTexture' | 'FillColorsTextureSize' |
'ViewBoxOrigin',
'TessCoord' | 'TileOrigin' | 'Object'>;
private maskTileProgram: private maskTileProgram:
Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize' | Program<'FramebufferSize' |
'TileSize' |
'StencilTexture' | 'StencilTextureSize' |
'FillColorsTexture' | 'FillColorsTextureSize' |
'ViewBoxOrigin', 'ViewBoxOrigin',
'TessCoord' | 'TileOrigin' | 'Backdrop' | 'Color'>; 'TessCoord' | 'TileOrigin' | 'Backdrop' | 'Object'>;
private quadVertexBuffer: WebGLBuffer; private quadVertexBuffer: WebGLBuffer;
private fillVertexBuffer: WebGLBuffer; private fillVertexBuffer: WebGLBuffer;
private fillVertexArray: WebGLVertexArrayObject; private fillVertexArray: WebGLVertexArrayObject;
@ -77,6 +84,7 @@ class App {
private fillPrimitiveCount: number; private fillPrimitiveCount: number;
private solidTileCount: number; private solidTileCount: number;
private maskTileCount: number; private maskTileCount: number;
private objectCount: number;
constructor(areaLUT: HTMLImageElement) { constructor(areaLUT: HTMLImageElement) {
const canvas = staticCast(document.getElementById('canvas'), HTMLCanvasElement); const canvas = staticCast(document.getElementById('canvas'), HTMLCanvasElement);
@ -106,6 +114,8 @@ class App {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
this.fillColorsTexture = unwrapNull(gl.createTexture());
this.stencilTexture = unwrapNull(gl.createTexture()); this.stencilTexture = unwrapNull(gl.createTexture());
gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture); gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture);
gl.texImage2D(gl.TEXTURE_2D, gl.texImage2D(gl.TEXTURE_2D,
@ -140,6 +150,8 @@ class App {
'TileSize', 'TileSize',
'StencilTexture', 'StencilTexture',
'StencilTextureSize', 'StencilTextureSize',
'FillColorsTexture',
'FillColorsTextureSize',
'ViewBoxOrigin', 'ViewBoxOrigin',
], ],
[ [
@ -147,15 +159,21 @@ class App {
'TileOrigin', 'TileOrigin',
'TileIndex', 'TileIndex',
'Backdrop', 'Backdrop',
'Color', 'Object',
]); ]);
this.maskTileProgram = maskTileProgram; this.maskTileProgram = maskTileProgram;
const solidTileProgram = new Program(gl, const solidTileProgram = new Program(gl,
OPAQUE_VERTEX_SHADER_SOURCE, OPAQUE_VERTEX_SHADER_SOURCE,
OPAQUE_FRAGMENT_SHADER_SOURCE, OPAQUE_FRAGMENT_SHADER_SOURCE,
['FramebufferSize', 'TileSize', 'ViewBoxOrigin'], [
['TessCoord', 'TileOrigin', 'Color']); 'FramebufferSize',
'TileSize',
'FillColorsTexture',
'FillColorsTextureSize',
'ViewBoxOrigin',
],
['TessCoord', 'TileOrigin', 'Object']);
this.solidTileProgram = solidTileProgram; this.solidTileProgram = solidTileProgram;
const fillProgram = new Program(gl, const fillProgram = new Program(gl,
@ -233,16 +251,15 @@ class App {
SOLID_TILE_INSTANCE_SIZE, SOLID_TILE_INSTANCE_SIZE,
0); 0);
gl.vertexAttribDivisor(solidTileProgram.attributes.TileOrigin, 1); gl.vertexAttribDivisor(solidTileProgram.attributes.TileOrigin, 1);
gl.vertexAttribPointer(solidTileProgram.attributes.Color, gl.vertexAttribIPointer(solidTileProgram.attributes.Object,
4, 1,
gl.UNSIGNED_BYTE, gl.INT,
true, SOLID_TILE_INSTANCE_SIZE,
SOLID_TILE_INSTANCE_SIZE, 4);
4); gl.vertexAttribDivisor(solidTileProgram.attributes.Object, 1);
gl.vertexAttribDivisor(solidTileProgram.attributes.Color, 1);
gl.enableVertexAttribArray(solidTileProgram.attributes.TessCoord); gl.enableVertexAttribArray(solidTileProgram.attributes.TessCoord);
gl.enableVertexAttribArray(solidTileProgram.attributes.TileOrigin); gl.enableVertexAttribArray(solidTileProgram.attributes.TileOrigin);
gl.enableVertexAttribArray(solidTileProgram.attributes.Color); gl.enableVertexAttribArray(solidTileProgram.attributes.Object);
// Initialize mask tile VAO. // Initialize mask tile VAO.
this.maskVertexArray = unwrapNull(gl.createVertexArray()); this.maskVertexArray = unwrapNull(gl.createVertexArray());
@ -270,17 +287,16 @@ class App {
MASK_TILE_INSTANCE_SIZE, MASK_TILE_INSTANCE_SIZE,
4); 4);
gl.vertexAttribDivisor(maskTileProgram.attributes.Backdrop, 1); gl.vertexAttribDivisor(maskTileProgram.attributes.Backdrop, 1);
gl.vertexAttribPointer(maskTileProgram.attributes.Color, gl.vertexAttribIPointer(maskTileProgram.attributes.Object,
4, 1,
gl.UNSIGNED_BYTE, gl.INT,
true, MASK_TILE_INSTANCE_SIZE,
MASK_TILE_INSTANCE_SIZE, 8);
8); gl.vertexAttribDivisor(maskTileProgram.attributes.Object, 1);
gl.vertexAttribDivisor(maskTileProgram.attributes.Color, 1);
gl.enableVertexAttribArray(maskTileProgram.attributes.TessCoord); gl.enableVertexAttribArray(maskTileProgram.attributes.TessCoord);
gl.enableVertexAttribArray(maskTileProgram.attributes.TileOrigin); gl.enableVertexAttribArray(maskTileProgram.attributes.TileOrigin);
gl.enableVertexAttribArray(maskTileProgram.attributes.Backdrop); gl.enableVertexAttribArray(maskTileProgram.attributes.Backdrop);
gl.enableVertexAttribArray(maskTileProgram.attributes.Color); gl.enableVertexAttribArray(maskTileProgram.attributes.Object);
this.viewBox = new Rect(new Point2D(0.0, 0.0), new Size2D(0.0, 0.0)); this.viewBox = new Rect(new Point2D(0.0, 0.0), new Size2D(0.0, 0.0));
@ -290,6 +306,7 @@ class App {
this.fillPrimitiveCount = 0; this.fillPrimitiveCount = 0;
this.solidTileCount = 0; this.solidTileCount = 0;
this.maskTileCount = 0; this.maskTileCount = 0;
this.objectCount = 0;
} }
redraw(): void { redraw(): void {
@ -341,6 +358,13 @@ class App {
framebufferSize.width, framebufferSize.width,
framebufferSize.height); framebufferSize.height);
gl.uniform2f(this.solidTileProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height); gl.uniform2f(this.solidTileProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.fillColorsTexture);
gl.uniform1i(this.solidTileProgram.uniforms.FillColorsTexture, 0);
// FIXME(pcwalton): Maybe this should be an ivec2 or uvec2?
gl.uniform2f(this.solidTileProgram.uniforms.FillColorsTextureSize,
this.objectCount,
1.0);
gl.uniform2f(this.solidTileProgram.uniforms.ViewBoxOrigin, gl.uniform2f(this.solidTileProgram.uniforms.ViewBoxOrigin,
this.viewBox.origin.x, this.viewBox.origin.x,
this.viewBox.origin.y); this.viewBox.origin.y);
@ -360,6 +384,13 @@ class App {
gl.uniform2f(this.maskTileProgram.uniforms.StencilTextureSize, gl.uniform2f(this.maskTileProgram.uniforms.StencilTextureSize,
STENCIL_FRAMEBUFFER_SIZE.width, STENCIL_FRAMEBUFFER_SIZE.width,
STENCIL_FRAMEBUFFER_SIZE.height); STENCIL_FRAMEBUFFER_SIZE.height);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this.fillColorsTexture);
gl.uniform1i(this.maskTileProgram.uniforms.FillColorsTexture, 1);
// FIXME(pcwalton): Maybe this should be an ivec2 or uvec2?
gl.uniform2f(this.maskTileProgram.uniforms.FillColorsTextureSize,
this.objectCount,
1.0);
gl.uniform2f(this.maskTileProgram.uniforms.ViewBoxOrigin, gl.uniform2f(this.maskTileProgram.uniforms.ViewBoxOrigin,
this.viewBox.origin.x, this.viewBox.origin.x,
this.viewBox.origin.y); this.viewBox.origin.y);
@ -416,6 +447,8 @@ class App {
const arrayBuffer = staticCast(reader.result, ArrayBuffer); const arrayBuffer = staticCast(reader.result, ArrayBuffer);
const root = new RIFFChunk(new DataView(arrayBuffer)); const root = new RIFFChunk(new DataView(arrayBuffer));
for (const subchunk of root.subchunks()) { for (const subchunk of root.subchunks()) {
const self = this;
const id = subchunk.stringID(); const id = subchunk.stringID();
if (id === 'head') { if (id === 'head') {
const headerData = subchunk.contents(); const headerData = subchunk.contents();
@ -426,36 +459,54 @@ class App {
continue; continue;
} }
let bindPoint, buffer;
let countFieldName: 'fillPrimitiveCount' | 'totalTileCount' | 'solidTileCount' |
'maskTileCount';
let instanceSize;
switch (id) { switch (id) {
case 'fill': case 'fill':
bindPoint = gl.ARRAY_BUFFER; uploadArrayBuffer(this.fillVertexBuffer,
buffer = this.fillVertexBuffer; 'fillPrimitiveCount',
countFieldName = 'fillPrimitiveCount'; FILL_INSTANCE_SIZE);
instanceSize = FILL_INSTANCE_SIZE;
break; break;
case 'soli': case 'soli':
bindPoint = gl.ARRAY_BUFFER; uploadArrayBuffer(this.solidTileVertexBuffer,
buffer = this.solidTileVertexBuffer; 'solidTileCount',
countFieldName = 'solidTileCount'; SOLID_TILE_INSTANCE_SIZE);
instanceSize = SOLID_TILE_INSTANCE_SIZE;
break; break;
case 'mask': case 'mask':
bindPoint = gl.ARRAY_BUFFER; uploadArrayBuffer(this.maskTileVertexBuffer,
buffer = this.maskTileVertexBuffer; 'maskTileCount',
countFieldName = 'maskTileCount'; MASK_TILE_INSTANCE_SIZE);
instanceSize = MASK_TILE_INSTANCE_SIZE; break;
case 'shad':
this.objectCount = subchunk.length() / 4;
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.fillColorsTexture);
gl.texImage2D(gl.TEXTURE_2D,
0,
gl.RGBA,
this.objectCount,
1,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
subchunk.contents());
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
break; break;
default: default:
throw new Error("Unexpected subchunk ID: " + id); throw new Error("Unexpected subchunk ID: " + id);
} }
gl.bindBuffer(bindPoint, buffer); type CountFieldName = 'fillPrimitiveCount' | 'solidTileCount' | 'maskTileCount';
gl.bufferData(bindPoint, subchunk.contents(), gl.DYNAMIC_DRAW);
this[countFieldName] = subchunk.length() / instanceSize; function uploadArrayBuffer(buffer: WebGLBuffer,
countFieldName: CountFieldName,
instanceSize: number):
void {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, subchunk.contents(), gl.DYNAMIC_DRAW);
self[countFieldName] = subchunk.length() / instanceSize;
}
} }
this.redraw(); this.redraw();

View File

@ -28,6 +28,7 @@ use lyon_path::iterator::PathIter;
use pathfinder_path_utils::stroke::{StrokeStyle, StrokeToFillIter}; use pathfinder_path_utils::stroke::{StrokeStyle, StrokeToFillIter};
use quick_xml::Reader; use quick_xml::Reader;
use quick_xml::events::{BytesStart, Event}; use quick_xml::events::{BytesStart, Event};
use rayon::ThreadPoolBuilder;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
@ -58,9 +59,11 @@ fn main() {
.value_name("COUNT") .value_name("COUNT")
.takes_value(true) .takes_value(true)
.help("Run a benchmark with COUNT runs")) .help("Run a benchmark with COUNT runs"))
.arg(Arg::with_name("sequential").short("s") .arg(Arg::with_name("jobs").short("j")
.long("sequential") .long("jobs")
.help("Use only one thread")) .value_name("THREADS")
.takes_value(true)
.help("Number of threads to use"))
.arg(Arg::with_name("INPUT").help("Path to the SVG file to render") .arg(Arg::with_name("INPUT").help("Path to the SVG file to render")
.required(true) .required(true)
.index(1)) .index(1))
@ -72,20 +75,26 @@ fn main() {
Some(runs) => runs.parse().unwrap(), Some(runs) => runs.parse().unwrap(),
None => 1, None => 1,
}; };
let sequential = matches.is_present("sequential"); let jobs: Option<usize> = matches.value_of("jobs").map(|string| string.parse().unwrap());
let input_path = PathBuf::from(matches.value_of("INPUT").unwrap()); let input_path = PathBuf::from(matches.value_of("INPUT").unwrap());
let output_path = matches.value_of("OUTPUT").map(PathBuf::from); let output_path = matches.value_of("OUTPUT").map(PathBuf::from);
// Set up Rayon.
let mut thread_pool_builder = ThreadPoolBuilder::new();
if let Some(jobs) = jobs {
thread_pool_builder = thread_pool_builder.num_threads(jobs);
}
thread_pool_builder.build_global().unwrap();
let scene = Scene::from_path(&input_path); let scene = Scene::from_path(&input_path);
println!("Scene bounds: {:?}", scene.bounds); println!("Scene bounds: {:?}", scene.bounds);
let start_time = Instant::now(); let start_time = Instant::now();
let mut built_scene = BuiltScene::new(&scene.view_box, scene.objects.len() as u32); let mut built_scene = BuiltScene::new(&scene.view_box, scene.objects.len() as u32);
for _ in 0..runs { for _ in 0..runs {
let built_objects = if sequential { let built_objects = match jobs {
scene.build_objects_sequentially() Some(1) => scene.build_objects_sequentially(),
} else { _ => scene.build_objects(),
scene.build_objects_in_parallel()
}; };
built_scene = BuiltScene::from_objects(&scene.view_box, &built_objects); built_scene = BuiltScene::from_objects(&scene.view_box, &built_objects);
} }
@ -321,17 +330,30 @@ impl Scene {
&self.styles[style.0 as usize] &self.styles[style.0 as usize]
} }
fn build_shader(&self, object_index: u32) -> ObjectShader {
ObjectShader {
fill_color: self.objects[object_index as usize].color,
}
}
// This function exists to make profiling easier.
fn build_objects_sequentially(&self) -> Vec<BuiltObject> { fn build_objects_sequentially(&self) -> Vec<BuiltObject> {
self.objects.iter().enumerate().map(|(object_index, object)| { self.objects.iter().enumerate().map(|(object_index, object)| {
let mut tiler = Tiler::new(&object.outline, object_index as u32, &self.view_box); let mut tiler = Tiler::new(&object.outline,
object_index as u32,
&self.view_box,
&self.build_shader(object_index as u32));
tiler.generate_tiles(); tiler.generate_tiles();
tiler.built_object tiler.built_object
}).collect() }).collect()
} }
fn build_objects_in_parallel(&self) -> Vec<BuiltObject> { fn build_objects(&self) -> Vec<BuiltObject> {
self.objects.par_iter().enumerate().map(|(object_index, object)| { self.objects.par_iter().enumerate().map(|(object_index, object)| {
let mut tiler = Tiler::new(&object.outline, object_index as u32, &self.view_box); let mut tiler = Tiler::new(&object.outline,
object_index as u32,
&self.view_box,
&self.build_shader(object_index as u32));
tiler.generate_tiles(); tiler.generate_tiles();
tiler.built_object tiler.built_object
}).collect() }).collect()
@ -955,9 +977,10 @@ struct Tiler<'o> {
} }
impl<'o> Tiler<'o> { impl<'o> Tiler<'o> {
fn new(outline: &'o Outline, object_index: u32, view_box: &Rect<f32>) -> Tiler<'o> { fn new(outline: &'o Outline, object_index: u32, view_box: &Rect<f32>, shader: &ObjectShader)
-> Tiler<'o> {
let bounds = outline.bounds.intersection(&view_box).unwrap_or(Rect::zero()); let bounds = outline.bounds.intersection(&view_box).unwrap_or(Rect::zero());
let built_object = BuiltObject::new(&bounds); let built_object = BuiltObject::new(&bounds, shader);
Tiler { Tiler {
outline, outline,
@ -1023,7 +1046,7 @@ impl<'o> Tiler<'o> {
}; };
// Move over to the correct tile, filling in as we go. // Move over to the correct tile, filling in as we go.
let mut segment_tile_x = f32::floor(segment_x / TILE_WIDTH) as i16; let segment_tile_x = f32::floor(segment_x / TILE_WIDTH) as i16;
while current_tile_x < segment_tile_x { while current_tile_x < segment_tile_x {
//println!("... filling!"); //println!("... filling!");
self.built_object.get_tile_mut(current_tile_x, tile_y).backdrop = current_winding; self.built_object.get_tile_mut(current_tile_x, tile_y).backdrop = current_winding;
@ -1153,10 +1176,10 @@ impl BuiltScene {
fn new(view_box: &Rect<f32>, object_count: u32) -> BuiltScene { fn new(view_box: &Rect<f32>, object_count: u32) -> BuiltScene {
BuiltScene { BuiltScene {
view_box: *view_box, view_box: *view_box,
object_count,
fills: vec![], fills: vec![],
solid_tiles: vec![], solid_tiles: vec![],
mask_tiles: vec![], mask_tiles: vec![],
shaders: vec![ObjectShader::default(); object_count as usize],
tile_rect: round_rect_out_to_tile_bounds(view_box), tile_rect: round_rect_out_to_tile_bounds(view_box),
} }
@ -1237,6 +1260,7 @@ struct BuiltObject {
tiles: Vec<TileObjectPrimitive>, tiles: Vec<TileObjectPrimitive>,
fills: Vec<FillObjectPrimitive>, fills: Vec<FillObjectPrimitive>,
mask_tiles: FixedBitSet, mask_tiles: FixedBitSet,
shader: ObjectShader,
} }
#[derive(Debug)] #[derive(Debug)]
@ -1245,7 +1269,7 @@ struct BuiltScene {
fills: Vec<FillScenePrimitive>, fills: Vec<FillScenePrimitive>,
solid_tiles: Vec<SolidTileScenePrimitive>, solid_tiles: Vec<SolidTileScenePrimitive>,
mask_tiles: Vec<MaskTileScenePrimitive>, mask_tiles: Vec<MaskTileScenePrimitive>,
object_count: u32, shaders: Vec<ObjectShader>,
tile_rect: Rect<i16>, tile_rect: Rect<i16>,
} }
@ -1285,7 +1309,12 @@ struct MaskTileScenePrimitive {
object_index: u32, object_index: u32,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
struct ObjectShader {
fill_color: ColorU,
}
#[derive(Clone, Copy, Debug, Default)]
struct ColorU { struct ColorU {
r: u8, r: u8,
g: u8, g: u8,
@ -1296,7 +1325,7 @@ struct ColorU {
// Utilities for built objects // Utilities for built objects
impl BuiltObject { impl BuiltObject {
fn new(bounds: &Rect<f32>) -> BuiltObject { fn new(bounds: &Rect<f32>, shader: &ObjectShader) -> BuiltObject {
// Compute the tile rect. // Compute the tile rect.
let tile_rect = round_rect_out_to_tile_bounds(&bounds); let tile_rect = round_rect_out_to_tile_bounds(&bounds);
@ -1315,6 +1344,7 @@ impl BuiltObject {
tiles, tiles,
fills: vec![], fills: vec![],
mask_tiles: FixedBitSet::with_capacity(tile_count), mask_tiles: FixedBitSet::with_capacity(tile_count),
shader: *shader,
} }
} }
@ -1350,11 +1380,13 @@ impl BuiltScene {
let fill_size = self.fills.len() * mem::size_of::<FillScenePrimitive>(); let fill_size = self.fills.len() * mem::size_of::<FillScenePrimitive>();
let solid_tiles_size = self.solid_tiles.len() * mem::size_of::<SolidTileScenePrimitive>(); let solid_tiles_size = self.solid_tiles.len() * mem::size_of::<SolidTileScenePrimitive>();
let mask_tiles_size = self.mask_tiles.len() * mem::size_of::<MaskTileScenePrimitive>(); let mask_tiles_size = self.mask_tiles.len() * mem::size_of::<MaskTileScenePrimitive>();
let shaders_size = self.shaders.len() * mem::size_of::<ObjectShader>();
writer.write_u32::<LittleEndian>((4 + writer.write_u32::<LittleEndian>((4 +
8 + header_size + 8 + header_size +
8 + fill_size + 8 + fill_size +
8 + solid_tiles_size + 8 + solid_tiles_size +
8 + mask_tiles_size) as u32)?; 8 + mask_tiles_size +
8 + shaders_size) as u32)?;
writer.write_all(b"PF3S")?; writer.write_all(b"PF3S")?;
@ -1390,6 +1422,13 @@ impl BuiltScene {
writer.write_u32::<LittleEndian>(tile_primitive.object_index)?; writer.write_u32::<LittleEndian>(tile_primitive.object_index)?;
} }
writer.write_all(b"shad")?;
writer.write_u32::<LittleEndian>(shaders_size as u32)?;
for &shader in &self.shaders {
let fill_color = shader.fill_color;
writer.write_all(&[fill_color.r, fill_color.g, fill_color.b, fill_color.a])?;
}
return Ok(()); return Ok(());
fn write_point<W>(writer: &mut W, point: &Point2D<f32>) -> io::Result<()> where W: Write { fn write_point<W>(writer: &mut W, point: &Point2D<f32>) -> io::Result<()> where W: Write {