diff --git a/.gitignore b/.gitignore index 82600a0a..4150bad5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ target /site/dist node_modules /examples/c_canvas_minimal/build +/shaders/build # Editors *.swp diff --git a/Cargo.lock b/Cargo.lock index 18ff3a79..c35ee108 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,6 +161,24 @@ dependencies = [ "pathfinder_renderer 0.1.0", ] +[[package]] +name = "canvas_metal_minimal" +version = "0.1.0" +dependencies = [ + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "metal 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pathfinder_canvas 0.1.0", + "pathfinder_geometry 0.3.0", + "pathfinder_gl 0.1.0", + "pathfinder_gpu 0.1.0", + "pathfinder_metal 0.1.0", + "pathfinder_renderer 0.1.0", + "sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sdl2-sys 0.32.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "canvas_minimal" version = "0.1.0" @@ -419,13 +437,16 @@ name = "demo" version = "0.1.0" dependencies = [ "color-backtrace 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "metal 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "pathfinder_demo 0.1.0", "pathfinder_geometry 0.3.0", "pathfinder_gl 0.1.0", "pathfinder_gpu 0.1.0", + "pathfinder_metal 0.1.0", "pathfinder_simd 0.3.0", "pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1083,6 +1104,23 @@ name = "memoffset" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "metal" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nfd" version = "0.0.4" @@ -1187,6 +1225,33 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc_exception 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "objc_exception" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1280,9 +1345,11 @@ dependencies = [ "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "metal 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "pathfinder_geometry 0.3.0", "pathfinder_gl 0.1.0", "pathfinder_gpu 0.1.0", + "pathfinder_metal 0.1.0", "pathfinder_renderer 0.1.0", "pathfinder_simd 0.3.0", "pathfinder_svg 0.1.0", @@ -1356,10 +1423,28 @@ dependencies = [ "usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pathfinder_metal" +version = "0.1.0" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "metal 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pathfinder_geometry 0.3.0", + "pathfinder_gpu 0.1.0", + "pathfinder_simd 0.3.0", +] + [[package]] name = "pathfinder_renderer" version = "0.1.0" dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2374,6 +2459,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum metal 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cd3f21d259068945192293b7a98b1c6844af9eb7602e393c405198b229efc157" "checksum nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e752e3c216bc8a491c5b59fa46da10f1379ae450b19ac688e07f4bb55042e98" "checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" @@ -2387,6 +2473,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" +"checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +"checksum objc_exception 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "098cd29a2fa3c230d3463ae069cecccc3fdfd64c0d2496ab5b96f82dab6a00dc" +"checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" diff --git a/Cargo.toml b/Cargo.toml index 34e73625..83f67f54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "demo/magicleap", "demo/native", "examples/canvas_glutin_minimal", + "examples/canvas_metal_minimal", "examples/canvas_minimal", "examples/canvas_moire", "examples/canvas_text", @@ -15,6 +16,7 @@ members = [ "gl", "gpu", "lottie", + "metal", "renderer", "simd", "svg", diff --git a/c/include/pathfinder/pathfinder.h b/c/include/pathfinder/pathfinder.h index 8962e782..d924ce7b 100644 --- a/c/include/pathfinder/pathfinder.h +++ b/c/include/pathfinder/pathfinder.h @@ -30,12 +30,9 @@ extern "C" { #define PF_GL_VERSION_GL3 0 #define PF_GL_VERSION_GLES3 1 -// `gpu` +// `renderer` -#define PF_CLEAR_FLAGS_HAS_COLOR 0x1 -#define PF_CLEAR_FLAGS_HAS_DEPTH 0x2 -#define PF_CLEAR_FLAGS_HAS_STENCIL 0x4 -#define PF_CLEAR_FLAGS_HAS_RECT 0x8 +#define PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR 0x1 // Types @@ -85,24 +82,21 @@ typedef uint32_t PFGLVersion; // `gpu` -typedef uint8_t PFClearFlags; -struct PFClearParams { - PFColorF color; - float depth; - uint8_t stencil; - PFRectI rect; - PFClearFlags flags; -}; -typedef struct PFClearParams PFClearParams; struct PFResourceLoader; typedef struct PFResourceLoader *PFResourceLoaderRef; // `renderer` -struct PFRenderOptions { +typedef uint8_t PFRendererOptionsFlags; +struct PFRendererOptions { + PFColorF background_color; + PFRendererOptionsFlags flags; +}; +struct PFBuildOptions { uint32_t placeholder; }; -typedef struct PFRenderOptions PFRenderOptions; +typedef struct PFRendererOptions PFRendererOptions; +typedef struct PFBuildOptions PFBuildOptions; struct PFScene; typedef struct PFScene *PFSceneRef; struct PFSceneProxy; @@ -142,17 +136,17 @@ PFGLDestFramebufferRef PFGLDestFramebufferCreateFullWindow(const PFVector2I *win void PFGLDestFramebufferDestroy(PFGLDestFramebufferRef dest_framebuffer); PFGLDeviceRef PFGLDeviceCreate(PFGLVersion version, uint32_t default_framebuffer); void PFGLDeviceDestroy(PFGLDeviceRef device); -void PFGLDeviceClear(PFGLDeviceRef device, const PFClearParams *params); void PFGLLoadWith(PFGLFunctionLoader loader, void *userdata); PFGLRendererRef PFGLRendererCreate(PFGLDeviceRef device, PFResourceLoaderRef resources, - PFGLDestFramebufferRef dest_framebuffer); + PFGLDestFramebufferRef dest_framebuffer, + const PFRendererOptions *options); void PFGLRendererDestroy(PFGLRendererRef renderer); /// Returns a borrowed reference to the device. PFGLDeviceRef PFGLRendererGetDevice(PFGLRendererRef renderer); void PFSceneProxyBuildAndRenderGL(PFSceneProxyRef scene_proxy, PFGLRendererRef renderer, - const PFRenderOptions *options); + const PFBuildOptions *build_options); // `gpu` diff --git a/c/src/lib.rs b/c/src/lib.rs index f3ec9c71..7454e90c 100644 --- a/c/src/lib.rs +++ b/c/src/lib.rs @@ -18,11 +18,11 @@ use pathfinder_geometry::color::ColorF; use pathfinder_geometry::stroke::LineCap; use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gpu::resources::{FilesystemResourceLoader, ResourceLoader}; -use pathfinder_gpu::{ClearParams, Device}; use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; -use pathfinder_renderer::gpu::renderer::{DestFramebuffer, Renderer}; -use pathfinder_renderer::options::RenderOptions; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::gpu::renderer::Renderer; +use pathfinder_renderer::options::BuildOptions; use pathfinder_renderer::scene::Scene; use pathfinder_simd::default::F32x4; use std::ffi::CString; @@ -30,14 +30,13 @@ use std::os::raw::{c_char, c_void}; // Constants +// `canvas` pub const PF_LINE_CAP_BUTT: u8 = 0; pub const PF_LINE_CAP_SQUARE: u8 = 1; pub const PF_LINE_CAP_ROUND: u8 = 2; -pub const PF_CLEAR_FLAGS_HAS_COLOR: u8 = 0x1; -pub const PF_CLEAR_FLAGS_HAS_DEPTH: u8 = 0x2; -pub const PF_CLEAR_FLAGS_HAS_STENCIL: u8 = 0x4; -pub const PF_CLEAR_FLAGS_HAS_RECT: u8 = 0x8; +// `renderer` +pub const PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR: u8 = 0x1; // Types @@ -87,22 +86,19 @@ pub type PFGLRendererRef = *mut Renderer; // FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is // stable? pub type PFResourceLoaderRef = *mut Box; -#[repr(C)] -pub struct PFClearParams { - pub color: PFColorF, - pub depth: f32, - pub stencil: u8, - pub rect: PFRectI, - pub flags: PFClearFlags, -} -pub type PFClearFlags = u8; // `renderer` pub type PFSceneRef = *mut Scene; pub type PFSceneProxyRef = *mut SceneProxy; +#[repr(C)] +pub struct PFRendererOptions { + pub background_color: PFColorF, + pub flags: PFRendererOptionsFlags, +} +pub type PFRendererOptionsFlags = u8; // TODO(pcwalton) #[repr(C)] -pub struct PFRenderOptions { +pub struct PFBuildOptions { pub placeholder: u32, } @@ -252,11 +248,6 @@ pub unsafe extern "C" fn PFGLDeviceDestroy(device: PFGLDeviceRef) { drop(Box::from_raw(device)) } -#[no_mangle] -pub unsafe extern "C" fn PFGLDeviceClear(device: PFGLDeviceRef, params: *const PFClearParams) { - (*device).clear(&(*params).to_rust()) -} - #[no_mangle] pub unsafe extern "C" fn PFResourceLoaderDestroy(loader: PFResourceLoaderRef) { drop(Box::from_raw(loader)) @@ -279,11 +270,13 @@ pub unsafe extern "C" fn PFGLDestFramebufferDestroy(dest_framebuffer: PFGLDestFr #[no_mangle] pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef, resources: PFResourceLoaderRef, - dest_framebuffer: PFGLDestFramebufferRef) + dest_framebuffer: PFGLDestFramebufferRef, + options: *const PFRendererOptions) -> PFGLRendererRef { Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device), &**resources, - *Box::from_raw(dest_framebuffer)))) + *Box::from_raw(dest_framebuffer), + (*options).to_rust()))) } #[no_mangle] @@ -299,8 +292,8 @@ pub unsafe extern "C" fn PFGLRendererGetDevice(renderer: PFGLRendererRef) -> PFG #[no_mangle] pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(scene_proxy: PFSceneProxyRef, renderer: PFGLRendererRef, - options: *const PFRenderOptions) { - (*scene_proxy).build_and_render(&mut *renderer, (*options).to_rust()) + build_options: *const PFBuildOptions) { + (*scene_proxy).build_and_render(&mut *renderer, (*build_options).to_rust()) } // `renderer` @@ -358,28 +351,14 @@ impl PFVector2I { } } -// Helpers for `gpu` +// Helpers for `renderer` -impl PFClearParams { - pub fn to_rust(&self) -> ClearParams { - ClearParams { - color: if (self.flags & PF_CLEAR_FLAGS_HAS_COLOR) != 0 { - Some(self.color.to_rust()) - } else { - None - }, - rect: if (self.flags & PF_CLEAR_FLAGS_HAS_RECT) != 0 { - Some(self.rect.to_rust()) - } else { - None - }, - depth: if (self.flags & PF_CLEAR_FLAGS_HAS_DEPTH) != 0 { - Some(self.depth) - } else { - None - }, - stencil: if (self.flags & PF_CLEAR_FLAGS_HAS_STENCIL) != 0 { - Some(self.stencil) +impl PFRendererOptions { + pub fn to_rust(&self) -> RendererOptions { + let has_background_color = self.flags & PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR; + RendererOptions { + background_color: if has_background_color != 0 { + Some(self.background_color.to_rust()) } else { None }, @@ -387,10 +366,8 @@ impl PFClearParams { } } -// Helpers for `renderer` - -impl PFRenderOptions { - pub fn to_rust(&self) -> RenderOptions { - RenderOptions::default() +impl PFBuildOptions { + pub fn to_rust(&self) -> BuildOptions { + BuildOptions::default() } } diff --git a/demo/common/Cargo.toml b/demo/common/Cargo.toml index 95cb3976..3d2a88cb 100644 --- a/demo/common/Cargo.toml +++ b/demo/common/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2018" authors = ["Patrick Walton "] +[features] +pf-gl = [] + [dependencies] clap = "2.32" gl = "0.6" @@ -28,6 +31,9 @@ path = "../../gl" [dependencies.pathfinder_gpu] path = "../../gpu" +[dependencies.pathfinder_metal] +path = "../../metal" + [dependencies.pathfinder_renderer] path = "../../renderer" @@ -39,3 +45,6 @@ path = "../../svg" [dependencies.pathfinder_ui] path = "../../ui" + +[target.'cfg(target_os = "macos")'.dependencies] +metal = "0.14" diff --git a/demo/common/src/device.rs b/demo/common/src/device.rs index a34f7f83..1417bb0b 100644 --- a/demo/common/src/device.rs +++ b/demo/common/src/device.rs @@ -65,18 +65,17 @@ where let position_attr = device.get_vertex_attr(&ground_program.program, "Position").unwrap(); - device.bind_vertex_array(&vertex_array); - device.use_program(&ground_program.program); - device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U8, - stride: 0, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::I16, + stride: 4, offset: 0, divisor: 0, + buffer_index: 0, }); - device.bind_buffer(quad_vertex_indices_buffer, BufferTarget::Index); + device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); GroundVertexArray { vertex_array } } diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index 2121a93d..76c5a964 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -27,12 +27,12 @@ use pathfinder_geometry::basic::rect::RectF; use pathfinder_geometry::basic::transform2d::Transform2DF; use pathfinder_geometry::basic::transform3d::Transform3DF; use pathfinder_geometry::color::ColorU; -use pathfinder_gl::GLDevice; -use pathfinder_gpu::Device; use pathfinder_gpu::resources::ResourceLoader; +use pathfinder_gpu::Device; use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy}; -use pathfinder_renderer::gpu::renderer::{DestFramebuffer, RenderStats, RenderTime, Renderer}; -use pathfinder_renderer::options::{RenderOptions, RenderTransform}; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::gpu::renderer::{RenderStats, RenderTime, Renderer}; +use pathfinder_renderer::options::{BuildOptions, RenderTransform}; use pathfinder_renderer::post::STEM_DARKENING_FACTORS; use pathfinder_renderer::scene::Scene; use pathfinder_svg::BuiltSVG; @@ -44,6 +44,11 @@ use std::thread; use std::time::Duration; use usvg::{Options as UsvgOptions, Tree}; +#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] +use pathfinder_gl::GLDevice as DeviceImpl; +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use pathfinder_metal::MetalDevice as DeviceImpl; + static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg"; const MOUSELOOK_ROTATION_SPEED: f32 = 0.007; @@ -112,22 +117,31 @@ pub struct DemoApp where W: Window { build_time: Option, ui_model: DemoUIModel, - ui_presenter: DemoUIPresenter, + ui_presenter: DemoUIPresenter, scene_proxy: SceneProxy, - renderer: Renderer, + renderer: Renderer, - scene_framebuffer: Option<::Framebuffer>, + scene_framebuffer: Option<::Framebuffer>, - ground_program: GroundProgram, - ground_vertex_array: GroundVertexArray, + ground_program: GroundProgram, + ground_vertex_array: GroundVertexArray, } impl DemoApp where W: Window { pub fn new(window: W, window_size: WindowSize, mut options: Options) -> DemoApp { let expire_message_event_id = window.create_user_event_id(); - let device = GLDevice::new(window.gl_version(), window.gl_default_framebuffer()); + let device; + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + { + device = DeviceImpl::new(window.metal_layer()); + } + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] + { + device = DeviceImpl::new(window.gl_version(), window.gl_default_framebuffer()); + } + let resources = window.resource_loader(); // Read command line options. @@ -144,8 +158,12 @@ impl DemoApp where W: Window { viewport, window_size: window_size.device_size(), }; + // FIXME(pcwalton) + let render_options = RendererOptions { + background_color: None, + }; - let renderer = Renderer::new(device, resources, dest_framebuffer); + let renderer = Renderer::new(device, resources, dest_framebuffer, render_options); let scene_metadata = SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport.size()); let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size()); @@ -246,7 +264,7 @@ impl DemoApp where W: Window { Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)), }; - let render_options = RenderOptions { + let build_options = BuildOptions { transform: self.render_transform.clone().unwrap(), dilation: if self.ui_model.stem_darkening_effect_enabled { let font_size = APPROX_FONT_SIZE * self.window_size.backing_scale_factor; @@ -258,7 +276,7 @@ impl DemoApp where W: Window { subpixel_aa_enabled: self.ui_model.subpixel_aa_effect_enabled, }; - self.render_command_stream = Some(self.scene_proxy.build_with_stream(render_options)); + self.render_command_stream = Some(self.scene_proxy.build_with_stream(build_options)); } fn handle_events(&mut self, events: Vec) -> Vec { @@ -488,7 +506,9 @@ impl DemoApp where W: Window { self.handle_ui_events(frame, &mut ui_action); - self.window.present(); + self.renderer.device.end_commands(); + + self.window.present(&mut self.renderer.device); self.frame_counter += 1; } diff --git a/demo/common/src/renderer.rs b/demo/common/src/renderer.rs index 742186c1..fb42869d 100644 --- a/demo/common/src/renderer.rs +++ b/demo/common/src/renderer.rs @@ -15,10 +15,13 @@ use crate::window::{View, Window}; use crate::{BackgroundColor, DemoApp, UIVisibility}; use image::ColorType; use pathfinder_geometry::color::{ColorF, ColorU}; -use pathfinder_gpu::{ClearParams, DepthFunc, DepthState, Device, Primitive, RenderState}; -use pathfinder_gpu::{TextureFormat, UniformData}; +use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions}; +use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData}; +use pathfinder_geometry::basic::rect::RectI; use pathfinder_geometry::basic::transform3d::Transform3DF; -use pathfinder_renderer::gpu::renderer::{DestFramebuffer, RenderMode}; +use pathfinder_geometry::basic::vector::Vector2I; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::gpu::renderer::RenderMode; use pathfinder_renderer::gpu_data::RenderCommand; use pathfinder_renderer::options::RenderTransform; use pathfinder_renderer::post::DEFRINGING_KERNEL_CORE_GRAPHICS; @@ -42,13 +45,14 @@ const GRIDLINE_COUNT: i32 = 10; impl DemoApp where W: Window { pub fn prepare_frame_rendering(&mut self) -> u32 { - // Make the GL context current. + // Make the context current. let view = self.ui_model.mode.view(0); self.window.make_current(view); // Set up framebuffers. let window_size = self.window_size.device_size(); - let scene_count = match self.camera.mode() { + let mode = self.camera.mode(); + let scene_count = match mode { Mode::VR => { let viewport = self.window.viewport(View::Stereo(0)); if self.scene_framebuffer.is_none() @@ -82,49 +86,47 @@ impl DemoApp where W: Window { } }; - // Begin drawing the scene. - self.renderer.bind_dest_framebuffer(); - // Clear to the appropriate color. - let clear_color = if scene_count == 2 { - ColorF::transparent_black() - } else { - self.background_color().to_f32() + let clear_color = match mode { + Mode::TwoD => Some(self.background_color().to_f32()), + Mode::ThreeD => None, + Mode::VR => Some(ColorF::transparent_black()), }; - self.renderer.device.clear(&ClearParams { - color: Some(clear_color), - depth: Some(1.0), - stencil: Some(0), - ..ClearParams::default() - }); + self.renderer.set_options(RendererOptions { background_color: clear_color }); scene_count } pub fn draw_scene(&mut self) { + self.renderer.device.begin_commands(); + let view = self.ui_model.mode.view(0); self.window.make_current(view); if self.camera.mode() != Mode::VR { - self.draw_environment(); + self.draw_environment(0); } + self.renderer.device.end_commands(); + self.render_vector_scene(); // Reattach default framebuffer. - if self.camera.mode() != Mode::VR { - return; + if self.camera.mode() == Mode::VR { + if let DestFramebuffer::Other(scene_framebuffer) = + self.renderer + .replace_dest_framebuffer(DestFramebuffer::Default { + viewport: self.window.viewport(View::Mono), + window_size: self.window_size.device_size(), + }) + { + self.scene_framebuffer = Some(scene_framebuffer); + } } + } - if let DestFramebuffer::Other(scene_framebuffer) = - self.renderer - .replace_dest_framebuffer(DestFramebuffer::Default { - viewport: self.window.viewport(View::Mono), - window_size: self.window_size.device_size(), - }) - { - self.scene_framebuffer = Some(scene_framebuffer); - } + pub fn begin_compositing(&mut self) { + self.renderer.device.begin_commands(); } pub fn composite_scene(&mut self, render_scene_index: u32) { @@ -151,21 +153,12 @@ impl DemoApp where W: Window { let viewport = self.window.viewport(View::Stereo(render_scene_index)); self.window.make_current(View::Stereo(render_scene_index)); - self.renderer - .replace_dest_framebuffer(DestFramebuffer::Default { - viewport, - window_size: self.window_size.device_size(), - }); - - self.renderer.bind_draw_framebuffer(); - self.renderer.device.clear(&ClearParams { - color: Some(self.background_color().to_f32()), - depth: Some(1.0), - stencil: Some(0), - rect: Some(viewport), + self.renderer.replace_dest_framebuffer(DestFramebuffer::Default { + viewport, + window_size: self.window_size.device_size(), }); - self.draw_environment(); + self.draw_environment(render_scene_index); let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap(); let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer); @@ -207,7 +200,7 @@ impl DemoApp where W: Window { } // Draws the ground, if applicable. - fn draw_environment(&self) { + fn draw_environment(&self, render_scene_index: u32) { let frame = &self.current_frame.as_ref().unwrap(); let perspective = match frame.transform { @@ -233,31 +226,35 @@ impl DemoApp where W: Window { transform = transform.post_mul(&Transform3DF::from_scale(ground_scale, 1.0, ground_scale)); - let device = &self.renderer.device; - device.bind_vertex_array(&self.ground_vertex_array.vertex_array); - device.use_program(&self.ground_program.program); - device.set_uniform( - &self.ground_program.transform_uniform, - UniformData::from_transform_3d(&transform), - ); - device.set_uniform( - &self.ground_program.ground_color_uniform, - UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0), - ); - device.set_uniform( - &self.ground_program.gridline_color_uniform, - UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0), - ); - device.set_uniform(&self.ground_program.gridline_count_uniform, - UniformData::Int(GRIDLINE_COUNT)); - device.draw_elements( - Primitive::Triangles, - 6, - &RenderState { + // Don't clear the first scene after drawing it. + let clear_color = if render_scene_index == 0 { + Some(self.background_color().to_f32()) + } else { + None + }; + + self.renderer.device.draw_elements(6, &RenderState { + target: &self.renderer.draw_render_target(), + program: &self.ground_program.program, + vertex_array: &self.ground_vertex_array.vertex_array, + primitive: Primitive::Triangles, + textures: &[], + uniforms: &[ + (&self.ground_program.transform_uniform, + UniformData::from_transform_3d(&transform)), + (&self.ground_program.ground_color_uniform, + UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0)), + (&self.ground_program.gridline_color_uniform, + UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0)), + (&self.ground_program.gridline_count_uniform, UniformData::Int(GRIDLINE_COUNT)), + ], + viewport: self.renderer.draw_viewport(), + options: RenderOptions { depth: Some(DepthState { func: DepthFunc::Less, write: true }), - ..RenderState::default() + clear_ops: ClearOps { color: clear_color, depth: Some(1.0), stencil: Some(0) }, + ..RenderOptions::default() }, - ); + }); } fn render_vector_scene(&mut self) { @@ -305,10 +302,11 @@ impl DemoApp where W: Window { pub fn take_raster_screenshot(&mut self, path: PathBuf) { let drawable_size = self.window_size.device_size(); - let pixels = self - .renderer - .device - .read_pixels_from_default_framebuffer(drawable_size); + let viewport = RectI::new(Vector2I::default(), drawable_size); + let pixels = match self.renderer.device.read_pixels(&RenderTarget::Default, viewport) { + TextureData::U8(pixels) => pixels, + TextureData::U16(_) => panic!("Unexpected pixel format for default framebuffer!"), + }; image::save_buffer( path, &pixels, diff --git a/demo/common/src/ui.rs b/demo/common/src/ui.rs index 0a13c3bc..270893c5 100644 --- a/demo/common/src/ui.rs +++ b/demo/common/src/ui.rs @@ -500,9 +500,7 @@ where Vector2I::new(widget_x, slider_track_y), Vector2I::new(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT), ); - debug_ui_presenter - .ui_presenter - .draw_rect_outline(device, slider_track_rect, TEXT_COLOR); + debug_ui_presenter.ui_presenter.draw_rect_outline(device, slider_track_rect, TEXT_COLOR); let slider_knob_x = widget_x + model.rotation - SLIDER_KNOB_WIDTH / 2; let slider_knob_rect = RectI::new( @@ -528,7 +526,11 @@ where let widget_origin = panel_position + Vector2I::new(0, widget_size.y() * index); let widget_rect = RectI::new(widget_origin, widget_size); - if self.draw_menu_item(device, debug_ui_presenter, &text, widget_rect, false) { + if self.draw_menu_item(device, + debug_ui_presenter, + &text, + widget_rect, + false) { // FIXME(pcwalton): This is not sufficient for Android, where we will need to take in // the contents of the file. if let Ok(path) = window.run_save_dialog(screenshot_type.extension()) { @@ -554,7 +556,11 @@ where let widget_rect = RectI::new(widget_origin, widget_size); let selected = color == model.background_color; - if self.draw_menu_item(device, debug_ui_presenter, text, widget_rect, selected) { + if self.draw_menu_item(device, + debug_ui_presenter, + text, + widget_rect, + selected) { model.background_color = color; *action = UIAction::ModelChanged; } diff --git a/demo/common/src/window.rs b/demo/common/src/window.rs index 6ca9852c..d8fc0019 100644 --- a/demo/common/src/window.rs +++ b/demo/common/src/window.rs @@ -10,24 +10,38 @@ //! A minimal cross-platform windowing layer. -use gl::types::GLuint; use pathfinder_geometry::basic::vector::Vector2I; use pathfinder_geometry::basic::rect::RectI; use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF}; -use pathfinder_gl::GLVersion; use pathfinder_gpu::resources::ResourceLoader; use rayon::ThreadPoolBuilder; use std::path::PathBuf; -pub trait Window { - fn gl_version(&self) -> GLVersion; - fn gl_default_framebuffer(&self) -> GLuint { - 0 - } +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use metal::CoreAnimationLayerRef; +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use pathfinder_metal::MetalDevice; + +#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] +use gl::types::GLuint; +#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] +use pathfinder_gl::{GLDevice, GLVersion}; + +pub trait Window { + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] + fn gl_version(&self) -> GLVersion; + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] + fn gl_default_framebuffer(&self) -> GLuint { 0 } + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] + fn present(&mut self, device: &mut GLDevice); + + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + fn metal_layer(&self) -> &CoreAnimationLayerRef; + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + fn present(&mut self, device: &mut MetalDevice); - fn viewport(&self, view: View) -> RectI; fn make_current(&mut self, view: View); - fn present(&mut self); + fn viewport(&self, view: View) -> RectI; fn resource_loader(&self) -> &dyn ResourceLoader; fn create_user_event_id(&self) -> u32; fn push_user_event(message_type: u32, message_data: u32); diff --git a/demo/magicleap/src/lib.rs b/demo/magicleap/src/lib.rs index 8238b068..632023c0 100644 --- a/demo/magicleap/src/lib.rs +++ b/demo/magicleap/src/lib.rs @@ -113,6 +113,7 @@ pub unsafe extern "C" fn magicleap_pathfinder_demo_run(app: *mut c_void) { } let scene_count = app.demo.prepare_frame(events); app.demo.draw_scene(); + app.demo.begin_compositing(); for scene_index in 0..scene_count { app.demo.composite_scene(scene_index); } diff --git a/demo/native/Cargo.toml b/demo/native/Cargo.toml index 5e4468de..12515c04 100644 --- a/demo/native/Cargo.toml +++ b/demo/native/Cargo.toml @@ -5,6 +5,7 @@ edition = "2018" authors = ["Patrick Walton "] [features] +pf-gl = ["pathfinder_demo/pf-gl"] pf-no-simd = ["pathfinder_simd/pf-no-simd"] [dependencies] @@ -27,8 +28,15 @@ path = "../../gl" [dependencies.pathfinder_gpu] path = "../../gpu" +[dependencies.pathfinder_metal] +path = "../../metal" + [dependencies.pathfinder_simd] path = "../../simd" +[target.'cfg(target_os = "macos")'.dependencies] +foreign-types = "0.3" +metal = "0.14" + [target.'cfg(not(windows))'.dependencies] jemallocator = "0.1" diff --git a/demo/native/src/main.rs b/demo/native/src/main.rs index ad166dae..78636e4c 100644 --- a/demo/native/src/main.rs +++ b/demo/native/src/main.rs @@ -15,16 +15,33 @@ use pathfinder_demo::window::{Event, Keycode, SVGPath, View, Window, WindowSize} use pathfinder_demo::{DemoApp, Options}; use pathfinder_geometry::basic::vector::Vector2I; use pathfinder_geometry::basic::rect::RectI; -use pathfinder_gl::GLVersion; use pathfinder_gpu::resources::{FilesystemResourceLoader, ResourceLoader}; use sdl2::event::{Event as SDLEvent, WindowEvent}; use sdl2::keyboard::Keycode as SDLKeycode; -use sdl2::video::{GLContext, GLProfile, Window as SDLWindow}; +use sdl2::video::Window as SDLWindow; use sdl2::{EventPump, EventSubsystem, Sdl, VideoSubsystem}; use sdl2_sys::{SDL_Event, SDL_UserEvent}; use std::path::PathBuf; use std::ptr; +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use foreign_types::ForeignTypeRef; +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use metal::{CAMetalLayer, CoreAnimationLayerRef}; +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use pathfinder_metal::MetalDevice; +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use sdl2::hint; +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use sdl2::render::Canvas; +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +use sdl2_sys::SDL_RenderGetMetalLayer; + +#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] +use pathfinder_gl::{GLDevice, GLVersion}; +#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] +use sdl2::video::{GLContext, GLProfile}; + #[cfg(not(windows))] use jemallocator; @@ -55,6 +72,7 @@ fn main() { let scene_count = app.prepare_frame(events); app.draw_scene(); + app.begin_compositing(); for scene_index in 0..scene_count { app.composite_scene(scene_index); } @@ -69,22 +87,36 @@ thread_local! { } struct WindowImpl { + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] window: SDLWindow, + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] + gl_context: GLContext, + + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + canvas: Canvas, + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + metal_layer: *mut CAMetalLayer, + event_pump: EventPump, #[allow(dead_code)] - gl_context: GLContext, resource_loader: FilesystemResourceLoader, selected_file: Option, open_svg_message_type: u32, } impl Window for WindowImpl { + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] fn gl_version(&self) -> GLVersion { GLVersion::GL3 } + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + fn metal_layer(&self) -> &CoreAnimationLayerRef { + unsafe { CoreAnimationLayerRef::from_ptr(self.metal_layer) } + } + fn viewport(&self, view: View) -> RectI { - let (width, height) = self.window.drawable_size(); + let (width, height) = self.window().drawable_size(); let mut width = width as i32; let height = height as i32; let mut x_offset = 0; @@ -95,12 +127,22 @@ impl Window for WindowImpl { RectI::new(Vector2I::new(x_offset, 0), Vector2I::new(width, height)) } + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] fn make_current(&mut self, _view: View) { - self.window.gl_make_current(&self.gl_context).unwrap(); + self.window().gl_make_current(&self.gl_context).unwrap(); } - fn present(&mut self) { - self.window.gl_swap_window(); + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + fn make_current(&mut self, _: View) {} + + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] + fn present(&mut self, _: &mut GLDevice) { + self.window().gl_swap_window(); + } + + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + fn present(&mut self, device: &mut MetalDevice) { + device.present_drawable(); } fn resource_loader(&self) -> &dyn ResourceLoader { @@ -141,6 +183,7 @@ impl Window for WindowImpl { } impl WindowImpl { + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] fn new() -> WindowImpl { SDL_VIDEO.with(|sdl_video| { SDL_EVENT.with(|sdl_event| { @@ -185,9 +228,55 @@ impl WindowImpl { }) } + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + fn new() -> WindowImpl { + assert!(hint::set("SDL_RENDER_DRIVER", "metal")); + + SDL_VIDEO.with(|sdl_video| { + SDL_EVENT.with(|sdl_event| { + let window = sdl_video + .window( + "Pathfinder Demo", + DEFAULT_WINDOW_WIDTH, + DEFAULT_WINDOW_HEIGHT, + ) + .opengl() + .resizable() + .allow_highdpi() + .build() + .unwrap(); + + let canvas = window.into_canvas().present_vsync().build().unwrap(); + let metal_layer = unsafe { + SDL_RenderGetMetalLayer(canvas.raw()) as *mut CAMetalLayer + }; + + let event_pump = SDL_CONTEXT.with(|sdl_context| sdl_context.event_pump().unwrap()); + + let resource_loader = FilesystemResourceLoader::locate(); + + let open_svg_message_type = unsafe { sdl_event.register_event().unwrap() }; + + WindowImpl { + event_pump, + canvas, + metal_layer, + resource_loader, + open_svg_message_type, + selected_file: None, + } + }) + }) + } + + #[cfg(any(not(target_os = "macos"), feature = "pf-gl"))] + fn window(&self) -> &SDLWindow { &self.window } + #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] + fn window(&self) -> &SDLWindow { self.canvas.window() } + fn size(&self) -> WindowSize { - let (logical_width, logical_height) = self.window.size(); - let (drawable_width, _) = self.window.drawable_size(); + let (logical_width, logical_height) = self.window().size(); + let (drawable_width, _) = self.window().drawable_size(); WindowSize { logical_size: Vector2I::new(logical_width as i32, logical_height as i32), backing_scale_factor: drawable_width as f32 / logical_width as f32, diff --git a/examples/c_canvas_minimal/c_canvas_minimal.c b/examples/c_canvas_minimal/c_canvas_minimal.c index 9e773b02..9721ee3f 100644 --- a/examples/c_canvas_minimal/c_canvas_minimal.c +++ b/examples/c_canvas_minimal/c_canvas_minimal.c @@ -58,11 +58,9 @@ int main(int argc, const char **argv) { PFGLDestFramebufferCreateFullWindow(&(PFVector2I){640, 480}); PFGLRendererRef renderer = PFGLRendererCreate(PFGLDeviceCreate(PF_GL_VERSION_GL3, 0), PFFilesystemResourceLoaderLocate(), - dest_framebuffer); - - // Clear to white. - PFGLDeviceClear(PFGLRendererGetDevice(renderer), &(PFClearParams){ - (PFColorF){1.0, 1.0, 1.0, 1.0}, 0.0, 0, {0}, PF_CLEAR_FLAGS_HAS_COLOR + dest_framebuffer, + &(PFRendererOptions){ + (PFColorF){1.0, 1.0, 1.0, 1.0}, PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR }); // Make a canvas. We're going to draw a house. @@ -89,7 +87,7 @@ int main(int argc, const char **argv) { // Render the canvas to screen. PFSceneRef scene = PFCanvasCreateScene(canvas); PFSceneProxyRef scene_proxy = PFSceneProxyCreateFromSceneAndRayonExecutor(scene); - PFSceneProxyBuildAndRenderGL(scene_proxy, renderer, &(PFRenderOptions){0}); + PFSceneProxyBuildAndRenderGL(scene_proxy, renderer, &(PFBuildOptions){0}); SDL_GL_SwapWindow(window); // Wait for a keypress. diff --git a/examples/canvas_glutin_minimal/src/main.rs b/examples/canvas_glutin_minimal/src/main.rs index ce9360f4..b73a0fdb 100644 --- a/examples/canvas_glutin_minimal/src/main.rs +++ b/examples/canvas_glutin_minimal/src/main.rs @@ -12,18 +12,18 @@ use glutin::dpi::PhysicalSize; use glutin::{ContextBuilder, ControlFlow, Event, EventsLoop, GlProfile, GlRequest, KeyboardInput}; -use glutin::{VirtualKeyCode, WindowBuilder, WindowEvent}; +use glutin::{VirtualKeyCode, WindowBuilder, WindowEvent}; use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, Path2D}; use pathfinder_geometry::basic::vector::{Vector2F, Vector2I}; use pathfinder_geometry::basic::rect::RectF; use pathfinder_geometry::color::ColorF; use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gpu::resources::FilesystemResourceLoader; -use pathfinder_gpu::{ClearParams, Device}; use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; -use pathfinder_renderer::gpu::renderer::{DestFramebuffer, Renderer}; -use pathfinder_renderer::options::RenderOptions; +use pathfinder_renderer::gpu::renderer::Renderer; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::options::BuildOptions; fn main() { // Calculate the right logical size of the window. @@ -50,10 +50,8 @@ fn main() { // Create a Pathfinder renderer. let mut renderer = Renderer::new(GLDevice::new(GLVersion::GL3, 0), &FilesystemResourceLoader::locate(), - DestFramebuffer::full_window(window_size)); - - // Clear to white. - renderer.device.clear(&ClearParams { color: Some(ColorF::white()), ..ClearParams::default() }); + DestFramebuffer::full_window(window_size), + RendererOptions { background_color: Some(ColorF::white()) }); // Make a canvas. We're going to draw a house. let mut canvas = CanvasRenderingContext2D::new(CanvasFontContext::new(), window_size.to_f32()); @@ -77,7 +75,7 @@ fn main() { // Render the canvas to screen. let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor); - scene.build_and_render(&mut renderer, RenderOptions::default()); + scene.build_and_render(&mut renderer, BuildOptions::default()); gl_context.swap_buffers().unwrap(); // Wait for a keypress. diff --git a/examples/canvas_metal_minimal/Cargo.toml b/examples/canvas_metal_minimal/Cargo.toml new file mode 100644 index 00000000..41673409 --- /dev/null +++ b/examples/canvas_metal_minimal/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "canvas_metal_minimal" +version = "0.1.0" +authors = ["Patrick Walton "] +edition = "2018" + +[dependencies] +foreign-types = "0.3" +gl = "0.6" +metal = "0.14" +objc = "0.2" +sdl2 = "0.32" +sdl2-sys = "0.32" + +[dependencies.pathfinder_canvas] +path = "../../canvas" + +[dependencies.pathfinder_geometry] +path = "../../geometry" + +[dependencies.pathfinder_gl] +path = "../../gl" + +[dependencies.pathfinder_gpu] +path = "../../gpu" + +[dependencies.pathfinder_metal] +path = "../../metal" + +[dependencies.pathfinder_renderer] +path = "../../renderer" diff --git a/examples/canvas_metal_minimal/src/main.rs b/examples/canvas_metal_minimal/src/main.rs new file mode 100644 index 00000000..043b9d5d --- /dev/null +++ b/examples/canvas_metal_minimal/src/main.rs @@ -0,0 +1,87 @@ +// pathfinder/examples/canvas_metal_minimal/src/main.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use foreign_types::ForeignTypeRef; +use metal::{CAMetalLayer, CoreAnimationLayerRef}; +use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, Path2D}; +use pathfinder_geometry::basic::vector::{Vector2F, Vector2I}; +use pathfinder_geometry::basic::rect::RectF; +use pathfinder_geometry::color::ColorF; +use pathfinder_gpu::resources::FilesystemResourceLoader; +use pathfinder_metal::MetalDevice; +use pathfinder_renderer::concurrent::rayon::RayonExecutor; +use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::gpu::renderer::Renderer; +use pathfinder_renderer::options::BuildOptions; +use sdl2::event::Event; +use sdl2::hint; +use sdl2::keyboard::Keycode; +use sdl2_sys::SDL_RenderGetMetalLayer; + +fn main() { + // Set up SDL2. + assert!(hint::set("SDL_RENDER_DRIVER", "metal")); + let sdl_context = sdl2::init().unwrap(); + let video = sdl_context.video().unwrap(); + + // Open a window. + let window_size = Vector2I::new(640, 480); + let window = video.window("Minimal example", window_size.x() as u32, window_size.y() as u32) + .opengl() + .build() + .unwrap(); + + // Create a Metal context. + let canvas = window.into_canvas().present_vsync().build().unwrap(); + let metal_layer = unsafe { + CoreAnimationLayerRef::from_ptr(SDL_RenderGetMetalLayer(canvas.raw()) as *mut CAMetalLayer) + }; + + // Create a Pathfinder renderer. + let mut renderer = Renderer::new(MetalDevice::new(metal_layer), + &FilesystemResourceLoader::locate(), + DestFramebuffer::full_window(window_size), + RendererOptions { background_color: Some(ColorF::white()) }); + + // Make a canvas. We're going to draw a house. + let mut canvas = CanvasRenderingContext2D::new(CanvasFontContext::new(), window_size.to_f32()); + + // Set line width. + canvas.set_line_width(10.0); + + // Draw walls. + canvas.stroke_rect(RectF::new(Vector2F::new(75.0, 140.0), Vector2F::new(150.0, 110.0))); + + // Draw door. + canvas.fill_rect(RectF::new(Vector2F::new(130.0, 190.0), Vector2F::new(40.0, 60.0))); + + // Draw roof. + let mut path = Path2D::new(); + path.move_to(Vector2F::new(50.0, 140.0)); + path.line_to(Vector2F::new(150.0, 60.0)); + path.line_to(Vector2F::new(250.0, 140.0)); + path.close_path(); + canvas.stroke_path(path); + + // Render the canvas to screen. + let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor); + scene.build_and_render(&mut renderer, BuildOptions::default()); + renderer.device.present_drawable(); + + // Wait for a keypress. + let mut event_pump = sdl_context.event_pump().unwrap(); + loop { + match event_pump.wait_event() { + Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return, + _ => {} + } + } +} diff --git a/examples/canvas_minimal/src/main.rs b/examples/canvas_minimal/src/main.rs index 07588dcc..06fc1a7e 100644 --- a/examples/canvas_minimal/src/main.rs +++ b/examples/canvas_minimal/src/main.rs @@ -14,11 +14,11 @@ use pathfinder_geometry::basic::rect::RectF; use pathfinder_geometry::color::ColorF; use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gpu::resources::FilesystemResourceLoader; -use pathfinder_gpu::{ClearParams, Device}; use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; -use pathfinder_renderer::gpu::renderer::{DestFramebuffer, Renderer}; -use pathfinder_renderer::options::RenderOptions; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::gpu::renderer::Renderer; +use pathfinder_renderer::options::BuildOptions; use sdl2::event::Event; use sdl2::keyboard::Keycode; use sdl2::video::GLProfile; @@ -48,10 +48,8 @@ fn main() { // Create a Pathfinder renderer. let mut renderer = Renderer::new(GLDevice::new(GLVersion::GL3, 0), &FilesystemResourceLoader::locate(), - DestFramebuffer::full_window(window_size)); - - // Clear to white. - renderer.device.clear(&ClearParams { color: Some(ColorF::white()), ..ClearParams::default() }); + DestFramebuffer::full_window(window_size), + RendererOptions { background_color: Some(ColorF::white()) }); // Make a canvas. We're going to draw a house. let mut canvas = CanvasRenderingContext2D::new(CanvasFontContext::new(), window_size.to_f32()); @@ -75,7 +73,7 @@ fn main() { // Render the canvas to screen. let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor); - scene.build_and_render(&mut renderer, RenderOptions::default()); + scene.build_and_render(&mut renderer, BuildOptions::default()); window.gl_swap_window(); // Wait for a keypress. diff --git a/examples/canvas_moire/src/main.rs b/examples/canvas_moire/src/main.rs index 7a55404e..0ac8fffd 100644 --- a/examples/canvas_moire/src/main.rs +++ b/examples/canvas_moire/src/main.rs @@ -13,11 +13,11 @@ use pathfinder_geometry::basic::vector::{Vector2F, Vector2I}; use pathfinder_geometry::color::{ColorF, ColorU}; use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gpu::resources::FilesystemResourceLoader; -use pathfinder_gpu::{ClearParams, Device}; use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; -use pathfinder_renderer::gpu::renderer::{DestFramebuffer, Renderer}; -use pathfinder_renderer::options::RenderOptions; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::gpu::renderer::Renderer; +use pathfinder_renderer::options::BuildOptions; use sdl2::event::Event; use sdl2::keyboard::Keycode; use sdl2::video::GLProfile; @@ -67,7 +67,8 @@ fn main() { // Create our renderers. let renderer = Renderer::new(GLDevice::new(GLVersion::GL3, 0), &FilesystemResourceLoader::locate(), - DestFramebuffer::full_window(drawable_size)); + DestFramebuffer::full_window(drawable_size), + RendererOptions { background_color: Some(ColorF::white()) }); let mut moire_renderer = MoireRenderer::new(renderer, window_size, drawable_size); // Enter main render loop. @@ -124,10 +125,7 @@ impl MoireRenderer { Vector2F::new(1.0, sin_time).scale(cos_time * INNER_RADIUS); // Clear to background color. - self.renderer.device.clear(&ClearParams { - color: Some(background_color), - ..ClearParams::default() - }); + self.renderer.set_options(RendererOptions { background_color: Some(background_color) }); // Make a canvas. let mut canvas = CanvasRenderingContext2D::new(self.font_context.clone(), @@ -142,7 +140,7 @@ impl MoireRenderer { // Build and render scene. self.scene.replace_scene(canvas.into_scene()); - self.scene.build_and_render(&mut self.renderer, RenderOptions::default()); + self.scene.build_and_render(&mut self.renderer, BuildOptions::default()); self.frame += 1; } diff --git a/examples/canvas_text/src/main.rs b/examples/canvas_text/src/main.rs index 0829723e..de69fbe4 100644 --- a/examples/canvas_text/src/main.rs +++ b/examples/canvas_text/src/main.rs @@ -13,11 +13,11 @@ use pathfinder_geometry::basic::vector::{Vector2F, Vector2I}; use pathfinder_geometry::color::ColorF; use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gpu::resources::FilesystemResourceLoader; -use pathfinder_gpu::{ClearParams, Device}; use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; -use pathfinder_renderer::gpu::renderer::{DestFramebuffer, Renderer}; -use pathfinder_renderer::options::RenderOptions; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::gpu::renderer::Renderer; +use pathfinder_renderer::options::BuildOptions; use sdl2::event::Event; use sdl2::keyboard::Keycode; use sdl2::video::GLProfile; @@ -47,10 +47,8 @@ fn main() { // Create a Pathfinder renderer. let mut renderer = Renderer::new(GLDevice::new(GLVersion::GL3, 0), &FilesystemResourceLoader::locate(), - DestFramebuffer::full_window(window_size)); - - // Clear to white. - renderer.device.clear(&ClearParams { color: Some(ColorF::white()), ..ClearParams::default() }); + DestFramebuffer::full_window(window_size), + RendererOptions { background_color: Some(ColorF::white()) }); // Make a canvas. We're going to draw some text. let mut canvas = CanvasRenderingContext2D::new(CanvasFontContext::new(), window_size.to_f32()); @@ -63,7 +61,7 @@ fn main() { // Render the canvas to screen. let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor); - scene.build_and_render(&mut renderer, RenderOptions::default()); + scene.build_and_render(&mut renderer, BuildOptions::default()); window.gl_swap_window(); // Wait for a keypress. diff --git a/geometry/src/color.rs b/geometry/src/color.rs index cde14be6..5f3676be 100644 --- a/geometry/src/color.rs +++ b/geometry/src/color.rs @@ -75,13 +75,18 @@ impl Debug for ColorU { } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct ColorF(pub F32x4); impl ColorF { + #[inline] + pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF { + ColorF(F32x4::new(r, g, b, a)) + } + #[inline] pub fn transparent_black() -> ColorF { - ColorF(F32x4::default()) + ColorF::default() } #[inline] @@ -120,3 +125,16 @@ impl ColorF { self.0[3] } } + +impl Debug for ColorF { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + write!( + formatter, + "rgba({}, {}, {}, {})", + self.r() * 255.0, + self.g() * 255.0, + self.b() * 255.0, + self.a() + ) + } +} diff --git a/gl/src/lib.rs b/gl/src/lib.rs index aa08233a..f53faeaf 100644 --- a/gl/src/lib.rs +++ b/gl/src/lib.rs @@ -14,13 +14,13 @@ extern crate log; use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid}; -use pathfinder_geometry::basic::vector::Vector2I; use pathfinder_geometry::basic::rect::RectI; +use pathfinder_geometry::basic::vector::Vector2I; use pathfinder_gpu::resources::ResourceLoader; -use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearParams}; -use pathfinder_gpu::{DepthFunc, Device, Primitive, RenderState, ShaderKind, StencilFunc}; -use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrClass}; -use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType}; +use pathfinder_gpu::{RenderTarget, BlendState, BufferData, BufferTarget, BufferUploadMode}; +use pathfinder_gpu::{ClearOps, DepthFunc, Device, Primitive, RenderOptions, RenderState}; +use pathfinder_gpu::{ShaderKind, StencilFunc, TextureData, TextureFormat, UniformData}; +use pathfinder_gpu::{VertexAttrClass, VertexAttrDescriptor, VertexAttrType}; use pathfinder_simd::default::F32x4; use std::ffi::CString; use std::mem; @@ -60,10 +60,32 @@ impl GLDevice { } } - fn set_render_state(&self, render_state: &RenderState) { + fn set_render_state(&self, render_state: &RenderState) { + self.bind_render_target(render_state.target); + + unsafe { + let (origin, size) = (render_state.viewport.origin(), render_state.viewport.size()); + gl::Viewport(origin.x(), origin.y(), size.x(), size.y()); + } + + if render_state.options.clear_ops.has_ops() { + self.clear(&render_state.options.clear_ops); + } + + self.use_program(render_state.program); + self.bind_vertex_array(render_state.vertex_array); + for (texture_unit, texture) in render_state.textures.iter().enumerate() { + self.bind_texture(texture, texture_unit as u32); + } + + render_state.uniforms.iter().for_each(|(uniform, data)| self.set_uniform(uniform, data)); + self.set_render_options(&render_state.options); + } + + fn set_render_options(&self, render_options: &RenderOptions) { unsafe { // Set blend. - match render_state.blend { + match render_options.blend { BlendState::Off => { gl::Disable(gl::BLEND); ck(); } @@ -91,7 +113,7 @@ impl GLDevice { } // Set depth. - match render_state.depth { + match render_options.depth { None => { gl::Disable(gl::DEPTH_TEST); ck(); } @@ -103,7 +125,7 @@ impl GLDevice { } // Set stencil. - match render_state.stencil { + match render_options.stencil { None => { gl::Disable(gl::STENCIL_TEST); ck(); } @@ -123,14 +145,50 @@ impl GLDevice { } // Set color mask. - let color_mask = render_state.color_mask as GLboolean; + let color_mask = render_options.color_mask as GLboolean; gl::ColorMask(color_mask, color_mask, color_mask, color_mask); ck(); } } - fn reset_render_state(&self, render_state: &RenderState) { + fn set_uniform(&self, uniform: &GLUniform, data: &UniformData) { unsafe { - match render_state.blend { + match *data { + UniformData::Int(value) => { + gl::Uniform1i(uniform.location, value); ck(); + } + UniformData::Mat4(data) => { + assert_eq!(mem::size_of::<[F32x4; 4]>(), 4 * 4 * 4); + let data_ptr: *const F32x4 = data.as_ptr(); + gl::UniformMatrix4fv(uniform.location, + 1, + gl::FALSE, + data_ptr as *const GLfloat); + } + UniformData::Vec2(data) => { + gl::Uniform2f(uniform.location, data.x(), data.y()); ck(); + } + UniformData::Vec4(data) => { + gl::Uniform4f(uniform.location, data.x(), data.y(), data.z(), data.w()); ck(); + } + UniformData::TextureUnit(unit) => { + gl::Uniform1i(uniform.location, unit as GLint); ck(); + } + } + } + } + + fn reset_render_state(&self, render_state: &RenderState) { + self.reset_render_options(&render_state.options); + for texture_unit in 0..(render_state.textures.len() as u32) { + self.unbind_texture(texture_unit); + } + self.unuse_program(); + self.unbind_vertex_array(); + } + + fn reset_render_options(&self, render_options: &RenderOptions) { + unsafe { + match render_options.blend { BlendState::Off => {} BlendState::RGBOneAlphaOneMinusSrcAlpha | BlendState::RGBOneAlphaOne | @@ -139,11 +197,11 @@ impl GLDevice { } } - if render_state.depth.is_some() { + if render_options.depth.is_some() { gl::Disable(gl::DEPTH_TEST); ck(); } - if render_state.stencil.is_some() { + if render_options.stencil.is_some() { gl::StencilMask(!0); ck(); gl::Disable(gl::STENCIL_TEST); ck(); } @@ -165,37 +223,18 @@ impl Device for GLDevice { type VertexAttr = GLVertexAttr; fn create_texture(&self, format: TextureFormat, size: Vector2I) -> GLTexture { - let (gl_internal_format, gl_format, gl_type); - match format { - TextureFormat::R8 => { - gl_internal_format = gl::R8 as GLint; - gl_format = gl::RED; - gl_type = gl::UNSIGNED_BYTE; - } - TextureFormat::R16F => { - gl_internal_format = gl::R16F as GLint; - gl_format = gl::RED; - gl_type = gl::HALF_FLOAT; - } - TextureFormat::RGBA8 => { - gl_internal_format = gl::RGBA as GLint; - gl_format = gl::RGBA; - gl_type = gl::UNSIGNED_BYTE; - } - } - - let mut texture = GLTexture { gl_texture: 0, size }; + let mut texture = GLTexture { gl_texture: 0, size, format }; unsafe { gl::GenTextures(1, &mut texture.gl_texture); ck(); self.bind_texture(&texture, 0); gl::TexImage2D(gl::TEXTURE_2D, 0, - gl_internal_format, + format.gl_internal_format(), size.x() as GLsizei, size.y() as GLsizei, 0, - gl_format, - gl_type, + format.gl_format(), + format.gl_type(), ptr::null()); ck(); } @@ -206,7 +245,7 @@ impl Device for GLDevice { fn create_texture_from_data(&self, size: Vector2I, data: &[u8]) -> GLTexture { assert!(data.len() >= size.x() as usize * size.y() as usize); - let mut texture = GLTexture { gl_texture: 0, size }; + let mut texture = GLTexture { gl_texture: 0, size, format: TextureFormat::R8 }; unsafe { gl::GenTextures(1, &mut texture.gl_texture); ck(); self.bind_texture(&texture, 0); @@ -324,13 +363,14 @@ impl Device for GLDevice { GLUniform { location } } - fn use_program(&self, program: &Self::Program) { - unsafe { - gl::UseProgram(program.gl_program); ck(); - } - } + fn configure_vertex_attr(&self, + vertex_array: &GLVertexArray, + attr: &GLVertexAttr, + descriptor: &VertexAttrDescriptor) { + debug_assert_ne!(descriptor.stride, 0); + + self.bind_vertex_array(vertex_array); - fn configure_vertex_attr(&self, attr: &GLVertexAttr, descriptor: &VertexAttrDescriptor) { unsafe { let attr_type = descriptor.attr_type.to_gl_type(); match descriptor.class { @@ -359,41 +399,8 @@ impl Device for GLDevice { gl::VertexAttribDivisor(attr.attr, descriptor.divisor); ck(); gl::EnableVertexAttribArray(attr.attr); ck(); } - } - fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData) { - unsafe { - match data { - UniformData::Int(value) => { - gl::Uniform1i(uniform.location, value); ck(); - } - UniformData::Mat2(data) => { - assert_eq!(mem::size_of::(), 4 * 4); - let data_ptr: *const F32x4 = &data; - gl::UniformMatrix2fv(uniform.location, - 1, - gl::FALSE, - data_ptr as *const GLfloat); - } - UniformData::Mat4(data) => { - assert_eq!(mem::size_of::<[F32x4; 4]>(), 4 * 4 * 4); - let data_ptr: *const F32x4 = data.as_ptr(); - gl::UniformMatrix4fv(uniform.location, - 1, - gl::FALSE, - data_ptr as *const GLfloat); - } - UniformData::Vec2(data) => { - gl::Uniform2f(uniform.location, data.x(), data.y()); ck(); - } - UniformData::Vec4(data) => { - gl::Uniform4f(uniform.location, data.x(), data.y(), data.z(), data.w()); ck(); - } - UniformData::TextureUnit(unit) => { - gl::Uniform1i(uniform.location, unit as GLint); ck(); - } - } - } + self.unbind_vertex_array(); } fn create_framebuffer(&self, texture: GLTexture) -> GLFramebuffer { @@ -470,77 +477,67 @@ impl Device for GLDevice { self.set_texture_parameters(texture); } - fn read_pixels_from_default_framebuffer(&self, size: Vector2I) -> Vec { - let mut pixels = vec![0; size.x() as usize * size.y() as usize * 4]; - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, self.default_framebuffer); ck(); - gl::ReadPixels(0, - 0, - size.x() as GLsizei, - size.y() as GLsizei, - gl::RGBA, - gl::UNSIGNED_BYTE, - pixels.as_mut_ptr() as *mut GLvoid); ck(); - } + fn read_pixels(&self, render_target: &RenderTarget, viewport: RectI) -> TextureData { + let (origin, size) = (viewport.origin(), viewport.size()); + let format = self.render_target_format(render_target); + self.bind_render_target(render_target); - // Flip right-side-up. - let stride = size.x() as usize * 4; - for y in 0..(size.y() as usize / 2) { - let (index_a, index_b) = (y * stride, (size.y() as usize - y - 1) * stride); - for offset in 0..stride { - pixels.swap(index_a + offset, index_b + offset); + match format { + TextureFormat::R8 | TextureFormat::RGBA8 => { + let channels = format.channels(); + let mut pixels = vec![0; size.x() as usize * size.y() as usize * channels]; + unsafe { + gl::ReadPixels(origin.x(), + origin.y(), + size.x() as GLsizei, + size.y() as GLsizei, + format.gl_format(), + format.gl_type(), + pixels.as_mut_ptr() as *mut GLvoid); ck(); + } + flip_y(&mut pixels, size, channels); + TextureData::U8(pixels) } - } - - pixels - } - - fn clear(&self, params: &ClearParams) { - unsafe { - if let Some(rect) = params.rect { - let (origin, size) = (rect.origin(), rect.size()); - gl::Scissor(origin.x(), origin.y(), size.x(), size.y()); ck(); - gl::Enable(gl::SCISSOR_TEST); ck(); - } - - let mut flags = 0; - if let Some(color) = params.color { - gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); ck(); - gl::ClearColor(color.r(), color.g(), color.b(), color.a()); ck(); - flags |= gl::COLOR_BUFFER_BIT; - } - if let Some(depth) = params.depth { - gl::DepthMask(gl::TRUE); ck(); - gl::ClearDepthf(depth as _); ck(); // FIXME(pcwalton): GLES - flags |= gl::DEPTH_BUFFER_BIT; - } - if let Some(stencil) = params.stencil { - gl::StencilMask(!0); ck(); - gl::ClearStencil(stencil as GLint); ck(); - flags |= gl::STENCIL_BUFFER_BIT; - } - if flags != 0 { - gl::Clear(flags); ck(); - } - - if params.rect.is_some() { - gl::Disable(gl::SCISSOR_TEST); ck(); + TextureFormat::R16F => { + let mut pixels = vec![0; size.x() as usize * size.y() as usize]; + unsafe { + gl::ReadPixels(origin.x(), + origin.y(), + size.x() as GLsizei, + size.y() as GLsizei, + format.gl_format(), + format.gl_type(), + pixels.as_mut_ptr() as *mut GLvoid); ck(); + } + flip_y(&mut pixels, size, 1); + TextureData::U16(pixels) } } } - fn draw_arrays(&self, primitive: Primitive, index_count: u32, render_state: &RenderState) { + fn begin_commands(&self) { + // TODO(pcwalton): Add some checks in debug mode to make sure render commands are bracketed + // by these? + } + + fn end_commands(&self) { + unsafe { gl::Flush(); } + } + + fn draw_arrays(&self, index_count: u32, render_state: &RenderState) { self.set_render_state(render_state); unsafe { - gl::DrawArrays(primitive.to_gl_primitive(), 0, index_count as GLsizei); ck(); + gl::DrawArrays(render_state.primitive.to_gl_primitive(), + 0, + index_count as GLsizei); ck(); } self.reset_render_state(render_state); } - fn draw_elements(&self, primitive: Primitive, index_count: u32, render_state: &RenderState) { + fn draw_elements(&self, index_count: u32, render_state: &RenderState) { self.set_render_state(render_state); unsafe { - gl::DrawElements(primitive.to_gl_primitive(), + gl::DrawElements(render_state.primitive.to_gl_primitive(), index_count as GLsizei, gl::UNSIGNED_INT, ptr::null()); ck(); @@ -549,13 +546,12 @@ impl Device for GLDevice { } fn draw_elements_instanced(&self, - primitive: Primitive, index_count: u32, instance_count: u32, - render_state: &RenderState) { + render_state: &RenderState) { self.set_render_state(render_state); unsafe { - gl::DrawElementsInstanced(primitive.to_gl_primitive(), + gl::DrawElementsInstanced(render_state.primitive.to_gl_primitive(), index_count as GLsizei, gl::UNSIGNED_INT, ptr::null(), @@ -588,66 +584,102 @@ impl Device for GLDevice { } #[inline] - fn timer_query_is_available(&self, query: &Self::TimerQuery) -> bool { + fn get_timer_query(&self, query: &Self::TimerQuery) -> Option { unsafe { let mut result = 0; gl::GetQueryObjectiv(query.gl_query, gl::QUERY_RESULT_AVAILABLE, &mut result); ck(); - result != gl::FALSE as GLint - } - } - - #[inline] - fn get_timer_query(&self, query: &Self::TimerQuery) -> Duration { - unsafe { + if result == gl::FALSE as GLint { + return None; + } let mut result = 0; gl::GetQueryObjectui64v(query.gl_query, gl::QUERY_RESULT, &mut result); ck(); - Duration::from_nanos(result) + Some(Duration::from_nanos(result)) } } #[inline] + fn bind_buffer(&self, vertex_array: &GLVertexArray, buffer: &GLBuffer, target: BufferTarget) { + self.bind_vertex_array(vertex_array); + unsafe { + gl::BindBuffer(target.to_gl_target(), buffer.gl_buffer); ck(); + } + self.unbind_vertex_array(); + } + + #[inline] + fn create_shader( + &self, + resources: &dyn ResourceLoader, + name: &str, + kind: ShaderKind, + ) -> Self::Shader { + let suffix = match kind { + ShaderKind::Vertex => 'v', + ShaderKind::Fragment => 'f', + }; + let path = format!("shaders/gl3/{}.{}s.glsl", name, suffix); + self.create_shader_from_source(name, &resources.slurp(&path).unwrap(), kind) + } +} + +impl GLDevice { + fn bind_render_target(&self, attachment: &RenderTarget) { + match *attachment { + RenderTarget::Default => self.bind_default_framebuffer(), + RenderTarget::Framebuffer(framebuffer) => self.bind_framebuffer(framebuffer), + } + } + fn bind_vertex_array(&self, vertex_array: &GLVertexArray) { unsafe { gl::BindVertexArray(vertex_array.gl_vertex_array); ck(); } } - #[inline] - fn bind_buffer(&self, buffer: &GLBuffer, target: BufferTarget) { + fn unbind_vertex_array(&self) { unsafe { - gl::BindBuffer(target.to_gl_target(), buffer.gl_buffer); ck(); + gl::BindVertexArray(0); ck(); } } - #[inline] - fn bind_default_framebuffer(&self, viewport: RectI) { - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, self.default_framebuffer); ck(); - gl::Viewport(viewport.origin().x(), - viewport.origin().y(), - viewport.size().x(), - viewport.size().y()); ck(); - } - } - - #[inline] - fn bind_framebuffer(&self, framebuffer: &GLFramebuffer) { - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.gl_framebuffer); ck(); - gl::Viewport(0, 0, framebuffer.texture.size.x(), framebuffer.texture.size.y()); ck(); - } - } - - #[inline] fn bind_texture(&self, texture: &GLTexture, unit: u32) { unsafe { gl::ActiveTexture(gl::TEXTURE0 + unit); ck(); gl::BindTexture(gl::TEXTURE_2D, texture.gl_texture); ck(); } } -} -impl GLDevice { + fn unbind_texture(&self, unit: u32) { + unsafe { + gl::ActiveTexture(gl::TEXTURE0 + unit); ck(); + gl::BindTexture(gl::TEXTURE_2D, 0); ck(); + } + } + + fn use_program(&self, program: &GLProgram) { + unsafe { + gl::UseProgram(program.gl_program); ck(); + } + } + + fn unuse_program(&self) { + unsafe { + gl::UseProgram(0); ck(); + } + } + + fn bind_default_framebuffer(&self) { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, self.default_framebuffer); ck(); + } + } + + fn bind_framebuffer(&self, framebuffer: &GLFramebuffer) { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer.gl_framebuffer); ck(); + } + } + fn preprocess(&self, output: &mut Vec, source: &[u8], version: &str) { let mut index = 0; while index < source.len() { @@ -669,6 +701,39 @@ impl GLDevice { } } } + + fn clear(&self, ops: &ClearOps) { + unsafe { + let mut flags = 0; + if let Some(color) = ops.color { + gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE); ck(); + gl::ClearColor(color.r(), color.g(), color.b(), color.a()); ck(); + flags |= gl::COLOR_BUFFER_BIT; + } + if let Some(depth) = ops.depth { + gl::DepthMask(gl::TRUE); ck(); + gl::ClearDepthf(depth as _); ck(); // FIXME(pcwalton): GLES + flags |= gl::DEPTH_BUFFER_BIT; + } + if let Some(stencil) = ops.stencil { + gl::StencilMask(!0); ck(); + gl::ClearStencil(stencil as GLint); ck(); + flags |= gl::STENCIL_BUFFER_BIT; + } + if flags != 0 { + gl::Clear(flags); ck(); + } + } + } + + fn render_target_format(&self, render_target: &RenderTarget) -> TextureFormat { + match *render_target { + RenderTarget::Default => TextureFormat::RGBA8, + RenderTarget::Framebuffer(ref framebuffer) => { + self.framebuffer_texture(framebuffer).format + } + } + } } pub struct GLVertexArray { @@ -753,7 +818,7 @@ impl Drop for GLBuffer { #[derive(Debug)] pub struct GLUniform { - pub location: GLint, + location: GLint, } pub struct GLProgram { @@ -787,6 +852,7 @@ impl Drop for GLShader { pub struct GLTexture { gl_texture: GLuint, pub size: Vector2I, + pub format: TextureFormat, } pub struct GLTimerQuery { @@ -863,7 +929,36 @@ impl StencilFuncExt for StencilFunc { match self { StencilFunc::Always => gl::ALWAYS, StencilFunc::Equal => gl::EQUAL, - StencilFunc::NotEqual => gl::NOTEQUAL, + } + } +} + +trait TextureFormatExt { + fn gl_internal_format(self) -> GLint; + fn gl_format(self) -> GLuint; + fn gl_type(self) -> GLuint; +} + +impl TextureFormatExt for TextureFormat { + fn gl_internal_format(self) -> GLint { + match self { + TextureFormat::R8 => gl::R8 as GLint, + TextureFormat::R16F => gl::R16F as GLint, + TextureFormat::RGBA8 => gl::RGBA as GLint, + } + } + + fn gl_format(self) -> GLuint { + match self { + TextureFormat::R8 | TextureFormat::R16F => gl::RED, + TextureFormat::RGBA8 => gl::RGBA, + } + } + + fn gl_type(self) -> GLuint { + match self { + TextureFormat::R8 | TextureFormat::RGBA8 => gl::UNSIGNED_BYTE, + TextureFormat::R16F => gl::HALF_FLOAT, } } } @@ -929,4 +1024,15 @@ fn ck() { #[cfg(not(debug_assertions))] fn ck() {} -// Shader preprocessing +// Utilities + +// Flips a buffer of image data upside-down. +fn flip_y(pixels: &mut [T], size: Vector2I, channels: usize) { + let stride = size.x() as usize * channels; + for y in 0..(size.y() as usize / 2) { + let (index_a, index_b) = (y * stride, (size.y() as usize - y - 1) * stride); + for offset in 0..stride { + pixels.swap(index_a + offset, index_b + offset); + } + } +} diff --git a/gpu/src/lib.rs b/gpu/src/lib.rs index 7d1fde49..227b0993 100644 --- a/gpu/src/lib.rs +++ b/gpu/src/lib.rs @@ -21,7 +21,7 @@ use std::time::Duration; pub mod resources; -pub trait Device { +pub trait Device: Sized { type Buffer; type Framebuffer; type Program; @@ -34,6 +34,8 @@ pub trait Device { fn create_texture(&self, format: TextureFormat, size: Vector2I) -> Self::Texture; fn create_texture_from_data(&self, size: Vector2I, data: &[u8]) -> Self::Texture; + fn create_shader(&self, resources: &dyn ResourceLoader, name: &str, kind: ShaderKind) + -> Self::Shader; fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind) -> Self::Shader; fn create_vertex_array(&self) -> Self::VertexArray; @@ -46,9 +48,14 @@ pub trait Device { ) -> Self::Program; fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Option; fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform; - fn use_program(&self, program: &Self::Program); - fn configure_vertex_attr(&self, attr: &Self::VertexAttr, descriptor: &VertexAttrDescriptor); - fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData); + fn bind_buffer(&self, + vertex_array: &Self::VertexArray, + buffer: &Self::Buffer, + target: BufferTarget); + fn configure_vertex_attr(&self, + vertex_array: &Self::VertexArray, + attr: &Self::VertexAttr, + descriptor: &VertexAttrDescriptor); fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer; fn create_buffer(&self) -> Self::Buffer; fn allocate_buffer( @@ -61,27 +68,19 @@ pub trait Device { fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture; fn texture_size(&self, texture: &Self::Texture) -> Vector2I; fn upload_to_texture(&self, texture: &Self::Texture, size: Vector2I, data: &[u8]); - fn read_pixels_from_default_framebuffer(&self, size: Vector2I) -> Vec; - fn clear(&self, params: &ClearParams); - fn draw_arrays(&self, primitive: Primitive, index_count: u32, render_state: &RenderState); - fn draw_elements(&self, primitive: Primitive, index_count: u32, render_state: &RenderState); + fn read_pixels(&self, target: &RenderTarget, viewport: RectI) -> TextureData; + fn begin_commands(&self); + fn end_commands(&self); + fn draw_arrays(&self, index_count: u32, render_state: &RenderState); + fn draw_elements(&self, index_count: u32, render_state: &RenderState); fn draw_elements_instanced(&self, - primitive: Primitive, index_count: u32, instance_count: u32, - render_state: &RenderState); + render_state: &RenderState); fn create_timer_query(&self) -> Self::TimerQuery; fn begin_timer_query(&self, query: &Self::TimerQuery); fn end_timer_query(&self, query: &Self::TimerQuery); - fn timer_query_is_available(&self, query: &Self::TimerQuery) -> bool; - fn get_timer_query(&self, query: &Self::TimerQuery) -> Duration; - - // TODO(pcwalton): Go bindless... - fn bind_vertex_array(&self, vertex_array: &Self::VertexArray); - fn bind_buffer(&self, buffer: &Self::Buffer, target: BufferTarget); - fn bind_default_framebuffer(&self, viewport: RectI); - fn bind_framebuffer(&self, framebuffer: &Self::Framebuffer); - fn bind_texture(&self, texture: &Self::Texture, unit: u32); + fn get_timer_query(&self, query: &Self::TimerQuery) -> Option; fn create_texture_from_png(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Texture { let data = resources.slurp(&format!("textures/{}.png", name)).unwrap(); @@ -92,20 +91,6 @@ pub trait Device { self.create_texture_from_data(size, &image) } - fn create_shader( - &self, - resources: &dyn ResourceLoader, - name: &str, - kind: ShaderKind, - ) -> Self::Shader { - let suffix = match kind { - ShaderKind::Vertex => 'v', - ShaderKind::Fragment => 'f', - }; - let source = resources.slurp(&format!("shaders/gl3/{}.{}s.glsl", name, suffix)).unwrap(); - self.create_shader_from_source(name, &source, kind) - } - fn create_program_from_shader_names( &self, resources: &dyn ResourceLoader, @@ -124,7 +109,7 @@ pub trait Device { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum TextureFormat { R8, R16F, @@ -167,7 +152,6 @@ pub enum ShaderKind { #[derive(Clone, Copy)] pub enum UniformData { Int(i32), - Mat2(F32x4), Mat4([F32x4; 4]), Vec2(F32x4), Vec4(F32x4), @@ -180,23 +164,41 @@ pub enum Primitive { Lines, } -#[derive(Clone, Copy, Default)] -pub struct ClearParams { +#[derive(Clone)] +pub struct RenderState<'a, D> where D: Device { + pub target: &'a RenderTarget<'a, D>, + pub program: &'a D::Program, + pub vertex_array: &'a D::VertexArray, + pub primitive: Primitive, + pub uniforms: &'a [(&'a D::Uniform, UniformData)], + pub textures: &'a [&'a D::Texture], + pub viewport: RectI, + pub options: RenderOptions, +} + +#[derive(Clone, Debug)] +pub struct RenderOptions { + pub blend: BlendState, + pub depth: Option, + pub stencil: Option, + pub clear_ops: ClearOps, + pub color_mask: bool, +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct ClearOps { pub color: Option, - pub rect: Option, pub depth: Option, pub stencil: Option, } -#[derive(Clone, Debug)] -pub struct RenderState { - pub blend: BlendState, - pub depth: Option, - pub stencil: Option, - pub color_mask: bool, +#[derive(Clone, Copy, Debug)] +pub enum RenderTarget<'a, D> where D: Device { + Default, + Framebuffer(&'a D::Framebuffer), } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum BlendState { Off, RGBOneAlphaOne, @@ -228,16 +230,16 @@ pub struct StencilState { pub enum StencilFunc { Always, Equal, - NotEqual, } -impl Default for RenderState { +impl Default for RenderOptions { #[inline] - fn default() -> RenderState { - RenderState { + fn default() -> RenderOptions { + RenderOptions { blend: BlendState::default(), depth: None, stencil: None, + clear_ops: ClearOps::default(), color_mask: true, } } @@ -276,6 +278,12 @@ impl Default for StencilFunc { } } +#[derive(Clone, Debug)] +pub enum TextureData { + U8(Vec), + U16(Vec), +} + impl UniformData { #[inline] pub fn from_transform_3d(transform: &Transform3DF) -> UniformData { @@ -291,6 +299,7 @@ pub struct VertexAttrDescriptor { pub stride: usize, pub offset: usize, pub divisor: u32, + pub buffer_index: u32, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -299,3 +308,20 @@ pub enum VertexAttrClass { FloatNorm, Int, } + +impl TextureFormat { + #[inline] + pub fn channels(self) -> usize { + match self { + TextureFormat::R8 | TextureFormat::R16F => 1, + TextureFormat::RGBA8 => 4, + } + } +} + +impl ClearOps { + #[inline] + pub fn has_ops(&self) -> bool { + self.color.is_some() || self.depth.is_some() || self.stencil.is_some() + } +} diff --git a/metal/Cargo.toml b/metal/Cargo.toml new file mode 100644 index 00000000..8ddef24b --- /dev/null +++ b/metal/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "pathfinder_metal" +version = "0.1.0" +authors = ["Patrick Walton "] +edition = "2018" + +[dependencies] +bitflags = "1.0" +byteorder = "1.3" +block = "0.1" +cocoa = "0.18" +core-foundation = "0.6" +foreign-types = "0.3" +metal = "0.14" +objc = "0.2" + +[dependencies.pathfinder_geometry] +path = "../geometry" + +[dependencies.pathfinder_gpu] +path = "../gpu" + +[dependencies.pathfinder_simd] +path = "../simd" diff --git a/metal/src/lib.rs b/metal/src/lib.rs new file mode 100644 index 00000000..63856a09 --- /dev/null +++ b/metal/src/lib.rs @@ -0,0 +1,1484 @@ +// pathfinder/metal/src/lib.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A Metal implementation of the device abstraction, for macOS and iOS. + +#![allow(non_upper_case_globals)] + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate objc; + +use block::{Block, ConcreteBlock, RcBlock}; +use byteorder::{NativeEndian, WriteBytesExt}; +use cocoa::foundation::{NSRange, NSUInteger}; +use core_foundation::base::TCFType; +use core_foundation::string::{CFString, CFStringRef}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use metal::{self, Argument, ArgumentEncoder, Buffer, CommandBuffer, CommandBufferRef}; +use metal::{CommandQueue, CompileOptions, CoreAnimationDrawable, CoreAnimationDrawableRef}; +use metal::{CoreAnimationLayer, CoreAnimationLayerRef, DepthStencilDescriptor, Function, Library}; +use metal::{MTLArgument, MTLArgumentEncoder, MTLBlendFactor, MTLClearColor, MTLColorWriteMask}; +use metal::{MTLCompareFunction, MTLDataType, MTLDevice, MTLFunctionType, MTLIndexType}; +use metal::{MTLLoadAction, MTLOrigin, MTLPixelFormat, MTLPrimitiveType, MTLRegion}; +use metal::{MTLRenderPipelineReflection, MTLRenderPipelineState, MTLResourceOptions}; +use metal::{MTLResourceUsage, MTLSamplerAddressMode, MTLSamplerMinMagFilter, MTLSize}; +use metal::{MTLStencilOperation, MTLStorageMode, MTLStoreAction, MTLTextureType, MTLTextureUsage}; +use metal::{MTLVertexFormat, MTLVertexStepFunction, MTLViewport, RenderCommandEncoder}; +use metal::{RenderCommandEncoderRef, RenderPassDescriptor, RenderPassDescriptorRef}; +use metal::{RenderPipelineColorAttachmentDescriptorRef, RenderPipelineDescriptor}; +use metal::{RenderPipelineReflection, RenderPipelineReflectionRef, RenderPipelineState}; +use metal::{SamplerDescriptor, SamplerState, StencilDescriptor, StructMemberRef, StructType}; +use metal::{StructTypeRef, TextureDescriptor, Texture, TextureRef, VertexAttribute}; +use metal::{VertexAttributeRef, VertexDescriptor, VertexDescriptorRef}; +use objc::runtime::{Class, Object}; +use pathfinder_geometry::basic::rect::RectI; +use pathfinder_geometry::basic::vector::Vector2I; +use pathfinder_gpu::resources::ResourceLoader; +use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, DepthFunc, Device}; +use pathfinder_gpu::{Primitive, RenderState, RenderTarget, ShaderKind, StencilFunc, TextureData}; +use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrClass}; +use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType}; +use pathfinder_simd::default::F32x4; +use std::cell::{Cell, RefCell}; +use std::mem; +use std::ptr; +use std::rc::Rc; +use std::slice; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +const FIRST_VERTEX_BUFFER_INDEX: u64 = 1; + +pub struct MetalDevice { + device: metal::Device, + layer: CoreAnimationLayer, + drawable: CoreAnimationDrawable, + main_depth_stencil_texture: Texture, + command_queue: CommandQueue, + command_buffers: RefCell>, + sampler: SamplerState, + shared_event: SharedEvent, + shared_event_listener: SharedEventListener, + next_timer_query_event_value: Cell, +} + +pub struct MetalProgram { + vertex: MetalShader, + fragment: MetalShader, +} + +#[derive(Clone)] +pub struct MetalBuffer { + buffer: Rc>>, +} + +impl MetalDevice { + #[inline] + pub fn new(layer: &CoreAnimationLayerRef) -> MetalDevice { + let layer = layer.retain(); + let device = layer.device(); + let drawable = layer.next_drawable().unwrap().retain(); + let command_queue = device.new_command_queue(); + + let sampler_descriptor = SamplerDescriptor::new(); + sampler_descriptor.set_support_argument_buffers(true); + sampler_descriptor.set_normalized_coordinates(true); + sampler_descriptor.set_min_filter(MTLSamplerMinMagFilter::Linear); + sampler_descriptor.set_mag_filter(MTLSamplerMinMagFilter::Linear); + sampler_descriptor.set_address_mode_s(MTLSamplerAddressMode::ClampToEdge); + sampler_descriptor.set_address_mode_t(MTLSamplerAddressMode::ClampToEdge); + let sampler = device.new_sampler(&sampler_descriptor); + + let main_color_texture = drawable.texture(); + let framebuffer_size = Vector2I::new(main_color_texture.width() as i32, + main_color_texture.height() as i32); + let main_depth_stencil_texture = device.create_depth_stencil_texture(framebuffer_size); + + let shared_event = device.new_shared_event(); + + MetalDevice { + device, + layer, + drawable, + main_depth_stencil_texture, + command_queue, + command_buffers: RefCell::new(vec![]), + sampler, + shared_event, + shared_event_listener: SharedEventListener::new(), + next_timer_query_event_value: Cell::new(1), + } + } + + pub fn present_drawable(&mut self) { + self.begin_commands(); + self.command_buffers.borrow_mut().last().unwrap().present_drawable(&self.drawable); + self.end_commands(); + self.drawable = self.layer.next_drawable().unwrap().retain(); + } +} + +pub struct MetalFramebuffer(MetalTexture); + +pub struct MetalShader { + #[allow(dead_code)] + library: Library, + function: Function, + uniforms: RefCell, +} + +enum ShaderUniforms { + Unknown, + NoUniforms, + Uniforms { encoder: ArgumentEncoder, struct_type: StructType } +} + +pub struct MetalTexture { + texture: Texture, + dirty: Cell, +} + +pub struct MetalTimerQuery { + event_value: u64, + start_time: Cell>, + end_time: Cell>, +} + +#[derive(Clone)] +pub struct MetalUniform { + indices: RefCell>, + name: String, +} + +#[derive(Clone, Copy)] +pub struct MetalUniformIndices { + vertex: Option, + fragment: Option, +} + +#[derive(Clone, Copy)] +pub struct MetalUniformIndex { + main: u64, + sampler: Option, +} + +pub struct MetalVertexArray { + descriptor: VertexDescriptor, + vertex_buffers: RefCell>, + index_buffer: RefCell>, +} + +impl Device for MetalDevice { + type Buffer = MetalBuffer; + type Framebuffer = MetalFramebuffer; + type Program = MetalProgram; + type Shader = MetalShader; + type Texture = MetalTexture; + type TimerQuery = Arc; + type Uniform = MetalUniform; + type VertexArray = MetalVertexArray; + type VertexAttr = VertexAttribute; + + // TODO: Add texture usage hint. + fn create_texture(&self, format: TextureFormat, size: Vector2I) -> MetalTexture { + let descriptor = TextureDescriptor::new(); + descriptor.set_texture_type(MTLTextureType::D2); + match format { + TextureFormat::R8 => descriptor.set_pixel_format(MTLPixelFormat::R8Unorm), + TextureFormat::R16F => descriptor.set_pixel_format(MTLPixelFormat::R16Float), + TextureFormat::RGBA8 => descriptor.set_pixel_format(MTLPixelFormat::RGBA8Unorm), + } + descriptor.set_width(size.x() as u64); + descriptor.set_height(size.y() as u64); + descriptor.set_storage_mode(MTLStorageMode::Managed); + descriptor.set_usage(MTLTextureUsage::Unknown); + MetalTexture { texture: self.device.new_texture(&descriptor), dirty: Cell::new(false) } + } + + fn create_texture_from_data(&self, size: Vector2I, data: &[u8]) -> MetalTexture { + assert!(data.len() >= size.x() as usize * size.y() as usize); + let texture = self.create_texture(TextureFormat::R8, size); + self.upload_to_texture(&texture, size, data); + texture + } + + fn create_shader_from_source(&self, _: &str, source: &[u8], _: ShaderKind) -> MetalShader { + let source = String::from_utf8(source.to_vec()).expect("Source wasn't valid UTF-8!"); + + let compile_options = CompileOptions::new(); + let library = self.device.new_library_with_source(&source, &compile_options).unwrap(); + let function = library.get_function("main0", None).unwrap(); + + MetalShader { library, function, uniforms: RefCell::new(ShaderUniforms::Unknown) } + } + + fn create_vertex_array(&self) -> MetalVertexArray { + MetalVertexArray { + descriptor: VertexDescriptor::new().retain(), + vertex_buffers: RefCell::new(vec![]), + index_buffer: RefCell::new(None), + } + } + + fn bind_buffer(&self, + vertex_array: &MetalVertexArray, + buffer: &MetalBuffer, + target: BufferTarget) { + match target { + BufferTarget::Vertex => { + vertex_array.vertex_buffers.borrow_mut().push((*buffer).clone()) + } + BufferTarget::Index => { + *vertex_array.index_buffer.borrow_mut() = Some((*buffer).clone()) + } + } + } + + fn create_program_from_shaders(&self, + _: &dyn ResourceLoader, + _: &str, + vertex_shader: MetalShader, + fragment_shader: MetalShader) + -> MetalProgram { + MetalProgram { vertex: vertex_shader, fragment: fragment_shader } + } + + fn get_vertex_attr(&self, program: &MetalProgram, name: &str) -> Option { + // TODO(pcwalton): Cache the function? + let attributes = program.vertex.function.real_vertex_attributes(); + for attribute_index in 0..attributes.len() { + let attribute = attributes.object_at(attribute_index); + let this_name = attribute.name().as_bytes(); + if this_name[0] == b'a' && this_name[1..] == *name.as_bytes() { + return Some(attribute.retain()) + } + } + None + } + + fn get_uniform(&self, _: &Self::Program, name: &str) -> MetalUniform { + MetalUniform { indices: RefCell::new(None), name: name.to_owned() } + } + + fn configure_vertex_attr(&self, + vertex_array: &MetalVertexArray, + attr: &VertexAttribute, + descriptor: &VertexAttrDescriptor) { + debug_assert_ne!(descriptor.stride, 0); + + let attribute_index = attr.attribute_index(); + + let attr_info = vertex_array.descriptor + .attributes() + .object_at(attribute_index as usize) + .unwrap(); + let format = match (descriptor.class, descriptor.attr_type, descriptor.size) { + (VertexAttrClass::Int, VertexAttrType::I8, 2) => MTLVertexFormat::Char2, + (VertexAttrClass::Int, VertexAttrType::I8, 3) => MTLVertexFormat::Char3, + (VertexAttrClass::Int, VertexAttrType::I8, 4) => MTLVertexFormat::Char4, + (VertexAttrClass::Int, VertexAttrType::U8, 2) => MTLVertexFormat::UChar2, + (VertexAttrClass::Int, VertexAttrType::U8, 3) => MTLVertexFormat::UChar3, + (VertexAttrClass::Int, VertexAttrType::U8, 4) => MTLVertexFormat::UChar4, + (VertexAttrClass::FloatNorm, VertexAttrType::U8, 2) => { + MTLVertexFormat::UChar2Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::U8, 3) => { + MTLVertexFormat::UChar3Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::U8, 4) => { + MTLVertexFormat::UChar4Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::I8, 2) => { + MTLVertexFormat::Char2Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::I8, 3) => { + MTLVertexFormat::Char3Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::I8, 4) => { + MTLVertexFormat::Char4Normalized + } + (VertexAttrClass::Int, VertexAttrType::I16, 2) => MTLVertexFormat::Short2, + (VertexAttrClass::Int, VertexAttrType::I16, 3) => MTLVertexFormat::Short3, + (VertexAttrClass::Int, VertexAttrType::I16, 4) => MTLVertexFormat::Short4, + (VertexAttrClass::Int, VertexAttrType::U16, 2) => MTLVertexFormat::UShort2, + (VertexAttrClass::Int, VertexAttrType::U16, 3) => MTLVertexFormat::UShort3, + (VertexAttrClass::Int, VertexAttrType::U16, 4) => MTLVertexFormat::UShort4, + (VertexAttrClass::FloatNorm, VertexAttrType::U16, 2) => { + MTLVertexFormat::UShort2Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::U16, 3) => { + MTLVertexFormat::UShort3Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::U16, 4) => { + MTLVertexFormat::UShort4Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::I16, 2) => { + MTLVertexFormat::Short2Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::I16, 3) => { + MTLVertexFormat::Short3Normalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::I16, 4) => { + MTLVertexFormat::Short4Normalized + } + (VertexAttrClass::Float, VertexAttrType::F32, 1) => MTLVertexFormat::Float, + (VertexAttrClass::Float, VertexAttrType::F32, 2) => MTLVertexFormat::Float2, + (VertexAttrClass::Float, VertexAttrType::F32, 3) => MTLVertexFormat::Float3, + (VertexAttrClass::Float, VertexAttrType::F32, 4) => MTLVertexFormat::Float4, + (VertexAttrClass::Int, VertexAttrType::I8, 1) => MTLVertexFormat::Char, + (VertexAttrClass::Int, VertexAttrType::U8, 1) => MTLVertexFormat::UChar, + (VertexAttrClass::FloatNorm, VertexAttrType::I8, 1) => MTLVertexFormat::CharNormalized, + (VertexAttrClass::Int, VertexAttrType::I16, 1) => MTLVertexFormat::Short, + (VertexAttrClass::Int, VertexAttrType::U16, 1) => MTLVertexFormat::UShort, + (VertexAttrClass::FloatNorm, VertexAttrType::U16, 1) => { + MTLVertexFormat::UShortNormalized + } + (VertexAttrClass::FloatNorm, VertexAttrType::I16, 1) => { + MTLVertexFormat::ShortNormalized + } + (attr_class, attr_type, attr_size) => { + panic!("Unsupported vertex class/type/size combination: {:?}/{:?}/{}!", + attr_class, + attr_type, + attr_size) + } + }; + attr_info.set_format(format); + attr_info.set_offset(descriptor.offset as u64); + let buffer_index = descriptor.buffer_index as u64 + FIRST_VERTEX_BUFFER_INDEX; + attr_info.set_buffer_index(buffer_index); + + // FIXME(pcwalton): Metal separates out per-buffer info from per-vertex info, while our + // GL-like API does not. So we end up setting this state over and over again. Not great. + let layout = vertex_array.descriptor.layouts().object_at(buffer_index as usize).unwrap(); + if descriptor.divisor == 0 { + layout.set_step_function(MTLVertexStepFunction::PerVertex); + layout.set_step_rate(1); + } else { + layout.set_step_function(MTLVertexStepFunction::PerInstance); + layout.set_step_rate(descriptor.divisor as u64); + } + layout.set_stride(descriptor.stride as u64); + } + + fn create_framebuffer(&self, texture: MetalTexture) -> MetalFramebuffer { + MetalFramebuffer(texture) + } + + fn create_buffer(&self) -> MetalBuffer { + MetalBuffer { buffer: Rc::new(RefCell::new(None)) } + } + + fn allocate_buffer(&self, + buffer: &MetalBuffer, + data: BufferData, + _: BufferTarget, + mode: BufferUploadMode) { + let mut options = match mode { + BufferUploadMode::Static => MTLResourceOptions::CPUCacheModeWriteCombined, + BufferUploadMode::Dynamic => MTLResourceOptions::CPUCacheModeDefaultCache, + }; + options |= MTLResourceOptions::StorageModeManaged; + + match data { + BufferData::Uninitialized(size) => { + let size = (size * mem::size_of::()) as u64; + let new_buffer = self.device.new_buffer(size, options); + *buffer.buffer.borrow_mut() = Some(new_buffer); + } + BufferData::Memory(slice) => { + let size = (slice.len() * mem::size_of::()) as u64; + let new_buffer = self.device.new_buffer_with_data(slice.as_ptr() as *const _, + size, + options); + *buffer.buffer.borrow_mut() = Some(new_buffer); + } + } + } + + fn framebuffer_texture<'f>(&self, framebuffer: &'f MetalFramebuffer) -> &'f MetalTexture { + &framebuffer.0 + } + + fn texture_size(&self, texture: &MetalTexture) -> Vector2I { + Vector2I::new(texture.texture.width() as i32, texture.texture.height() as i32) + } + + fn upload_to_texture(&self, texture: &MetalTexture, size: Vector2I, data: &[u8]) { + assert!(data.len() >= size.x() as usize * size.y() as usize); + let format = self.texture_format(&texture.texture).expect("Unexpected texture format!"); + assert!(format == TextureFormat::R8 || format == TextureFormat::RGBA8); + + let origin = MTLOrigin { x: 0, y: 0, z: 0 }; + let size = MTLSize { width: size.x() as u64, height: size.y() as u64, depth: 1 }; + let region = MTLRegion { origin, size }; + let stride = size.width * format.channels() as u64; + texture.texture.replace_region(region, 0, stride, data.as_ptr() as *const _); + + texture.dirty.set(true); + } + + fn read_pixels(&self, target: &RenderTarget, viewport: RectI) -> TextureData { + let texture = self.render_target_color_texture(target); + self.synchronize_texture(&texture); + + let (origin, size) = (viewport.origin(), viewport.size()); + let metal_origin = MTLOrigin { x: origin.x() as u64, y: origin.y() as u64, z: 0 }; + let metal_size = MTLSize { width: size.x() as u64, height: size.y() as u64, depth: 1 }; + let metal_region = MTLRegion { origin: metal_origin, size: metal_size }; + + let format = self.texture_format(&texture) + .expect("Unexpected framebuffer texture format!"); + match format { + TextureFormat::R8 | TextureFormat::RGBA8 => { + let channels = format.channels(); + let stride = size.x() as usize * channels; + let mut pixels = vec![0; stride * size.y() as usize]; + texture.get_bytes(pixels.as_mut_ptr() as *mut _, metal_region, 0, stride as u64); + TextureData::U8(pixels) + } + TextureFormat::R16F => { + let stride = size.x() as usize; + let mut pixels = vec![0; stride * size.y() as usize]; + texture.get_bytes(pixels.as_mut_ptr() as *mut _, + metal_region, + 0, + stride as u64 * 2); + TextureData::U16(pixels) + } + } + } + + fn begin_commands(&self) { + self.command_buffers.borrow_mut().push(self.command_queue.new_command_buffer().retain()); + } + + fn end_commands(&self) { + let command_buffer = self.command_buffers.borrow_mut().pop().unwrap(); + command_buffer.commit(); + } + + fn draw_arrays(&self, index_count: u32, render_state: &RenderState) { + let encoder = self.prepare_to_draw(render_state); + let primitive = render_state.primitive.to_metal_primitive(); + encoder.draw_primitives(primitive, 0, index_count as u64); + encoder.end_encoding(); + } + + fn draw_elements(&self, index_count: u32, render_state: &RenderState) { + let encoder = self.prepare_to_draw(render_state); + let primitive = render_state.primitive.to_metal_primitive(); + let index_type = MTLIndexType::UInt32; + let index_count = index_count as u64; + let index_buffer = render_state.vertex_array + .index_buffer + .borrow(); + let index_buffer = index_buffer.as_ref().expect("No index buffer bound to VAO!"); + let index_buffer = index_buffer.buffer.borrow(); + let index_buffer = index_buffer.as_ref().expect("Index buffer not allocated!"); + encoder.draw_indexed_primitives(primitive, index_count, index_type, index_buffer, 0); + encoder.end_encoding(); + } + + fn draw_elements_instanced(&self, + index_count: u32, + instance_count: u32, + render_state: &RenderState) { + let encoder = self.prepare_to_draw(render_state); + let primitive = render_state.primitive.to_metal_primitive(); + let index_type = MTLIndexType::UInt32; + let index_buffer = render_state.vertex_array + .index_buffer + .borrow(); + let index_buffer = index_buffer.as_ref().expect("No index buffer bound to VAO!"); + let index_buffer = index_buffer.buffer.borrow(); + let index_buffer = index_buffer.as_ref().expect("Index buffer not allocated!"); + encoder.draw_indexed_primitives_instanced(primitive, + index_count as u64, + index_type, + index_buffer, + 0, + instance_count as u64); + encoder.end_encoding(); + } + + fn create_timer_query(&self) -> Arc { + let event_value = self.next_timer_query_event_value.get(); + self.next_timer_query_event_value.set(event_value + 2); + + let query = Arc::new(MetalTimerQuery { + event_value, + start_time: Cell::new(None), + end_time: Cell::new(None), + }); + + let captured_query = query.clone(); + let start_block = ConcreteBlock::new(move |_: *mut Object, _: u64| { + captured_query.start_time.set(Some(Instant::now())) + }); + let captured_query = query.clone(); + let end_block = ConcreteBlock::new(move |_: *mut Object, _: u64| { + captured_query.end_time.set(Some(Instant::now())) + }); + self.shared_event.notify_listener_at_value(&self.shared_event_listener, + event_value, + start_block.copy()); + self.shared_event.notify_listener_at_value(&self.shared_event_listener, + event_value + 1, + end_block.copy()); + + query + } + + fn begin_timer_query(&self, query: &Arc) { + self.command_buffers + .borrow_mut() + .last() + .unwrap() + .encode_signal_event(&self.shared_event, query.event_value); + } + + fn end_timer_query(&self, query: &Arc) { + self.command_buffers + .borrow_mut() + .last() + .unwrap() + .encode_signal_event(&self.shared_event, query.event_value + 1); + } + + fn get_timer_query(&self, query: &Arc) -> Option { + match (query.start_time.get(), query.end_time.get()) { + (Some(start_time), Some(end_time)) => Some(end_time - start_time), + _ => None, + } + } + + #[inline] + fn create_shader( + &self, + resources: &dyn ResourceLoader, + name: &str, + kind: ShaderKind, + ) -> Self::Shader { + let suffix = match kind { + ShaderKind::Vertex => 'v', + ShaderKind::Fragment => 'f', + }; + let path = format!("shaders/metal/{}.{}s.metal", name, suffix); + self.create_shader_from_source(name, &resources.slurp(&path).unwrap(), kind) + } +} + +impl MetalDevice { + fn get_uniform_index(&self, shader: &MetalShader, name: &str) -> Option { + let uniforms = shader.uniforms.borrow(); + let struct_type = match *uniforms { + ShaderUniforms::Unknown => panic!("get_uniform_index() called before reflection!"), + ShaderUniforms::NoUniforms => return None, + ShaderUniforms::Uniforms { ref struct_type, .. } => struct_type, + }; + let main_member = match struct_type.member_from_name(&format!("u{}", name)) { + None => return None, + Some(main_member) => main_member, + }; + let main_index = main_member.argument_index(); + let sampler_index = match struct_type.member_from_name(&format!("u{}Smplr", name)) { + None => None, + Some(sampler_member) => Some(sampler_member.argument_index()), + }; + Some(MetalUniformIndex { main: main_index, sampler: sampler_index }) + } + + fn populate_uniform_indices_if_necessary(&self, + uniform: &MetalUniform, + program: &MetalProgram) { + + let mut indices = uniform.indices.borrow_mut(); + if indices.is_some() { + return; + } + + *indices = Some(MetalUniformIndices { + vertex: self.get_uniform_index(&program.vertex, &uniform.name), + fragment: self.get_uniform_index(&program.fragment, &uniform.name), + }); + } + + fn render_target_color_texture(&self, render_target: &RenderTarget) + -> Texture { + match *render_target { + RenderTarget::Default {..} => self.drawable.texture().retain(), + RenderTarget::Framebuffer(framebuffer) => framebuffer.0.texture.retain(), + } + } + + fn render_target_depth_texture(&self, render_target: &RenderTarget) + -> Option { + match *render_target { + RenderTarget::Default {..} => Some(self.main_depth_stencil_texture.retain()), + RenderTarget::Framebuffer(_) => None, + } + } + + fn render_target_has_depth(&self, render_target: &RenderTarget) -> bool { + match *render_target { + RenderTarget::Default {..} => true, + RenderTarget::Framebuffer(_) => false, + } + } + + fn prepare_to_draw(&self, render_state: &RenderState) -> RenderCommandEncoder { + let command_buffers = self.command_buffers.borrow(); + let command_buffer = command_buffers.last().unwrap(); + + // FIXME(pcwalton): Is this necessary? + let mut blit_command_encoder = None; + for texture in render_state.textures { + if !texture.dirty.get() { + continue; + } + if blit_command_encoder.is_none() { + blit_command_encoder = Some(command_buffer.new_blit_command_encoder()); + } + let blit_command_encoder = blit_command_encoder.as_ref().unwrap(); + blit_command_encoder.synchronize_resource(&texture.texture); + texture.dirty.set(false); + } + if let Some(blit_command_encoder) = blit_command_encoder { + blit_command_encoder.end_encoding(); + } + + let render_pass_descriptor = self.create_render_pass_descriptor(render_state); + + let encoder = command_buffer.new_render_command_encoder(&render_pass_descriptor).retain(); + self.set_viewport(&encoder, &render_state.viewport); + + let render_pipeline_descriptor = RenderPipelineDescriptor::new(); + render_pipeline_descriptor.set_vertex_function(Some(&render_state.program + .vertex + .function)); + render_pipeline_descriptor.set_fragment_function(Some(&render_state.program + .fragment + .function)); + render_pipeline_descriptor.set_vertex_descriptor(Some(&render_state.vertex_array + .descriptor)); + + // Create render pipeline state. + let pipeline_color_attachment = render_pipeline_descriptor.color_attachments() + .object_at(0) + .unwrap(); + self.prepare_pipeline_color_attachment_for_render(pipeline_color_attachment, + render_state); + + if self.render_target_has_depth(render_state.target) { + let depth_stencil_format = MTLPixelFormat::Depth32Float_Stencil8; + render_pipeline_descriptor.set_depth_attachment_pixel_format(depth_stencil_format); + render_pipeline_descriptor.set_stencil_attachment_pixel_format(depth_stencil_format); + } + + let reflection_options = MTLPipelineOption::ArgumentInfo | + MTLPipelineOption::BufferTypeInfo; + let (render_pipeline_state, reflection) = + self.device.real_new_render_pipeline_state_with_reflection(&render_pipeline_descriptor, + reflection_options); + + self.populate_shader_uniforms_if_necessary(&render_state.program.vertex, &reflection); + self.populate_shader_uniforms_if_necessary(&render_state.program.fragment, &reflection); + + for (vertex_buffer_index, vertex_buffer) in render_state.vertex_array + .vertex_buffers + .borrow() + .iter() + .enumerate() { + let real_index = vertex_buffer_index as u64 + FIRST_VERTEX_BUFFER_INDEX; + let buffer = vertex_buffer.buffer.borrow(); + let buffer = buffer.as_ref().map(|buffer| buffer.as_ref()).unwrap(); + encoder.set_vertex_buffer(real_index, Some(buffer), 0); + encoder.use_resource(buffer, MTLResourceUsage::Read); + } + + self.set_uniforms(&encoder, render_state); + encoder.set_render_pipeline_state(&render_pipeline_state); + self.set_depth_stencil_state(&encoder, render_state); + encoder + } + + fn populate_shader_uniforms_if_necessary(&self, + shader: &MetalShader, + reflection: &RenderPipelineReflectionRef) { + let mut uniforms = shader.uniforms.borrow_mut(); + match *uniforms { + ShaderUniforms::Unknown => {} + ShaderUniforms::NoUniforms | ShaderUniforms::Uniforms { .. } => return, + } + + let arguments = match shader.function.function_type() { + MTLFunctionType::Vertex => reflection.real_vertex_arguments(), + MTLFunctionType::Fragment => reflection.real_fragment_arguments(), + _ => panic!("Unexpected shader function type!"), + }; + + let mut has_descriptor_set = false; + for argument_index in 0..arguments.len() { + let argument = arguments.object_at(argument_index); + if argument.name() == "spvDescriptorSet0" { + has_descriptor_set = true; + break; + } + } + if !has_descriptor_set { + *uniforms = ShaderUniforms::NoUniforms; + return; + } + + let (encoder, argument) = shader.function.new_argument_encoder_with_reflection(0); + match argument.buffer_data_type() { + MTLDataType::Struct => {} + data_type => { + panic!("Unexpected data type for argument buffer: {}!", data_type as u32) + } + } + let struct_type = argument.buffer_struct_type().retain(); + *uniforms = ShaderUniforms::Uniforms { encoder, struct_type }; + } + + fn create_argument_buffer(&self, shader: &MetalShader) -> Option { + let uniforms = shader.uniforms.borrow(); + let encoder = match *uniforms { + ShaderUniforms::Unknown => unreachable!(), + ShaderUniforms::NoUniforms => return None, + ShaderUniforms::Uniforms { ref encoder, .. } => encoder, + }; + + let buffer_options = MTLResourceOptions::CPUCacheModeDefaultCache | + MTLResourceOptions::StorageModeManaged; + let buffer = self.device.new_buffer(encoder.encoded_length(), buffer_options); + encoder.set_argument_buffer(&buffer, 0); + Some(buffer) + } + + fn set_uniforms(&self, + render_command_encoder: &RenderCommandEncoderRef, + render_state: &RenderState) { + let vertex_argument_buffer = self.create_argument_buffer(&render_state.program.vertex); + let fragment_argument_buffer = self.create_argument_buffer(&render_state.program.fragment); + + let vertex_uniforms = render_state.program.vertex.uniforms.borrow(); + let fragment_uniforms = render_state.program.fragment.uniforms.borrow(); + + let (mut have_vertex_uniforms, mut have_fragment_uniforms) = (false, false); + if let ShaderUniforms::Uniforms { .. } = *vertex_uniforms { + have_vertex_uniforms = true; + let vertex_argument_buffer = vertex_argument_buffer.as_ref().unwrap(); + render_command_encoder.use_resource(vertex_argument_buffer, MTLResourceUsage::Read); + render_command_encoder.set_vertex_buffer(0, Some(vertex_argument_buffer), 0); + } + if let ShaderUniforms::Uniforms { .. } = *fragment_uniforms { + have_fragment_uniforms = true; + let fragment_argument_buffer = fragment_argument_buffer.as_ref().unwrap(); + render_command_encoder.use_resource(fragment_argument_buffer, MTLResourceUsage::Read); + render_command_encoder.set_fragment_buffer(0, Some(fragment_argument_buffer), 0); + } + + if !have_vertex_uniforms && !have_fragment_uniforms { + return; + } + + let (mut uniform_buffer_data, mut uniform_buffer_ranges) = (vec![], vec![]); + for &(_, uniform_data) in render_state.uniforms.iter() { + let start_index = uniform_buffer_data.len(); + match uniform_data { + UniformData::Int(value) => { + uniform_buffer_data.write_i32::(value).unwrap() + } + UniformData::Mat4(matrix) => { + for column in &matrix { + uniform_buffer_data.write_f32::(column.x()).unwrap(); + uniform_buffer_data.write_f32::(column.y()).unwrap(); + uniform_buffer_data.write_f32::(column.z()).unwrap(); + uniform_buffer_data.write_f32::(column.w()).unwrap(); + } + } + UniformData::Vec2(vector) => { + uniform_buffer_data.write_f32::(vector.x()).unwrap(); + uniform_buffer_data.write_f32::(vector.y()).unwrap(); + } + UniformData::Vec4(vector) => { + uniform_buffer_data.write_f32::(vector.x()).unwrap(); + uniform_buffer_data.write_f32::(vector.y()).unwrap(); + uniform_buffer_data.write_f32::(vector.z()).unwrap(); + uniform_buffer_data.write_f32::(vector.w()).unwrap(); + } + UniformData::TextureUnit(_) => {} + } + let end_index = uniform_buffer_data.len(); + uniform_buffer_ranges.push(start_index..end_index); + } + + let buffer_options = MTLResourceOptions::CPUCacheModeWriteCombined | + MTLResourceOptions::StorageModeManaged; + let data_buffer = self.device + .new_buffer_with_data(uniform_buffer_data.as_ptr() as *const _, + uniform_buffer_data.len() as u64, + buffer_options); + + for (&(uniform, ref uniform_data), buffer_range) in + render_state.uniforms.iter().zip(uniform_buffer_ranges.iter()) { + self.populate_uniform_indices_if_necessary(uniform, &render_state.program); + let indices = uniform.indices.borrow_mut(); + let indices = indices.as_ref().unwrap(); + if let Some(vertex_index) = indices.vertex { + if let ShaderUniforms::Uniforms { + encoder: ref argument_encoder, + .. + } = *vertex_uniforms { + self.set_uniform(vertex_index, + argument_encoder, + uniform_data, + &data_buffer, + buffer_range.start as u64, + render_command_encoder, + render_state); + } + } + if let Some(fragment_index) = indices.fragment { + if let ShaderUniforms::Uniforms { + encoder: ref argument_encoder, + .. + } = *fragment_uniforms { + self.set_uniform(fragment_index, + argument_encoder, + uniform_data, + &data_buffer, + buffer_range.start as u64, + render_command_encoder, + render_state); + } + } + } + + render_command_encoder.use_resource(&data_buffer, MTLResourceUsage::Read); + + if let Some(vertex_argument_buffer) = vertex_argument_buffer { + let range = NSRange::new(0, vertex_argument_buffer.length()); + vertex_argument_buffer.did_modify_range(range); + } + if let Some(fragment_argument_buffer) = fragment_argument_buffer { + let range = NSRange::new(0, fragment_argument_buffer.length()); + fragment_argument_buffer.did_modify_range(range); + } + } + + fn set_uniform(&self, + argument_index: MetalUniformIndex, + argument_encoder: &ArgumentEncoder, + uniform_data: &UniformData, + buffer: &Buffer, + buffer_offset: u64, + render_command_encoder: &RenderCommandEncoderRef, + render_state: &RenderState) { + match *uniform_data { + UniformData::TextureUnit(unit) => { + let texture = render_state.textures[unit as usize]; + argument_encoder.set_texture(&texture.texture, argument_index.main); + let mut resource_usage = MTLResourceUsage::Read; + if let Some(sampler_index) = argument_index.sampler { + argument_encoder.set_sampler_state(&self.sampler, sampler_index); + resource_usage |= MTLResourceUsage::Sample; + } + render_command_encoder.use_resource(&texture.texture, resource_usage); + } + _ => argument_encoder.set_buffer(buffer, buffer_offset, argument_index.main), + } + } + + fn prepare_pipeline_color_attachment_for_render( + &self, + pipeline_color_attachment: &RenderPipelineColorAttachmentDescriptorRef, + render_state: &RenderState) { + let pixel_format = self.render_target_color_texture(&render_state.target).pixel_format(); + pipeline_color_attachment.set_pixel_format(pixel_format); + + let blending_enabled = render_state.options.blend != BlendState::Off; + pipeline_color_attachment.set_blending_enabled(blending_enabled); + match render_state.options.blend { + BlendState::Off => {} + BlendState::RGBOneAlphaOne => { + pipeline_color_attachment.set_source_rgb_blend_factor(MTLBlendFactor::One); + pipeline_color_attachment.set_destination_rgb_blend_factor(MTLBlendFactor::One); + pipeline_color_attachment.set_source_alpha_blend_factor(MTLBlendFactor::One); + pipeline_color_attachment.set_destination_alpha_blend_factor(MTLBlendFactor::One); + } + BlendState::RGBOneAlphaOneMinusSrcAlpha => { + pipeline_color_attachment.set_source_rgb_blend_factor(MTLBlendFactor::One); + pipeline_color_attachment.set_destination_rgb_blend_factor( + MTLBlendFactor::OneMinusSourceAlpha); + pipeline_color_attachment.set_source_alpha_blend_factor(MTLBlendFactor::One); + pipeline_color_attachment.set_destination_alpha_blend_factor(MTLBlendFactor::One); + } + BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha => { + pipeline_color_attachment.set_source_rgb_blend_factor(MTLBlendFactor::SourceAlpha); + pipeline_color_attachment.set_destination_rgb_blend_factor( + MTLBlendFactor::OneMinusSourceAlpha); + pipeline_color_attachment.set_source_alpha_blend_factor(MTLBlendFactor::One); + pipeline_color_attachment.set_destination_alpha_blend_factor(MTLBlendFactor::One); + } + } + + if render_state.options.color_mask { + pipeline_color_attachment.set_write_mask(MTLColorWriteMask::all()); + } else { + pipeline_color_attachment.set_write_mask(MTLColorWriteMask::empty()); + } + } + + fn create_render_pass_descriptor(&self, render_state: &RenderState) + -> RenderPassDescriptor { + let render_pass_descriptor = RenderPassDescriptor::new().retain(); + let color_attachment = render_pass_descriptor.color_attachments().object_at(0).unwrap(); + color_attachment.set_texture(Some(&self.render_target_color_texture(render_state.target))); + + match render_state.options.clear_ops.color { + Some(color) => { + let color = MTLClearColor::new(color.r() as f64, + color.g() as f64, + color.b() as f64, + color.a() as f64); + color_attachment.set_clear_color(color); + color_attachment.set_load_action(MTLLoadAction::Clear); + } + None => color_attachment.set_load_action(MTLLoadAction::Load), + } + color_attachment.set_store_action(MTLStoreAction::Store); + + let depth_stencil_texture = self.render_target_depth_texture(render_state.target); + if let Some(depth_stencil_texture) = depth_stencil_texture { + let depth_attachment = render_pass_descriptor.depth_attachment().unwrap(); + let stencil_attachment = render_pass_descriptor.stencil_attachment().unwrap(); + depth_attachment.set_texture(Some(&depth_stencil_texture)); + stencil_attachment.set_texture(Some(&depth_stencil_texture)); + + match render_state.options.clear_ops.depth { + Some(depth) => { + depth_attachment.set_clear_depth(depth as f64); + depth_attachment.set_load_action(MTLLoadAction::Clear); + } + None => depth_attachment.set_load_action(MTLLoadAction::Load), + } + depth_attachment.set_store_action(MTLStoreAction::Store); + + match render_state.options.clear_ops.stencil { + Some(value) => { + stencil_attachment.set_clear_stencil(value as u32); + stencil_attachment.set_load_action(MTLLoadAction::Clear); + } + None => stencil_attachment.set_load_action(MTLLoadAction::Load), + } + stencil_attachment.set_store_action(MTLStoreAction::Store); + } + + render_pass_descriptor + } + + fn set_depth_stencil_state(&self, + encoder: &RenderCommandEncoderRef, + render_state: &RenderState) { + let depth_stencil_descriptor = DepthStencilDescriptor::new(); + + match render_state.options.depth { + Some(depth_state) => { + let compare_function = depth_state.func.to_metal_compare_function(); + depth_stencil_descriptor.set_depth_compare_function(compare_function); + depth_stencil_descriptor.set_depth_write_enabled(depth_state.write); + } + None => { + depth_stencil_descriptor.set_depth_compare_function(MTLCompareFunction::Always); + depth_stencil_descriptor.set_depth_write_enabled(false); + } + } + + match render_state.options.stencil { + Some(stencil_state) => { + let stencil_descriptor = StencilDescriptor::new(); + let compare_function = stencil_state.func.to_metal_compare_function(); + let (pass_operation, write_mask) = if stencil_state.write { + (MTLStencilOperation::Replace, stencil_state.mask) + } else { + (MTLStencilOperation::Keep, 0) + }; + stencil_descriptor.set_stencil_compare_function(compare_function); + stencil_descriptor.set_stencil_failure_operation(MTLStencilOperation::Keep); + stencil_descriptor.set_depth_failure_operation(MTLStencilOperation::Keep); + stencil_descriptor.set_depth_stencil_pass_operation(pass_operation); + stencil_descriptor.set_write_mask(write_mask); + depth_stencil_descriptor.set_front_face_stencil(Some(&stencil_descriptor)); + depth_stencil_descriptor.set_back_face_stencil(Some(&stencil_descriptor)); + encoder.set_stencil_reference_value(stencil_state.reference); + } + None => { + depth_stencil_descriptor.set_front_face_stencil(None); + depth_stencil_descriptor.set_back_face_stencil(None); + } + } + + let depth_stencil_state = self.device.new_depth_stencil_state(&depth_stencil_descriptor); + encoder.set_depth_stencil_state(&depth_stencil_state); + } + + fn texture_format(&self, texture: &Texture) -> Option { + match texture.pixel_format() { + MTLPixelFormat::R8Unorm => Some(TextureFormat::R8), + MTLPixelFormat::R16Float => Some(TextureFormat::R16F), + MTLPixelFormat::RGBA8Unorm => Some(TextureFormat::RGBA8), + _ => None, + } + } + + fn set_viewport(&self, encoder: &RenderCommandEncoderRef, viewport: &RectI) { + encoder.set_viewport(MTLViewport { + originX: viewport.origin().x() as f64, + originY: viewport.origin().y() as f64, + width: viewport.size().x() as f64, + height: viewport.size().y() as f64, + znear: 0.0, + zfar: 1.0, + }) + } + + fn synchronize_texture(&self, texture: &Texture) { + { + let command_buffers = self.command_buffers.borrow(); + let encoder = command_buffers.last().unwrap().new_blit_command_encoder(); + encoder.synchronize_resource(&texture); + encoder.end_encoding(); + } + + self.end_commands(); + self.begin_commands(); + } +} + +trait DeviceExtra { + fn create_depth_stencil_texture(&self, size: Vector2I) -> Texture; +} + +impl DeviceExtra for metal::Device { + fn create_depth_stencil_texture(&self, size: Vector2I) -> Texture { + let descriptor = TextureDescriptor::new(); + descriptor.set_texture_type(MTLTextureType::D2); + descriptor.set_pixel_format(MTLPixelFormat::Depth32Float_Stencil8); + descriptor.set_width(size.x() as u64); + descriptor.set_height(size.y() as u64); + descriptor.set_storage_mode(MTLStorageMode::Private); + descriptor.set_usage(MTLTextureUsage::Unknown); + self.new_texture(&descriptor) + } +} + +// Conversion helpers + +trait DepthFuncExt { + fn to_metal_compare_function(self) -> MTLCompareFunction; +} + +impl DepthFuncExt for DepthFunc { + fn to_metal_compare_function(self) -> MTLCompareFunction { + match self { + DepthFunc::Less => MTLCompareFunction::Less, + DepthFunc::Always => MTLCompareFunction::Always, + } + } +} + +trait PrimitiveExt { + fn to_metal_primitive(self) -> MTLPrimitiveType; +} + +impl PrimitiveExt for Primitive { + fn to_metal_primitive(self) -> MTLPrimitiveType { + match self { + Primitive::Triangles => MTLPrimitiveType::Triangle, + Primitive::Lines => MTLPrimitiveType::Line, + } + } +} + +trait StencilFuncExt { + fn to_metal_compare_function(self) -> MTLCompareFunction; +} + +impl StencilFuncExt for StencilFunc { + fn to_metal_compare_function(self) -> MTLCompareFunction { + match self { + StencilFunc::Always => MTLCompareFunction::Always, + StencilFunc::Equal => MTLCompareFunction::Equal, + } + } +} + +trait UniformDataExt { + fn as_bytes(&self) -> Option<&[u8]>; +} + +impl UniformDataExt for UniformData { + fn as_bytes(&self) -> Option<&[u8]> { + unsafe { + match *self { + UniformData::TextureUnit(_) => None, + UniformData::Int(ref data) => { + Some(slice::from_raw_parts(data as *const i32 as *const u8, 4 * 1)) + } + UniformData::Mat4(ref data) => { + Some(slice::from_raw_parts(&data[0] as *const F32x4 as *const u8, 4 * 16)) + } + UniformData::Vec2(ref data) => { + Some(slice::from_raw_parts(data as *const F32x4 as *const u8, 4 * 2)) + } + UniformData::Vec4(ref data) => { + Some(slice::from_raw_parts(data as *const F32x4 as *const u8, 4 * 4)) + } + } + } + } +} + +// Extra structs missing from `metal-rs` + +bitflags! { + struct MTLPipelineOption: NSUInteger { + const ArgumentInfo = 1 << 0; + const BufferTypeInfo = 1 << 1; + } +} + +// Extra objects missing from `metal-rs` + +struct ArgumentArray(*mut Object); + +impl Drop for ArgumentArray { + fn drop(&mut self) { + unsafe { msg_send![self.0, release] } + } +} + +impl ArgumentArray { + unsafe fn from_ptr(object: *mut Object) -> ArgumentArray { + ArgumentArray(msg_send![object, retain]) + } + + fn len(&self) -> u64 { + unsafe { msg_send![self.0, count] } + } + + fn object_at(&self, index: u64) -> Argument { + unsafe { + let argument: *mut MTLArgument = msg_send![self.0, objectAtIndex:index]; + Argument::from_ptr(msg_send![argument, retain]) + } + } +} + +struct SharedEvent(*mut Object); + +impl Drop for SharedEvent { + fn drop(&mut self) { + unsafe { msg_send![self.0, release] } + } +} + +impl SharedEvent { + fn notify_listener_at_value(&self, + listener: &SharedEventListener, + value: u64, + block: RcBlock<(*mut Object, u64), ()>) { + unsafe { + // If the block doesn't have a signature, this segfaults. + let block = &*block as + *const Block<(*mut Object, u64), ()> as + *mut Block<(*mut Object, u64), ()> as + *mut BlockBase<(*mut Object, u64), ()>; + (*block).flags |= BLOCK_HAS_SIGNATURE | BLOCK_HAS_COPY_DISPOSE; + (*block).extra = &BLOCK_EXTRA; + msg_send![self.0, notifyListener:listener.0 atValue:value block:block]; + mem::forget(block); + } + + extern "C" fn dtor(_: *mut BlockBase<(*mut Object, u64), ()>) {} + + static mut SIGNATURE: &[u8] = b"v16@?0Q8\0"; + static mut SIGNATURE_PTR: *const i8 = unsafe { &SIGNATURE[0] as *const u8 as *const i8 }; + static mut BLOCK_EXTRA: BlockExtra<(*mut Object, u64), ()> = BlockExtra { + unknown0: 0 as *mut i32, + unknown1: 0 as *mut i32, + unknown2: 0 as *mut i32, + dtor: dtor, + signature: unsafe { &SIGNATURE_PTR }, + }; + } +} + +struct SharedEventListener(*mut Object); + +impl Drop for SharedEventListener { + fn drop(&mut self) { + unsafe { msg_send![self.0, release] } + } +} + +impl SharedEventListener { + fn new() -> SharedEventListener { + unsafe { + let listener: *mut Object = msg_send![class!(MTLSharedEventListener), alloc]; + SharedEventListener(msg_send![listener, init]) + } + } +} + +struct VertexAttributeArray(*mut Object); + +impl Drop for VertexAttributeArray { + fn drop(&mut self) { + unsafe { msg_send![self.0, release] } + } +} + +impl VertexAttributeArray { + unsafe fn from_ptr(object: *mut Object) -> VertexAttributeArray { + VertexAttributeArray(msg_send![object, retain]) + } + + fn len(&self) -> u64 { + unsafe { msg_send![self.0, count] } + } + + fn object_at(&self, index: u64) -> &VertexAttributeRef { + unsafe { VertexAttributeRef::from_ptr(msg_send![self.0, objectAtIndex:index]) } + } +} + +// Extra methods missing from `metal-rs` + +trait CoreAnimationLayerExt { + fn device(&self) -> metal::Device; +} + +impl CoreAnimationLayerExt for CoreAnimationLayer { + fn device(&self) -> metal::Device { + unsafe { + let device: *mut MTLDevice = msg_send![self.as_ptr(), device]; + metal::Device::from_ptr(msg_send![device, retain]) + } + } +} + +trait CommandBufferExt { + fn encode_signal_event(&self, event: &SharedEvent, value: u64); +} + +impl CommandBufferExt for CommandBuffer { + fn encode_signal_event(&self, event: &SharedEvent, value: u64) { + unsafe { + msg_send![self.as_ptr(), encodeSignalEvent:event.0 value:value] + } + } +} + +trait DeviceExt { + // `new_render_pipeline_state_with_reflection()` in `metal-rs` doesn't correctly initialize the + // `reflection` argument. This is a better definition. + fn real_new_render_pipeline_state_with_reflection(&self, + descriptor: &RenderPipelineDescriptor, + options: MTLPipelineOption) + -> (RenderPipelineState, + RenderPipelineReflection); + fn new_shared_event(&self) -> SharedEvent; +} + +impl DeviceExt for metal::Device { + fn real_new_render_pipeline_state_with_reflection(&self, + descriptor: &RenderPipelineDescriptor, + options: MTLPipelineOption) + -> (RenderPipelineState, + RenderPipelineReflection) { + unsafe { + let mut reflection_ptr: *mut MTLRenderPipelineReflection = ptr::null_mut(); + let mut error_ptr: *mut Object = ptr::null_mut(); + let render_pipeline_state_ptr: *mut MTLRenderPipelineState = + msg_send![self.as_ptr(), + newRenderPipelineStateWithDescriptor:descriptor.as_ptr() + options:options + reflection:&mut reflection_ptr + error:&mut error_ptr]; + if !error_ptr.is_null() { + let description: CFStringRef = msg_send![error_ptr, description]; + panic!("Render pipeline state construction failed: {}", + CFString::wrap_under_get_rule(description).to_string()); + } + assert!(!render_pipeline_state_ptr.is_null()); + assert!(!reflection_ptr.is_null()); + (RenderPipelineState::from_ptr(render_pipeline_state_ptr), + RenderPipelineReflection::from_ptr(msg_send![reflection_ptr, retain])) + } + } + + fn new_shared_event(&self) -> SharedEvent { + unsafe { SharedEvent(msg_send![self.as_ptr(), newSharedEvent]) } + } +} + +trait FunctionExt { + // `vertex_attributes()` in `metal-rs` segfaults! This is a better definition. + fn real_vertex_attributes(&self) -> VertexAttributeArray; + fn new_argument_encoder_with_reflection(&self, buffer_index: u64) + -> (ArgumentEncoder, Argument); +} + +impl FunctionExt for Function { + fn real_vertex_attributes(&self) -> VertexAttributeArray { + unsafe { + VertexAttributeArray::from_ptr(msg_send![(*self).as_ptr(), vertexAttributes]) + } + } + + fn new_argument_encoder_with_reflection(&self, buffer_index: u64) + -> (ArgumentEncoder, Argument) { + unsafe { + let mut reflection = ptr::null_mut(); + let encoder: *mut MTLArgumentEncoder = + msg_send![self.as_ptr(), newArgumentEncoderWithBufferIndex:buffer_index + reflection:&mut reflection]; + (ArgumentEncoder::from_ptr(encoder), Argument::from_ptr(reflection)) + } + } +} + +trait RenderPipelineReflectionExt { + // `vertex_arguments()` in `metal-rs` segfaults! This is a better definition. + fn real_vertex_arguments(&self) -> ArgumentArray; + // `fragment_arguments()` in `metal-rs` segfaults! This is a better definition. + fn real_fragment_arguments(&self) -> ArgumentArray; +} + +impl RenderPipelineReflectionExt for RenderPipelineReflectionRef { + fn real_vertex_arguments(&self) -> ArgumentArray { + unsafe { ArgumentArray::from_ptr(msg_send![self.as_ptr(), vertexArguments]) } + } + + fn real_fragment_arguments(&self) -> ArgumentArray { + unsafe { ArgumentArray::from_ptr(msg_send![self.as_ptr(), fragmentArguments]) } + } +} + +trait StructMemberExt { + fn argument_index(&self) -> u64; +} + +impl StructMemberExt for StructMemberRef { + fn argument_index(&self) -> u64 { + unsafe { msg_send![self.as_ptr(), argumentIndex] } + } +} + +// Memory management helpers + +trait Retain { + type Owned; + fn retain(&self) -> Self::Owned; +} + +impl Retain for CommandBufferRef { + type Owned = CommandBuffer; + fn retain(&self) -> CommandBuffer { + unsafe { CommandBuffer::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +impl Retain for CoreAnimationDrawableRef { + type Owned = CoreAnimationDrawable; + fn retain(&self) -> CoreAnimationDrawable { + unsafe { CoreAnimationDrawable::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +impl Retain for CoreAnimationLayerRef { + type Owned = CoreAnimationLayer; + fn retain(&self) -> CoreAnimationLayer { + unsafe { CoreAnimationLayer::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +impl Retain for RenderCommandEncoderRef { + type Owned = RenderCommandEncoder; + fn retain(&self) -> RenderCommandEncoder { + unsafe { RenderCommandEncoder::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +impl Retain for RenderPassDescriptorRef { + type Owned = RenderPassDescriptor; + fn retain(&self) -> RenderPassDescriptor { + unsafe { RenderPassDescriptor::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +impl Retain for StructTypeRef { + type Owned = StructType; + fn retain(&self) -> StructType { + unsafe { StructType::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +impl Retain for TextureRef { + type Owned = Texture; + fn retain(&self) -> Texture { + unsafe { Texture::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +impl Retain for VertexAttributeRef { + type Owned = VertexAttribute; + fn retain(&self) -> VertexAttribute { + unsafe { VertexAttribute::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +impl Retain for VertexDescriptorRef { + type Owned = VertexDescriptor; + fn retain(&self) -> VertexDescriptor { + unsafe { VertexDescriptor::from_ptr(msg_send![self.as_ptr(), retain]) } + } +} + +// Extra block stuff not supported by `block` + +const BLOCK_HAS_COPY_DISPOSE: i32 = 0x02000000; +const BLOCK_HAS_SIGNATURE: i32 = 0x40000000; + +#[repr(C)] +struct BlockBase { + isa: *const Class, // 0x00 + flags: i32, // 0x08 + _reserved: i32, // 0x0c + invoke: unsafe extern fn(*mut Block, ...) -> R, // 0x10 + extra: *const BlockExtra, // 0x18 +} + +type BlockExtraDtor = extern "C" fn(*mut BlockBase); + +#[repr(C)] +struct BlockExtra { + unknown0: *mut i32, // 0x00 + unknown1: *mut i32, // 0x08 + unknown2: *mut i32, // 0x10 + dtor: BlockExtraDtor, // 0x18 + signature: *const *const i8, // 0x20 +} diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index 0a575b30..106c0bab 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -5,6 +5,7 @@ edition = "2018" authors = ["Patrick Walton "] [dependencies] +bitflags = "1.0" byteorder = "1.2" fixedbitset = "0.1" hashbrown = "0.1" diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 73562f8d..ff054432 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -12,7 +12,7 @@ use crate::concurrent::executor::Executor; use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, FillBatchPrimitive, RenderCommand}; -use crate::options::{PreparedRenderOptions, RenderCommandListener}; +use crate::options::{PreparedBuildOptions, RenderCommandListener}; use crate::scene::Scene; use crate::tile_map::DenseTileMap; use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler}; @@ -28,7 +28,7 @@ use std::u16; pub(crate) struct SceneBuilder<'a> { scene: &'a Scene, - built_options: &'a PreparedRenderOptions, + built_options: &'a PreparedBuildOptions, pub(crate) next_alpha_tile_index: AtomicUsize, pub(crate) z_buffer: ZBuffer, @@ -38,7 +38,7 @@ pub(crate) struct SceneBuilder<'a> { impl<'a> SceneBuilder<'a> { pub(crate) fn new( scene: &'a Scene, - built_options: &'a PreparedRenderOptions, + built_options: &'a PreparedBuildOptions, listener: Box, ) -> SceneBuilder<'a> { let effective_view_box = scene.effective_view_box(built_options); @@ -76,7 +76,7 @@ impl<'a> SceneBuilder<'a> { &self, path_index: usize, view_box: RectF, - built_options: &PreparedRenderOptions, + built_options: &PreparedBuildOptions, scene: &Scene, ) -> Vec { let path_object = &scene.paths[path_index]; diff --git a/renderer/src/concurrent/scene_proxy.rs b/renderer/src/concurrent/scene_proxy.rs index 09c0e38d..a85d3e89 100644 --- a/renderer/src/concurrent/scene_proxy.rs +++ b/renderer/src/concurrent/scene_proxy.rs @@ -22,7 +22,7 @@ use crate::concurrent::executor::Executor; use crate::gpu::renderer::Renderer; use crate::gpu_data::RenderCommand; -use crate::options::{RenderCommandListener, RenderOptions}; +use crate::options::{BuildOptions, RenderCommandListener}; use crate::scene::Scene; use pathfinder_geometry::basic::rect::RectF; use pathfinder_gpu::Device; @@ -59,15 +59,15 @@ impl SceneProxy { #[inline] pub fn build_with_listener(&self, - options: RenderOptions, + options: BuildOptions, listener: Box) { self.sender.send(MainToWorkerMsg::Build(options, listener)).unwrap(); } #[inline] - pub fn build_with_stream(&self, options: RenderOptions) -> RenderCommandStream { + pub fn build_with_stream(&self, options: BuildOptions) -> RenderCommandStream { let (sender, receiver) = mpsc::sync_channel(MAX_MESSAGES_IN_FLIGHT); - let listener = Box::new(move |command| sender.send(command).unwrap()); + let listener = Box::new(move |command| drop(sender.send(command))); self.build_with_listener(options, listener); RenderCommandStream::new(receiver) } @@ -81,11 +81,11 @@ impl SceneProxy { /// renderer.render_command(&command) /// } #[inline] - pub fn build_and_render(&self, renderer: &mut Renderer, options: RenderOptions) + pub fn build_and_render(&self, renderer: &mut Renderer, build_options: BuildOptions) where D: Device { renderer.begin_scene(); - for command in self.build_with_stream(options) { - renderer.render_command(&command) + for command in self.build_with_stream(build_options) { + renderer.render_command(&command); } renderer.end_scene(); } @@ -118,7 +118,7 @@ fn scene_thread(mut scene: Scene, enum MainToWorkerMsg { ReplaceScene(Scene), SetViewBox(RectF), - Build(RenderOptions, Box), + Build(BuildOptions, Box), GetSVG(Sender>), } diff --git a/renderer/src/gpu/mod.rs b/renderer/src/gpu/mod.rs index cc28fc80..a5e818e0 100644 --- a/renderer/src/gpu/mod.rs +++ b/renderer/src/gpu/mod.rs @@ -11,4 +11,5 @@ //! The GPU renderer for Pathfinder 3. pub mod debug; +pub mod options; pub mod renderer; diff --git a/renderer/src/gpu/options.rs b/renderer/src/gpu/options.rs new file mode 100644 index 00000000..04c9af59 --- /dev/null +++ b/renderer/src/gpu/options.rs @@ -0,0 +1,60 @@ +// pathfinder/renderer/src/gpu/options.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use pathfinder_geometry::basic::rect::RectI; +use pathfinder_geometry::basic::vector::Vector2I; +use pathfinder_geometry::color::ColorF; +use pathfinder_gpu::Device; + +/// Options that influence rendering. +#[derive(Default)] +pub struct RendererOptions { + pub background_color: Option, +} + +#[derive(Clone)] +pub enum DestFramebuffer +where + D: Device, +{ + Default { + viewport: RectI, + window_size: Vector2I, + }, + Other(D::Framebuffer), +} + +impl Default for DestFramebuffer where D: Device { + #[inline] + fn default() -> DestFramebuffer { + DestFramebuffer::Default { viewport: RectI::default(), window_size: Vector2I::default() } + } +} + +impl DestFramebuffer +where + D: Device, +{ + #[inline] + pub fn full_window(window_size: Vector2I) -> DestFramebuffer { + let viewport = RectI::new(Vector2I::default(), window_size); + DestFramebuffer::Default { viewport, window_size } + } + + #[inline] + pub fn window_size(&self, device: &D) -> Vector2I { + match *self { + DestFramebuffer::Default { window_size, .. } => window_size, + DestFramebuffer::Other(ref framebuffer) => { + device.texture_size(device.framebuffer_texture(framebuffer)) + } + } + } +} diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 80bdc477..e616615e 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -9,6 +9,7 @@ // except according to those terms. use crate::gpu::debug::DebugUIPresenter; +use crate::gpu::options::{DestFramebuffer, RendererOptions}; use crate::gpu_data::{AlphaTileBatchPrimitive, FillBatchPrimitive, PaintData}; use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive}; use crate::post::DefringingKernel; @@ -18,10 +19,10 @@ use pathfinder_geometry::basic::rect::RectI; use pathfinder_geometry::basic::transform3d::Transform3DF; use pathfinder_geometry::color::ColorF; use pathfinder_gpu::resources::ResourceLoader; -use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearParams}; -use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc}; -use pathfinder_gpu::{StencilState, TextureFormat, UniformData, VertexAttrClass}; -use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType}; +use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearOps}; +use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderOptions, RenderState}; +use pathfinder_gpu::{RenderTarget, StencilFunc, StencilState, TextureFormat, UniformData}; +use pathfinder_gpu::{VertexAttrClass, VertexAttrDescriptor, VertexAttrType}; use pathfinder_simd::default::{F32x4, I32x4}; use std::cmp; use std::collections::VecDeque; @@ -30,7 +31,7 @@ use std::ops::{Add, Div}; 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: [u16; 8] = [0, 0, 1, 0, 1, 1, 0, 1]; static QUAD_VERTEX_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3]; // FIXME(pcwalton): Shrink this again! @@ -39,7 +40,7 @@ const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * 256; // TODO(pcwalton): Replace with `mem::size_of` calls? const FILL_INSTANCE_SIZE: usize = 8; -const SOLID_TILE_INSTANCE_SIZE: usize = 10; +const SOLID_TILE_INSTANCE_SIZE: usize = 12; const MASK_TILE_INSTANCE_SIZE: usize = 12; const MAX_FILLS_PER_BATCH: usize = 0x4000; @@ -53,6 +54,7 @@ where // Core data dest_framebuffer: DestFramebuffer, + options: RendererOptions, fill_program: FillProgram, solid_multicolor_tile_program: SolidTileMulticolorProgram, alpha_multicolor_tile_program: AlphaTileMulticolorProgram, @@ -84,7 +86,7 @@ where reprojection_vertex_array: ReprojectionVertexArray, // Rendering state - mask_framebuffer_cleared: bool, + framebuffer_flags: FramebufferFlags, buffered_fills: Vec, // Debug @@ -103,11 +105,11 @@ impl Renderer where D: Device, { - pub fn new( - device: D, - resources: &dyn ResourceLoader, - dest_framebuffer: DestFramebuffer, - ) -> Renderer { + pub fn new(device: D, + resources: &dyn ResourceLoader, + dest_framebuffer: DestFramebuffer, + options: RendererOptions) + -> Renderer { let fill_program = FillProgram::new(&device, resources); let solid_multicolor_tile_program = SolidTileMulticolorProgram::new(&device, resources); @@ -190,10 +192,11 @@ where let window_size = dest_framebuffer.window_size(&device); let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size); - let renderer = Renderer { + Renderer { device, dest_framebuffer, + options, fill_program, solid_monochrome_tile_program, alpha_monochrome_tile_program, @@ -227,23 +230,18 @@ where free_timer_queries: vec![], debug_ui_presenter, - mask_framebuffer_cleared: false, + framebuffer_flags: FramebufferFlags::empty(), buffered_fills: vec![], render_mode: RenderMode::default(), use_depth: false, - }; - - // As a convenience, bind the destination framebuffer. - renderer.bind_dest_framebuffer(); - - renderer + } } pub fn begin_scene(&mut self) { + self.framebuffer_flags = FramebufferFlags::empty(); + self.device.begin_commands(); self.init_postprocessing_framebuffer(); - - self.mask_framebuffer_cleared = false; self.stats = RenderStats::default(); } @@ -258,8 +256,8 @@ where RenderCommand::AddPaintData(ref paint_data) => self.upload_paint_data(paint_data), RenderCommand::AddFills(ref fills) => self.add_fills(fills), RenderCommand::FlushFills => { - self.begin_composite_timer_query(); self.draw_buffered_fills(); + self.begin_composite_timer_query(); } RenderCommand::SolidTile(ref solid_tiles) => { let count = solid_tiles.len(); @@ -284,10 +282,11 @@ where self.end_composite_timer_query(); self.pending_timers.push_back(mem::replace(&mut self.current_timers, RenderTimers::new())); + + self.device.end_commands(); } pub fn draw_debug_ui(&self) { - self.bind_dest_framebuffer(); self.debug_ui_presenter.draw(&self.device); } @@ -297,19 +296,19 @@ where // Accumulate stage-0 time. let mut total_stage_0_time = Duration::new(0, 0); for timer_query in &timers.stage_0 { - if !self.device.timer_query_is_available(timer_query) { - return None; + match self.device.get_timer_query(timer_query) { + None => return None, + Some(stage_0_time) => total_stage_0_time += stage_0_time, } - total_stage_0_time += self.device.get_timer_query(timer_query); } // Get stage-1 time. let stage_1_time = { let stage_1_timer_query = timers.stage_1.as_ref().unwrap(); - if !self.device.timer_query_is_available(&stage_1_timer_query) { - return None; + match self.device.get_timer_query(stage_1_timer_query) { + None => return None, + Some(query) => query, } - self.device.get_timer_query(stage_1_timer_query) }; // Recycle all timer queries. @@ -333,6 +332,11 @@ where mem::replace(&mut self.dest_framebuffer, new_dest_framebuffer) } + #[inline] + pub fn set_options(&mut self, new_options: RendererOptions) { + self.options = new_options + } + #[inline] pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: Vector2I) { self.debug_ui_presenter.ui_presenter.set_framebuffer_size(new_framebuffer_size); @@ -396,24 +400,11 @@ where ); } - fn clear_mask_framebuffer(&mut self) { - self.device.bind_framebuffer(&self.mask_framebuffer); - - // TODO(pcwalton): Only clear the appropriate portion? - self.device.clear(&ClearParams { - color: Some(ColorF::transparent_black()), - ..ClearParams::default() - }); - } - fn add_fills(&mut self, mut fills: &[FillBatchPrimitive]) { if fills.is_empty() { return; } - let timer_query = self.allocate_timer_query(); - self.device.begin_timer_query(&timer_query); - self.stats.fill_count += fills.len(); while !fills.is_empty() { @@ -424,9 +415,6 @@ where self.draw_buffered_fills(); } } - - self.device.end_timer_query(&timer_query); - self.current_timers.stage_0.push(timer_query); } fn draw_buffered_fills(&mut self) { @@ -441,178 +429,184 @@ where 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_vertex_array(&self.fill_vertex_array.vertex_array); - self.device.use_program(&self.fill_program.program); - self.device.set_uniform( - &self.fill_program.framebuffer_size_uniform, - UniformData::Vec2( - I32x4::new(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT, 0, 0).to_f32x4(), - ), - ); - self.device.set_uniform( - &self.fill_program.tile_size_uniform, - UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, TILE_HEIGHT as i32, 0, 0).to_f32x4()), - ); - self.device.bind_texture(&self.area_lut_texture, 0); - self.device.set_uniform( - &self.fill_program.area_lut_uniform, - UniformData::TextureUnit(0), - ); - let render_state = RenderState { - blend: BlendState::RGBOneAlphaOne, - ..RenderState::default() + let mut clear_color = None; + if !self.framebuffer_flags.contains( + FramebufferFlags::MUST_PRESERVE_MASK_FRAMEBUFFER_CONTENTS) { + clear_color = Some(ColorF::default()); }; - debug_assert!(self.buffered_fills.len() <= u32::MAX as usize); - self.device.draw_elements_instanced( - Primitive::Triangles, - 6, - self.buffered_fills.len() as u32, - &render_state, - ); - self.buffered_fills.clear() + let timer_query = self.allocate_timer_query(); + self.device.begin_timer_query(&timer_query); + + debug_assert!(self.buffered_fills.len() <= u32::MAX as usize); + self.device.draw_elements_instanced(6, self.buffered_fills.len() as u32, &RenderState { + target: &RenderTarget::Framebuffer(&self.mask_framebuffer), + program: &self.fill_program.program, + vertex_array: &self.fill_vertex_array.vertex_array, + primitive: Primitive::Triangles, + textures: &[&self.area_lut_texture], + uniforms: &[ + (&self.fill_program.framebuffer_size_uniform, + UniformData::Vec2(I32x4::new(MASK_FRAMEBUFFER_WIDTH, + MASK_FRAMEBUFFER_HEIGHT, + 0, + 0).to_f32x4())), + (&self.fill_program.tile_size_uniform, + UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, + TILE_HEIGHT as i32, + 0, + 0).to_f32x4())), + (&self.fill_program.area_lut_uniform, UniformData::TextureUnit(0)), + ], + viewport: self.mask_viewport(), + options: RenderOptions { + blend: BlendState::RGBOneAlphaOne, + clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, + ..RenderOptions::default() + }, + }); + + self.device.end_timer_query(&timer_query); + self.current_timers.stage_0.push(timer_query); + + self.framebuffer_flags.insert(FramebufferFlags::MUST_PRESERVE_MASK_FRAMEBUFFER_CONTENTS); + self.buffered_fills.clear(); } fn draw_alpha_tiles(&mut self, count: u32) { - self.bind_draw_framebuffer(); + let clear_color = self.clear_color_for_draw_operation(); let alpha_tile_vertex_array = self.alpha_tile_vertex_array(); let alpha_tile_program = self.alpha_tile_program(); - self.device - .bind_vertex_array(&alpha_tile_vertex_array.vertex_array); - self.device.use_program(&alpha_tile_program.program); - self.device.set_uniform( - &alpha_tile_program.framebuffer_size_uniform, - UniformData::Vec2(self.draw_viewport().size().to_f32().0), - ); - self.device.set_uniform( - &alpha_tile_program.tile_size_uniform, - UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, TILE_HEIGHT as i32, 0, 0).to_f32x4()), - ); - self.device - .bind_texture(self.device.framebuffer_texture(&self.mask_framebuffer), 0); - self.device.set_uniform( - &alpha_tile_program.stencil_texture_uniform, - UniformData::TextureUnit(0), - ); - self.device.set_uniform( - &alpha_tile_program.stencil_texture_size_uniform, - UniformData::Vec2( - I32x4::new(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT, 0, 0).to_f32x4(), - ), - ); + let draw_viewport = self.draw_viewport(); + let mut textures = vec![self.device.framebuffer_texture(&self.mask_framebuffer)]; + let mut uniforms = vec![ + (&alpha_tile_program.framebuffer_size_uniform, + UniformData::Vec2(draw_viewport.size().to_f32().0)), + (&alpha_tile_program.tile_size_uniform, + UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, + TILE_HEIGHT as i32, + 0, + 0).to_f32x4())), + (&alpha_tile_program.stencil_texture_uniform, UniformData::TextureUnit(0)), + (&alpha_tile_program.stencil_texture_size_uniform, + UniformData::Vec2(I32x4::new(MASK_FRAMEBUFFER_WIDTH, + MASK_FRAMEBUFFER_HEIGHT, + 0, + 0).to_f32x4())), + // FIXME(pcwalton): Fill this in properly! + (&alpha_tile_program.view_box_origin_uniform, UniformData::Vec2(F32x4::default())), + ]; match self.render_mode { RenderMode::Multicolor => { let paint_texture = self.paint_texture.as_ref().unwrap(); - self.device.bind_texture(paint_texture, 1); - self.device.set_uniform( - &self.alpha_multicolor_tile_program.paint_texture_uniform, - UniformData::TextureUnit(1), - ); - self.device.set_uniform( - &self.alpha_multicolor_tile_program.paint_texture_size_uniform, - UniformData::Vec2(self.device.texture_size(paint_texture).0.to_f32x4()) - ); + textures.push(paint_texture); + uniforms.push((&self.alpha_multicolor_tile_program.paint_texture_uniform, + UniformData::TextureUnit(1))); + uniforms.push((&self.alpha_multicolor_tile_program.paint_texture_size_uniform, + UniformData::Vec2(self.device + .texture_size(paint_texture) + .0 + .to_f32x4()))); } RenderMode::Monochrome { .. } if self.postprocessing_needed() => { - self.device.set_uniform( - &self.alpha_monochrome_tile_program.color_uniform, - UniformData::Vec4(F32x4::splat(1.0)), - ); + uniforms.push((&self.alpha_monochrome_tile_program.color_uniform, + UniformData::Vec4(F32x4::splat(1.0)))); } RenderMode::Monochrome { fg_color, .. } => { - self.device.set_uniform( - &self.alpha_monochrome_tile_program.color_uniform, - UniformData::Vec4(fg_color.0), - ); + uniforms.push((&self.alpha_monochrome_tile_program.color_uniform, + UniformData::Vec4(fg_color.0))); } } - // FIXME(pcwalton): Fill this in properly! - self.device.set_uniform( - &alpha_tile_program.view_box_origin_uniform, - UniformData::Vec2(F32x4::default()), - ); - let render_state = RenderState { - blend: BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha, - stencil: self.stencil_state(), - ..RenderState::default() - }; - self.device.draw_elements_instanced(Primitive::Triangles, 6, count, &render_state); + self.device.draw_elements_instanced(6, count, &RenderState { + target: &self.draw_render_target(), + program: &alpha_tile_program.program, + vertex_array: &alpha_tile_vertex_array.vertex_array, + primitive: Primitive::Triangles, + textures: &textures, + uniforms: &uniforms, + viewport: draw_viewport, + options: RenderOptions { + blend: BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha, + stencil: self.stencil_state(), + clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, + ..RenderOptions::default() + }, + }); + + self.preserve_draw_framebuffer(); } fn draw_solid_tiles(&mut self, count: u32) { - self.bind_draw_framebuffer(); + let clear_color = self.clear_color_for_draw_operation(); let solid_tile_vertex_array = self.solid_tile_vertex_array(); let solid_tile_program = self.solid_tile_program(); - self.device - .bind_vertex_array(&solid_tile_vertex_array.vertex_array); - self.device.use_program(&solid_tile_program.program); - self.device.set_uniform( - &solid_tile_program.framebuffer_size_uniform, - UniformData::Vec2(self.draw_viewport().size().0.to_f32x4()), - ); - self.device.set_uniform( - &solid_tile_program.tile_size_uniform, - UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, TILE_HEIGHT as i32, 0, 0).to_f32x4()), - ); + let draw_viewport = self.draw_viewport(); + let mut textures = vec![]; + let mut uniforms = vec![ + (&solid_tile_program.framebuffer_size_uniform, + UniformData::Vec2(draw_viewport.size().0.to_f32x4())), + (&solid_tile_program.tile_size_uniform, + UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, + TILE_HEIGHT as i32, + 0, + 0).to_f32x4())), + // FIXME(pcwalton): Fill this in properly! + (&solid_tile_program.view_box_origin_uniform, UniformData::Vec2(F32x4::default())), + ]; match self.render_mode { RenderMode::Multicolor => { let paint_texture = self.paint_texture.as_ref().unwrap(); - self.device.bind_texture(paint_texture, 0); - self.device.set_uniform( - &self - .solid_multicolor_tile_program - .paint_texture_uniform, - UniformData::TextureUnit(0), - ); - self.device.set_uniform( - &self - .solid_multicolor_tile_program - .paint_texture_size_uniform, - UniformData::Vec2(self.device.texture_size(paint_texture).0.to_f32x4()) - ); + textures.push(paint_texture); + uniforms.push((&self.solid_multicolor_tile_program.paint_texture_uniform, + UniformData::TextureUnit(0))); + uniforms.push((&self.solid_multicolor_tile_program.paint_texture_size_uniform, + UniformData::Vec2(self.device + .texture_size(paint_texture) + .0 + .to_f32x4()))); } RenderMode::Monochrome { .. } if self.postprocessing_needed() => { - self.device.set_uniform( - &self.solid_monochrome_tile_program.color_uniform, - UniformData::Vec4(F32x4::splat(1.0)), - ); + uniforms.push((&self.solid_monochrome_tile_program.color_uniform, + UniformData::Vec4(F32x4::splat(1.0)))); } RenderMode::Monochrome { fg_color, .. } => { - self.device.set_uniform( - &self.solid_monochrome_tile_program.color_uniform, - UniformData::Vec4(fg_color.0), - ); + uniforms.push((&self.solid_monochrome_tile_program.color_uniform, + UniformData::Vec4(fg_color.0))); } } - // FIXME(pcwalton): Fill this in properly! - self.device.set_uniform( - &solid_tile_program.view_box_origin_uniform, - UniformData::Vec2(F32x4::default()), - ); - let render_state = RenderState { - stencil: self.stencil_state(), - ..RenderState::default() - }; - self.device.draw_elements_instanced(Primitive::Triangles, 6, count, &render_state); + self.device.draw_elements_instanced(6, count, &RenderState { + target: &self.draw_render_target(), + program: &solid_tile_program.program, + vertex_array: &solid_tile_vertex_array.vertex_array, + primitive: Primitive::Triangles, + textures: &textures, + uniforms: &uniforms, + viewport: draw_viewport, + options: RenderOptions { + stencil: self.stencil_state(), + clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, + ..RenderOptions::default() + }, + }); + + self.preserve_draw_framebuffer(); } fn postprocess(&mut self) { + let mut clear_color = None; + if !self.framebuffer_flags + .contains(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS) { + clear_color = self.options.background_color; + } + let (fg_color, bg_color, defringing_kernel, gamma_correction_enabled); match self.render_mode { RenderMode::Multicolor => return, @@ -629,62 +623,52 @@ where } } - self.bind_dest_framebuffer(); - - self.device - .bind_vertex_array(&self.postprocess_vertex_array.vertex_array); - self.device.use_program(&self.postprocess_program.program); - self.device.set_uniform( - &self.postprocess_program.framebuffer_size_uniform, - UniformData::Vec2(self.main_viewport().size().to_f32().0), - ); - match defringing_kernel { - Some(ref kernel) => { - self.device.set_uniform( - &self.postprocess_program.kernel_uniform, - UniformData::Vec4(F32x4::from_slice(&kernel.0)), - ); - } - None => { - self.device.set_uniform( - &self.postprocess_program.kernel_uniform, - UniformData::Vec4(F32x4::default()), - ); - } - } - let postprocess_source_framebuffer = self.postprocess_source_framebuffer.as_ref().unwrap(); let source_texture = self .device .framebuffer_texture(postprocess_source_framebuffer); let source_texture_size = self.device.texture_size(source_texture); - self.device.bind_texture(&source_texture, 0); - self.device.set_uniform( - &self.postprocess_program.source_uniform, - UniformData::TextureUnit(0), - ); - self.device.set_uniform( - &self.postprocess_program.source_size_uniform, - UniformData::Vec2(source_texture_size.0.to_f32x4()), - ); - self.device.bind_texture(&self.gamma_lut_texture, 1); - self.device.set_uniform( - &self.postprocess_program.gamma_lut_uniform, - UniformData::TextureUnit(1), - ); - self.device.set_uniform( - &self.postprocess_program.fg_color_uniform, - UniformData::Vec4(fg_color.0), - ); - self.device.set_uniform( - &self.postprocess_program.bg_color_uniform, - UniformData::Vec4(bg_color.0), - ); - self.device.set_uniform( - &self.postprocess_program.gamma_correction_enabled_uniform, - UniformData::Int(gamma_correction_enabled as i32), - ); - self.device.draw_arrays(Primitive::Triangles, 4, &RenderState::default()); + let main_viewport = self.main_viewport(); + + let mut uniforms = vec![ + (&self.postprocess_program.framebuffer_size_uniform, + UniformData::Vec2(main_viewport.size().to_f32().0)), + (&self.postprocess_program.source_uniform, UniformData::TextureUnit(0)), + (&self.postprocess_program.source_size_uniform, + UniformData::Vec2(source_texture_size.0.to_f32x4())), + (&self.postprocess_program.gamma_lut_uniform, UniformData::TextureUnit(1)), + (&self.postprocess_program.fg_color_uniform, UniformData::Vec4(fg_color.0)), + (&self.postprocess_program.bg_color_uniform, UniformData::Vec4(bg_color.0)), + (&self.postprocess_program.gamma_correction_enabled_uniform, + UniformData::Int(gamma_correction_enabled as i32)), + ]; + + match defringing_kernel { + Some(ref kernel) => { + uniforms.push((&self.postprocess_program.kernel_uniform, + UniformData::Vec4(F32x4::from_slice(&kernel.0)))); + } + None => { + uniforms.push((&self.postprocess_program.kernel_uniform, + UniformData::Vec4(F32x4::default()))); + } + } + + self.device.draw_arrays(4, &RenderState { + target: &self.dest_render_target(), + program: &self.postprocess_program.program, + vertex_array: &self.postprocess_vertex_array.vertex_array, + primitive: Primitive::Triangles, + textures: &[&source_texture, &self.gamma_lut_texture], + uniforms: &uniforms, + viewport: main_viewport, + options: RenderOptions { + clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, + ..RenderOptions::default() + }, + }); + + self.framebuffer_flags.insert(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS); } fn solid_tile_program(&self) -> &SolidTileProgram { @@ -715,7 +699,7 @@ where } } - fn draw_stencil(&self, quad_positions: &[Vector4F]) { + fn draw_stencil(&mut self, quad_positions: &[Vector4F]) { self.device.allocate_buffer( &self.stencil_vertex_array.vertex_buffer, BufferData::Memory(quad_positions), @@ -736,19 +720,17 @@ where BufferUploadMode::Dynamic, ); - self.bind_draw_framebuffer(); - - self.device.bind_vertex_array(&self.stencil_vertex_array.vertex_array); - self.device.use_program(&self.stencil_program.program); - self.device.draw_elements( - Primitive::Triangles, - indices.len() as u32, - &RenderState { + self.device.draw_elements(indices.len() as u32, &RenderState { + target: &self.draw_render_target(), + program: &self.stencil_program.program, + vertex_array: &self.stencil_vertex_array.vertex_array, + primitive: Primitive::Triangles, + textures: &[], + uniforms: &[], + viewport: self.draw_viewport(), + options: RenderOptions { // FIXME(pcwalton): Should we really write to the depth buffer? - depth: Some(DepthState { - func: DepthFunc::Less, - write: true, - }), + depth: Some(DepthState { func: DepthFunc::Less, write: true }), stencil: Some(StencilState { func: StencilFunc::Always, reference: 1, @@ -756,63 +738,57 @@ where write: true, }), color_mask: false, - ..RenderState::default() + clear_ops: ClearOps { stencil: Some(0), ..ClearOps::default() }, + ..RenderOptions::default() }, - ) + }); } pub fn reproject_texture( - &self, + &mut self, texture: &D::Texture, old_transform: &Transform3DF, new_transform: &Transform3DF, ) { - self.bind_draw_framebuffer(); + let clear_color = self.clear_color_for_draw_operation(); - self.device.bind_vertex_array(&self.reprojection_vertex_array.vertex_array); - self.device.use_program(&self.reprojection_program.program); - self.device.set_uniform( - &self.reprojection_program.old_transform_uniform, - UniformData::from_transform_3d(old_transform), - ); - self.device.set_uniform( - &self.reprojection_program.new_transform_uniform, - UniformData::from_transform_3d(new_transform), - ); - self.device.bind_texture(texture, 0); - self.device.set_uniform( - &self.reprojection_program.texture_uniform, - UniformData::TextureUnit(0), - ); - self.device.draw_elements( - Primitive::Triangles, - 6, - &RenderState { + self.device.draw_elements(6, &RenderState { + target: &self.draw_render_target(), + program: &self.reprojection_program.program, + vertex_array: &self.reprojection_vertex_array.vertex_array, + primitive: Primitive::Triangles, + textures: &[texture], + uniforms: &[ + (&self.reprojection_program.old_transform_uniform, + UniformData::from_transform_3d(old_transform)), + (&self.reprojection_program.new_transform_uniform, + UniformData::from_transform_3d(new_transform)), + (&self.reprojection_program.texture_uniform, UniformData::TextureUnit(0)), + ], + viewport: self.draw_viewport(), + options: RenderOptions { blend: BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha, - depth: Some(DepthState { - func: DepthFunc::Less, - write: false, - }), - ..RenderState::default() + depth: Some(DepthState { func: DepthFunc::Less, write: false, }), + clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, + ..RenderOptions::default() }, - ); + }); + + self.preserve_draw_framebuffer(); } - pub fn bind_draw_framebuffer(&self) { + pub fn draw_render_target(&self) -> RenderTarget { if self.postprocessing_needed() { - self.device - .bind_framebuffer(self.postprocess_source_framebuffer.as_ref().unwrap()); + RenderTarget::Framebuffer(self.postprocess_source_framebuffer.as_ref().unwrap()) } else { - self.bind_dest_framebuffer(); + self.dest_render_target() } } - pub fn bind_dest_framebuffer(&self) { + pub fn dest_render_target(&self) -> RenderTarget { match self.dest_framebuffer { - DestFramebuffer::Default { viewport, .. } => { - self.device.bind_default_framebuffer(viewport) - } - DestFramebuffer::Other(ref framebuffer) => self.device.bind_framebuffer(framebuffer), + DestFramebuffer::Default { .. } => RenderTarget::Default, + DestFramebuffer::Other(ref framebuffer) => RenderTarget::Framebuffer(framebuffer), } } @@ -833,16 +809,21 @@ where let texture = self .device .create_texture(TextureFormat::R8, source_framebuffer_size); - self.postprocess_source_framebuffer = Some(self.device.create_framebuffer(texture)) + self.postprocess_source_framebuffer = + Some(self.device.create_framebuffer(texture)); } }; - self.device - .bind_framebuffer(self.postprocess_source_framebuffer.as_ref().unwrap()); - self.device.clear(&ClearParams { - color: Some(ColorF::transparent_black()), - ..ClearParams::default() - }); + /* + self.device.clear(&RenderTarget::Framebuffer(self.postprocess_source_framebuffer + .as_ref() + .unwrap()), + RectI::new(Vector2I::default(), source_framebuffer_size), + &ClearParams { + color: Some(ColorF::transparent_black()), + ..ClearParams::default() + }); + */ } fn postprocessing_needed(&self) -> bool { @@ -869,7 +850,33 @@ where }) } - fn draw_viewport(&self) -> RectI { + fn clear_color_for_draw_operation(&mut self) -> Option { + let postprocessing_needed = self.postprocessing_needed(); + let flag = if postprocessing_needed { + FramebufferFlags::MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS + } else { + FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS + }; + + if self.framebuffer_flags.contains(flag) { + None + } else if !postprocessing_needed { + self.options.background_color + } else { + Some(ColorF::default()) + } + } + + fn preserve_draw_framebuffer(&mut self) { + let flag = if self.postprocessing_needed() { + FramebufferFlags::MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS + } else { + FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS + }; + self.framebuffer_flags.insert(flag); + } + + pub fn draw_viewport(&self) -> RectI { let main_viewport = self.main_viewport(); match self.render_mode { RenderMode::Monochrome { @@ -895,6 +902,11 @@ where } } + fn mask_viewport(&self) -> RectI { + let texture = self.device.framebuffer_texture(&self.mask_framebuffer); + RectI::new(Vector2I::default(), self.device.texture_size(texture)) + } + fn allocate_timer_query(&mut self) -> D::TimerQuery { match self.free_timer_queries.pop() { Some(query) => query, @@ -909,8 +921,9 @@ where } fn end_composite_timer_query(&mut self) { - let query = self.current_timers.stage_1.as_ref().expect("No stage 1 timer query yet?!"); - self.device.end_timer_query(&query); + if let Some(ref query) = self.current_timers.stage_1 { + self.device.end_timer_query(query); + } } } @@ -951,59 +964,63 @@ where let to_subpx_attr = device.get_vertex_attr(&fill_program.program, "ToSubpx").unwrap(); let tile_index_attr = device.get_vertex_attr(&fill_program.program, "TileIndex").unwrap(); - device.bind_vertex_array(&vertex_array); - device.use_program(&fill_program.program); - device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&tess_coord_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &tess_coord_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U8, - stride: 0, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::U16, + stride: 4, offset: 0, divisor: 0, + buffer_index: 0, }); - device.bind_buffer(&vertex_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&from_px_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &from_px_attr, &VertexAttrDescriptor { size: 1, class: VertexAttrClass::Int, attr_type: VertexAttrType::U8, stride: FILL_INSTANCE_SIZE, offset: 0, divisor: 1, + buffer_index: 1, }); - device.configure_vertex_attr(&to_px_attr, &VertexAttrDescriptor { + device.configure_vertex_attr(&vertex_array, &to_px_attr, &VertexAttrDescriptor { size: 1, class: VertexAttrClass::Int, attr_type: VertexAttrType::U8, stride: FILL_INSTANCE_SIZE, offset: 1, divisor: 1, + buffer_index: 1, }); - device.configure_vertex_attr(&from_subpx_attr, &VertexAttrDescriptor { + device.configure_vertex_attr(&vertex_array, &from_subpx_attr, &VertexAttrDescriptor { size: 2, class: VertexAttrClass::FloatNorm, attr_type: VertexAttrType::U8, stride: FILL_INSTANCE_SIZE, offset: 2, divisor: 1, + buffer_index: 1, }); - device.configure_vertex_attr(&to_subpx_attr, &VertexAttrDescriptor { + device.configure_vertex_attr(&vertex_array, &to_subpx_attr, &VertexAttrDescriptor { size: 2, class: VertexAttrClass::FloatNorm, attr_type: VertexAttrType::U8, stride: FILL_INSTANCE_SIZE, offset: 4, divisor: 1, + buffer_index: 1, }); - device.configure_vertex_attr(&tile_index_attr, &VertexAttrDescriptor { + device.configure_vertex_attr(&vertex_array, &tile_index_attr, &VertexAttrDescriptor { size: 1, class: VertexAttrClass::Int, attr_type: VertexAttrType::U16, stride: FILL_INSTANCE_SIZE, offset: 6, divisor: 1, + buffer_index: 1, }); - device.bind_buffer(quad_vertex_indices_buffer, BufferTarget::Index); + device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); FillVertexArray { vertex_array, vertex_buffer } } @@ -1042,53 +1059,58 @@ where // NB: The object must be of type `I16`, not `U16`, to work around a macOS Radeon // driver bug. - device.bind_vertex_array(&vertex_array); - device.use_program(&alpha_tile_program.program); - device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&tess_coord_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &tess_coord_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U8, - stride: 0, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::U16, + stride: 4, offset: 0, divisor: 0, + buffer_index: 0, }); - device.bind_buffer(&vertex_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&tile_origin_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &tile_origin_attr, &VertexAttrDescriptor { size: 3, class: VertexAttrClass::Int, attr_type: VertexAttrType::U8, stride: MASK_TILE_INSTANCE_SIZE, offset: 0, divisor: 1, + buffer_index: 1, }); - device.configure_vertex_attr(&backdrop_attr, &VertexAttrDescriptor { + device.configure_vertex_attr(&vertex_array, &backdrop_attr, &VertexAttrDescriptor { size: 1, class: VertexAttrClass::Int, attr_type: VertexAttrType::I8, stride: MASK_TILE_INSTANCE_SIZE, offset: 3, divisor: 1, + buffer_index: 1, }); - device.configure_vertex_attr(&tile_index_attr, &VertexAttrDescriptor { + device.configure_vertex_attr(&vertex_array, &tile_index_attr, &VertexAttrDescriptor { size: 1, class: VertexAttrClass::Int, attr_type: VertexAttrType::I16, stride: MASK_TILE_INSTANCE_SIZE, offset: 6, divisor: 1, + buffer_index: 1, }); if let Some(color_tex_coord_attr) = color_tex_coord_attr { - device.configure_vertex_attr(&color_tex_coord_attr, &VertexAttrDescriptor { - size: 2, - class: VertexAttrClass::FloatNorm, - attr_type: VertexAttrType::U16, - stride: MASK_TILE_INSTANCE_SIZE, - offset: 8, - divisor: 1, - }); + device.configure_vertex_attr(&vertex_array, + &color_tex_coord_attr, + &VertexAttrDescriptor { + size: 2, + class: VertexAttrClass::FloatNorm, + attr_type: VertexAttrType::U16, + stride: MASK_TILE_INSTANCE_SIZE, + offset: 8, + divisor: 1, + buffer_index: 1, + }); } - device.bind_buffer(quad_vertex_indices_buffer, BufferTarget::Index); + device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); AlphaTileVertexArray { vertex_array, vertex_buffer } } @@ -1123,37 +1145,40 @@ where // NB: The object must be of type short, not unsigned short, to work around a macOS // Radeon driver bug. - device.bind_vertex_array(&vertex_array); - device.use_program(&solid_tile_program.program); - device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&tess_coord_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &tess_coord_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U8, - stride: 0, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::U16, + stride: 4, offset: 0, divisor: 0, + buffer_index: 0, }); - device.bind_buffer(&vertex_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&tile_origin_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &tile_origin_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, + class: VertexAttrClass::Int, attr_type: VertexAttrType::I16, stride: SOLID_TILE_INSTANCE_SIZE, offset: 0, divisor: 1, + buffer_index: 1, }); if let Some(color_tex_coord_attr) = color_tex_coord_attr { - device.configure_vertex_attr(&color_tex_coord_attr, &VertexAttrDescriptor { - size: 2, - class: VertexAttrClass::FloatNorm, - attr_type: VertexAttrType::U16, - stride: SOLID_TILE_INSTANCE_SIZE, - offset: 4, - divisor: 1, - }); + device.configure_vertex_attr(&vertex_array, + &color_tex_coord_attr, + &VertexAttrDescriptor { + size: 2, + class: VertexAttrClass::FloatNorm, + attr_type: VertexAttrType::U16, + stride: SOLID_TILE_INSTANCE_SIZE, + offset: 4, + divisor: 1, + buffer_index: 1, + }); } - device.bind_buffer(quad_vertex_indices_buffer, BufferTarget::Index); + device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); SolidTileVertexArray { vertex_array, vertex_buffer } } @@ -1235,10 +1260,10 @@ where { fn new(device: &D, resources: &dyn ResourceLoader) -> SolidTileMulticolorProgram { let solid_tile_program = SolidTileProgram::new(device, "tile_solid_multicolor", resources); - let paint_texture_uniform = - device.get_uniform(&solid_tile_program.program, "PaintTexture"); - let paint_texture_size_uniform = - device.get_uniform(&solid_tile_program.program, "PaintTextureSize"); + let paint_texture_uniform = device.get_uniform(&solid_tile_program.program, + "PaintTexture"); + let paint_texture_size_uniform = device.get_uniform(&solid_tile_program.program, + "PaintTextureSize"); SolidTileMulticolorProgram { solid_tile_program, paint_texture_uniform, @@ -1383,8 +1408,8 @@ where let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let kernel_uniform = device.get_uniform(&program, "Kernel"); let gamma_lut_uniform = device.get_uniform(&program, "GammaLUT"); - let gamma_correction_enabled_uniform = - device.get_uniform(&program, "GammaCorrectionEnabled"); + let gamma_correction_enabled_uniform = device.get_uniform(&program, + "GammaCorrectionEnabled"); let fg_color_uniform = device.get_uniform(&program, "FGColor"); let bg_color_uniform = device.get_uniform(&program, "BGColor"); PostprocessProgram { @@ -1422,18 +1447,17 @@ where let position_attr = device.get_vertex_attr(&postprocess_program.program, "Position") .unwrap(); - device.bind_vertex_array(&vertex_array); - device.use_program(&postprocess_program.program); - device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U8, - stride: 0, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::U16, + stride: 4, offset: 0, divisor: 0, + buffer_index: 0, }); - device.bind_buffer(quad_vertex_indices_buffer, BufferTarget::Index); + device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); PostprocessVertexArray { vertex_array } } @@ -1474,18 +1498,18 @@ where let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer()); let position_attr = device.get_vertex_attr(&stencil_program.program, "Position").unwrap(); - device.bind_vertex_array(&vertex_array); - device.use_program(&stencil_program.program); - device.bind_buffer(&vertex_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor { + + device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { size: 3, class: VertexAttrClass::Float, attr_type: VertexAttrType::F32, stride: 4 * 4, offset: 0, divisor: 0, + buffer_index: 0, }); - device.bind_buffer(&index_buffer, BufferTarget::Index); + device.bind_buffer(&vertex_array, &index_buffer, BufferTarget::Index); StencilVertexArray { vertex_array, vertex_buffer, index_buffer } } @@ -1538,58 +1562,25 @@ where quad_vertex_indices_buffer: &D::Buffer, ) -> ReprojectionVertexArray { let vertex_array = device.create_vertex_array(); - let position_attr = device.get_vertex_attr(&reprojection_program.program, "Position") .unwrap(); - device.bind_vertex_array(&vertex_array); - device.use_program(&reprojection_program.program); - device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor { + + device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); + device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U8, - stride: 0, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::I16, + stride: 4, offset: 0, divisor: 0, + buffer_index: 0, }); - device.bind_buffer(quad_vertex_indices_buffer, BufferTarget::Index); + device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); ReprojectionVertexArray { vertex_array } } } -#[derive(Clone)] -pub enum DestFramebuffer -where - D: Device, -{ - Default { - viewport: RectI, - window_size: Vector2I, - }, - Other(D::Framebuffer), -} - -impl DestFramebuffer -where - D: Device, -{ - #[inline] - pub fn full_window(window_size: Vector2I) -> DestFramebuffer { - let viewport = RectI::new(Vector2I::default(), window_size); - DestFramebuffer::Default { viewport, window_size } - } - - fn window_size(&self, device: &D) -> Vector2I { - match *self { - DestFramebuffer::Default { window_size, .. } => window_size, - DestFramebuffer::Other(ref framebuffer) => { - device.texture_size(device.framebuffer_texture(framebuffer)) - } - } - } -} - #[derive(Clone, Copy)] pub enum RenderMode { Multicolor, @@ -1675,3 +1666,11 @@ impl Add for RenderTime { } } } + +bitflags! { + struct FramebufferFlags: u8 { + const MUST_PRESERVE_MASK_FRAMEBUFFER_CONTENTS = 0x01; + const MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS = 0x02; + const MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS = 0x04; + } +} diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index 76734d59..6c5045f7 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -75,6 +75,7 @@ pub struct SolidTileBatchPrimitive { pub origin_u: u16, pub origin_v: u16, pub object_index: u16, + pub pad: u16, } #[derive(Clone, Copy, Debug, Default)] diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index 0f80405e..ea7f7072 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -10,6 +10,8 @@ //! The CPU portion of Pathfinder's renderer. +#[macro_use] +extern crate bitflags; #[macro_use] extern crate log; diff --git a/renderer/src/options.rs b/renderer/src/options.rs index 2cb34fe6..32357c08 100644 --- a/renderer/src/options.rs +++ b/renderer/src/options.rs @@ -11,10 +11,10 @@ //! Options that control how rendering is to be performed. use crate::gpu_data::RenderCommand; -use pathfinder_geometry::basic::vector::{Vector2F, Vector4F}; use pathfinder_geometry::basic::rect::RectF; use pathfinder_geometry::basic::transform2d::Transform2DF; use pathfinder_geometry::basic::transform3d::Perspective; +use pathfinder_geometry::basic::vector::{Vector2F, Vector4F}; use pathfinder_geometry::clip::PolygonClipper3D; pub trait RenderCommandListener: Send + Sync { @@ -31,16 +31,17 @@ where } } +/// Options that influence scene building. #[derive(Clone, Default)] -pub struct RenderOptions { +pub struct BuildOptions { pub transform: RenderTransform, pub dilation: Vector2F, pub subpixel_aa_enabled: bool, } -impl RenderOptions { - pub(crate) fn prepare(self, bounds: RectF) -> PreparedRenderOptions { - PreparedRenderOptions { +impl BuildOptions { + pub(crate) fn prepare(self, bounds: RectF) -> PreparedBuildOptions { + PreparedBuildOptions { transform: self.transform.prepare(bounds), dilation: self.dilation, subpixel_aa_enabled: self.subpixel_aa_enabled, @@ -119,13 +120,13 @@ impl RenderTransform { } } -pub(crate) struct PreparedRenderOptions { +pub(crate) struct PreparedBuildOptions { pub(crate) transform: PreparedRenderTransform, pub(crate) dilation: Vector2F, pub(crate) subpixel_aa_enabled: bool, } -impl PreparedRenderOptions { +impl PreparedBuildOptions { #[inline] pub(crate) fn bounding_quad(&self) -> BoundingQuad { match self.transform { diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 45cc1c44..db5319c6 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -12,8 +12,8 @@ use crate::builder::SceneBuilder; use crate::concurrent::executor::Executor; -use crate::options::{PreparedRenderOptions, PreparedRenderTransform}; -use crate::options::{RenderCommandListener, RenderOptions}; +use crate::options::{BuildOptions, PreparedBuildOptions}; +use crate::options::{PreparedRenderTransform, RenderCommandListener}; use crate::paint::{Paint, PaintId}; use hashbrown::HashMap; use pathfinder_geometry::basic::vector::Vector2F; @@ -89,7 +89,7 @@ impl Scene { pub(crate) fn apply_render_options( &self, original_outline: &Outline, - options: &PreparedRenderOptions, + options: &PreparedBuildOptions, ) -> Outline { let effective_view_box = self.effective_view_box(options); @@ -156,7 +156,7 @@ impl Scene { } #[inline] - pub(crate) fn effective_view_box(&self, render_options: &PreparedRenderOptions) -> RectF { + pub(crate) fn effective_view_box(&self, render_options: &PreparedBuildOptions) -> RectF { if render_options.subpixel_aa_enabled { self.view_box.scale_xy(Vector2F::new(3.0, 1.0)) } else { @@ -166,7 +166,7 @@ impl Scene { #[inline] pub fn build(&self, - options: RenderOptions, + options: BuildOptions, listener: Box, executor: &E) where E: Executor { diff --git a/renderer/src/z_buffer.rs b/renderer/src/z_buffer.rs index 889cc666..858a9e90 100644 --- a/renderer/src/z_buffer.rs +++ b/renderer/src/z_buffer.rs @@ -91,6 +91,7 @@ impl SolidTileBatchPrimitive { object_index: object_index, origin_u: origin_uv.x() as u16, origin_v: origin_uv.y() as u16, + pad: 0, } } } diff --git a/resources/shaders/gl3/debug_solid.fs.glsl b/resources/shaders/gl3/debug_solid.fs.glsl index eeaaf489..06550da6 100644 --- a/resources/shaders/gl3/debug_solid.fs.glsl +++ b/resources/shaders/gl3/debug_solid.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/debug_solid.vs.glsl b/resources/shaders/gl3/debug_solid.vs.glsl index 3c38f3f7..a5ac2df8 100644 --- a/resources/shaders/gl3/debug_solid.vs.glsl +++ b/resources/shaders/gl3/debug_solid.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -14,10 +16,10 @@ precision highp float; uniform vec2 uFramebufferSize; -in vec2 aPosition; +in ivec2 aPosition; void main(){ - vec2 position = aPosition / uFramebufferSize * 2.0 - 1.0; + vec2 position = vec2(aPosition)/ uFramebufferSize * 2.0 - 1.0; gl_Position = vec4(position . x, - position . y, 0.0, 1.0); } diff --git a/resources/shaders/gl3/debug_texture.fs.glsl b/resources/shaders/gl3/debug_texture.fs.glsl index 9601adf2..5670374b 100644 --- a/resources/shaders/gl3/debug_texture.fs.glsl +++ b/resources/shaders/gl3/debug_texture.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/debug_texture.vs.glsl b/resources/shaders/gl3/debug_texture.vs.glsl index b3b546d8..d037b869 100644 --- a/resources/shaders/gl3/debug_texture.vs.glsl +++ b/resources/shaders/gl3/debug_texture.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -15,14 +17,14 @@ precision highp float; uniform vec2 uFramebufferSize; uniform vec2 uTextureSize; -in vec2 aPosition; -in vec2 aTexCoord; +in ivec2 aPosition; +in ivec2 aTexCoord; out vec2 vTexCoord; void main(){ - vTexCoord = aTexCoord / uTextureSize; - vec2 position = aPosition / uFramebufferSize * 2.0 - 1.0; + vTexCoord = vec2(aTexCoord)/ uTextureSize; + vec2 position = vec2(aPosition)/ uFramebufferSize * 2.0 - 1.0; gl_Position = vec4(position . x, - position . y, 0.0, 1.0); } diff --git a/resources/shaders/gl3/demo_ground.fs.glsl b/resources/shaders/gl3/demo_ground.fs.glsl index 3bf84270..49dd50dc 100644 --- a/resources/shaders/gl3/demo_ground.fs.glsl +++ b/resources/shaders/gl3/demo_ground.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/demo_ground.vs.glsl b/resources/shaders/gl3/demo_ground.vs.glsl index 418eb50a..4f216416 100644 --- a/resources/shaders/gl3/demo_ground.vs.glsl +++ b/resources/shaders/gl3/demo_ground.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -15,12 +17,12 @@ precision highp float; uniform mat4 uTransform; uniform int uGridlineCount; -in vec2 aPosition; +in ivec2 aPosition; out vec2 vTexCoord; void main(){ - vTexCoord = aPosition * float(uGridlineCount); - gl_Position = uTransform * vec4(aPosition . x, 0.0, aPosition . y, 1.0); + vTexCoord = vec2(aPosition * uGridlineCount); + gl_Position = uTransform * vec4(ivec4(aPosition . x, 0, aPosition . y, 1)); } diff --git a/resources/shaders/gl3/fill.fs.glsl b/resources/shaders/gl3/fill.fs.glsl index aeebe141..d70a93bd 100644 --- a/resources/shaders/gl3/fill.fs.glsl +++ b/resources/shaders/gl3/fill.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/fill.vs.glsl b/resources/shaders/gl3/fill.vs.glsl index fac4560a..83314eb1 100644 --- a/resources/shaders/gl3/fill.vs.glsl +++ b/resources/shaders/gl3/fill.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -15,7 +17,7 @@ precision highp float; uniform vec2 uFramebufferSize; uniform vec2 uTileSize; -in vec2 aTessCoord; +in uvec2 aTessCoord; in uint aFromPx; in uint aToPx; in vec2 aFromSubpx; @@ -27,7 +29,7 @@ out vec2 vTo; vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth){ uint tilesPerRow = uint(stencilTextureWidth / uTileSize . x); - uvec2 tileOffset = uvec2(aTileIndex % tilesPerRow, aTileIndex / tilesPerRow); + uvec2 tileOffset = uvec2(tileIndex % tilesPerRow, tileIndex / tilesPerRow); return vec2(tileOffset)* uTileSize; } @@ -38,11 +40,11 @@ void main(){ vec2 to = vec2(aToPx & 15u, aToPx >> 4u)+ aToSubpx; vec2 position; - if(aTessCoord . x < 0.5) + if(aTessCoord . x == 0u) position . x = floor(min(from . x, to . x)); else position . x = ceil(max(from . x, to . x)); - if(aTessCoord . y < 0.5) + if(aTessCoord . y == 0u) position . y = floor(min(from . y, to . y)); else position . y = uTileSize . y; @@ -50,6 +52,10 @@ void main(){ vFrom = from - position; vTo = to - position; - gl_Position = vec4((tileOrigin + position)/ uFramebufferSize * 2.0 - 1.0, 0.0, 1.0); + vec2 globalPosition =(tileOrigin + position)/ uFramebufferSize * 2.0 - 1.0; + + + + gl_Position = vec4(globalPosition, 0.0, 1.0); } diff --git a/resources/shaders/gl3/post.fs.glsl b/resources/shaders/gl3/post.fs.glsl index 0a74556b..b9de3d7b 100644 --- a/resources/shaders/gl3/post.fs.glsl +++ b/resources/shaders/gl3/post.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/post.vs.glsl b/resources/shaders/gl3/post.vs.glsl index 29e75ade..b30dcf8d 100644 --- a/resources/shaders/gl3/post.vs.glsl +++ b/resources/shaders/gl3/post.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -12,12 +14,12 @@ precision highp float; -in vec2 aPosition; +in ivec2 aPosition; out vec2 vTexCoord; void main(){ - vTexCoord = aPosition; - gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0); + vTexCoord = vec2(aPosition); + gl_Position = vec4(vec2(aPosition)* 2.0 - 1.0, 0.0, 1.0); } diff --git a/resources/shaders/gl3/reproject.fs.glsl b/resources/shaders/gl3/reproject.fs.glsl index 427b632c..f1344a2e 100644 --- a/resources/shaders/gl3/reproject.fs.glsl +++ b/resources/shaders/gl3/reproject.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/reproject.vs.glsl b/resources/shaders/gl3/reproject.vs.glsl index d1c70544..35a3db40 100644 --- a/resources/shaders/gl3/reproject.vs.glsl +++ b/resources/shaders/gl3/reproject.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -14,12 +16,17 @@ precision highp float; uniform mat4 uNewTransform; -in vec2 aPosition; +in ivec2 aPosition; out vec2 vTexCoord; void main(){ - vTexCoord = aPosition; - gl_Position = uNewTransform * vec4(aPosition, 0.0, 1.0); + vec2 position = vec2(aPosition); + vTexCoord = position; + + + + + gl_Position = uNewTransform * vec4(position, 0.0, 1.0); } diff --git a/resources/shaders/gl3/stencil.fs.glsl b/resources/shaders/gl3/stencil.fs.glsl index e2e093ab..b37ee045 100644 --- a/resources/shaders/gl3/stencil.fs.glsl +++ b/resources/shaders/gl3/stencil.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/stencil.vs.glsl b/resources/shaders/gl3/stencil.vs.glsl index 2fee81a2..0f44276e 100644 --- a/resources/shaders/gl3/stencil.vs.glsl +++ b/resources/shaders/gl3/stencil.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/tile_alpha.fs.glsl b/resources/shaders/gl3/tile_alpha.fs.glsl index 3af9cf4d..0e7247d7 100644 --- a/resources/shaders/gl3/tile_alpha.fs.glsl +++ b/resources/shaders/gl3/tile_alpha.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/tile_alpha_monochrome.vs.glsl b/resources/shaders/gl3/tile_alpha_monochrome.vs.glsl index d99d7dd7..df40901e 100644 --- a/resources/shaders/gl3/tile_alpha_monochrome.vs.glsl +++ b/resources/shaders/gl3/tile_alpha_monochrome.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -30,10 +32,10 @@ uniform vec2 uTileSize; uniform vec2 uStencilTextureSize; uniform vec2 uViewBoxOrigin; -in vec2 aTessCoord; +in uvec2 aTessCoord; in uvec3 aTileOrigin; in int aBackdrop; -in uint aTileIndex; +in int aTileIndex; out vec2 vTexCoord; out float vBackdrop; @@ -49,9 +51,9 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth){ void computeVaryings(){ vec2 origin = vec2(aTileOrigin . xy)+ vec2(aTileOrigin . z & 15u, aTileOrigin . z >> 4u)* 256.0; - vec2 pixelPosition =(origin + aTessCoord)* uTileSize + uViewBoxOrigin; + vec2 pixelPosition =(origin + vec2(aTessCoord))* uTileSize + uViewBoxOrigin; vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0); - vec2 maskTexCoordOrigin = computeTileOffset(aTileIndex, uStencilTextureSize . x); + vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize . x); vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vTexCoord = maskTexCoord / uStencilTextureSize; diff --git a/resources/shaders/gl3/tile_alpha_multicolor.vs.glsl b/resources/shaders/gl3/tile_alpha_multicolor.vs.glsl index be655748..2adf4dc3 100644 --- a/resources/shaders/gl3/tile_alpha_multicolor.vs.glsl +++ b/resources/shaders/gl3/tile_alpha_multicolor.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -30,10 +32,10 @@ uniform vec2 uTileSize; uniform vec2 uStencilTextureSize; uniform vec2 uViewBoxOrigin; -in vec2 aTessCoord; +in uvec2 aTessCoord; in uvec3 aTileOrigin; in int aBackdrop; -in uint aTileIndex; +in int aTileIndex; out vec2 vTexCoord; out float vBackdrop; @@ -49,9 +51,9 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth){ void computeVaryings(){ vec2 origin = vec2(aTileOrigin . xy)+ vec2(aTileOrigin . z & 15u, aTileOrigin . z >> 4u)* 256.0; - vec2 pixelPosition =(origin + aTessCoord)* uTileSize + uViewBoxOrigin; + vec2 pixelPosition =(origin + vec2(aTessCoord))* uTileSize + uViewBoxOrigin; vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0); - vec2 maskTexCoordOrigin = computeTileOffset(aTileIndex, uStencilTextureSize . x); + vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize . x); vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vTexCoord = maskTexCoord / uStencilTextureSize; diff --git a/resources/shaders/gl3/tile_solid.fs.glsl b/resources/shaders/gl3/tile_solid.fs.glsl index 2079d945..f8f8f7a7 100644 --- a/resources/shaders/gl3/tile_solid.fs.glsl +++ b/resources/shaders/gl3/tile_solid.fs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + diff --git a/resources/shaders/gl3/tile_solid_monochrome.vs.glsl b/resources/shaders/gl3/tile_solid_monochrome.vs.glsl index d7544c26..c0808cbd 100644 --- a/resources/shaders/gl3/tile_solid_monochrome.vs.glsl +++ b/resources/shaders/gl3/tile_solid_monochrome.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -29,19 +31,18 @@ uniform vec2 uFramebufferSize; uniform vec2 uTileSize; uniform vec2 uViewBoxOrigin; -in vec2 aTessCoord; -in vec2 aTileOrigin; +in uvec2 aTessCoord; +in ivec2 aTileOrigin; out vec4 vColor; vec4 getColor(); void computeVaryings(){ - vec2 pixelPosition =(aTileOrigin + aTessCoord)* uTileSize + uViewBoxOrigin; + vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize + uViewBoxOrigin; vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0); vColor = getColor(); - gl_Position = vec4(position, 0.0, 1.0); } diff --git a/resources/shaders/gl3/tile_solid_multicolor.vs.glsl b/resources/shaders/gl3/tile_solid_multicolor.vs.glsl index 0e23e0a4..4ff5208c 100644 --- a/resources/shaders/gl3/tile_solid_multicolor.vs.glsl +++ b/resources/shaders/gl3/tile_solid_multicolor.vs.glsl @@ -1,4 +1,6 @@ #version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + @@ -29,19 +31,18 @@ uniform vec2 uFramebufferSize; uniform vec2 uTileSize; uniform vec2 uViewBoxOrigin; -in vec2 aTessCoord; -in vec2 aTileOrigin; +in uvec2 aTessCoord; +in ivec2 aTileOrigin; out vec4 vColor; vec4 getColor(); void computeVaryings(){ - vec2 pixelPosition =(aTileOrigin + aTessCoord)* uTileSize + uViewBoxOrigin; + vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize + uViewBoxOrigin; vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0); vColor = getColor(); - gl_Position = vec4(position, 0.0, 1.0); } diff --git a/resources/shaders/metal/debug_solid.fs.metal b/resources/shaders/metal/debug_solid.fs.metal index 6fc13e2f..49f35a94 100644 --- a/resources/shaders/metal/debug_solid.fs.metal +++ b/resources/shaders/metal/debug_solid.fs.metal @@ -1,17 +1,23 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float4* uColor [[id(0)]]; +}; + struct main0_out { float4 oFragColor [[color(0)]]; }; -fragment main0_out main0(float4 uColor [[buffer(0)]]) +fragment main0_out main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - out.oFragColor = float4(uColor.xyz, 1.0) * uColor.w; + out.oFragColor = float4((*spvDescriptorSet0.uColor).xyz, 1.0) * (*spvDescriptorSet0.uColor).w; return out; } diff --git a/resources/shaders/metal/debug_solid.vs.metal b/resources/shaders/metal/debug_solid.vs.metal index bcad2a03..ec9d440f 100644 --- a/resources/shaders/metal/debug_solid.vs.metal +++ b/resources/shaders/metal/debug_solid.vs.metal @@ -1,8 +1,14 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float2* uFramebufferSize [[id(0)]]; +}; + struct main0_out { float4 gl_Position [[position]]; @@ -10,13 +16,13 @@ struct main0_out struct main0_in { - float2 aPosition [[attribute(0)]]; + int2 aPosition [[attribute(0)]]; }; -vertex main0_out main0(main0_in in [[stage_in]], float2 uFramebufferSize [[buffer(0)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - float2 position = ((in.aPosition / uFramebufferSize) * 2.0) - float2(1.0); + float2 position = ((float2(in.aPosition) / (*spvDescriptorSet0.uFramebufferSize)) * 2.0) - float2(1.0); out.gl_Position = float4(position.x, -position.y, 0.0, 1.0); return out; } diff --git a/resources/shaders/metal/debug_texture.fs.metal b/resources/shaders/metal/debug_texture.fs.metal index 67bc2370..9949a558 100644 --- a/resources/shaders/metal/debug_texture.fs.metal +++ b/resources/shaders/metal/debug_texture.fs.metal @@ -1,8 +1,16 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float4* uColor [[id(0)]]; + texture2d uTexture [[id(1)]]; + sampler uTextureSmplr [[id(2)]]; +}; + struct main0_out { float4 oFragColor [[color(0)]]; @@ -13,11 +21,11 @@ struct main0_in float2 vTexCoord [[user(locn0)]]; }; -fragment main0_out main0(main0_in in [[stage_in]], float4 uColor [[buffer(0)]], texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]]) +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - float alpha = uTexture.sample(uTextureSmplr, in.vTexCoord).x * uColor.w; - out.oFragColor = float4(uColor.xyz, 1.0) * alpha; + float alpha = spvDescriptorSet0.uTexture.sample(spvDescriptorSet0.uTextureSmplr, in.vTexCoord).x * (*spvDescriptorSet0.uColor).w; + out.oFragColor = float4((*spvDescriptorSet0.uColor).xyz, 1.0) * alpha; return out; } diff --git a/resources/shaders/metal/debug_texture.vs.metal b/resources/shaders/metal/debug_texture.vs.metal index 25eb20b8..87a31507 100644 --- a/resources/shaders/metal/debug_texture.vs.metal +++ b/resources/shaders/metal/debug_texture.vs.metal @@ -1,8 +1,15 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float2* uTextureSize [[id(0)]]; + constant float2* uFramebufferSize [[id(1)]]; +}; + struct main0_out { float2 vTexCoord [[user(locn0)]]; @@ -11,15 +18,15 @@ struct main0_out struct main0_in { - float2 aPosition [[attribute(0)]]; - float2 aTexCoord [[attribute(1)]]; + int2 aPosition [[attribute(0)]]; + int2 aTexCoord [[attribute(1)]]; }; -vertex main0_out main0(main0_in in [[stage_in]], float2 uTextureSize [[buffer(0)]], float2 uFramebufferSize [[buffer(1)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - out.vTexCoord = in.aTexCoord / uTextureSize; - float2 position = ((in.aPosition / uFramebufferSize) * 2.0) - float2(1.0); + out.vTexCoord = float2(in.aTexCoord) / (*spvDescriptorSet0.uTextureSize); + float2 position = ((float2(in.aPosition) / (*spvDescriptorSet0.uFramebufferSize)) * 2.0) - float2(1.0); out.gl_Position = float4(position.x, -position.y, 0.0, 1.0); return out; } diff --git a/resources/shaders/metal/demo_ground.fs.metal b/resources/shaders/metal/demo_ground.fs.metal index 919b2f37..1b64111f 100644 --- a/resources/shaders/metal/demo_ground.fs.metal +++ b/resources/shaders/metal/demo_ground.fs.metal @@ -1,8 +1,15 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float4* uGridlineColor [[id(0)]]; + constant float4* uGroundColor [[id(1)]]; +}; + struct main0_out { float4 oFragColor [[color(0)]]; @@ -13,12 +20,12 @@ struct main0_in float2 vTexCoord [[user(locn0)]]; }; -fragment main0_out main0(main0_in in [[stage_in]], float4 uGridlineColor [[buffer(0)]], float4 uGroundColor [[buffer(1)]]) +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; float2 texCoordPx = fract(in.vTexCoord) / fwidth(in.vTexCoord); bool4 _33 = bool4(any(texCoordPx <= float2(1.0))); - out.oFragColor = float4(_33.x ? uGridlineColor.x : uGroundColor.x, _33.y ? uGridlineColor.y : uGroundColor.y, _33.z ? uGridlineColor.z : uGroundColor.z, _33.w ? uGridlineColor.w : uGroundColor.w); + out.oFragColor = float4(_33.x ? (*spvDescriptorSet0.uGridlineColor).x : (*spvDescriptorSet0.uGroundColor).x, _33.y ? (*spvDescriptorSet0.uGridlineColor).y : (*spvDescriptorSet0.uGroundColor).y, _33.z ? (*spvDescriptorSet0.uGridlineColor).z : (*spvDescriptorSet0.uGroundColor).z, _33.w ? (*spvDescriptorSet0.uGridlineColor).w : (*spvDescriptorSet0.uGroundColor).w); return out; } diff --git a/resources/shaders/metal/demo_ground.vs.metal b/resources/shaders/metal/demo_ground.vs.metal index 049083ea..997185f5 100644 --- a/resources/shaders/metal/demo_ground.vs.metal +++ b/resources/shaders/metal/demo_ground.vs.metal @@ -1,8 +1,15 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant int* uGridlineCount [[id(0)]]; + constant float4x4* uTransform [[id(1)]]; +}; + struct main0_out { float2 vTexCoord [[user(locn0)]]; @@ -11,14 +18,14 @@ struct main0_out struct main0_in { - float2 aPosition [[attribute(0)]]; + int2 aPosition [[attribute(0)]]; }; -vertex main0_out main0(main0_in in [[stage_in]], int uGridlineCount [[buffer(0)]], float4x4 uTransform [[buffer(1)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - out.vTexCoord = in.aPosition * float(uGridlineCount); - out.gl_Position = uTransform * float4(in.aPosition.x, 0.0, in.aPosition.y, 1.0); + out.vTexCoord = float2(in.aPosition * int2((*spvDescriptorSet0.uGridlineCount))); + out.gl_Position = (*spvDescriptorSet0.uTransform) * float4(int4(in.aPosition.x, 0, in.aPosition.y, 1)); return out; } diff --git a/resources/shaders/metal/fill.fs.metal b/resources/shaders/metal/fill.fs.metal index d002bba6..bfa765e5 100644 --- a/resources/shaders/metal/fill.fs.metal +++ b/resources/shaders/metal/fill.fs.metal @@ -1,8 +1,15 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + texture2d uAreaLUT [[id(0)]]; + sampler uAreaLUTSmplr [[id(1)]]; +}; + struct main0_out { float4 oFragColor [[color(0)]]; @@ -14,7 +21,7 @@ struct main0_in float2 vTo [[user(locn1)]]; }; -fragment main0_out main0(main0_in in [[stage_in]], texture2d uAreaLUT [[texture(0)]], sampler uAreaLUTSmplr [[sampler(0)]]) +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; float2 from = in.vFrom; @@ -29,7 +36,7 @@ fragment main0_out main0(main0_in in [[stage_in]], texture2d uAreaLUT [[t float y = mix(left.y, right.y, t); float d = (right.y - left.y) / (right.x - left.x); float dX = window.x - window.y; - out.oFragColor = float4(uAreaLUT.sample(uAreaLUTSmplr, (float2(y + 8.0, abs(d * dX)) / float2(16.0))).x * dX); + out.oFragColor = float4(spvDescriptorSet0.uAreaLUT.sample(spvDescriptorSet0.uAreaLUTSmplr, (float2(y + 8.0, abs(d * dX)) / float2(16.0))).x * dX); return out; } diff --git a/resources/shaders/metal/fill.vs.metal b/resources/shaders/metal/fill.vs.metal index 1c545156..87f77d2a 100644 --- a/resources/shaders/metal/fill.vs.metal +++ b/resources/shaders/metal/fill.vs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #pragma clang diagnostic ignored "-Wmissing-prototypes" #include @@ -5,6 +6,12 @@ using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float2* uTileSize [[id(0)]]; + constant float2* uFramebufferSize [[id(1)]]; +}; + struct main0_out { float2 vFrom [[user(locn0)]]; @@ -14,7 +21,7 @@ struct main0_out struct main0_in { - float2 aTessCoord [[attribute(0)]]; + uint2 aTessCoord [[attribute(0)]]; uint aFromPx [[attribute(1)]]; uint aToPx [[attribute(2)]]; float2 aFromSubpx [[attribute(3)]]; @@ -22,23 +29,23 @@ struct main0_in uint aTileIndex [[attribute(5)]]; }; -float2 computeTileOffset(thread const uint& tileIndex, thread const float& stencilTextureWidth, thread float2 uTileSize, thread uint& aTileIndex) +float2 computeTileOffset(thread const uint& tileIndex, thread const float& stencilTextureWidth, thread float2 uTileSize) { uint tilesPerRow = uint(stencilTextureWidth / uTileSize.x); - uint2 tileOffset = uint2(aTileIndex % tilesPerRow, aTileIndex / tilesPerRow); + uint2 tileOffset = uint2(tileIndex % tilesPerRow, tileIndex / tilesPerRow); return float2(tileOffset) * uTileSize; } -vertex main0_out main0(main0_in in [[stage_in]], float2 uTileSize [[buffer(0)]], float2 uFramebufferSize [[buffer(1)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; uint param = in.aTileIndex; - float param_1 = uFramebufferSize.x; - float2 tileOrigin = computeTileOffset(param, param_1, uTileSize, in.aTileIndex); + float param_1 = (*spvDescriptorSet0.uFramebufferSize).x; + float2 tileOrigin = computeTileOffset(param, param_1, (*spvDescriptorSet0.uTileSize)); float2 from = float2(float(in.aFromPx & 15u), float(in.aFromPx >> 4u)) + in.aFromSubpx; float2 to = float2(float(in.aToPx & 15u), float(in.aToPx >> 4u)) + in.aToSubpx; float2 position; - if (in.aTessCoord.x < 0.5) + if (in.aTessCoord.x == 0u) { position.x = floor(fast::min(from.x, to.x)); } @@ -46,17 +53,19 @@ vertex main0_out main0(main0_in in [[stage_in]], float2 uTileSize [[buffer(0)]], { position.x = ceil(fast::max(from.x, to.x)); } - if (in.aTessCoord.y < 0.5) + if (in.aTessCoord.y == 0u) { position.y = floor(fast::min(from.y, to.y)); } else { - position.y = uTileSize.y; + position.y = (*spvDescriptorSet0.uTileSize).y; } out.vFrom = from - position; out.vTo = to - position; - out.gl_Position = float4((((tileOrigin + position) / uFramebufferSize) * 2.0) - float2(1.0), 0.0, 1.0); + float2 globalPosition = (((tileOrigin + position) / (*spvDescriptorSet0.uFramebufferSize)) * 2.0) - float2(1.0); + globalPosition.y = -globalPosition.y; + out.gl_Position = float4(globalPosition, 0.0, 1.0); return out; } diff --git a/resources/shaders/metal/post.fs.metal b/resources/shaders/metal/post.fs.metal index 66ca87f8..989fd5d7 100644 --- a/resources/shaders/metal/post.fs.metal +++ b/resources/shaders/metal/post.fs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #pragma clang diagnostic ignored "-Wmissing-prototypes" #include @@ -5,6 +6,19 @@ using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float4* uKernel [[id(0)]]; + texture2d uGammaLUT [[id(1)]]; + texture2d uSource [[id(2)]]; + constant float2* uSourceSize [[id(3)]]; + sampler uSourceSmplr [[id(4)]]; + sampler uGammaLUTSmplr [[id(5)]]; + constant int* uGammaCorrectionEnabled [[id(6)]]; + constant float4* uBGColor [[id(7)]]; + constant float4* uFGColor [[id(8)]]; +}; + struct main0_out { float4 oFragColor [[color(0)]]; @@ -78,42 +92,42 @@ float3 gammaCorrect(thread const float3& bgColor, thread const float3& fgColor, return float3(gammaCorrectChannel(param, param_1, uGammaLUT, uGammaLUTSmplr), gammaCorrectChannel(param_2, param_3, uGammaLUT, uGammaLUTSmplr), gammaCorrectChannel(param_4, param_5, uGammaLUT, uGammaLUTSmplr)); } -fragment main0_out main0(main0_in in [[stage_in]], int uGammaCorrectionEnabled [[buffer(2)]], float4 uKernel [[buffer(0)]], float2 uSourceSize [[buffer(1)]], float4 uBGColor [[buffer(3)]], float4 uFGColor [[buffer(4)]], texture2d uGammaLUT [[texture(0)]], texture2d uSource [[texture(0)]], sampler uGammaLUTSmplr [[sampler(0)]], sampler uSourceSmplr [[sampler(0)]]) +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; float3 alpha; - if (uKernel.w == 0.0) + if ((*spvDescriptorSet0.uKernel).w == 0.0) { - alpha = uSource.sample(uSourceSmplr, in.vTexCoord).xxx; + alpha = spvDescriptorSet0.uSource.sample(spvDescriptorSet0.uSourceSmplr, in.vTexCoord).xxx; } else { - float param_3 = 1.0 / uSourceSize.x; + float param_3 = 1.0 / (*spvDescriptorSet0.uSourceSize).x; float4 param; float param_1; float4 param_2; - sample9Tap(param, param_1, param_2, param_3, uKernel, uSource, uSourceSmplr, in.vTexCoord); + sample9Tap(param, param_1, param_2, param_3, (*spvDescriptorSet0.uKernel), spvDescriptorSet0.uSource, spvDescriptorSet0.uSourceSmplr, in.vTexCoord); float4 alphaLeft = param; float alphaCenter = param_1; float4 alphaRight = param_2; float4 param_4 = alphaLeft; float3 param_5 = float3(alphaCenter, alphaRight.xy); - float r = convolve7Tap(param_4, param_5, uKernel); + float r = convolve7Tap(param_4, param_5, (*spvDescriptorSet0.uKernel)); float4 param_6 = float4(alphaLeft.yzw, alphaCenter); float3 param_7 = alphaRight.xyz; - float g = convolve7Tap(param_6, param_7, uKernel); + float g = convolve7Tap(param_6, param_7, (*spvDescriptorSet0.uKernel)); float4 param_8 = float4(alphaLeft.zw, alphaCenter, alphaRight.x); float3 param_9 = alphaRight.yzw; - float b = convolve7Tap(param_8, param_9, uKernel); + float b = convolve7Tap(param_8, param_9, (*spvDescriptorSet0.uKernel)); alpha = float3(r, g, b); } - if (uGammaCorrectionEnabled != 0) + if ((*spvDescriptorSet0.uGammaCorrectionEnabled) != 0) { - float3 param_10 = uBGColor.xyz; + float3 param_10 = (*spvDescriptorSet0.uBGColor).xyz; float3 param_11 = alpha; - alpha = gammaCorrect(param_10, param_11, uGammaLUT, uGammaLUTSmplr); + alpha = gammaCorrect(param_10, param_11, spvDescriptorSet0.uGammaLUT, spvDescriptorSet0.uGammaLUTSmplr); } - out.oFragColor = float4(mix(uBGColor.xyz, uFGColor.xyz, alpha), 1.0); + out.oFragColor = float4(mix((*spvDescriptorSet0.uBGColor).xyz, (*spvDescriptorSet0.uFGColor).xyz, alpha), 1.0); return out; } diff --git a/resources/shaders/metal/post.vs.metal b/resources/shaders/metal/post.vs.metal index a960ca1b..0fcc0bf6 100644 --- a/resources/shaders/metal/post.vs.metal +++ b/resources/shaders/metal/post.vs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include @@ -11,14 +12,14 @@ struct main0_out struct main0_in { - float2 aPosition [[attribute(0)]]; + int2 aPosition [[attribute(0)]]; }; -vertex main0_out main0(main0_in in [[stage_in]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]]) { main0_out out = {}; - out.vTexCoord = in.aPosition; - out.gl_Position = float4((in.aPosition * 2.0) - float2(1.0), 0.0, 1.0); + out.vTexCoord = float2(in.aPosition); + out.gl_Position = float4((float2(in.aPosition) * 2.0) - float2(1.0), 0.0, 1.0); return out; } diff --git a/resources/shaders/metal/reproject.fs.metal b/resources/shaders/metal/reproject.fs.metal index 3e2a2039..be9ff4dd 100644 --- a/resources/shaders/metal/reproject.fs.metal +++ b/resources/shaders/metal/reproject.fs.metal @@ -1,8 +1,16 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float4x4* uOldTransform [[id(0)]]; + texture2d uTexture [[id(1)]]; + sampler uTextureSmplr [[id(2)]]; +}; + struct main0_out { float4 oFragColor [[color(0)]]; @@ -13,12 +21,12 @@ struct main0_in float2 vTexCoord [[user(locn0)]]; }; -fragment main0_out main0(main0_in in [[stage_in]], float4x4 uOldTransform [[buffer(0)]], texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]]) +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - float4 normTexCoord = uOldTransform * float4(in.vTexCoord, 0.0, 1.0); + float4 normTexCoord = (*spvDescriptorSet0.uOldTransform) * float4(in.vTexCoord, 0.0, 1.0); float2 texCoord = ((normTexCoord.xy / float2(normTexCoord.w)) + float2(1.0)) * 0.5; - out.oFragColor = uTexture.sample(uTextureSmplr, texCoord); + out.oFragColor = spvDescriptorSet0.uTexture.sample(spvDescriptorSet0.uTextureSmplr, texCoord); return out; } diff --git a/resources/shaders/metal/reproject.vs.metal b/resources/shaders/metal/reproject.vs.metal index d125c376..3bdc9034 100644 --- a/resources/shaders/metal/reproject.vs.metal +++ b/resources/shaders/metal/reproject.vs.metal @@ -1,8 +1,14 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float4x4* uNewTransform [[id(0)]]; +}; + struct main0_out { float2 vTexCoord [[user(locn0)]]; @@ -11,14 +17,16 @@ struct main0_out struct main0_in { - float2 aPosition [[attribute(0)]]; + int2 aPosition [[attribute(0)]]; }; -vertex main0_out main0(main0_in in [[stage_in]], float4x4 uNewTransform [[buffer(0)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - out.vTexCoord = in.aPosition; - out.gl_Position = uNewTransform * float4(in.aPosition, 0.0, 1.0); + float2 position = float2(in.aPosition); + out.vTexCoord = position; + position.y = 1.0 - position.y; + out.gl_Position = (*spvDescriptorSet0.uNewTransform) * float4(position, 0.0, 1.0); return out; } diff --git a/resources/shaders/metal/stencil.fs.metal b/resources/shaders/metal/stencil.fs.metal index 426311ef..999cc7e1 100644 --- a/resources/shaders/metal/stencil.fs.metal +++ b/resources/shaders/metal/stencil.fs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include diff --git a/resources/shaders/metal/stencil.vs.metal b/resources/shaders/metal/stencil.vs.metal index 2dc23a5d..6b6182d4 100644 --- a/resources/shaders/metal/stencil.vs.metal +++ b/resources/shaders/metal/stencil.vs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include @@ -13,7 +14,7 @@ struct main0_in float3 aPosition [[attribute(0)]]; }; -vertex main0_out main0(main0_in in [[stage_in]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]]) { main0_out out = {}; out.gl_Position = float4(in.aPosition, 1.0); diff --git a/resources/shaders/metal/tile_alpha.fs.metal b/resources/shaders/metal/tile_alpha.fs.metal index 30afe4cd..2e72b19e 100644 --- a/resources/shaders/metal/tile_alpha.fs.metal +++ b/resources/shaders/metal/tile_alpha.fs.metal @@ -1,8 +1,15 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include using namespace metal; +struct spvDescriptorSetBuffer0 +{ + texture2d uStencilTexture [[id(0)]]; + sampler uStencilTextureSmplr [[id(1)]]; +}; + struct main0_out { float4 oFragColor [[color(0)]]; @@ -15,10 +22,10 @@ struct main0_in float4 vColor [[user(locn2)]]; }; -fragment main0_out main0(main0_in in [[stage_in]], texture2d uStencilTexture [[texture(0)]], sampler uStencilTextureSmplr [[sampler(0)]]) +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - float coverage = abs(uStencilTexture.sample(uStencilTextureSmplr, in.vTexCoord).x + in.vBackdrop); + float coverage = abs(spvDescriptorSet0.uStencilTexture.sample(spvDescriptorSet0.uStencilTextureSmplr, in.vTexCoord).x + in.vBackdrop); out.oFragColor = float4(in.vColor.xyz, in.vColor.w * coverage); return out; } diff --git a/resources/shaders/metal/tile_alpha_monochrome.vs.metal b/resources/shaders/metal/tile_alpha_monochrome.vs.metal index e92a3af0..9c9e1b56 100644 --- a/resources/shaders/metal/tile_alpha_monochrome.vs.metal +++ b/resources/shaders/metal/tile_alpha_monochrome.vs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #pragma clang diagnostic ignored "-Wmissing-prototypes" #include @@ -5,6 +6,15 @@ using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float2* uTileSize [[id(0)]]; + constant float2* uViewBoxOrigin [[id(1)]]; + constant float2* uFramebufferSize [[id(2)]]; + constant float2* uStencilTextureSize [[id(3)]]; + constant float4* uColor [[id(4)]]; +}; + struct main0_out { float2 vTexCoord [[user(locn0)]]; @@ -15,10 +25,10 @@ struct main0_out struct main0_in { - float2 aTessCoord [[attribute(0)]]; + uint2 aTessCoord [[attribute(0)]]; uint3 aTileOrigin [[attribute(1)]]; int aBackdrop [[attribute(2)]]; - uint aTileIndex [[attribute(3)]]; + int aTileIndex [[attribute(3)]]; }; float2 computeTileOffset(thread const uint& tileIndex, thread const float& stencilTextureWidth, thread float2 uTileSize) @@ -33,25 +43,25 @@ float4 getColor(thread float4 uColor) return uColor; } -void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread float2& aTessCoord, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread uint& aTileIndex, thread float2 uStencilTextureSize, thread float2& vTexCoord, thread float& vBackdrop, thread int& aBackdrop, thread float4& vColor, thread float4& gl_Position, thread float4 uColor) +void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread uint2& aTessCoord, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread int& aTileIndex, thread float2 uStencilTextureSize, thread float2& vTexCoord, thread float& vBackdrop, thread int& aBackdrop, thread float4& vColor, thread float4& gl_Position, thread float4 uColor) { float2 origin = float2(aTileOrigin.xy) + (float2(float(aTileOrigin.z & 15u), float(aTileOrigin.z >> 4u)) * 256.0); - float2 pixelPosition = ((origin + aTessCoord) * uTileSize) + uViewBoxOrigin; + float2 pixelPosition = ((origin + float2(aTessCoord)) * uTileSize) + uViewBoxOrigin; float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0); - uint param = aTileIndex; + uint param = uint(aTileIndex); float param_1 = uStencilTextureSize.x; float2 maskTexCoordOrigin = computeTileOffset(param, param_1, uTileSize); - float2 maskTexCoord = maskTexCoordOrigin + (aTessCoord * uTileSize); + float2 maskTexCoord = maskTexCoordOrigin + (float2(aTessCoord) * uTileSize); vTexCoord = maskTexCoord / uStencilTextureSize; vBackdrop = float(aBackdrop); vColor = getColor(uColor); gl_Position = float4(position, 0.0, 1.0); } -vertex main0_out main0(main0_in in [[stage_in]], float2 uTileSize [[buffer(0)]], float2 uViewBoxOrigin [[buffer(1)]], float2 uFramebufferSize [[buffer(2)]], float2 uStencilTextureSize [[buffer(3)]], float4 uColor [[buffer(4)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - computeVaryings(uTileSize, in.aTileOrigin, in.aTessCoord, uViewBoxOrigin, uFramebufferSize, in.aTileIndex, uStencilTextureSize, out.vTexCoord, out.vBackdrop, in.aBackdrop, out.vColor, out.gl_Position, uColor); + computeVaryings((*spvDescriptorSet0.uTileSize), in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uViewBoxOrigin), (*spvDescriptorSet0.uFramebufferSize), in.aTileIndex, (*spvDescriptorSet0.uStencilTextureSize), out.vTexCoord, out.vBackdrop, in.aBackdrop, out.vColor, out.gl_Position, (*spvDescriptorSet0.uColor)); return out; } diff --git a/resources/shaders/metal/tile_alpha_multicolor.vs.metal b/resources/shaders/metal/tile_alpha_multicolor.vs.metal index 525d7fe3..bc25f729 100644 --- a/resources/shaders/metal/tile_alpha_multicolor.vs.metal +++ b/resources/shaders/metal/tile_alpha_multicolor.vs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #pragma clang diagnostic ignored "-Wmissing-prototypes" #include @@ -5,6 +6,16 @@ using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float2* uTileSize [[id(0)]]; + texture2d uPaintTexture [[id(1)]]; + constant float2* uViewBoxOrigin [[id(2)]]; + sampler uPaintTextureSmplr [[id(3)]]; + constant float2* uFramebufferSize [[id(4)]]; + constant float2* uStencilTextureSize [[id(5)]]; +}; + struct main0_out { float2 vTexCoord [[user(locn0)]]; @@ -15,10 +26,10 @@ struct main0_out struct main0_in { - float2 aTessCoord [[attribute(0)]]; + uint2 aTessCoord [[attribute(0)]]; uint3 aTileOrigin [[attribute(1)]]; int aBackdrop [[attribute(2)]]; - uint aTileIndex [[attribute(3)]]; + int aTileIndex [[attribute(3)]]; float2 aColorTexCoord [[attribute(4)]]; }; @@ -34,25 +45,25 @@ float4 getColor(thread texture2d uPaintTexture, thread const sampler uPai return uPaintTexture.sample(uPaintTextureSmplr, aColorTexCoord, level(0.0)); } -void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread float2& aTessCoord, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread uint& aTileIndex, thread float2 uStencilTextureSize, thread float2& vTexCoord, thread float& vBackdrop, thread int& aBackdrop, thread float4& vColor, thread float4& gl_Position, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord) +void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread uint2& aTessCoord, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread int& aTileIndex, thread float2 uStencilTextureSize, thread float2& vTexCoord, thread float& vBackdrop, thread int& aBackdrop, thread float4& vColor, thread float4& gl_Position, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord) { float2 origin = float2(aTileOrigin.xy) + (float2(float(aTileOrigin.z & 15u), float(aTileOrigin.z >> 4u)) * 256.0); - float2 pixelPosition = ((origin + aTessCoord) * uTileSize) + uViewBoxOrigin; + float2 pixelPosition = ((origin + float2(aTessCoord)) * uTileSize) + uViewBoxOrigin; float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0); - uint param = aTileIndex; + uint param = uint(aTileIndex); float param_1 = uStencilTextureSize.x; float2 maskTexCoordOrigin = computeTileOffset(param, param_1, uTileSize); - float2 maskTexCoord = maskTexCoordOrigin + (aTessCoord * uTileSize); + float2 maskTexCoord = maskTexCoordOrigin + (float2(aTessCoord) * uTileSize); vTexCoord = maskTexCoord / uStencilTextureSize; vBackdrop = float(aBackdrop); vColor = getColor(uPaintTexture, uPaintTextureSmplr, aColorTexCoord); gl_Position = float4(position, 0.0, 1.0); } -vertex main0_out main0(main0_in in [[stage_in]], float2 uTileSize [[buffer(0)]], float2 uViewBoxOrigin [[buffer(1)]], float2 uFramebufferSize [[buffer(2)]], float2 uStencilTextureSize [[buffer(3)]], texture2d uPaintTexture [[texture(0)]], sampler uPaintTextureSmplr [[sampler(0)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - computeVaryings(uTileSize, in.aTileOrigin, in.aTessCoord, uViewBoxOrigin, uFramebufferSize, in.aTileIndex, uStencilTextureSize, out.vTexCoord, out.vBackdrop, in.aBackdrop, out.vColor, out.gl_Position, uPaintTexture, uPaintTextureSmplr, in.aColorTexCoord); + computeVaryings((*spvDescriptorSet0.uTileSize), in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uViewBoxOrigin), (*spvDescriptorSet0.uFramebufferSize), in.aTileIndex, (*spvDescriptorSet0.uStencilTextureSize), out.vTexCoord, out.vBackdrop, in.aBackdrop, out.vColor, out.gl_Position, spvDescriptorSet0.uPaintTexture, spvDescriptorSet0.uPaintTextureSmplr, in.aColorTexCoord); return out; } diff --git a/resources/shaders/metal/tile_solid.fs.metal b/resources/shaders/metal/tile_solid.fs.metal index fed4023d..ac500c97 100644 --- a/resources/shaders/metal/tile_solid.fs.metal +++ b/resources/shaders/metal/tile_solid.fs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #include #include diff --git a/resources/shaders/metal/tile_solid_monochrome.vs.metal b/resources/shaders/metal/tile_solid_monochrome.vs.metal index 83410055..46328863 100644 --- a/resources/shaders/metal/tile_solid_monochrome.vs.metal +++ b/resources/shaders/metal/tile_solid_monochrome.vs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #pragma clang diagnostic ignored "-Wmissing-prototypes" #include @@ -5,6 +6,14 @@ using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float2* uTileSize [[id(0)]]; + constant float2* uViewBoxOrigin [[id(1)]]; + constant float2* uFramebufferSize [[id(2)]]; + constant float4* uColor [[id(3)]]; +}; + struct main0_out { float4 vColor [[user(locn0)]]; @@ -13,8 +22,8 @@ struct main0_out struct main0_in { - float2 aTessCoord [[attribute(0)]]; - float2 aTileOrigin [[attribute(1)]]; + uint2 aTessCoord [[attribute(0)]]; + int2 aTileOrigin [[attribute(1)]]; }; float4 getColor(thread float4 uColor) @@ -22,18 +31,18 @@ float4 getColor(thread float4 uColor) return uColor; } -void computeVaryings(thread float2& aTileOrigin, thread float2& aTessCoord, thread float2 uTileSize, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread float4& vColor, thread float4& gl_Position, thread float4 uColor) +void computeVaryings(thread int2& aTileOrigin, thread uint2& aTessCoord, thread float2 uTileSize, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread float4& vColor, thread float4& gl_Position, thread float4 uColor) { - float2 pixelPosition = ((aTileOrigin + aTessCoord) * uTileSize) + uViewBoxOrigin; + float2 pixelPosition = (float2(aTileOrigin + int2(aTessCoord)) * uTileSize) + uViewBoxOrigin; float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0); vColor = getColor(uColor); gl_Position = float4(position, 0.0, 1.0); } -vertex main0_out main0(main0_in in [[stage_in]], float2 uTileSize [[buffer(0)]], float2 uViewBoxOrigin [[buffer(1)]], float2 uFramebufferSize [[buffer(2)]], float4 uColor [[buffer(3)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - computeVaryings(in.aTileOrigin, in.aTessCoord, uTileSize, uViewBoxOrigin, uFramebufferSize, out.vColor, out.gl_Position, uColor); + computeVaryings(in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uTileSize), (*spvDescriptorSet0.uViewBoxOrigin), (*spvDescriptorSet0.uFramebufferSize), out.vColor, out.gl_Position, (*spvDescriptorSet0.uColor)); return out; } diff --git a/resources/shaders/metal/tile_solid_multicolor.vs.metal b/resources/shaders/metal/tile_solid_multicolor.vs.metal index 3f5d0c45..edfe17d0 100644 --- a/resources/shaders/metal/tile_solid_multicolor.vs.metal +++ b/resources/shaders/metal/tile_solid_multicolor.vs.metal @@ -1,3 +1,4 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! #pragma clang diagnostic ignored "-Wmissing-prototypes" #include @@ -5,6 +6,15 @@ using namespace metal; +struct spvDescriptorSetBuffer0 +{ + constant float2* uTileSize [[id(0)]]; + texture2d uPaintTexture [[id(1)]]; + constant float2* uViewBoxOrigin [[id(2)]]; + sampler uPaintTextureSmplr [[id(3)]]; + constant float2* uFramebufferSize [[id(4)]]; +}; + struct main0_out { float4 vColor [[user(locn0)]]; @@ -13,8 +23,8 @@ struct main0_out struct main0_in { - float2 aTessCoord [[attribute(0)]]; - float2 aTileOrigin [[attribute(1)]]; + uint2 aTessCoord [[attribute(0)]]; + int2 aTileOrigin [[attribute(1)]]; float2 aColorTexCoord [[attribute(2)]]; }; @@ -23,18 +33,18 @@ float4 getColor(thread texture2d uPaintTexture, thread const sampler uPai return uPaintTexture.sample(uPaintTextureSmplr, aColorTexCoord, level(0.0)); } -void computeVaryings(thread float2& aTileOrigin, thread float2& aTessCoord, thread float2 uTileSize, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread float4& vColor, thread float4& gl_Position, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord) +void computeVaryings(thread int2& aTileOrigin, thread uint2& aTessCoord, thread float2 uTileSize, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread float4& vColor, thread float4& gl_Position, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord) { - float2 pixelPosition = ((aTileOrigin + aTessCoord) * uTileSize) + uViewBoxOrigin; + float2 pixelPosition = (float2(aTileOrigin + int2(aTessCoord)) * uTileSize) + uViewBoxOrigin; float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0); vColor = getColor(uPaintTexture, uPaintTextureSmplr, aColorTexCoord); gl_Position = float4(position, 0.0, 1.0); } -vertex main0_out main0(main0_in in [[stage_in]], float2 uTileSize [[buffer(0)]], float2 uViewBoxOrigin [[buffer(1)]], float2 uFramebufferSize [[buffer(2)]], texture2d uPaintTexture [[texture(0)]], sampler uPaintTextureSmplr [[sampler(0)]], uint gl_VertexID [[vertex_id]], uint gl_InstanceID [[instance_id]]) +vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - computeVaryings(in.aTileOrigin, in.aTessCoord, uTileSize, uViewBoxOrigin, uFramebufferSize, out.vColor, out.gl_Position, uPaintTexture, uPaintTextureSmplr, in.aColorTexCoord); + computeVaryings(in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uTileSize), (*spvDescriptorSet0.uViewBoxOrigin), (*spvDescriptorSet0.uFramebufferSize), out.vColor, out.gl_Position, spvDescriptorSet0.uPaintTexture, spvDescriptorSet0.uPaintTextureSmplr, in.aColorTexCoord); return out; } diff --git a/resources/shaders/spirv/debug_solid.fs.spv b/resources/shaders/spirv/debug_solid.fs.spv deleted file mode 100644 index ae1e7b85..00000000 Binary files a/resources/shaders/spirv/debug_solid.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/debug_solid.vs.spv b/resources/shaders/spirv/debug_solid.vs.spv deleted file mode 100644 index 0c94d0df..00000000 Binary files a/resources/shaders/spirv/debug_solid.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/debug_texture.fs.spv b/resources/shaders/spirv/debug_texture.fs.spv deleted file mode 100644 index 1be8ae23..00000000 Binary files a/resources/shaders/spirv/debug_texture.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/debug_texture.vs.spv b/resources/shaders/spirv/debug_texture.vs.spv deleted file mode 100644 index 22f838c7..00000000 Binary files a/resources/shaders/spirv/debug_texture.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/demo_ground.fs.spv b/resources/shaders/spirv/demo_ground.fs.spv deleted file mode 100644 index 8dc291cf..00000000 Binary files a/resources/shaders/spirv/demo_ground.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/demo_ground.vs.spv b/resources/shaders/spirv/demo_ground.vs.spv deleted file mode 100644 index 0a0926a6..00000000 Binary files a/resources/shaders/spirv/demo_ground.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/fill.fs.spv b/resources/shaders/spirv/fill.fs.spv deleted file mode 100644 index 4cb3b288..00000000 Binary files a/resources/shaders/spirv/fill.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/fill.vs.spv b/resources/shaders/spirv/fill.vs.spv deleted file mode 100644 index 1ae09e95..00000000 Binary files a/resources/shaders/spirv/fill.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/post.fs.spv b/resources/shaders/spirv/post.fs.spv deleted file mode 100644 index b6d2fec3..00000000 Binary files a/resources/shaders/spirv/post.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/post.vs.spv b/resources/shaders/spirv/post.vs.spv deleted file mode 100644 index 6d35fb90..00000000 Binary files a/resources/shaders/spirv/post.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/reproject.fs.spv b/resources/shaders/spirv/reproject.fs.spv deleted file mode 100644 index 34b4930a..00000000 Binary files a/resources/shaders/spirv/reproject.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/reproject.vs.spv b/resources/shaders/spirv/reproject.vs.spv deleted file mode 100644 index 9b39096e..00000000 Binary files a/resources/shaders/spirv/reproject.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/stencil.fs.spv b/resources/shaders/spirv/stencil.fs.spv deleted file mode 100644 index 4c89a941..00000000 Binary files a/resources/shaders/spirv/stencil.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/stencil.vs.spv b/resources/shaders/spirv/stencil.vs.spv deleted file mode 100644 index e24f65ab..00000000 Binary files a/resources/shaders/spirv/stencil.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/tile_alpha.fs.spv b/resources/shaders/spirv/tile_alpha.fs.spv deleted file mode 100644 index 006cb101..00000000 Binary files a/resources/shaders/spirv/tile_alpha.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/tile_alpha_monochrome.vs.spv b/resources/shaders/spirv/tile_alpha_monochrome.vs.spv deleted file mode 100644 index 782bc4f9..00000000 Binary files a/resources/shaders/spirv/tile_alpha_monochrome.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/tile_alpha_multicolor.vs.spv b/resources/shaders/spirv/tile_alpha_multicolor.vs.spv deleted file mode 100644 index 99363c6b..00000000 Binary files a/resources/shaders/spirv/tile_alpha_multicolor.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/tile_solid.fs.spv b/resources/shaders/spirv/tile_solid.fs.spv deleted file mode 100644 index cce99735..00000000 Binary files a/resources/shaders/spirv/tile_solid.fs.spv and /dev/null differ diff --git a/resources/shaders/spirv/tile_solid_monochrome.vs.spv b/resources/shaders/spirv/tile_solid_monochrome.vs.spv deleted file mode 100644 index 14cf6c14..00000000 Binary files a/resources/shaders/spirv/tile_solid_monochrome.vs.spv and /dev/null differ diff --git a/resources/shaders/spirv/tile_solid_multicolor.vs.spv b/resources/shaders/spirv/tile_solid_multicolor.vs.spv deleted file mode 100644 index 9e6cdceb..00000000 Binary files a/resources/shaders/spirv/tile_solid_multicolor.vs.spv and /dev/null differ diff --git a/shaders/Makefile b/shaders/Makefile index 42e78baa..327144c8 100644 --- a/shaders/Makefile +++ b/shaders/Makefile @@ -37,14 +37,20 @@ INCLUDES=\ OUT=\ $(SHADERS:%=$(TARGET_DIR)/gl3/%) \ $(SHADERS:%.glsl=$(TARGET_DIR)/metal/%.metal) \ + $(SHADERS:%.glsl=build/metal/%.spv) \ $(EMPTY) GLSL_VERSION=330 GLSLANGFLAGS=--auto-map-locations -I. +GLSLANGFLAGS_METAL=$(GLSLANGFLAGS) -DPF_ORIGIN_UPPER_LEFT=1 -SPIRVCROSSFLAGS=--msl +SPIRVCROSS?=spirv-cross +SPIRVCROSSFLAGS=--msl --msl-version 020100 --msl-argument-buffers -SED_ARGS=-e "s/\#version 330/\#version \{\{version\}\}/" -e "s/\#line.*$$//" +GLSL_VERSION_HEADER="\#version {{version}}" +HEADER="// Automatically generated from files in pathfinder/shaders/. Do not edit!" + +GLSL_SED_ARGS=-e "s/\#version 330//" -e "s/\#line.*$$//" all: $(OUT) @@ -53,17 +59,17 @@ all: $(OUT) clean: rm -f $(OUT) -$(TARGET_DIR)/spirv/%.fs.spv: %.fs.glsl $(INCLUDES) - mkdir -p $(TARGET_DIR)/spirv && glslangValidator $(GLSLANGFLAGS) -G$(GLSL_VERSION) -S frag -o $@ $< +build/metal/%.fs.spv: %.fs.glsl $(INCLUDES) + mkdir -p build/metal && glslangValidator $(GLSLANGFLAGS_METAL) -G$(GLSL_VERSION) -S frag -o $@ $< $(TARGET_DIR)/gl3/%.fs.glsl: %.fs.glsl $(INCLUDES) - mkdir -p $(TARGET_DIR)/gl3 && glslangValidator $(GLSLANGFLAGS) -S frag -E $< | sed $(SED_ARGS) > $@ + mkdir -p $(TARGET_DIR)/gl3 && echo $(GLSL_VERSION_HEADER) > $@ && echo $(HEADER) >> $@ && ( glslangValidator $(GLSLANGFLAGS) -S frag -E $< | sed $(GLSL_SED_ARGS) >> $@ ) || ( rm $@ && exit 1 ) -$(TARGET_DIR)/spirv/%.vs.spv: %.vs.glsl $(INCLUDES) - mkdir -p $(TARGET_DIR)/spirv && glslangValidator $(GLSLANGFLAGS) -G$(GLSL_VERSION) -S vert -o $@ $< +build/metal/%.vs.spv: %.vs.glsl $(INCLUDES) + mkdir -p build/metal && glslangValidator $(GLSLANGFLAGS_METAL) -G$(GLSL_VERSION) -S vert -o $@ $< $(TARGET_DIR)/gl3/%.vs.glsl: %.vs.glsl $(INCLUDES) - mkdir -p $(TARGET_DIR)/gl3 && glslangValidator $(GLSLANGFLAGS) -S vert -E $< | sed $(SED_ARGS) > $@ + mkdir -p $(TARGET_DIR)/gl3 && echo $(GLSL_VERSION_HEADER) > $@ && echo $(HEADER) >> $@ && ( glslangValidator $(GLSLANGFLAGS) -S vert -E $< | sed $(GLSL_SED_ARGS) >> $@ ) || ( rm $@ && exit 1 ) -$(TARGET_DIR)/metal/%.metal: $(TARGET_DIR)/spirv/%.spv - mkdir -p $(TARGET_DIR)/metal && spirv-cross $(SPIRVCROSSFLAGS) --output $@ $< +$(TARGET_DIR)/metal/%.metal: build/metal/%.spv + mkdir -p $(TARGET_DIR)/metal && echo $(HEADER) > $@ && ( $(SPIRVCROSS) $(SPIRVCROSSFLAGS) $< | sed $(METAL_SED_ARGS) >> $@ ) || ( rm $@ && exit 1 ) diff --git a/shaders/debug_solid.vs.glsl b/shaders/debug_solid.vs.glsl index 10b92ad8..8dd2f383 100644 --- a/shaders/debug_solid.vs.glsl +++ b/shaders/debug_solid.vs.glsl @@ -14,9 +14,9 @@ precision highp float; uniform vec2 uFramebufferSize; -in vec2 aPosition; +in ivec2 aPosition; void main() { - vec2 position = aPosition / uFramebufferSize * 2.0 - 1.0; + vec2 position = vec2(aPosition) / uFramebufferSize * 2.0 - 1.0; gl_Position = vec4(position.x, -position.y, 0.0, 1.0); } diff --git a/shaders/debug_texture.vs.glsl b/shaders/debug_texture.vs.glsl index 734916f1..7df2a288 100644 --- a/shaders/debug_texture.vs.glsl +++ b/shaders/debug_texture.vs.glsl @@ -15,13 +15,13 @@ precision highp float; uniform vec2 uFramebufferSize; uniform vec2 uTextureSize; -in vec2 aPosition; -in vec2 aTexCoord; +in ivec2 aPosition; +in ivec2 aTexCoord; out vec2 vTexCoord; void main() { - vTexCoord = aTexCoord / uTextureSize; - vec2 position = aPosition / uFramebufferSize * 2.0 - 1.0; + vTexCoord = vec2(aTexCoord) / uTextureSize; + vec2 position = vec2(aPosition) / uFramebufferSize * 2.0 - 1.0; gl_Position = vec4(position.x, -position.y, 0.0, 1.0); } diff --git a/shaders/demo_ground.vs.glsl b/shaders/demo_ground.vs.glsl index b145ce04..e2a618b6 100644 --- a/shaders/demo_ground.vs.glsl +++ b/shaders/demo_ground.vs.glsl @@ -15,11 +15,11 @@ precision highp float; uniform mat4 uTransform; uniform int uGridlineCount; -in vec2 aPosition; +in ivec2 aPosition; out vec2 vTexCoord; void main() { - vTexCoord = aPosition * float(uGridlineCount); - gl_Position = uTransform * vec4(aPosition.x, 0.0, aPosition.y, 1.0); + vTexCoord = vec2(aPosition * uGridlineCount); + gl_Position = uTransform * vec4(ivec4(aPosition.x, 0, aPosition.y, 1)); } diff --git a/shaders/fill.vs.glsl b/shaders/fill.vs.glsl index f5abbf7e..7b75578b 100644 --- a/shaders/fill.vs.glsl +++ b/shaders/fill.vs.glsl @@ -1,6 +1,6 @@ #version 330 -// pathfinder/resources/fill.vs.glsl +// pathfinder/shaders/fill.vs.glsl // // Copyright © 2019 The Pathfinder Project Developers. // @@ -15,7 +15,7 @@ precision highp float; uniform vec2 uFramebufferSize; uniform vec2 uTileSize; -in vec2 aTessCoord; +in uvec2 aTessCoord; in uint aFromPx; in uint aToPx; in vec2 aFromSubpx; @@ -27,7 +27,7 @@ out vec2 vTo; vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) { uint tilesPerRow = uint(stencilTextureWidth / uTileSize.x); - uvec2 tileOffset = uvec2(aTileIndex % tilesPerRow, aTileIndex / tilesPerRow); + uvec2 tileOffset = uvec2(tileIndex % tilesPerRow, tileIndex / tilesPerRow); return vec2(tileOffset) * uTileSize; } @@ -38,11 +38,11 @@ void main() { vec2 to = vec2(aToPx & 15u, aToPx >> 4u) + aToSubpx; vec2 position; - if (aTessCoord.x < 0.5) + if (aTessCoord.x == 0u) position.x = floor(min(from.x, to.x)); else position.x = ceil(max(from.x, to.x)); - if (aTessCoord.y < 0.5) + if (aTessCoord.y == 0u) position.y = floor(min(from.y, to.y)); else position.y = uTileSize.y; @@ -50,5 +50,9 @@ void main() { vFrom = from - position; vTo = to - position; - gl_Position = vec4((tileOrigin + position) / uFramebufferSize * 2.0 - 1.0, 0.0, 1.0); + vec2 globalPosition = (tileOrigin + position) / uFramebufferSize * 2.0 - 1.0; +#ifdef PF_ORIGIN_UPPER_LEFT + globalPosition.y = -globalPosition.y; +#endif + gl_Position = vec4(globalPosition, 0.0, 1.0); } diff --git a/shaders/post.vs.glsl b/shaders/post.vs.glsl index 2ee824a7..137cf388 100644 --- a/shaders/post.vs.glsl +++ b/shaders/post.vs.glsl @@ -12,11 +12,11 @@ precision highp float; -in vec2 aPosition; +in ivec2 aPosition; out vec2 vTexCoord; void main() { - vTexCoord = aPosition; - gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0); + vTexCoord = vec2(aPosition); + gl_Position = vec4(vec2(aPosition) * 2.0 - 1.0, 0.0, 1.0); } diff --git a/shaders/reproject.vs.glsl b/shaders/reproject.vs.glsl index 91d9f9a9..f10433b1 100644 --- a/shaders/reproject.vs.glsl +++ b/shaders/reproject.vs.glsl @@ -14,11 +14,17 @@ precision highp float; uniform mat4 uNewTransform; -in vec2 aPosition; +in ivec2 aPosition; out vec2 vTexCoord; void main() { - vTexCoord = aPosition; - gl_Position = uNewTransform * vec4(aPosition, 0.0, 1.0); + vec2 position = vec2(aPosition); + vTexCoord = position; + +#ifdef PF_ORIGIN_UPPER_LEFT + // FIXME(pcwalton): This is wrong. + position.y = 1.0 - position.y; +#endif + gl_Position = uNewTransform * vec4(position, 0.0, 1.0); } diff --git a/shaders/tile_alpha_vertex.inc.glsl b/shaders/tile_alpha_vertex.inc.glsl index 6993a13d..e5211809 100644 --- a/shaders/tile_alpha_vertex.inc.glsl +++ b/shaders/tile_alpha_vertex.inc.glsl @@ -13,10 +13,10 @@ uniform vec2 uTileSize; uniform vec2 uStencilTextureSize; uniform vec2 uViewBoxOrigin; -in vec2 aTessCoord; +in uvec2 aTessCoord; in uvec3 aTileOrigin; in int aBackdrop; -in uint aTileIndex; +in int aTileIndex; out vec2 vTexCoord; out float vBackdrop; @@ -32,9 +32,9 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) { void computeVaryings() { vec2 origin = vec2(aTileOrigin.xy) + vec2(aTileOrigin.z & 15u, aTileOrigin.z >> 4u) * 256.0; - vec2 pixelPosition = (origin + aTessCoord) * uTileSize + uViewBoxOrigin; + vec2 pixelPosition = (origin + vec2(aTessCoord)) * uTileSize + uViewBoxOrigin; vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0); - vec2 maskTexCoordOrigin = computeTileOffset(aTileIndex, uStencilTextureSize.x); + vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize.x); vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vTexCoord = maskTexCoord / uStencilTextureSize; diff --git a/shaders/tile_solid_vertex.inc.glsl b/shaders/tile_solid_vertex.inc.glsl index 73f30cd0..fc7c8bc5 100644 --- a/shaders/tile_solid_vertex.inc.glsl +++ b/shaders/tile_solid_vertex.inc.glsl @@ -12,18 +12,17 @@ uniform vec2 uFramebufferSize; uniform vec2 uTileSize; uniform vec2 uViewBoxOrigin; -in vec2 aTessCoord; -in vec2 aTileOrigin; +in uvec2 aTessCoord; +in ivec2 aTileOrigin; out vec4 vColor; vec4 getColor(); void computeVaryings() { - vec2 pixelPosition = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin; + vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord)) * uTileSize + uViewBoxOrigin; vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0); vColor = getColor(); - //vColor = vec4(1.0, 0.0, 0.0, 1.0); gl_Position = vec4(position, 0.0, 1.0); } diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 794e5839..18817224 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -22,7 +22,7 @@ use pathfinder_geometry::basic::rect::RectI; use pathfinder_geometry::color::ColorU; use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, Device, Primitive}; -use pathfinder_gpu::{RenderState, UniformData, VertexAttrClass}; +use pathfinder_gpu::{RenderOptions, RenderState, RenderTarget, UniformData, VertexAttrClass}; use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType}; use pathfinder_simd::default::F32x4; use serde_json; @@ -131,7 +131,11 @@ impl UIPresenter where D: Device { self.draw_rect(device, rect, color, false); } - fn draw_rect(&self, device: &D, rect: RectI, color: ColorU, filled: bool) { + fn draw_rect(&self, + device: &D, + rect: RectI, + color: ColorU, + filled: bool) { let vertex_data = [ DebugSolidVertex::new(rect.origin()), DebugSolidVertex::new(rect.upper_right()), @@ -160,8 +164,6 @@ impl UIPresenter where D: Device { index_data: &[u32], color: ColorU, filled: bool) { - device.bind_vertex_array(&self.solid_vertex_array.vertex_array); - device.allocate_buffer(&self.solid_vertex_array.vertex_buffer, BufferData::Memory(vertex_data), BufferTarget::Vertex, @@ -171,15 +173,23 @@ impl UIPresenter where D: Device { BufferTarget::Index, BufferUploadMode::Dynamic); - device.use_program(&self.solid_program.program); - device.set_uniform(&self.solid_program.framebuffer_size_uniform, - UniformData::Vec2(self.framebuffer_size.0.to_f32x4())); - set_color_uniform(device, &self.solid_program.color_uniform, color); - let primitive = if filled { Primitive::Triangles } else { Primitive::Lines }; - device.draw_elements(primitive, index_data.len() as u32, &RenderState { - blend: BlendState::RGBOneAlphaOneMinusSrcAlpha, - ..RenderState::default() + device.draw_elements(index_data.len() as u32, &RenderState { + target: &RenderTarget::Default, + program: &self.solid_program.program, + vertex_array: &self.solid_vertex_array.vertex_array, + primitive, + uniforms: &[ + (&self.solid_program.framebuffer_size_uniform, + UniformData::Vec2(self.framebuffer_size.0.to_f32x4())), + (&self.solid_program.color_uniform, get_color_uniform(color)), + ], + textures: &[], + viewport: RectI::new(Vector2I::default(), self.framebuffer_size), + options: RenderOptions { + blend: BlendState::RGBOneAlphaOneMinusSrcAlpha, + ..RenderOptions::default() + }, }); } @@ -396,19 +406,25 @@ impl UIPresenter where D: Device { BufferTarget::Index, BufferUploadMode::Dynamic); - device.bind_vertex_array(&self.texture_vertex_array.vertex_array); - device.use_program(&self.texture_program.program); - device.set_uniform(&self.texture_program.framebuffer_size_uniform, - UniformData::Vec2(self.framebuffer_size.0.to_f32x4())); - device.set_uniform(&self.texture_program.texture_size_uniform, - UniformData::Vec2(device.texture_size(&texture).0.to_f32x4())); - set_color_uniform(device, &self.texture_program.color_uniform, color); - device.bind_texture(texture, 0); - device.set_uniform(&self.texture_program.texture_uniform, UniformData::TextureUnit(0)); - - device.draw_elements(Primitive::Triangles, index_data.len() as u32, &RenderState { - blend: BlendState::RGBOneAlphaOneMinusSrcAlpha, - ..RenderState::default() + device.draw_elements(index_data.len() as u32, &RenderState { + target: &RenderTarget::Default, + program: &self.texture_program.program, + vertex_array: &self.texture_vertex_array.vertex_array, + primitive: Primitive::Triangles, + textures: &[&texture], + uniforms: &[ + (&self.texture_program.framebuffer_size_uniform, + UniformData::Vec2(self.framebuffer_size.0.to_f32x4())), + (&self.texture_program.color_uniform, get_color_uniform(color)), + (&self.texture_program.texture_uniform, UniformData::TextureUnit(0)), + (&self.texture_program.texture_size_uniform, + UniformData::Vec2(device.texture_size(&texture).0.to_f32x4())) + ], + viewport: RectI::new(Vector2I::default(), self.framebuffer_size), + options: RenderOptions { + blend: BlendState::RGBOneAlphaOneMinusSrcAlpha, + ..RenderOptions::default() + }, }); } @@ -508,9 +524,9 @@ impl UIPresenter where D: Device { let highlight_size = Vector2I::new(SEGMENT_SIZE, BUTTON_HEIGHT); let x_offset = value as i32 * SEGMENT_SIZE + (value as i32 - 1); self.draw_solid_rounded_rect(device, - RectI::new(origin + Vector2I::new(x_offset, 0), + RectI::new(origin + Vector2I::new(x_offset, 0), highlight_size), - TEXT_COLOR); + TEXT_COLOR); } let mut segment_origin = origin + Vector2I::new(SEGMENT_SIZE + 1, 0); @@ -520,9 +536,9 @@ impl UIPresenter where D: Device { Some(value) if value == prev_segment_index || value == next_segment_index => {} _ => { self.draw_line(device, - segment_origin, - segment_origin + Vector2I::new(0, BUTTON_HEIGHT), - TEXT_COLOR); + segment_origin, + segment_origin + Vector2I::new(0, BUTTON_HEIGHT), + TEXT_COLOR); } } segment_origin = segment_origin + Vector2I::new(SEGMENT_SIZE + 1, 0); @@ -590,25 +606,25 @@ impl DebugTextureVertexArray where D: Device { let tex_coord_attr = device.get_vertex_attr(&debug_texture_program.program, "TexCoord") .unwrap(); - device.bind_vertex_array(&vertex_array); - device.use_program(&debug_texture_program.program); - device.bind_buffer(&vertex_buffer, BufferTarget::Vertex); - device.bind_buffer(&index_buffer, BufferTarget::Index); - device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor { + device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); + device.bind_buffer(&vertex_array, &index_buffer, BufferTarget::Index); + device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U16, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::I16, stride: DEBUG_TEXTURE_VERTEX_SIZE, offset: 0, divisor: 0, + buffer_index: 0, }); - device.configure_vertex_attr(&tex_coord_attr, &VertexAttrDescriptor { + device.configure_vertex_attr(&vertex_array, &tex_coord_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U16, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::I16, stride: DEBUG_TEXTURE_VERTEX_SIZE, offset: 4, divisor: 0, + buffer_index: 0, }); DebugTextureVertexArray { vertex_array, vertex_buffer, index_buffer } @@ -626,19 +642,18 @@ impl DebugSolidVertexArray where D: Device { let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer()); let vertex_array = device.create_vertex_array(); - let position_attr = device.get_vertex_attr(&debug_solid_program.program, "Position") - .unwrap(); - device.bind_vertex_array(&vertex_array); - device.use_program(&debug_solid_program.program); - device.bind_buffer(&vertex_buffer, BufferTarget::Vertex); - device.bind_buffer(&index_buffer, BufferTarget::Index); - device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor { + let position_attr = + device.get_vertex_attr(&debug_solid_program.program, "Position").unwrap(); + device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); + device.bind_buffer(&vertex_array, &index_buffer, BufferTarget::Index); + device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { size: 2, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::U16, + class: VertexAttrClass::Int, + attr_type: VertexAttrType::I16, stride: DEBUG_SOLID_VERTEX_SIZE, offset: 0, divisor: 0, + buffer_index: 0, }); DebugSolidVertexArray { vertex_array, vertex_buffer, index_buffer } @@ -714,9 +729,9 @@ impl CornerRects { } } -fn set_color_uniform(device: &D, uniform: &D::Uniform, color: ColorU) where D: Device { +fn get_color_uniform(color: ColorU) -> UniformData { let color = F32x4::new(color.r as f32, color.g as f32, color.b as f32, color.a as f32); - device.set_uniform(uniform, UniformData::Vec4(color * F32x4::splat(1.0 / 255.0))); + UniformData::Vec4(color * F32x4::splat(1.0 / 255.0)) } #[derive(Clone, Copy)]