Remove the scene assembly thread, and pipeline only fills instead of alpha
tiles. Removing a thread should make it easier to manually assign threads to CPUs, as is necessary on devices with poor schedulers like Magic Leap 1.
This commit is contained in:
parent
fbc2a56b30
commit
c688d04412
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
use crate::GRIDLINE_COUNT;
|
use crate::GRIDLINE_COUNT;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BufferTarget, BufferUploadMode, Device, VertexAttrType};
|
use pathfinder_gpu::{BufferData, BufferTarget, BufferUploadMode, Device, VertexAttrType};
|
||||||
|
|
||||||
pub struct GroundProgram<D> where D: Device {
|
pub struct GroundProgram<D> where D: Device {
|
||||||
pub program: D::Program,
|
pub program: D::Program,
|
||||||
|
@ -60,10 +60,10 @@ pub struct GroundLineVertexArray<D> where D: Device {
|
||||||
impl<D> GroundLineVertexArray<D> where D: Device {
|
impl<D> GroundLineVertexArray<D> where D: Device {
|
||||||
pub fn new(device: &D, ground_program: &GroundProgram<D>) -> GroundLineVertexArray<D> {
|
pub fn new(device: &D, ground_program: &GroundProgram<D>) -> GroundLineVertexArray<D> {
|
||||||
let grid_vertex_positions_buffer = device.create_buffer();
|
let grid_vertex_positions_buffer = device.create_buffer();
|
||||||
device.upload_to_buffer(&grid_vertex_positions_buffer,
|
device.allocate_buffer(&grid_vertex_positions_buffer,
|
||||||
&create_grid_vertex_positions(),
|
BufferData::Memory(&create_grid_vertex_positions()),
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Static);
|
BufferUploadMode::Static);
|
||||||
|
|
||||||
let vertex_array = device.create_vertex_array();
|
let vertex_array = device.create_vertex_array();
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,7 @@ use pathfinder_gl::GLDevice;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc};
|
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc};
|
||||||
use pathfinder_gpu::{StencilState, UniformData};
|
use pathfinder_gpu::{StencilState, UniformData};
|
||||||
use pathfinder_renderer::builder::{RenderOptions, RenderTransform};
|
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
|
||||||
use pathfinder_renderer::builder::{SceneBuilder, SceneBuilderContext};
|
|
||||||
use pathfinder_renderer::gpu::renderer::{RenderMode, Renderer};
|
use pathfinder_renderer::gpu::renderer::{RenderMode, Renderer};
|
||||||
use pathfinder_renderer::gpu_data::{BuiltScene, RenderCommand, Stats};
|
use pathfinder_renderer::gpu_data::{BuiltScene, RenderCommand, Stats};
|
||||||
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
|
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
|
||||||
|
@ -42,7 +41,7 @@ use std::iter;
|
||||||
use std::panic::{self, AssertUnwindSafe};
|
use std::panic::{self, AssertUnwindSafe};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::mpsc::{self, Receiver, Sender};
|
use std::sync::mpsc::{self, Receiver, Sender, SyncSender};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use usvg::{Options as UsvgOptions, Tree};
|
use usvg::{Options as UsvgOptions, Tree};
|
||||||
|
@ -71,6 +70,8 @@ const APPROX_FONT_SIZE: f32 = 16.0;
|
||||||
|
|
||||||
const MESSAGE_TIMEOUT_SECS: u64 = 5;
|
const MESSAGE_TIMEOUT_SECS: u64 = 5;
|
||||||
|
|
||||||
|
const MAX_MESSAGES_IN_FLIGHT: usize = 256;
|
||||||
|
|
||||||
pub const GRIDLINE_COUNT: u8 = 10;
|
pub const GRIDLINE_COUNT: u8 = 10;
|
||||||
|
|
||||||
pub mod window;
|
pub mod window;
|
||||||
|
@ -662,13 +663,9 @@ struct SceneThreadProxy {
|
||||||
impl SceneThreadProxy {
|
impl SceneThreadProxy {
|
||||||
fn new(scene: Scene, options: Options) -> SceneThreadProxy {
|
fn new(scene: Scene, options: Options) -> SceneThreadProxy {
|
||||||
let (main_to_scene_sender, main_to_scene_receiver) = mpsc::channel();
|
let (main_to_scene_sender, main_to_scene_receiver) = mpsc::channel();
|
||||||
let (scene_to_main_sender, scene_to_main_receiver) = mpsc::channel();
|
let (scene_to_main_sender, scene_to_main_receiver) =
|
||||||
let scene_builder_context = SceneBuilderContext::new();
|
mpsc::sync_channel(MAX_MESSAGES_IN_FLIGHT);
|
||||||
SceneThread::new(scene,
|
SceneThread::new(scene, scene_to_main_sender, main_to_scene_receiver, options);
|
||||||
scene_to_main_sender,
|
|
||||||
main_to_scene_receiver,
|
|
||||||
scene_builder_context,
|
|
||||||
options);
|
|
||||||
SceneThreadProxy { sender: main_to_scene_sender, receiver: scene_to_main_receiver }
|
SceneThreadProxy { sender: main_to_scene_sender, receiver: scene_to_main_receiver }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,19 +680,17 @@ impl SceneThreadProxy {
|
||||||
|
|
||||||
struct SceneThread {
|
struct SceneThread {
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
sender: Sender<SceneToMainMsg>,
|
sender: SyncSender<SceneToMainMsg>,
|
||||||
receiver: Receiver<MainToSceneMsg>,
|
receiver: Receiver<MainToSceneMsg>,
|
||||||
context: SceneBuilderContext,
|
|
||||||
options: Options,
|
options: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SceneThread {
|
impl SceneThread {
|
||||||
fn new(scene: Scene,
|
fn new(scene: Scene,
|
||||||
sender: Sender<SceneToMainMsg>,
|
sender: SyncSender<SceneToMainMsg>,
|
||||||
receiver: Receiver<MainToSceneMsg>,
|
receiver: Receiver<MainToSceneMsg>,
|
||||||
context: SceneBuilderContext,
|
|
||||||
options: Options) {
|
options: Options) {
|
||||||
thread::spawn(move || (SceneThread { scene, sender, receiver, context, options }).run());
|
thread::spawn(move || (SceneThread { scene, sender, receiver, options }).run());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(mut self) {
|
fn run(mut self) {
|
||||||
|
@ -715,8 +710,7 @@ impl SceneThread {
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
for render_transform in &build_options.render_transforms {
|
for render_transform in &build_options.render_transforms {
|
||||||
build_scene(&self.context,
|
build_scene(&self.scene,
|
||||||
&self.scene,
|
|
||||||
&build_options,
|
&build_options,
|
||||||
(*render_transform).clone(),
|
(*render_transform).clone(),
|
||||||
self.options.jobs,
|
self.options.jobs,
|
||||||
|
@ -766,12 +760,11 @@ impl Debug for SceneToMainMsg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_scene(context: &SceneBuilderContext,
|
fn build_scene(scene: &Scene,
|
||||||
scene: &Scene,
|
|
||||||
build_options: &BuildOptions,
|
build_options: &BuildOptions,
|
||||||
render_transform: RenderTransform,
|
render_transform: RenderTransform,
|
||||||
jobs: Option<usize>,
|
jobs: Option<usize>,
|
||||||
sink: &mut Sender<SceneToMainMsg>) {
|
sink: &mut SyncSender<SceneToMainMsg>) {
|
||||||
let render_options = RenderOptions {
|
let render_options = RenderOptions {
|
||||||
transform: render_transform.clone(),
|
transform: render_transform.clone(),
|
||||||
dilation: match build_options.stem_darkening_font_size {
|
dilation: match build_options.stem_darkening_font_size {
|
||||||
|
@ -792,18 +785,17 @@ fn build_scene(context: &SceneBuilderContext,
|
||||||
built_scene.shaders = scene.build_shaders();
|
built_scene.shaders = scene.build_shaders();
|
||||||
sink.send(SceneToMainMsg::BeginRenderScene(built_scene)).unwrap();
|
sink.send(SceneToMainMsg::BeginRenderScene(built_scene)).unwrap();
|
||||||
|
|
||||||
let (context, inner_sink) = (AssertUnwindSafe(context), AssertUnwindSafe(sink.clone()));
|
let inner_sink = AssertUnwindSafe(sink.clone());
|
||||||
let result = panic::catch_unwind(move || {
|
let result = panic::catch_unwind(move || {
|
||||||
let mut scene_builder = SceneBuilder::new(&context, scene, &built_options);
|
|
||||||
let sink = (*inner_sink).clone();
|
let sink = (*inner_sink).clone();
|
||||||
let listener = Box::new(move |command| {
|
let listener = Box::new(move |command| {
|
||||||
sink.send(SceneToMainMsg::Execute(command)).unwrap()
|
sink.send(SceneToMainMsg::Execute(command)).unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME(pcwalton): Actually take the number of jobs into account.
|
let mut scene_builder = SceneBuilder::new(scene, &built_options, listener);
|
||||||
match jobs {
|
match jobs {
|
||||||
Some(1) => scene_builder.build_sequentially(listener),
|
Some(1) => scene_builder.build_sequentially(),
|
||||||
_ => scene_builder.build_in_parallel(listener),
|
_ => scene_builder.build_in_parallel(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -274,10 +274,10 @@ impl Sub<Point2DF32> for LineSegmentF32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct LineSegmentU4(pub u16);
|
pub struct LineSegmentU4(pub u16);
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct LineSegmentU8(pub u32);
|
pub struct LineSegmentU8(pub u32);
|
||||||
|
|
|
@ -10,11 +10,12 @@
|
||||||
|
|
||||||
//! An OpenGL implementation of the device abstraction.
|
//! An OpenGL implementation of the device abstraction.
|
||||||
|
|
||||||
use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLintptr, GLsizei, GLsizeiptr};
|
||||||
|
use gl::types::{GLuint, GLvoid};
|
||||||
use pathfinder_geometry::basic::point::Point2DI32;
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
use pathfinder_geometry::basic::rect::RectI32;
|
use pathfinder_geometry::basic::rect::RectI32;
|
||||||
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, Device, Primitive};
|
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, DepthFunc, Device};
|
||||||
use pathfinder_gpu::{RenderState, ShaderKind, StencilFunc, TextureFormat};
|
use pathfinder_gpu::{Primitive, RenderState, ShaderKind, StencilFunc, TextureFormat};
|
||||||
use pathfinder_gpu::{UniformData, VertexAttrType};
|
use pathfinder_gpu::{UniformData, VertexAttrType};
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use rustache::{HashBuilder, Render};
|
use rustache::{HashBuilder, Render};
|
||||||
|
@ -413,25 +414,24 @@ impl Device for GLDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_to_buffer<T>(&self,
|
fn allocate_buffer<T>(&self,
|
||||||
buffer: &GLBuffer,
|
buffer: &GLBuffer,
|
||||||
data: &[T],
|
data: BufferData<T>,
|
||||||
target: BufferTarget,
|
target: BufferTarget,
|
||||||
mode: BufferUploadMode) {
|
mode: BufferUploadMode) {
|
||||||
let target = match target {
|
let target = match target {
|
||||||
BufferTarget::Vertex => gl::ARRAY_BUFFER,
|
BufferTarget::Vertex => gl::ARRAY_BUFFER,
|
||||||
BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER,
|
BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER,
|
||||||
};
|
};
|
||||||
let mode = match mode {
|
let (ptr, len) = match data {
|
||||||
BufferUploadMode::Static => gl::STATIC_DRAW,
|
BufferData::Uninitialized(len) => (ptr::null(), len),
|
||||||
BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW,
|
BufferData::Memory(buffer) => (buffer.as_ptr() as *const GLvoid, buffer.len()),
|
||||||
};
|
};
|
||||||
|
let len = (len * mem::size_of::<T>()) as GLsizeiptr;
|
||||||
|
let usage = mode.to_gl_usage();
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::BindBuffer(target, buffer.gl_buffer); ck();
|
gl::BindBuffer(target, buffer.gl_buffer); ck();
|
||||||
gl::BufferData(target,
|
gl::BufferData(target, len, ptr, usage); ck();
|
||||||
(data.len() * mem::size_of::<T>()) as GLsizeiptr,
|
|
||||||
data.as_ptr() as *const GLvoid,
|
|
||||||
mode); ck();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,6 +774,19 @@ impl BufferTargetExt for BufferTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait BufferUploadModeExt {
|
||||||
|
fn to_gl_usage(self) -> GLuint;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferUploadModeExt for BufferUploadMode {
|
||||||
|
fn to_gl_usage(self) -> GLuint {
|
||||||
|
match self {
|
||||||
|
BufferUploadMode::Static => gl::STATIC_DRAW,
|
||||||
|
BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trait DepthFuncExt {
|
trait DepthFuncExt {
|
||||||
fn to_gl_depth_func(self) -> GLenum;
|
fn to_gl_depth_func(self) -> GLenum;
|
||||||
}
|
}
|
||||||
|
@ -824,6 +837,7 @@ impl VertexAttrTypeExt for VertexAttrType {
|
||||||
match self {
|
match self {
|
||||||
VertexAttrType::F32 => gl::FLOAT,
|
VertexAttrType::F32 => gl::FLOAT,
|
||||||
VertexAttrType::I16 => gl::SHORT,
|
VertexAttrType::I16 => gl::SHORT,
|
||||||
|
VertexAttrType::I8 => gl::BYTE,
|
||||||
VertexAttrType::U16 => gl::UNSIGNED_SHORT,
|
VertexAttrType::U16 => gl::UNSIGNED_SHORT,
|
||||||
VertexAttrType::U8 => gl::UNSIGNED_BYTE,
|
VertexAttrType::U8 => gl::UNSIGNED_BYTE,
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,11 +66,11 @@ pub trait Device {
|
||||||
fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData);
|
fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData);
|
||||||
fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer;
|
fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer;
|
||||||
fn create_buffer(&self) -> Self::Buffer;
|
fn create_buffer(&self) -> Self::Buffer;
|
||||||
fn upload_to_buffer<T>(&self,
|
fn allocate_buffer<T>(&self,
|
||||||
buffer: &Self::Buffer,
|
buffer: &Self::Buffer,
|
||||||
data: &[T],
|
data: BufferData<T>,
|
||||||
target: BufferTarget,
|
target: BufferTarget,
|
||||||
mode: BufferUploadMode);
|
mode: BufferUploadMode);
|
||||||
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
|
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
|
||||||
fn texture_size(&self, texture: &Self::Texture) -> Point2DI32;
|
fn texture_size(&self, texture: &Self::Texture) -> Point2DI32;
|
||||||
fn upload_to_texture(&self, texture: &Self::Texture, size: Point2DI32, data: &[u8]);
|
fn upload_to_texture(&self, texture: &Self::Texture, size: Point2DI32, data: &[u8]);
|
||||||
|
@ -167,10 +167,17 @@ pub enum TextureFormat {
|
||||||
pub enum VertexAttrType {
|
pub enum VertexAttrType {
|
||||||
F32,
|
F32,
|
||||||
I16,
|
I16,
|
||||||
|
I8,
|
||||||
U16,
|
U16,
|
||||||
U8,
|
U8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum BufferData<'a, T> {
|
||||||
|
Uninitialized(usize),
|
||||||
|
Memory(&'a [T]),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum BufferTarget {
|
pub enum BufferTarget {
|
||||||
Vertex,
|
Vertex,
|
||||||
|
|
|
@ -10,334 +10,123 @@
|
||||||
|
|
||||||
//! Packs data onto the GPU.
|
//! Packs data onto the GPU.
|
||||||
|
|
||||||
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, FillBatchPrimitive};
|
use crate::gpu_data::{AlphaTileBatchPrimitive, RenderCommand};
|
||||||
use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive};
|
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
use crate::sorted_vector::SortedVector;
|
|
||||||
use crate::tiles::Tiler;
|
use crate::tiles::Tiler;
|
||||||
use crate::z_buffer::ZBuffer;
|
use crate::z_buffer::ZBuffer;
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32};
|
use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
||||||
use pathfinder_geometry::basic::transform3d::Perspective;
|
use pathfinder_geometry::basic::transform3d::Perspective;
|
||||||
use pathfinder_geometry::clip::PolygonClipper3D;
|
use pathfinder_geometry::clip::PolygonClipper3D;
|
||||||
use pathfinder_geometry::distortion::BarrelDistortionCoefficients;
|
use pathfinder_geometry::distortion::BarrelDistortionCoefficients;
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
use std::cmp::{Ordering, PartialOrd};
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::mem;
|
|
||||||
use std::ops::Range;
|
|
||||||
use std::sync::mpsc::{self, Receiver, SyncSender};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::thread;
|
|
||||||
use std::u16;
|
use std::u16;
|
||||||
|
|
||||||
const MAX_FILLS_PER_BATCH: usize = 0x0002_0000;
|
// Must be a power of two.
|
||||||
const MAX_ALPHA_TILES_PER_BATCH: usize = 0x1000;
|
pub const MAX_FILLS_PER_BATCH: u32 = 0x1000;
|
||||||
const MAX_CHANNEL_MESSAGES: usize = 16;
|
|
||||||
|
|
||||||
pub struct SceneBuilderContext {
|
pub trait RenderCommandListener: Send + Sync {
|
||||||
sender: SyncSender<MainToSceneAssemblyMsg>,
|
fn send(&self, command: RenderCommand);
|
||||||
receiver: Mutex<Receiver<SceneAssemblyToMainMsg>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SceneAssemblyThread {
|
|
||||||
receiver: Receiver<MainToSceneAssemblyMsg>,
|
|
||||||
sender: SyncSender<SceneAssemblyToMainMsg>,
|
|
||||||
info: Option<SceneAssemblyThreadInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SceneAssemblyThreadInfo {
|
|
||||||
listener: Box<dyn RenderCommandListener>,
|
|
||||||
built_object_queue: SortedVector<IndexedBuiltObject>,
|
|
||||||
next_object_index: u32,
|
|
||||||
|
|
||||||
pub(crate) z_buffer: Arc<ZBuffer>,
|
|
||||||
current_pass: Pass,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MainToSceneAssemblyMsg {
|
|
||||||
NewScene { listener: Box<dyn RenderCommandListener>, z_buffer: Arc<ZBuffer> },
|
|
||||||
AddObject(IndexedBuiltObject),
|
|
||||||
SceneFinished,
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SceneAssemblyToMainMsg {
|
|
||||||
FrameFinished,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for SceneBuilderContext {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.sender.send(MainToSceneAssemblyMsg::Exit).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RenderCommandListener: Send {
|
|
||||||
fn send(&mut self, command: RenderCommand);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SceneBuilder<'a> {
|
pub struct SceneBuilder<'a> {
|
||||||
context: &'a SceneBuilderContext,
|
|
||||||
scene: &'a Scene,
|
scene: &'a Scene,
|
||||||
built_options: &'a PreparedRenderOptions,
|
built_options: &'a PreparedRenderOptions,
|
||||||
}
|
|
||||||
|
|
||||||
struct IndexedBuiltObject {
|
pub(crate) next_alpha_tile_index: AtomicUsize,
|
||||||
object: BuiltObject,
|
pub(crate) z_buffer: ZBuffer,
|
||||||
index: u32,
|
pub(crate) listener: Box<dyn RenderCommandListener>,
|
||||||
}
|
|
||||||
|
|
||||||
impl SceneBuilderContext {
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> SceneBuilderContext {
|
|
||||||
let (main_to_scene_assembly_sender,
|
|
||||||
main_to_scene_assembly_receiver) = mpsc::sync_channel(MAX_CHANNEL_MESSAGES);
|
|
||||||
let (scene_assembly_to_main_sender,
|
|
||||||
scene_assembly_to_main_receiver) = mpsc::sync_channel(MAX_CHANNEL_MESSAGES);
|
|
||||||
thread::spawn(move || {
|
|
||||||
SceneAssemblyThread::new(main_to_scene_assembly_receiver,
|
|
||||||
scene_assembly_to_main_sender).run()
|
|
||||||
});
|
|
||||||
SceneBuilderContext {
|
|
||||||
sender: main_to_scene_assembly_sender,
|
|
||||||
receiver: Mutex::new(scene_assembly_to_main_receiver),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SceneAssemblyThread {
|
|
||||||
#[inline]
|
|
||||||
fn new(receiver: Receiver<MainToSceneAssemblyMsg>, sender: SyncSender<SceneAssemblyToMainMsg>)
|
|
||||||
-> SceneAssemblyThread {
|
|
||||||
SceneAssemblyThread { receiver, sender, info: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&mut self) {
|
|
||||||
while let Ok(msg) = self.receiver.recv() {
|
|
||||||
match msg {
|
|
||||||
MainToSceneAssemblyMsg::Exit => break,
|
|
||||||
MainToSceneAssemblyMsg::NewScene { listener, z_buffer } => {
|
|
||||||
self.info = Some(SceneAssemblyThreadInfo {
|
|
||||||
listener,
|
|
||||||
built_object_queue: SortedVector::new(),
|
|
||||||
next_object_index: 0,
|
|
||||||
|
|
||||||
z_buffer,
|
|
||||||
current_pass: Pass::new(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
MainToSceneAssemblyMsg::AddObject(indexed_built_object) => {
|
|
||||||
self.info.as_mut().unwrap().built_object_queue.push(indexed_built_object);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let next_object_index = self.info.as_ref().unwrap().next_object_index;
|
|
||||||
match self.info.as_mut().unwrap().built_object_queue.peek() {
|
|
||||||
Some(ref indexed_object) if
|
|
||||||
next_object_index == indexed_object.index => {}
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
let indexed_object = self.info.as_mut().unwrap().built_object_queue.pop();
|
|
||||||
self.add_object(indexed_object.unwrap().object);
|
|
||||||
self.info.as_mut().unwrap().next_object_index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MainToSceneAssemblyMsg::SceneFinished => {
|
|
||||||
self.flush_current_pass();
|
|
||||||
self.sender.send(SceneAssemblyToMainMsg::FrameFinished).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_object(&mut self, object: BuiltObject) {
|
|
||||||
// Flush current pass if necessary.
|
|
||||||
if self.info.as_ref().unwrap().current_pass.fills.len() + object.fills.len() >
|
|
||||||
MAX_FILLS_PER_BATCH {
|
|
||||||
self.flush_current_pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
// See whether we have room for the alpha tiles. If we don't, then flush.
|
|
||||||
let (tile_count, mut alpha_tile_count) = (object.tile_count() as usize, 0);
|
|
||||||
for tile_index in 0..(object.tile_count() as usize) {
|
|
||||||
if !object.solid_tiles[tile_index] {
|
|
||||||
alpha_tile_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.info.as_ref().unwrap().current_pass.alpha_tiles.len() + alpha_tile_count >
|
|
||||||
MAX_ALPHA_TILES_PER_BATCH {
|
|
||||||
self.flush_current_pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy alpha tiles.
|
|
||||||
let mut current_pass = &mut self.info.as_mut().unwrap().current_pass;
|
|
||||||
let mut object_tile_index_to_batch_alpha_tile_index = vec![u16::MAX; tile_count];
|
|
||||||
for (tile_index, tile_backdrop) in object.tile_backdrops.data.iter().cloned().enumerate() {
|
|
||||||
// Skip solid tiles.
|
|
||||||
if object.solid_tiles[tile_index] {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let batch_alpha_tile_index = current_pass.alpha_tiles.len() as u16;
|
|
||||||
object_tile_index_to_batch_alpha_tile_index[tile_index] = batch_alpha_tile_index;
|
|
||||||
|
|
||||||
let tile_coords = object.tile_index_to_coords(tile_index as u32);
|
|
||||||
current_pass.alpha_tiles.push(AlphaTileBatchPrimitive {
|
|
||||||
tile_x: tile_coords.x() as i16,
|
|
||||||
tile_y: tile_coords.y() as i16,
|
|
||||||
object_index: current_pass.object_range.end as u16,
|
|
||||||
backdrop: tile_backdrop,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remap and copy fills, culling as necessary.
|
|
||||||
for fill in &object.fills {
|
|
||||||
let tile_coords = Point2DI32::new(fill.tile_x as i32, fill.tile_y as i32);
|
|
||||||
let object_tile_index = object.tile_coords_to_index(tile_coords).unwrap();
|
|
||||||
let object_tile_index = object_tile_index as usize;
|
|
||||||
let alpha_tile_index = object_tile_index_to_batch_alpha_tile_index[object_tile_index];
|
|
||||||
current_pass.fills.push(FillBatchPrimitive {
|
|
||||||
px: fill.px,
|
|
||||||
subpx: fill.subpx,
|
|
||||||
alpha_tile_index,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
current_pass.object_range.end += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_current_pass(&mut self) {
|
|
||||||
self.cull_alpha_tiles();
|
|
||||||
|
|
||||||
let mut info = self.info.as_mut().unwrap();
|
|
||||||
info.current_pass.solid_tiles =
|
|
||||||
info.z_buffer.build_solid_tiles(info.current_pass.object_range.clone());
|
|
||||||
|
|
||||||
let have_solid_tiles = !info.current_pass.solid_tiles.is_empty();
|
|
||||||
let have_alpha_tiles = !info.current_pass.alpha_tiles.is_empty();
|
|
||||||
let have_fills = !info.current_pass.fills.is_empty();
|
|
||||||
if !have_solid_tiles && !have_alpha_tiles && !have_fills {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
info.listener.send(RenderCommand::ClearMaskFramebuffer);
|
|
||||||
if have_solid_tiles {
|
|
||||||
let tiles = mem::replace(&mut info.current_pass.solid_tiles, vec![]);
|
|
||||||
info.listener.send(RenderCommand::SolidTile(tiles));
|
|
||||||
}
|
|
||||||
if have_fills {
|
|
||||||
let fills = mem::replace(&mut info.current_pass.fills, vec![]);
|
|
||||||
info.listener.send(RenderCommand::Fill(fills));
|
|
||||||
}
|
|
||||||
if have_alpha_tiles {
|
|
||||||
let tiles = mem::replace(&mut info.current_pass.alpha_tiles, vec![]);
|
|
||||||
info.listener.send(RenderCommand::AlphaTile(tiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
info.current_pass.object_range.start = info.current_pass.object_range.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cull_alpha_tiles(&mut self) {
|
|
||||||
let info = self.info.as_mut().unwrap();
|
|
||||||
for alpha_tile in &mut info.current_pass.alpha_tiles {
|
|
||||||
let alpha_tile_coords = Point2DI32::new(alpha_tile.tile_x as i32,
|
|
||||||
alpha_tile.tile_y as i32);
|
|
||||||
if info.z_buffer.test(alpha_tile_coords, alpha_tile.object_index as u32) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(pcwalton): Hack!
|
|
||||||
alpha_tile.tile_x = -1;
|
|
||||||
alpha_tile.tile_y = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SceneBuilder<'a> {
|
impl<'a> SceneBuilder<'a> {
|
||||||
pub fn new(context: &'a SceneBuilderContext,
|
pub fn new(scene: &'a Scene,
|
||||||
scene: &'a Scene,
|
built_options: &'a PreparedRenderOptions,
|
||||||
built_options: &'a PreparedRenderOptions)
|
listener: Box<dyn RenderCommandListener>)
|
||||||
-> SceneBuilder<'a> {
|
-> SceneBuilder<'a> {
|
||||||
SceneBuilder { context, scene, built_options }
|
let effective_view_box = scene.effective_view_box(built_options);
|
||||||
}
|
SceneBuilder {
|
||||||
|
scene,
|
||||||
|
built_options,
|
||||||
|
|
||||||
pub fn build_sequentially(&mut self, listener: Box<dyn RenderCommandListener>) {
|
next_alpha_tile_index: AtomicUsize::new(0),
|
||||||
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
z_buffer: ZBuffer::new(effective_view_box),
|
||||||
let z_buffer = Arc::new(ZBuffer::new(effective_view_box));
|
|
||||||
self.send_new_scene_message_to_assembly_thread(listener, &z_buffer);
|
|
||||||
|
|
||||||
for object_index in 0..self.scene.objects.len() {
|
|
||||||
build_object(object_index,
|
|
||||||
effective_view_box,
|
|
||||||
&z_buffer,
|
|
||||||
&self.built_options,
|
|
||||||
&self.scene,
|
|
||||||
&self.context.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.finish_and_wait_for_scene_assembly_thread();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_in_parallel(&mut self, listener: Box<dyn RenderCommandListener>) {
|
|
||||||
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
|
||||||
let z_buffer = Arc::new(ZBuffer::new(effective_view_box));
|
|
||||||
self.send_new_scene_message_to_assembly_thread(listener, &z_buffer);
|
|
||||||
|
|
||||||
(0..self.scene.objects.len()).into_par_iter().for_each(|object_index| {
|
|
||||||
build_object(object_index,
|
|
||||||
effective_view_box,
|
|
||||||
&z_buffer,
|
|
||||||
&self.built_options,
|
|
||||||
&self.scene,
|
|
||||||
&self.context.sender);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.finish_and_wait_for_scene_assembly_thread();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_new_scene_message_to_assembly_thread(&mut self,
|
|
||||||
listener: Box<dyn RenderCommandListener>,
|
|
||||||
z_buffer: &Arc<ZBuffer>) {
|
|
||||||
self.context.sender.send(MainToSceneAssemblyMsg::NewScene {
|
|
||||||
listener,
|
listener,
|
||||||
z_buffer: z_buffer.clone(),
|
}
|
||||||
}).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_and_wait_for_scene_assembly_thread(&mut self) {
|
pub fn build_sequentially(&mut self) {
|
||||||
self.context.sender.send(MainToSceneAssemblyMsg::SceneFinished).unwrap();
|
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
||||||
self.context.receiver.lock().unwrap().recv().unwrap();
|
let object_count = self.scene.objects.len();
|
||||||
|
let alpha_tiles: Vec<_> = (0..object_count).into_iter().flat_map(|object_index| {
|
||||||
|
self.build_object(object_index,
|
||||||
|
effective_view_box,
|
||||||
|
&self.built_options,
|
||||||
|
&self.scene)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
self.finish_building(alpha_tiles);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn build_object(object_index: usize,
|
pub fn build_in_parallel(&mut self) {
|
||||||
effective_view_box: RectF32,
|
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
||||||
z_buffer: &ZBuffer,
|
let object_count = self.scene.objects.len();
|
||||||
built_options: &PreparedRenderOptions,
|
let alpha_tiles: Vec<_> = (0..object_count).into_par_iter().flat_map(|object_index| {
|
||||||
scene: &Scene,
|
self.build_object(object_index,
|
||||||
sender: &SyncSender<MainToSceneAssemblyMsg>) {
|
effective_view_box,
|
||||||
let object = &scene.objects[object_index];
|
&self.built_options,
|
||||||
let outline = scene.apply_render_options(object.outline(), built_options);
|
&self.scene)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
let mut tiler = Tiler::new(&outline, effective_view_box, object_index as u16, z_buffer);
|
self.finish_building(alpha_tiles);
|
||||||
tiler.generate_tiles();
|
}
|
||||||
|
|
||||||
sender.send(MainToSceneAssemblyMsg::AddObject(IndexedBuiltObject {
|
fn build_object(&self,
|
||||||
index: object_index as u32,
|
object_index: usize,
|
||||||
object: tiler.built_object,
|
view_box: RectF32,
|
||||||
})).unwrap();
|
built_options: &PreparedRenderOptions,
|
||||||
}
|
scene: &Scene)
|
||||||
|
-> Vec<AlphaTileBatchPrimitive> {
|
||||||
|
let object = &scene.objects[object_index];
|
||||||
|
let outline = scene.apply_render_options(object.outline(), built_options);
|
||||||
|
|
||||||
struct Pass {
|
let mut tiler = Tiler::new(self, &outline, view_box, object_index as u16);
|
||||||
solid_tiles: Vec<SolidTileBatchPrimitive>,
|
tiler.generate_tiles();
|
||||||
alpha_tiles: Vec<AlphaTileBatchPrimitive>,
|
|
||||||
fills: Vec<FillBatchPrimitive>,
|
|
||||||
object_range: Range<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pass {
|
self.listener.send(RenderCommand::AddFills(tiler.built_object.fills));
|
||||||
fn new() -> Pass {
|
tiler.built_object.alpha_tiles
|
||||||
Pass { solid_tiles: vec![], alpha_tiles: vec![], fills: vec![], object_range: 0..0 }
|
}
|
||||||
|
|
||||||
|
fn cull_alpha_tiles(&self, alpha_tiles: &mut Vec<AlphaTileBatchPrimitive>) {
|
||||||
|
for alpha_tile in alpha_tiles {
|
||||||
|
let alpha_tile_coords = alpha_tile.tile_coords();
|
||||||
|
if self.z_buffer.test(alpha_tile_coords, alpha_tile.object_index as u32) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Clean this up.
|
||||||
|
alpha_tile.tile_x_lo = 0xff;
|
||||||
|
alpha_tile.tile_y_lo = 0xff;
|
||||||
|
alpha_tile.tile_hi = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pack_alpha_tiles(&mut self, alpha_tiles: Vec<AlphaTileBatchPrimitive>) {
|
||||||
|
let object_count = self.scene.objects.len() as u32;
|
||||||
|
let solid_tiles = self.z_buffer.build_solid_tiles(0..object_count);
|
||||||
|
if !solid_tiles.is_empty() {
|
||||||
|
self.listener.send(RenderCommand::SolidTile(solid_tiles));
|
||||||
|
}
|
||||||
|
if !alpha_tiles.is_empty() {
|
||||||
|
self.listener.send(RenderCommand::AlphaTile(alpha_tiles));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_building(&mut self, mut alpha_tiles: Vec<AlphaTileBatchPrimitive>) {
|
||||||
|
self.listener.send(RenderCommand::FlushFills);
|
||||||
|
self.cull_alpha_tiles(&mut alpha_tiles);
|
||||||
|
self.pack_alpha_tiles(alpha_tiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,21 +243,7 @@ impl PreparedRenderTransform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for IndexedBuiltObject {
|
impl<F> RenderCommandListener for F where F: Fn(RenderCommand) + Send + Sync {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eq(&self, other: &IndexedBuiltObject) -> bool {
|
fn send(&self, command: RenderCommand) { (*self)(command) }
|
||||||
other.index == self.index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for IndexedBuiltObject {
|
|
||||||
#[inline]
|
|
||||||
fn partial_cmp(&self, other: &IndexedBuiltObject) -> Option<Ordering> {
|
|
||||||
other.index.partial_cmp(&self.index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> RenderCommandListener for F where F: FnMut(RenderCommand) + Send {
|
|
||||||
#[inline]
|
|
||||||
fn send(&mut self, command: RenderCommand) { (*self)(command) }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,20 @@ use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::RectI32;
|
use pathfinder_geometry::basic::rect::RectI32;
|
||||||
use pathfinder_geometry::color::ColorF;
|
use pathfinder_geometry::color::ColorF;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, DepthState, Device};
|
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, DepthFunc};
|
||||||
use pathfinder_gpu::{Primitive, RenderState, StencilFunc, StencilState, TextureFormat};
|
use pathfinder_gpu::{DepthState, Device, Primitive, RenderState, StencilFunc, StencilState};
|
||||||
use pathfinder_gpu::{UniformData, VertexAttrType};
|
use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrType};
|
||||||
use pathfinder_simd::default::{F32x4, I32x4};
|
use pathfinder_simd::default::{F32x4, I32x4};
|
||||||
|
use std::cmp;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
static QUAD_VERTEX_POSITIONS: [u8; 8] = [0, 0, 1, 0, 1, 1, 0, 1];
|
static QUAD_VERTEX_POSITIONS: [u8; 8] = [0, 0, 1, 0, 1, 1, 0, 1];
|
||||||
|
|
||||||
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * 64;
|
// FIXME(pcwalton): Shrink this again!
|
||||||
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * 64;
|
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * 256;
|
||||||
|
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * 256;
|
||||||
|
|
||||||
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
||||||
const FILL_INSTANCE_SIZE: usize = 8;
|
const FILL_INSTANCE_SIZE: usize = 8;
|
||||||
|
@ -38,6 +41,8 @@ const MASK_TILE_INSTANCE_SIZE: usize = 8;
|
||||||
const FILL_COLORS_TEXTURE_WIDTH: i32 = 256;
|
const FILL_COLORS_TEXTURE_WIDTH: i32 = 256;
|
||||||
const FILL_COLORS_TEXTURE_HEIGHT: i32 = 256;
|
const FILL_COLORS_TEXTURE_HEIGHT: i32 = 256;
|
||||||
|
|
||||||
|
const MAX_FILLS_PER_BATCH: usize = 0x4000;
|
||||||
|
|
||||||
pub struct Renderer<D> where D: Device {
|
pub struct Renderer<D> where D: Device {
|
||||||
// Device
|
// Device
|
||||||
pub device: D,
|
pub device: D,
|
||||||
|
@ -78,6 +83,10 @@ pub struct Renderer<D> where D: Device {
|
||||||
viewport: RectI32,
|
viewport: RectI32,
|
||||||
render_mode: RenderMode,
|
render_mode: RenderMode,
|
||||||
use_depth: bool,
|
use_depth: bool,
|
||||||
|
|
||||||
|
// Rendering state
|
||||||
|
mask_framebuffer_cleared: bool,
|
||||||
|
buffered_fills: Vec<FillBatchPrimitive>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> Renderer<D> where D: Device {
|
impl<D> Renderer<D> where D: Device {
|
||||||
|
@ -100,10 +109,10 @@ impl<D> Renderer<D> where D: Device {
|
||||||
let gamma_lut_texture = device.create_texture_from_png(resources, "gamma-lut");
|
let gamma_lut_texture = device.create_texture_from_png(resources, "gamma-lut");
|
||||||
|
|
||||||
let quad_vertex_positions_buffer = device.create_buffer();
|
let quad_vertex_positions_buffer = device.create_buffer();
|
||||||
device.upload_to_buffer(&quad_vertex_positions_buffer,
|
device.allocate_buffer(&quad_vertex_positions_buffer,
|
||||||
&QUAD_VERTEX_POSITIONS,
|
BufferData::Memory(&QUAD_VERTEX_POSITIONS),
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Static);
|
BufferUploadMode::Static);
|
||||||
|
|
||||||
let fill_vertex_array = FillVertexArray::new(&device,
|
let fill_vertex_array = FillVertexArray::new(&device,
|
||||||
&fill_program,
|
&fill_program,
|
||||||
|
@ -175,6 +184,9 @@ impl<D> Renderer<D> where D: Device {
|
||||||
viewport,
|
viewport,
|
||||||
render_mode: RenderMode::default(),
|
render_mode: RenderMode::default(),
|
||||||
use_depth: false,
|
use_depth: false,
|
||||||
|
|
||||||
|
mask_framebuffer_cleared: false,
|
||||||
|
buffered_fills: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,16 +204,14 @@ impl<D> Renderer<D> where D: Device {
|
||||||
if self.use_depth {
|
if self.use_depth {
|
||||||
self.draw_stencil(&built_scene.quad);
|
self.draw_stencil(&built_scene.quad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.mask_framebuffer_cleared = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_command(&mut self, command: &RenderCommand) {
|
pub fn render_command(&mut self, command: &RenderCommand) {
|
||||||
match *command {
|
match *command {
|
||||||
RenderCommand::ClearMaskFramebuffer => self.clear_mask_framebuffer(),
|
RenderCommand::AddFills(ref fills) => self.add_fills(fills),
|
||||||
RenderCommand::Fill(ref fills) => {
|
RenderCommand::FlushFills => self.draw_buffered_fills(),
|
||||||
let count = fills.len() as u32;
|
|
||||||
self.upload_fills(fills);
|
|
||||||
self.draw_fills(count);
|
|
||||||
}
|
|
||||||
RenderCommand::SolidTile(ref solid_tiles) => {
|
RenderCommand::SolidTile(ref solid_tiles) => {
|
||||||
let count = solid_tiles.len() as u32;
|
let count = solid_tiles.len() as u32;
|
||||||
self.upload_solid_tiles(solid_tiles);
|
self.upload_solid_tiles(solid_tiles);
|
||||||
|
@ -284,24 +294,17 @@ impl<D> Renderer<D> where D: Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileBatchPrimitive]) {
|
fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileBatchPrimitive]) {
|
||||||
self.device.upload_to_buffer(&self.solid_tile_vertex_array().vertex_buffer,
|
self.device.allocate_buffer(&self.solid_tile_vertex_array().vertex_buffer,
|
||||||
&solid_tiles,
|
BufferData::Memory(&solid_tiles),
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
}
|
|
||||||
|
|
||||||
fn upload_fills(&mut self, fills: &[FillBatchPrimitive]) {
|
|
||||||
self.device.upload_to_buffer(&self.fill_vertex_array.vertex_buffer,
|
|
||||||
&fills,
|
|
||||||
BufferTarget::Vertex,
|
|
||||||
BufferUploadMode::Dynamic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_alpha_tiles(&mut self, alpha_tiles: &[AlphaTileBatchPrimitive]) {
|
fn upload_alpha_tiles(&mut self, alpha_tiles: &[AlphaTileBatchPrimitive]) {
|
||||||
self.device.upload_to_buffer(&self.alpha_tile_vertex_array().vertex_buffer,
|
self.device.allocate_buffer(&self.alpha_tile_vertex_array().vertex_buffer,
|
||||||
&alpha_tiles,
|
BufferData::Memory(&alpha_tiles),
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_mask_framebuffer(&mut self) {
|
fn clear_mask_framebuffer(&mut self) {
|
||||||
|
@ -311,7 +314,36 @@ impl<D> Renderer<D> where D: Device {
|
||||||
self.device.clear(Some(F32x4::splat(0.0)), None, None);
|
self.device.clear(Some(F32x4::splat(0.0)), None, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_fills(&mut self, count: u32) {
|
fn add_fills(&mut self, mut fills: &[FillBatchPrimitive]) {
|
||||||
|
if fills.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while !fills.is_empty() {
|
||||||
|
let count = cmp::min(fills.len(), MAX_FILLS_PER_BATCH - self.buffered_fills.len());
|
||||||
|
self.buffered_fills.extend_from_slice(&fills[0..count]);
|
||||||
|
fills = &fills[count..];
|
||||||
|
if self.buffered_fills.len() == MAX_FILLS_PER_BATCH {
|
||||||
|
self.draw_buffered_fills();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_buffered_fills(&mut self) {
|
||||||
|
if self.buffered_fills.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.device.allocate_buffer(&self.fill_vertex_array.vertex_buffer,
|
||||||
|
BufferData::Memory(&self.buffered_fills),
|
||||||
|
BufferTarget::Vertex,
|
||||||
|
BufferUploadMode::Dynamic);
|
||||||
|
|
||||||
|
if !self.mask_framebuffer_cleared {
|
||||||
|
self.clear_mask_framebuffer();
|
||||||
|
self.mask_framebuffer_cleared = true;
|
||||||
|
}
|
||||||
|
|
||||||
self.device.bind_framebuffer(&self.mask_framebuffer);
|
self.device.bind_framebuffer(&self.mask_framebuffer);
|
||||||
|
|
||||||
self.device.bind_vertex_array(&self.fill_vertex_array.vertex_array);
|
self.device.bind_vertex_array(&self.fill_vertex_array.vertex_array);
|
||||||
|
@ -333,7 +365,13 @@ impl<D> Renderer<D> where D: Device {
|
||||||
blend: BlendState::RGBOneAlphaOne,
|
blend: BlendState::RGBOneAlphaOne,
|
||||||
..RenderState::default()
|
..RenderState::default()
|
||||||
};
|
};
|
||||||
self.device.draw_arrays_instanced(Primitive::TriangleFan, 4, count, &render_state);
|
debug_assert!(self.buffered_fills.len() <= u32::MAX as usize);
|
||||||
|
self.device.draw_arrays_instanced(Primitive::TriangleFan,
|
||||||
|
4,
|
||||||
|
self.buffered_fills.len() as u32,
|
||||||
|
&render_state);
|
||||||
|
|
||||||
|
self.buffered_fills.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_alpha_tiles(&mut self, count: u32) {
|
fn draw_alpha_tiles(&mut self, count: u32) {
|
||||||
|
@ -530,10 +568,10 @@ impl<D> Renderer<D> where D: Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_stencil(&self, quad_positions: &[Point3DF32]) {
|
fn draw_stencil(&self, quad_positions: &[Point3DF32]) {
|
||||||
self.device.upload_to_buffer(&self.stencil_vertex_array.vertex_buffer,
|
self.device.allocate_buffer(&self.stencil_vertex_array.vertex_buffer,
|
||||||
quad_positions,
|
BufferData::Memory(quad_positions),
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
self.bind_draw_framebuffer();
|
self.bind_draw_framebuffer();
|
||||||
|
|
||||||
self.device.bind_vertex_array(&self.stencil_vertex_array.vertex_array);
|
self.device.bind_vertex_array(&self.stencil_vertex_array.vertex_array);
|
||||||
|
@ -619,7 +657,14 @@ impl<D> FillVertexArray<D> where D: Device {
|
||||||
fn new(device: &D, fill_program: &FillProgram<D>, quad_vertex_positions_buffer: &D::Buffer)
|
fn new(device: &D, fill_program: &FillProgram<D>, quad_vertex_positions_buffer: &D::Buffer)
|
||||||
-> FillVertexArray<D> {
|
-> FillVertexArray<D> {
|
||||||
let vertex_array = device.create_vertex_array();
|
let vertex_array = device.create_vertex_array();
|
||||||
|
|
||||||
let vertex_buffer = device.create_buffer();
|
let vertex_buffer = device.create_buffer();
|
||||||
|
let vertex_buffer_data: BufferData<FillBatchPrimitive> =
|
||||||
|
BufferData::Uninitialized(MAX_FILLS_PER_BATCH);
|
||||||
|
device.allocate_buffer(&vertex_buffer,
|
||||||
|
vertex_buffer_data,
|
||||||
|
BufferTarget::Vertex,
|
||||||
|
BufferUploadMode::Dynamic);
|
||||||
|
|
||||||
let tess_coord_attr = device.get_vertex_attr(&fill_program.program, "TessCoord");
|
let tess_coord_attr = device.get_vertex_attr(&fill_program.program, "TessCoord");
|
||||||
let from_px_attr = device.get_vertex_attr(&fill_program.program, "FromPx");
|
let from_px_attr = device.get_vertex_attr(&fill_program.program, "FromPx");
|
||||||
|
@ -692,6 +737,7 @@ impl<D> AlphaTileVertexArray<D> where D: Device {
|
||||||
let tile_origin_attr = device.get_vertex_attr(&alpha_tile_program.program, "TileOrigin");
|
let tile_origin_attr = device.get_vertex_attr(&alpha_tile_program.program, "TileOrigin");
|
||||||
let backdrop_attr = device.get_vertex_attr(&alpha_tile_program.program, "Backdrop");
|
let backdrop_attr = device.get_vertex_attr(&alpha_tile_program.program, "Backdrop");
|
||||||
let object_attr = device.get_vertex_attr(&alpha_tile_program.program, "Object");
|
let object_attr = device.get_vertex_attr(&alpha_tile_program.program, "Object");
|
||||||
|
let tile_index_attr = device.get_vertex_attr(&alpha_tile_program.program, "TileIndex");
|
||||||
|
|
||||||
// NB: The object must be of type `I16`, not `U16`, to work around a macOS Radeon
|
// NB: The object must be of type `I16`, not `U16`, to work around a macOS Radeon
|
||||||
// driver bug.
|
// driver bug.
|
||||||
|
@ -699,32 +745,37 @@ impl<D> AlphaTileVertexArray<D> where D: Device {
|
||||||
device.use_program(&alpha_tile_program.program);
|
device.use_program(&alpha_tile_program.program);
|
||||||
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(&tess_coord_attr,
|
device.configure_float_vertex_attr(&tess_coord_attr,
|
||||||
2,
|
2,
|
||||||
VertexAttrType::U8,
|
VertexAttrType::U8,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(&tile_origin_attr,
|
device.configure_int_vertex_attr(&tile_origin_attr,
|
||||||
2,
|
3,
|
||||||
VertexAttrType::I16,
|
VertexAttrType::U8,
|
||||||
false,
|
MASK_TILE_INSTANCE_SIZE,
|
||||||
MASK_TILE_INSTANCE_SIZE,
|
0,
|
||||||
0,
|
1);
|
||||||
1);
|
|
||||||
device.configure_int_vertex_attr(&backdrop_attr,
|
device.configure_int_vertex_attr(&backdrop_attr,
|
||||||
1,
|
1,
|
||||||
VertexAttrType::I16,
|
VertexAttrType::I8,
|
||||||
MASK_TILE_INSTANCE_SIZE,
|
MASK_TILE_INSTANCE_SIZE,
|
||||||
4,
|
3,
|
||||||
1);
|
1);
|
||||||
device.configure_int_vertex_attr(&object_attr,
|
device.configure_int_vertex_attr(&object_attr,
|
||||||
2,
|
2,
|
||||||
VertexAttrType::I16,
|
VertexAttrType::I16,
|
||||||
MASK_TILE_INSTANCE_SIZE,
|
MASK_TILE_INSTANCE_SIZE,
|
||||||
6,
|
4,
|
||||||
1);
|
1);
|
||||||
|
device.configure_int_vertex_attr(&tile_index_attr,
|
||||||
|
2,
|
||||||
|
VertexAttrType::I16,
|
||||||
|
MASK_TILE_INSTANCE_SIZE,
|
||||||
|
6,
|
||||||
|
1);
|
||||||
|
|
||||||
AlphaTileVertexArray { vertex_array, vertex_buffer }
|
AlphaTileVertexArray { vertex_array, vertex_buffer }
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
|
|
||||||
//! Packed data ready to be sent to the GPU.
|
//! Packed data ready to be sent to the GPU.
|
||||||
|
|
||||||
|
use crate::builder::SceneBuilder;
|
||||||
use crate::scene::ObjectShader;
|
use crate::scene::ObjectShader;
|
||||||
use crate::tile_map::DenseTileMap;
|
use crate::tile_map::DenseTileMap;
|
||||||
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH};
|
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH};
|
||||||
use fixedbitset::FixedBitSet;
|
|
||||||
use pathfinder_geometry::basic::line_segment::{LineSegmentF32, LineSegmentU4, LineSegmentU8};
|
use pathfinder_geometry::basic::line_segment::{LineSegmentF32, LineSegmentU4, LineSegmentU8};
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32};
|
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
||||||
|
@ -21,13 +21,14 @@ use pathfinder_geometry::util;
|
||||||
use pathfinder_simd::default::{F32x4, I32x4};
|
use pathfinder_simd::default::{F32x4, I32x4};
|
||||||
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BuiltObject {
|
pub(crate) struct BuiltObject {
|
||||||
pub bounds: RectF32,
|
pub bounds: RectF32,
|
||||||
pub tile_backdrops: DenseTileMap<i16>,
|
pub fills: Vec<FillBatchPrimitive>,
|
||||||
pub fills: Vec<FillObjectPrimitive>,
|
pub alpha_tiles: Vec<AlphaTileBatchPrimitive>,
|
||||||
pub solid_tiles: FixedBitSet,
|
pub tiles: DenseTileMap<TileObjectPrimitive>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -39,8 +40,8 @@ pub struct BuiltScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RenderCommand {
|
pub enum RenderCommand {
|
||||||
ClearMaskFramebuffer,
|
AddFills(Vec<FillBatchPrimitive>),
|
||||||
Fill(Vec<FillBatchPrimitive>),
|
FlushFills,
|
||||||
AlphaTile(Vec<AlphaTileBatchPrimitive>),
|
AlphaTile(Vec<AlphaTileBatchPrimitive>),
|
||||||
SolidTile(Vec<SolidTileBatchPrimitive>),
|
SolidTile(Vec<SolidTileBatchPrimitive>),
|
||||||
}
|
}
|
||||||
|
@ -53,8 +54,16 @@ pub struct FillObjectPrimitive {
|
||||||
pub tile_y: i16,
|
pub tile_y: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(pcwalton): Move `subpx` before `px` and remove `repr(packed)`.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TileObjectPrimitive {
|
||||||
|
/// If `u16::MAX`, then this is a solid tile.
|
||||||
|
pub alpha_tile_index: u16,
|
||||||
|
pub backdrop: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Move `subpx` before `px` and remove `repr(packed)`.
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct FillBatchPrimitive {
|
pub struct FillBatchPrimitive {
|
||||||
pub px: LineSegmentU4,
|
pub px: LineSegmentU4,
|
||||||
|
@ -70,13 +79,15 @@ pub struct SolidTileBatchPrimitive {
|
||||||
pub object_index: u16,
|
pub object_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct AlphaTileBatchPrimitive {
|
pub struct AlphaTileBatchPrimitive {
|
||||||
pub tile_x: i16,
|
pub tile_x_lo: u8,
|
||||||
pub tile_y: i16,
|
pub tile_y_lo: u8,
|
||||||
pub backdrop: i16,
|
pub tile_hi: u8,
|
||||||
|
pub backdrop: i8,
|
||||||
pub object_index: u16,
|
pub object_index: u16,
|
||||||
|
pub tile_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
@ -90,33 +101,26 @@ pub struct Stats {
|
||||||
// Utilities for built objects
|
// Utilities for built objects
|
||||||
|
|
||||||
impl BuiltObject {
|
impl BuiltObject {
|
||||||
pub fn new(bounds: RectF32) -> BuiltObject {
|
pub(crate) fn new(bounds: RectF32) -> BuiltObject {
|
||||||
// Compute the tile rect.
|
|
||||||
let tile_rect = tiles::round_rect_out_to_tile_bounds(bounds);
|
let tile_rect = tiles::round_rect_out_to_tile_bounds(bounds);
|
||||||
|
let tiles = DenseTileMap::new(tile_rect);
|
||||||
// Allocate tiles.
|
BuiltObject { bounds, fills: vec![], alpha_tiles: vec![], tiles }
|
||||||
let tile_backdrops = DenseTileMap::new(tile_rect);
|
|
||||||
let mut solid_tiles = FixedBitSet::with_capacity(tile_backdrops.data.len());
|
|
||||||
solid_tiles.insert_range(..);
|
|
||||||
|
|
||||||
BuiltObject { bounds, tile_backdrops, fills: vec![], solid_tiles }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn tile_rect(&self) -> RectI32 {
|
pub(crate) fn tile_rect(&self) -> RectI32 {
|
||||||
self.tile_backdrops.rect
|
self.tiles.rect
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn add_fill(&mut self,
|
||||||
pub fn tile_count(&self) -> u32 {
|
builder: &SceneBuilder,
|
||||||
self.tile_backdrops.data.len() as u32
|
segment: &LineSegmentF32,
|
||||||
}
|
tile_coords: Point2DI32) {
|
||||||
|
|
||||||
fn add_fill(&mut self, segment: &LineSegmentF32, tile_coords: Point2DI32) {
|
|
||||||
//println!("add_fill({:?} ({}, {}))", segment, tile_x, tile_y);
|
//println!("add_fill({:?} ({}, {}))", segment, tile_x, tile_y);
|
||||||
let tile_index = match self.tile_coords_to_index(tile_coords) {
|
|
||||||
None => return,
|
// Ensure this fill is in bounds. If not, cull it.
|
||||||
Some(tile_index) => tile_index,
|
if self.tile_coords_to_local_index(tile_coords).is_none() {
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
debug_assert_eq!(TILE_WIDTH, TILE_HEIGHT);
|
debug_assert_eq!(TILE_WIDTH, TILE_HEIGHT);
|
||||||
|
@ -142,22 +146,34 @@ impl BuiltObject {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allocate global tile if necessary.
|
||||||
|
let alpha_tile_index = self.get_or_allocate_alpha_tile_index(builder, tile_coords);
|
||||||
|
|
||||||
//println!("... ... OK, pushing");
|
//println!("... ... OK, pushing");
|
||||||
|
|
||||||
self.fills.push(FillObjectPrimitive {
|
self.fills.push(FillBatchPrimitive { px, subpx, alpha_tile_index });
|
||||||
px,
|
|
||||||
subpx,
|
|
||||||
tile_x: tile_coords.x() as i16,
|
|
||||||
tile_y: tile_coords.y() as i16,
|
|
||||||
});
|
|
||||||
self.solid_tiles.set(tile_index as usize, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_active_fill(&mut self,
|
fn get_or_allocate_alpha_tile_index(&mut self, builder: &SceneBuilder, tile_coords: Point2DI32)
|
||||||
left: f32,
|
-> u16 {
|
||||||
right: f32,
|
let local_tile_index = self.tiles.coords_to_index_unchecked(tile_coords);
|
||||||
mut winding: i16,
|
let alpha_tile_index = self.tiles.data[local_tile_index].alpha_tile_index;
|
||||||
tile_coords: Point2DI32) {
|
if alpha_tile_index != !0 {
|
||||||
|
return alpha_tile_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alpha_tile_index = builder.next_alpha_tile_index
|
||||||
|
.fetch_add(1, Ordering::Relaxed) as u16;
|
||||||
|
self.tiles.data[local_tile_index].alpha_tile_index = alpha_tile_index;
|
||||||
|
alpha_tile_index
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_active_fill(&mut self,
|
||||||
|
builder: &SceneBuilder,
|
||||||
|
left: f32,
|
||||||
|
right: f32,
|
||||||
|
mut winding: i32,
|
||||||
|
tile_coords: Point2DI32) {
|
||||||
let tile_origin_y = (tile_coords.y() * TILE_HEIGHT as i32) as f32;
|
let tile_origin_y = (tile_coords.y() * TILE_HEIGHT as i32) as f32;
|
||||||
let left = Point2DF32::new(left, tile_origin_y);
|
let left = Point2DF32::new(left, tile_origin_y);
|
||||||
let right = Point2DF32::new(right, tile_origin_y);
|
let right = Point2DF32::new(right, tile_origin_y);
|
||||||
|
@ -176,7 +192,7 @@ impl BuiltObject {
|
||||||
tile_y);*/
|
tile_y);*/
|
||||||
|
|
||||||
while winding != 0 {
|
while winding != 0 {
|
||||||
self.add_fill(&segment, tile_coords);
|
self.add_fill(builder, &segment, tile_coords);
|
||||||
if winding < 0 {
|
if winding < 0 {
|
||||||
winding += 1
|
winding += 1
|
||||||
} else {
|
} else {
|
||||||
|
@ -185,7 +201,10 @@ impl BuiltObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_fill_primitives_for_line(&mut self, mut segment: LineSegmentF32, tile_y: i32) {
|
pub(crate) fn generate_fill_primitives_for_line(&mut self,
|
||||||
|
builder: &SceneBuilder,
|
||||||
|
mut segment: LineSegmentF32,
|
||||||
|
tile_y: i32) {
|
||||||
/*println!("... generate_fill_primitives_for_line(): segment={:?} tile_y={} ({}-{})",
|
/*println!("... generate_fill_primitives_for_line(): segment={:?} tile_y={} ({}-{})",
|
||||||
segment,
|
segment,
|
||||||
tile_y,
|
tile_y,
|
||||||
|
@ -223,18 +242,19 @@ impl BuiltObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
let fill_segment = LineSegmentF32::new(&fill_from, &fill_to);
|
let fill_segment = LineSegmentF32::new(&fill_from, &fill_to);
|
||||||
self.add_fill(&fill_segment, Point2DI32::new(subsegment_tile_x, tile_y));
|
let fill_tile_coords = Point2DI32::new(subsegment_tile_x, tile_y);
|
||||||
|
self.add_fill(builder, &fill_segment, fill_tile_coords);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn tile_coords_to_index(&self, coords: Point2DI32) -> Option<u32> {
|
pub(crate) fn tile_coords_to_local_index(&self, coords: Point2DI32) -> Option<u32> {
|
||||||
self.tile_backdrops.coords_to_index(coords).map(|index| index as u32)
|
self.tiles.coords_to_index(coords).map(|index| index as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn tile_index_to_coords(&self, tile_index: u32) -> Point2DI32 {
|
pub(crate) fn local_tile_index_to_coords(&self, tile_index: u32) -> Point2DI32 {
|
||||||
self.tile_backdrops.index_to_coords(tile_index as usize)
|
self.tiles.index_to_coords(tile_index as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,11 +274,46 @@ impl BuiltScene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TileObjectPrimitive {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> TileObjectPrimitive {
|
||||||
|
TileObjectPrimitive { backdrop: 0, alpha_tile_index: !0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TileObjectPrimitive {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_solid(&self) -> bool {
|
||||||
|
self.alpha_tile_index == !0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlphaTileBatchPrimitive {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(tile_coords: Point2DI32, backdrop: i8, object_index: u16, tile_index: u16)
|
||||||
|
-> AlphaTileBatchPrimitive {
|
||||||
|
AlphaTileBatchPrimitive {
|
||||||
|
tile_x_lo: (tile_coords.x() & 0xff) as u8,
|
||||||
|
tile_y_lo: (tile_coords.y() & 0xff) as u8,
|
||||||
|
tile_hi: (((tile_coords.x() >> 8) & 0x0f) | ((tile_coords.y() >> 4) & 0xf0)) as u8,
|
||||||
|
backdrop,
|
||||||
|
object_index,
|
||||||
|
tile_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn tile_coords(&self) -> Point2DI32 {
|
||||||
|
Point2DI32::new((self.tile_x_lo as i32) | (((self.tile_hi & 0xf) as i32) << 8),
|
||||||
|
(self.tile_y_lo as i32) | (((self.tile_hi & 0xf0) as i32) << 4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for RenderCommand {
|
impl Debug for RenderCommand {
|
||||||
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
|
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
|
||||||
match *self {
|
match *self {
|
||||||
RenderCommand::ClearMaskFramebuffer => write!(formatter, "ClearMaskFramebuffer"),
|
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
|
||||||
RenderCommand::Fill(ref fills) => write!(formatter, "Fill(x{})", fills.len()),
|
RenderCommand::FlushFills => write!(formatter, "FlushFills"),
|
||||||
RenderCommand::AlphaTile(ref tiles) => {
|
RenderCommand::AlphaTile(ref tiles) => {
|
||||||
write!(formatter, "AlphaTile(x{})", tiles.len())
|
write!(formatter, "AlphaTile(x{})", tiles.len())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use crate::gpu_data::BuiltObject;
|
use crate::builder::SceneBuilder;
|
||||||
|
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject};
|
||||||
use crate::sorted_vector::SortedVector;
|
use crate::sorted_vector::SortedVector;
|
||||||
use crate::z_buffer::ZBuffer;
|
|
||||||
use pathfinder_geometry::basic::line_segment::LineSegmentF32;
|
use pathfinder_geometry::basic::line_segment::LineSegmentF32;
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
||||||
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
||||||
|
@ -25,29 +25,32 @@ const FLATTENING_TOLERANCE: f32 = 0.1;
|
||||||
pub const TILE_WIDTH: u32 = 16;
|
pub const TILE_WIDTH: u32 = 16;
|
||||||
pub const TILE_HEIGHT: u32 = 16;
|
pub const TILE_HEIGHT: u32 = 16;
|
||||||
|
|
||||||
pub struct Tiler<'o, 'z> {
|
pub(crate) struct Tiler<'a> {
|
||||||
outline: &'o Outline,
|
builder: &'a SceneBuilder<'a>,
|
||||||
|
outline: &'a Outline,
|
||||||
pub built_object: BuiltObject,
|
pub built_object: BuiltObject,
|
||||||
object_index: u16,
|
object_index: u16,
|
||||||
z_buffer: &'z ZBuffer,
|
|
||||||
|
|
||||||
point_queue: SortedVector<QueuedEndpoint>,
|
point_queue: SortedVector<QueuedEndpoint>,
|
||||||
active_edges: SortedVector<ActiveEdge>,
|
active_edges: SortedVector<ActiveEdge>,
|
||||||
old_active_edges: Vec<ActiveEdge>,
|
old_active_edges: Vec<ActiveEdge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'o, 'z> Tiler<'o, 'z> {
|
impl<'a> Tiler<'a> {
|
||||||
#[allow(clippy::or_fun_call)]
|
#[allow(clippy::or_fun_call)]
|
||||||
pub fn new(outline: &'o Outline, view_box: RectF32, object_index: u16, z_buffer: &'z ZBuffer)
|
pub(crate) fn new(builder: &'a SceneBuilder<'a>,
|
||||||
-> Tiler<'o, 'z> {
|
outline: &'a Outline,
|
||||||
|
view_box: RectF32,
|
||||||
|
object_index: u16)
|
||||||
|
-> Tiler<'a> {
|
||||||
let bounds = outline.bounds().intersection(view_box).unwrap_or(RectF32::default());
|
let bounds = outline.bounds().intersection(view_box).unwrap_or(RectF32::default());
|
||||||
let built_object = BuiltObject::new(bounds);
|
let built_object = BuiltObject::new(bounds);
|
||||||
|
|
||||||
Tiler {
|
Tiler {
|
||||||
|
builder,
|
||||||
outline,
|
outline,
|
||||||
built_object,
|
built_object,
|
||||||
object_index,
|
object_index,
|
||||||
z_buffer,
|
|
||||||
|
|
||||||
point_queue: SortedVector::new(),
|
point_queue: SortedVector::new(),
|
||||||
active_edges: SortedVector::new(),
|
active_edges: SortedVector::new(),
|
||||||
|
@ -55,7 +58,7 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_tiles(&mut self) {
|
pub(crate) fn generate_tiles(&mut self) {
|
||||||
// Initialize the point queue.
|
// Initialize the point queue.
|
||||||
self.init_point_queue();
|
self.init_point_queue();
|
||||||
|
|
||||||
|
@ -69,8 +72,8 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
self.generate_strip(strip_origin_y);
|
self.generate_strip(strip_origin_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cull.
|
// Pack and cull.
|
||||||
self.cull();
|
self.pack_and_cull();
|
||||||
//println!("{:#?}", self.built_object);
|
//println!("{:#?}", self.built_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,12 +96,21 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cull(&self) {
|
fn pack_and_cull(&mut self) {
|
||||||
for solid_tile_index in self.built_object.solid_tiles.ones() {
|
for (tile_index, tile) in self.built_object.tiles.data.iter().enumerate() {
|
||||||
if self.built_object.tile_backdrops.data[solid_tile_index] != 0 {
|
let tile_coords = self.built_object.local_tile_index_to_coords(tile_index as u32);
|
||||||
let tile_coords = self.built_object.tile_index_to_coords(solid_tile_index as u32);
|
if tile.is_solid() {
|
||||||
self.z_buffer.update(tile_coords, self.object_index);
|
if tile.backdrop != 0 {
|
||||||
|
self.builder.z_buffer.update(tile_coords, self.object_index);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let alpha_tile = AlphaTileBatchPrimitive::new(tile_coords,
|
||||||
|
tile.backdrop,
|
||||||
|
self.object_index,
|
||||||
|
tile.alpha_tile_index as u16);
|
||||||
|
self.built_object.alpha_tiles.push(alpha_tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +162,8 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
(i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x;
|
(i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x;
|
||||||
let tile_right_x = ((i32::from(current_tile_x) + 1) * TILE_WIDTH as i32) as f32;
|
let tile_right_x = ((i32::from(current_tile_x) + 1) * TILE_WIDTH as i32) as f32;
|
||||||
let current_tile_coords = Point2DI32::new(current_tile_x, tile_y);
|
let current_tile_coords = Point2DI32::new(current_tile_x, tile_y);
|
||||||
self.built_object.add_active_fill(current_x,
|
self.built_object.add_active_fill(self.builder,
|
||||||
|
current_x,
|
||||||
tile_right_x,
|
tile_right_x,
|
||||||
current_winding,
|
current_winding,
|
||||||
current_tile_coords);
|
current_tile_coords);
|
||||||
|
@ -163,8 +176,10 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
//println!("... emitting backdrop {} @ tile {}", current_winding, current_tile_x);
|
//println!("... emitting backdrop {} @ tile {}", current_winding, current_tile_x);
|
||||||
let current_tile_coords = Point2DI32::new(current_tile_x, tile_y);
|
let current_tile_coords = Point2DI32::new(current_tile_x, tile_y);
|
||||||
if let Some(tile_index) = self.built_object
|
if let Some(tile_index) = self.built_object
|
||||||
.tile_coords_to_index(current_tile_coords) {
|
.tile_coords_to_local_index(current_tile_coords) {
|
||||||
self.built_object.tile_backdrops.data[tile_index as usize] = current_winding;
|
// FIXME(pcwalton): Handle winding overflow.
|
||||||
|
self.built_object.tiles.data[tile_index as usize].backdrop =
|
||||||
|
current_winding as i8;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_tile_x += 1;
|
current_tile_x += 1;
|
||||||
|
@ -180,7 +195,8 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
let current_x =
|
let current_x =
|
||||||
(i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x;
|
(i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x;
|
||||||
let current_tile_coords = Point2DI32::new(current_tile_x, tile_y);
|
let current_tile_coords = Point2DI32::new(current_tile_x, tile_y);
|
||||||
self.built_object.add_active_fill(current_x,
|
self.built_object.add_active_fill(self.builder,
|
||||||
|
current_x,
|
||||||
segment_x,
|
segment_x,
|
||||||
current_winding,
|
current_winding,
|
||||||
current_tile_coords);
|
current_tile_coords);
|
||||||
|
@ -193,7 +209,7 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
// Process the edge.
|
// Process the edge.
|
||||||
//println!("about to process existing active edge {:#?}", active_edge);
|
//println!("about to process existing active edge {:#?}", active_edge);
|
||||||
debug_assert!(f32::abs(active_edge.crossing.y() - tile_top) < 0.1);
|
debug_assert!(f32::abs(active_edge.crossing.y() - tile_top) < 0.1);
|
||||||
active_edge.process(&mut self.built_object, tile_y);
|
active_edge.process(self.builder, &mut self.built_object, tile_y);
|
||||||
if !active_edge.segment.is_none() {
|
if !active_edge.segment.is_none() {
|
||||||
self.active_edges.push(active_edge);
|
self.active_edges.push(active_edge);
|
||||||
}
|
}
|
||||||
|
@ -229,6 +245,7 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
contour,
|
contour,
|
||||||
prev_endpoint_index,
|
prev_endpoint_index,
|
||||||
&mut self.active_edges,
|
&mut self.active_edges,
|
||||||
|
self.builder,
|
||||||
&mut self.built_object,
|
&mut self.built_object,
|
||||||
tile_y,
|
tile_y,
|
||||||
);
|
);
|
||||||
|
@ -250,6 +267,7 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
contour,
|
contour,
|
||||||
point_index.point(),
|
point_index.point(),
|
||||||
&mut self.active_edges,
|
&mut self.active_edges,
|
||||||
|
self.builder,
|
||||||
&mut self.built_object,
|
&mut self.built_object,
|
||||||
tile_y,
|
tile_y,
|
||||||
);
|
);
|
||||||
|
@ -302,12 +320,13 @@ fn process_active_segment(
|
||||||
contour: &Contour,
|
contour: &Contour,
|
||||||
from_endpoint_index: u32,
|
from_endpoint_index: u32,
|
||||||
active_edges: &mut SortedVector<ActiveEdge>,
|
active_edges: &mut SortedVector<ActiveEdge>,
|
||||||
|
builder: &SceneBuilder,
|
||||||
built_object: &mut BuiltObject,
|
built_object: &mut BuiltObject,
|
||||||
tile_y: i32,
|
tile_y: i32,
|
||||||
) {
|
) {
|
||||||
let mut active_edge = ActiveEdge::from_segment(&contour.segment_after(from_endpoint_index));
|
let mut active_edge = ActiveEdge::from_segment(&contour.segment_after(from_endpoint_index));
|
||||||
//println!("... process_active_segment({:#?})", active_edge);
|
//println!("... process_active_segment({:#?})", active_edge);
|
||||||
active_edge.process(built_object, tile_y);
|
active_edge.process(builder, built_object, tile_y);
|
||||||
if !active_edge.segment.is_none() {
|
if !active_edge.segment.is_none() {
|
||||||
//println!("... ... pushing resulting active edge: {:#?}", active_edge);
|
//println!("... ... pushing resulting active edge: {:#?}", active_edge);
|
||||||
active_edges.push(active_edge);
|
active_edges.push(active_edge);
|
||||||
|
@ -357,7 +376,7 @@ impl ActiveEdge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(&mut self, built_object: &mut BuiltObject, tile_y: i32) {
|
fn process(&mut self, builder: &SceneBuilder, built_object: &mut BuiltObject, tile_y: i32) {
|
||||||
//let tile_bottom = ((i32::from(tile_y) + 1) * TILE_HEIGHT as i32) as f32;
|
//let tile_bottom = ((i32::from(tile_y) + 1) * TILE_HEIGHT as i32) as f32;
|
||||||
//println!("process_active_edge({:#?}, tile_y={}({}))", self, tile_y, tile_bottom);
|
//println!("process_active_edge({:#?}, tile_y={}({}))", self, tile_y, tile_bottom);
|
||||||
|
|
||||||
|
@ -366,7 +385,10 @@ impl ActiveEdge {
|
||||||
|
|
||||||
if segment.is_line() {
|
if segment.is_line() {
|
||||||
let line_segment = segment.as_line_segment();
|
let line_segment = segment.as_line_segment();
|
||||||
self.segment = match self.process_line_segment(&line_segment, built_object, tile_y) {
|
self.segment = match self.process_line_segment(&line_segment,
|
||||||
|
builder,
|
||||||
|
built_object,
|
||||||
|
tile_y) {
|
||||||
Some(lower_part) => Segment::line(&lower_part),
|
Some(lower_part) => Segment::line(&lower_part),
|
||||||
None => Segment::none(),
|
None => Segment::none(),
|
||||||
};
|
};
|
||||||
|
@ -383,10 +405,8 @@ impl ActiveEdge {
|
||||||
let first_line_segment =
|
let first_line_segment =
|
||||||
LineSegmentF32::new(&self.crossing, &segment.baseline.upper_point())
|
LineSegmentF32::new(&self.crossing, &segment.baseline.upper_point())
|
||||||
.orient(winding);
|
.orient(winding);
|
||||||
if self
|
if self.process_line_segment(&first_line_segment, builder, built_object, tile_y)
|
||||||
.process_line_segment(&first_line_segment, built_object, tile_y)
|
.is_some() {
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -406,7 +426,8 @@ impl ActiveEdge {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
println!("... tile_y={} winding={} segment={:?} t={} before_segment={:?} after_segment={:?}",
|
println!("... tile_y={} winding={} segment={:?} t={} before_segment={:?}
|
||||||
|
after_segment={:?}",
|
||||||
tile_y,
|
tile_y,
|
||||||
winding,
|
winding,
|
||||||
segment,
|
segment,
|
||||||
|
@ -416,7 +437,7 @@ impl ActiveEdge {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let line = before_segment.baseline.orient(winding);
|
let line = before_segment.baseline.orient(winding);
|
||||||
match self.process_line_segment(&line, built_object, tile_y) {
|
match self.process_line_segment(&line, builder, built_object, tile_y) {
|
||||||
Some(ref lower_part) if split_t == 1.0 => {
|
Some(ref lower_part) if split_t == 1.0 => {
|
||||||
self.segment = Segment::line(&lower_part);
|
self.segment = Segment::line(&lower_part);
|
||||||
return;
|
return;
|
||||||
|
@ -437,6 +458,7 @@ impl ActiveEdge {
|
||||||
fn process_line_segment(
|
fn process_line_segment(
|
||||||
&mut self,
|
&mut self,
|
||||||
line_segment: &LineSegmentF32,
|
line_segment: &LineSegmentF32,
|
||||||
|
builder: &SceneBuilder,
|
||||||
built_object: &mut BuiltObject,
|
built_object: &mut BuiltObject,
|
||||||
tile_y: i32,
|
tile_y: i32,
|
||||||
) -> Option<LineSegmentF32> {
|
) -> Option<LineSegmentF32> {
|
||||||
|
@ -444,12 +466,12 @@ impl ActiveEdge {
|
||||||
/*println!("process_line_segment({:?}, tile_y={}) tile_bottom={}",
|
/*println!("process_line_segment({:?}, tile_y={}) tile_bottom={}",
|
||||||
line_segment, tile_y, tile_bottom);*/
|
line_segment, tile_y, tile_bottom);*/
|
||||||
if line_segment.max_y() <= tile_bottom {
|
if line_segment.max_y() <= tile_bottom {
|
||||||
built_object.generate_fill_primitives_for_line(*line_segment, tile_y);
|
built_object.generate_fill_primitives_for_line(builder, *line_segment, tile_y);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (upper_part, lower_part) = line_segment.split_at_y(tile_bottom);
|
let (upper_part, lower_part) = line_segment.split_at_y(tile_bottom);
|
||||||
built_object.generate_fill_primitives_for_line(upper_part, tile_y);
|
built_object.generate_fill_primitives_for_line(builder, upper_part, tile_y);
|
||||||
self.crossing = lower_part.upper_point();
|
self.crossing = lower_part.upper_point();
|
||||||
Some(lower_part)
|
Some(lower_part)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,10 @@ uniform vec2 uStencilTextureSize;
|
||||||
uniform vec2 uViewBoxOrigin;
|
uniform vec2 uViewBoxOrigin;
|
||||||
|
|
||||||
in vec2 aTessCoord;
|
in vec2 aTessCoord;
|
||||||
in vec2 aTileOrigin;
|
in uvec3 aTileOrigin;
|
||||||
in int aBackdrop;
|
in int aBackdrop;
|
||||||
in uint aObject;
|
in uint aObject;
|
||||||
|
in uint aTileIndex;
|
||||||
|
|
||||||
out vec2 vTexCoord;
|
out vec2 vTexCoord;
|
||||||
out float vBackdrop;
|
out float vBackdrop;
|
||||||
|
@ -31,10 +32,10 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void computeVaryings() {
|
void computeVaryings() {
|
||||||
uint tileIndex = uint(gl_InstanceID);
|
vec2 origin = vec2(aTileOrigin.xy) + vec2(aTileOrigin.z & 15u, aTileOrigin.z >> 4u) * 256.0;
|
||||||
vec2 pixelPosition = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin;
|
vec2 pixelPosition = (origin + aTessCoord) * uTileSize + uViewBoxOrigin;
|
||||||
vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0);
|
vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0);
|
||||||
vec2 texCoord = computeTileOffset(tileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize;
|
vec2 texCoord = computeTileOffset(aTileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize;
|
||||||
|
|
||||||
vTexCoord = texCoord / uStencilTextureSize;
|
vTexCoord = texCoord / uStencilTextureSize;
|
||||||
vBackdrop = float(aBackdrop);
|
vBackdrop = float(aBackdrop);
|
||||||
|
|
|
@ -21,8 +21,8 @@ use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
||||||
use pathfinder_geometry::basic::rect::RectI32;
|
use pathfinder_geometry::basic::rect::RectI32;
|
||||||
use pathfinder_geometry::color::ColorU;
|
use pathfinder_geometry::color::ColorU;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, Device, Primitive, RenderState};
|
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, Device, Primitive};
|
||||||
use pathfinder_gpu::{UniformData, VertexAttrType};
|
use pathfinder_gpu::{RenderState, UniformData, VertexAttrType};
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -160,14 +160,14 @@ impl<D> UI<D> where D: Device {
|
||||||
filled: bool) {
|
filled: bool) {
|
||||||
device.bind_vertex_array(&self.solid_vertex_array.vertex_array);
|
device.bind_vertex_array(&self.solid_vertex_array.vertex_array);
|
||||||
|
|
||||||
device.upload_to_buffer(&self.solid_vertex_array.vertex_buffer,
|
device.allocate_buffer(&self.solid_vertex_array.vertex_buffer,
|
||||||
vertex_data,
|
BufferData::Memory(vertex_data),
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
device.upload_to_buffer(&self.solid_vertex_array.index_buffer,
|
device.allocate_buffer(&self.solid_vertex_array.index_buffer,
|
||||||
index_data,
|
BufferData::Memory(index_data),
|
||||||
BufferTarget::Index,
|
BufferTarget::Index,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
|
|
||||||
device.use_program(&self.solid_program.program);
|
device.use_program(&self.solid_program.program);
|
||||||
device.set_uniform(&self.solid_program.framebuffer_size_uniform,
|
device.set_uniform(&self.solid_program.framebuffer_size_uniform,
|
||||||
|
@ -385,14 +385,14 @@ impl<D> UI<D> where D: Device {
|
||||||
index_data: &[u32],
|
index_data: &[u32],
|
||||||
texture: &D::Texture,
|
texture: &D::Texture,
|
||||||
color: ColorU) {
|
color: ColorU) {
|
||||||
device.upload_to_buffer(&self.texture_vertex_array.vertex_buffer,
|
device.allocate_buffer(&self.texture_vertex_array.vertex_buffer,
|
||||||
vertex_data,
|
BufferData::Memory(vertex_data),
|
||||||
BufferTarget::Vertex,
|
BufferTarget::Vertex,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
device.upload_to_buffer(&self.texture_vertex_array.index_buffer,
|
device.allocate_buffer(&self.texture_vertex_array.index_buffer,
|
||||||
index_data,
|
BufferData::Memory(index_data),
|
||||||
BufferTarget::Index,
|
BufferTarget::Index,
|
||||||
BufferUploadMode::Dynamic);
|
BufferUploadMode::Dynamic);
|
||||||
|
|
||||||
device.bind_vertex_array(&self.texture_vertex_array.vertex_array);
|
device.bind_vertex_array(&self.texture_vertex_array.vertex_array);
|
||||||
device.use_program(&self.texture_program.program);
|
device.use_program(&self.texture_program.program);
|
||||||
|
|
Loading…
Reference in New Issue