vello_hybrid: Extract probing code into its module
diff --git a/sparse_strips/vello_hybrid/src/render/mod.rs b/sparse_strips/vello_hybrid/src/render/mod.rs index 79c6575..3e286ad 100644 --- a/sparse_strips/vello_hybrid/src/render/mod.rs +++ b/sparse_strips/vello_hybrid/src/render/mod.rs
@@ -9,7 +9,7 @@ //! - `webgl` contains a WebGL2 backend if the `webgl` feature is active. pub(crate) mod common; -#[cfg(feature = "probe")] +#[cfg(all(feature = "webgl", feature = "probe"))] mod probe; #[cfg(feature = "webgl")] mod webgl; @@ -19,10 +19,10 @@ pub use common::{Config, GpuStrip, RenderSize}; #[cfg(all(feature = "webgl", feature = "probe"))] +pub use probe::{WebGlPendingProbe, WebGlProbeError, WebGlProbeStatus}; +#[cfg(all(feature = "webgl", feature = "probe"))] pub use vello_common::probe::{Probe, ProbeResult}; #[cfg(feature = "webgl")] pub use webgl::{WebGlAtlasWriter, WebGlRenderer, WebGlTextureWithDimensions}; -#[cfg(all(feature = "webgl", feature = "probe"))] -pub use webgl::{WebGlPendingProbe, WebGlProbeError, WebGlProbeStatus}; #[cfg(feature = "wgpu")] pub use wgpu::{AtlasWriter, RenderTargetConfig, Renderer, TextureBindings};
diff --git a/sparse_strips/vello_hybrid/src/render/probe.rs b/sparse_strips/vello_hybrid/src/render/probe.rs index aa67da0..fb59b50 100644 --- a/sparse_strips/vello_hybrid/src/render/probe.rs +++ b/sparse_strips/vello_hybrid/src/render/probe.rs
@@ -1,13 +1,301 @@ // Copyright 2026 the Vello Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use crate::Scene; +use crate::render::common::IMAGE_PADDING; +use crate::render::webgl::resource::Framebuffer; +use crate::render::webgl::{ + WebGlStateConfig, WebGlStateGuard, create_atlas_texture_array, create_framebuffer_for_texture, + create_texture, +}; +use crate::schedule::RootRenderTarget; +use crate::{RenderError, RenderSize, Scene, WebGlRenderer}; +use alloc::sync::Arc; +use core::ops::Deref; +use thiserror::Error; use vello_common::filter_effects::Filter; +use vello_common::image_cache::ImageCache; use vello_common::kurbo::{Affine, BezPath, Rect}; -use vello_common::paint::PaintType; +use vello_common::multi_atlas::{AllocationStrategy, AtlasConfig}; +use vello_common::paint::{ImageSource, PaintType}; use vello_common::peniko::BlendMode; +use vello_common::pixmap::Pixmap; +use vello_common::probe::Probe; +use web_sys::{WebGl2RenderingContext, WebGlBuffer, WebGlSync}; + +/// A WebGL probe whose pixel readback has been queued but not completed. +#[derive(Debug)] +pub struct WebGlPendingProbe { + gl: WebGl2RenderingContext, + sync: Option<WebGlSync>, + buffer: Option<WebGlBuffer>, + width: u16, + height: u16, +} + +/// Error returned while running a WebGL probe. +#[derive(Debug, Clone, Error)] +pub enum WebGlProbeError { + /// Rendering the probe scene failed. + #[error("probe render failed: {0}")] + Render(RenderError), + /// Probe readback failed. + #[error("probe readback failed")] + ReadbackFailed, +} + +/// Result of polling the WebGL probe. +#[derive(Debug)] +pub enum WebGlProbeStatus { + /// The probe is still pending. + Pending(WebGlPendingProbe), + /// The probe has finished and the result is available. + Complete(Probe<RenderError>), +} + +impl WebGlRenderer { + /// Conduct a probing operation. + /// + /// The WebGL drivers of certain devices are known to be buggy and might therefore not work correctly + /// with Vello Hybrid. In the best case, it will simply result in a program crash, but in the worst + /// case it can instead result in a silent failure, meaning that no explicit error is + /// thrown, but the rendered contents of Vello Hybrid will either be completely empty or look glitchy. + /// + /// The purpose of this method is to run a sanity check to ensure that running Vello Hybrid on this + /// device actually results in visible and correct output. How this achieved is by drawing a selection + /// of small elements into a small canvas, and comparing the final output against a reference image. + /// + /// This method will return a handle that allows inspecting the results of the probe once the + /// results of the probe scene can be copied back from GPU to CPU. For performance reasons, + /// anything in-between mostly happens asynchronously. + pub fn probe(&mut self) -> Result<WebGlPendingProbe, WebGlProbeError> { + self.probe_inner().map_err(WebGlProbeError::Render) + } + + fn probe_inner(&mut self) -> Result<WebGlPendingProbe, RenderError> { + // IMPORTANT NOTE: When making any changes to the probe, make sure to + // unignore and rerun the "webgl_probe_succeeds" test locally. + + let _state_guard = WebGlStateGuard::with_config( + &self.gl, + WebGlStateConfig { + framebuffer: true, + active_texture: true, + texture_2d: true, + texture_2d_array: true, + pixel_pack_buffer: true, + viewport: true, + ..Default::default() + }, + ); + + let (width, height) = vello_common::probe::canvas_size(); + let render_size = RenderSize { + width: u32::from(width), + height: u32::from(height), + }; + + let probe_texture = create_texture(&self.gl); + self.gl + .tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_array_buffer_view( + WebGl2RenderingContext::TEXTURE_2D, + 0, + WebGl2RenderingContext::RGBA8 as i32, + render_size.width as i32, + render_size.height as i32, + 0, + WebGl2RenderingContext::RGBA, + WebGl2RenderingContext::UNSIGNED_BYTE, + None, + ) + .unwrap(); + let probe_framebuffer = create_framebuffer_for_texture(&self.gl, &probe_texture); + + let atlas_config = AtlasConfig { + initial_atlas_count: 1, + // These should be large enough for the probe scene. + atlas_size: (256, 256), + max_atlases: 1, + auto_grow: true, + allocation_strategy: AllocationStrategy::FirstFit, + }; + let (atlas_width, atlas_height) = atlas_config.atlas_size; + + let mut probe_image_cache = ImageCache::new_with_config(atlas_config); + let mut probe_atlas_texture_array = + create_atlas_texture_array(&self.gl, atlas_width, atlas_height, 1); + core::mem::swap( + &mut self.programs.resources.atlas_texture_array, + &mut probe_atlas_texture_array, + ); + + let probe_image = Arc::new(vello_common::probe::probe_image_pixmap()); + // Note: No need to destroy the image explicitly in the end, because we discard the image + // cache anyway. + let probe_image_id = + self.upload_image_with(&mut probe_image_cache, &probe_image, IMAGE_PADDING); + let mut scene = Scene::new(width, height); + vello_common::probe::draw_scene( + &mut scene, + ImageSource::opaque_id_with_transparency_hint( + probe_image_id, + probe_image.may_have_transparency(), + ), + ); + + let previous_view_framebuffer = self + .programs + .resources + .view_framebuffer_override + .replace(probe_framebuffer); + let render_result = self.render_scene( + &scene, + &mut probe_image_cache, + &render_size, + true, + RootRenderTarget::AtlasLayer, + ); + let probe_framebuffer = self + .programs + .resources + .view_framebuffer_override + .take() + .expect("probe framebuffer must be restored after rendering"); + self.programs.resources.view_framebuffer_override = previous_view_framebuffer; + + core::mem::swap( + &mut self.programs.resources.atlas_texture_array, + &mut probe_atlas_texture_array, + ); + + // We do this here instead of above such that in case the render result is not + // valid, we still properly restore the state (e.g. the old atlas texture array). + render_result?; + + let pending = launch_probe(&self.gl, &probe_framebuffer, width, height); + + Ok(pending) + } +} #[cfg(feature = "probe")] +impl WebGlPendingProbe { + /// Try to finish the probe. + /// + /// In case the result is not available yet, a new pending probe object will be returned + /// which can be checked again in the future. Otherwise, the probe result or an error will be + /// returned. + pub fn try_finish(mut self) -> Result<WebGlProbeStatus, WebGlProbeError> { + let status = self.gl.client_wait_sync_with_u32( + self.sync.as_ref().expect("probe sync must exist"), + 0, + 0, + ); + + if status == WebGl2RenderingContext::TIMEOUT_EXPIRED { + return Ok(WebGlProbeStatus::Pending(self)); + } + + if status == WebGl2RenderingContext::ALREADY_SIGNALED + || status == WebGl2RenderingContext::CONDITION_SATISFIED + { + Ok(WebGlProbeStatus::Complete(self.finish_success())) + } else { + Err(self.finish_failure()) + } + } + + fn finish_success(&mut self) -> Probe<RenderError> { + let _state_guard = WebGlStateGuard::with_config( + &self.gl, + WebGlStateConfig { + pixel_pack_buffer: true, + ..Default::default() + }, + ); + let mut pixmap = Pixmap::new(self.width, self.height); + + self.gl.bind_buffer( + WebGl2RenderingContext::PIXEL_PACK_BUFFER, + self.buffer.as_ref(), + ); + self.gl.get_buffer_sub_data_with_i32_and_u8_array( + WebGl2RenderingContext::PIXEL_PACK_BUFFER, + 0, + pixmap.data_as_u8_slice_mut(), + ); + + Probe::from_actual(pixmap) + } + + fn finish_failure(&mut self) -> WebGlProbeError { + WebGlProbeError::ReadbackFailed + } +} + +impl Drop for WebGlPendingProbe { + fn drop(&mut self) { + if let Some(sync) = self.sync.take() { + self.gl.delete_sync(Some(&sync)); + } + if let Some(buffer) = self.buffer.take() { + self.gl.delete_buffer(Some(&buffer)); + } + } +} + +#[cfg(feature = "probe")] +fn launch_probe( + gl: &WebGl2RenderingContext, + framebuffer: &Framebuffer, + width: u16, + height: u16, +) -> WebGlPendingProbe { + let pixel_pack_buffer = gl.create_buffer().unwrap(); + let byte_len = i32::from(width) * i32::from(height) * 4; + + gl.bind_buffer( + WebGl2RenderingContext::PIXEL_PACK_BUFFER, + Some(&pixel_pack_buffer), + ); + gl.buffer_data_with_i32( + WebGl2RenderingContext::PIXEL_PACK_BUFFER, + byte_len, + WebGl2RenderingContext::STREAM_READ, + ); + gl.bind_framebuffer( + WebGl2RenderingContext::FRAMEBUFFER, + Some(framebuffer.deref()), + ); + gl.read_pixels_with_i32( + 0, + 0, + i32::from(width), + i32::from(height), + WebGl2RenderingContext::RGBA, + WebGl2RenderingContext::UNSIGNED_BYTE, + 0, + ) + .unwrap(); + // Create a fence that notifies us once rendering is complete and the contents have been + // transferred from the framebuffer to the pixel pack buffer. + let sync = gl + .fence_sync(WebGl2RenderingContext::SYNC_GPU_COMMANDS_COMPLETE, 0) + .unwrap(); + // https://wikis.khronos.org/opengl/Sync_Object + // "It is important that syncs are properly flushed into the GPU's command queue. Without + // proper flushing, the sync object may never be signaled." + gl.flush(); + gl.bind_buffer(WebGl2RenderingContext::PIXEL_PACK_BUFFER, None); + + WebGlPendingProbe { + gl: gl.clone(), + sync: Some(sync), + buffer: Some(pixel_pack_buffer), + width, + height, + } +} + impl vello_common::probe::ProbeRenderer for Scene { fn set_transform(&mut self, transform: Affine) { Self::set_transform(self, transform);
diff --git a/sparse_strips/vello_hybrid/src/render/webgl.rs b/sparse_strips/vello_hybrid/src/render/webgl.rs index 5baae12..8e9e736 100644 --- a/sparse_strips/vello_hybrid/src/render/webgl.rs +++ b/sparse_strips/vello_hybrid/src/render/webgl.rs
@@ -46,19 +46,11 @@ use alloc::vec::Vec; use bytemuck::{Pod, Zeroable}; use core::fmt::Debug; -#[cfg(feature = "probe")] -use core::ops::Deref; #[cfg(feature = "text")] use glifo::{GLYPH_PADDING, PendingClearRect}; use resource::{Buffer, FragmentShader, Framebuffer, Program, Texture, VertexArray, VertexShader}; -#[cfg(feature = "probe")] -use thiserror::Error; use vello_common::image_cache::{ImageCache, ImageResource}; -#[cfg(feature = "probe")] -use vello_common::multi_atlas::AllocationStrategy; use vello_common::multi_atlas::{AtlasConfig, AtlasId}; -#[cfg(feature = "probe")] -use vello_common::probe::Probe; use vello_common::render_graph::LayerId; use vello_common::{ coarse::WideTile, @@ -72,8 +64,6 @@ tile::Tile, }; use vello_sparse_shaders::{clear_slots, filters, render_strips}; -#[cfg(feature = "probe")] -use web_sys::WebGlSync; use web_sys::wasm_bindgen::{JsCast, JsValue}; use web_sys::{ HtmlCanvasElement, WebGl2RenderingContext, WebGlBuffer, WebGlFramebuffer, WebGlProgram, @@ -106,13 +96,13 @@ #[derive(Debug)] pub struct WebGlRenderer { /// Programs for rendering. - programs: WebGlPrograms, + pub(crate) programs: WebGlPrograms, /// Scheduler for scheduling draws. scheduler: Scheduler, /// The state used by the scheduler. scheduler_state: SchedulerState, /// WebGL context. - gl: WebGl2RenderingContext, + pub(crate) gl: WebGl2RenderingContext, /// Encoded paints for storing encoded paints. encoded_paints: Vec<GpuEncodedPaint>, /// Stores the index (offset) of the encoded paints in the encoded paints texture. @@ -126,106 +116,6 @@ dummy_image_cache: Option<ImageCache>, } -/// A WebGL probe whose pixel readback has been queued but not completed. -#[cfg(feature = "probe")] -#[derive(Debug)] -pub struct WebGlPendingProbe { - gl: WebGl2RenderingContext, - sync: Option<WebGlSync>, - buffer: Option<WebGlBuffer>, - width: u16, - height: u16, -} - -/// Error returned while running a WebGL probe. -#[cfg(feature = "probe")] -#[derive(Debug, Clone, Error)] -pub enum WebGlProbeError { - /// Rendering the probe scene failed. - #[error("probe render failed: {0}")] - Render(RenderError), - /// Probe readback failed. - #[error("probe readback failed")] - ReadbackFailed, -} - -/// Result of polling the WebGL probe. -#[cfg(feature = "probe")] -#[derive(Debug)] -pub enum WebGlProbeStatus { - /// The probe is still pending. - Pending(WebGlPendingProbe), - /// The probe has finished and the result is available. - Complete(Probe<RenderError>), -} - -#[cfg(feature = "probe")] -impl WebGlPendingProbe { - /// Try to finish the probe. - /// - /// In case the result is not available yet, a new pending probe object will be returned - /// which can be checked again in the future. Otherwise, the probe result or an error will be - /// returned. - pub fn try_finish(mut self) -> Result<WebGlProbeStatus, WebGlProbeError> { - let status = self.gl.client_wait_sync_with_u32( - self.sync.as_ref().expect("probe sync must exist"), - 0, - 0, - ); - - if status == WebGl2RenderingContext::TIMEOUT_EXPIRED { - return Ok(WebGlProbeStatus::Pending(self)); - } - - if status == WebGl2RenderingContext::ALREADY_SIGNALED - || status == WebGl2RenderingContext::CONDITION_SATISFIED - { - Ok(WebGlProbeStatus::Complete(self.finish_success())) - } else { - Err(self.finish_failure()) - } - } - - fn finish_success(&mut self) -> Probe<RenderError> { - let _state_guard = WebGlStateGuard::with_config( - &self.gl, - WebGlStateConfig { - pixel_pack_buffer: true, - ..Default::default() - }, - ); - let mut pixmap = Pixmap::new(self.width, self.height); - - self.gl.bind_buffer( - WebGl2RenderingContext::PIXEL_PACK_BUFFER, - self.buffer.as_ref(), - ); - self.gl.get_buffer_sub_data_with_i32_and_u8_array( - WebGl2RenderingContext::PIXEL_PACK_BUFFER, - 0, - pixmap.data_as_u8_slice_mut(), - ); - - Probe::from_actual(pixmap) - } - - fn finish_failure(&mut self) -> WebGlProbeError { - WebGlProbeError::ReadbackFailed - } -} - -#[cfg(feature = "probe")] -impl Drop for WebGlPendingProbe { - fn drop(&mut self) { - if let Some(sync) = self.sync.take() { - self.gl.delete_sync(Some(&sync)); - } - if let Some(buffer) = self.buffer.take() { - self.gl.delete_buffer(Some(&buffer)); - } - } -} - impl WebGlRenderer { /// Creates a new WebGL2 renderer pub fn new(canvas: &HtmlCanvasElement) -> Self { @@ -488,131 +378,6 @@ result } - /// Conduct a probing operation. - /// - /// The WebGL drivers of certain devices are known to be buggy and might therefore not work correctly - /// with Vello Hybrid. In the best case, it will simply result in a program crash, but in the worst - /// case it can instead result in a silent failure, meaning that no explicit error is - /// thrown, but the rendered contents of Vello Hybrid will either be completely empty or look glitchy. - /// - /// The purpose of this method is to run a sanity check to ensure that running Vello Hybrid on this - /// device actually results in visible and correct output. How this achieved is by drawing a selection - /// of small elements into a small canvas, and comparing the final output against a reference image. - /// - /// This method will return a handle that allows inspecting the results of the probe once the - /// results of the probe scene can be copied back from GPU to CPU. For performance reasons, - /// anything in-between mostly happens asynchronously. - #[cfg(feature = "probe")] - pub fn probe(&mut self) -> Result<WebGlPendingProbe, WebGlProbeError> { - self.probe_inner().map_err(WebGlProbeError::Render) - } - - #[cfg(feature = "probe")] - fn probe_inner(&mut self) -> Result<WebGlPendingProbe, RenderError> { - // IMPORTANT NOTE: When making any changes to the probe, make sure to - // unignore and rerun the "webgl_probe_succeeds" test locally. - - let _state_guard = WebGlStateGuard::with_config( - &self.gl, - WebGlStateConfig { - framebuffer: true, - active_texture: true, - texture_2d: true, - texture_2d_array: true, - pixel_pack_buffer: true, - viewport: true, - ..Default::default() - }, - ); - - let (width, height) = vello_common::probe::canvas_size(); - let render_size = RenderSize { - width: u32::from(width), - height: u32::from(height), - }; - - let probe_texture = create_texture(&self.gl); - self.gl - .tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_array_buffer_view( - WebGl2RenderingContext::TEXTURE_2D, - 0, - WebGl2RenderingContext::RGBA8 as i32, - render_size.width as i32, - render_size.height as i32, - 0, - WebGl2RenderingContext::RGBA, - WebGl2RenderingContext::UNSIGNED_BYTE, - None, - ) - .unwrap(); - let probe_framebuffer = create_framebuffer_for_texture(&self.gl, &probe_texture); - - let atlas_config = AtlasConfig { - initial_atlas_count: 1, - // These should be large enough for the probe scene. - atlas_size: (256, 256), - max_atlases: 1, - auto_grow: true, - allocation_strategy: AllocationStrategy::FirstFit, - }; - let (atlas_width, atlas_height) = atlas_config.atlas_size; - - let mut probe_image_cache = ImageCache::new_with_config(atlas_config); - let mut probe_atlas_texture_array = - create_atlas_texture_array(&self.gl, atlas_width, atlas_height, 1); - core::mem::swap( - &mut self.programs.resources.atlas_texture_array, - &mut probe_atlas_texture_array, - ); - - let probe_image = Arc::new(vello_common::probe::probe_image_pixmap()); - // Note: No need to destroy the image explicitly in the end, because we discard the image - // cache anyway. - let probe_image_id = - self.upload_image_with(&mut probe_image_cache, &probe_image, IMAGE_PADDING); - let mut scene = Scene::new(width, height); - vello_common::probe::draw_scene( - &mut scene, - ImageSource::opaque_id_with_transparency_hint( - probe_image_id, - probe_image.may_have_transparency(), - ), - ); - - let previous_view_framebuffer = self - .programs - .resources - .view_framebuffer_override - .replace(probe_framebuffer); - let render_result = self.render_scene( - &scene, - &mut probe_image_cache, - &render_size, - true, - RootRenderTarget::AtlasLayer, - ); - let probe_framebuffer = self - .programs - .resources - .view_framebuffer_override - .take() - .expect("probe framebuffer must be restored after rendering"); - self.programs.resources.view_framebuffer_override = previous_view_framebuffer; - - core::mem::swap( - &mut self.programs.resources.atlas_texture_array, - &mut probe_atlas_texture_array, - ); - - // We do this here instead of above such that in case the render result is not - // valid, we still properly restore the state (e.g. the old atlas texture array). - render_result?; - - let pending = launch_probe(&self.gl, &probe_framebuffer, width, height); - - Ok(pending) - } - /// Shared render pipeline: prepares GPU resources, runs the scheduler, and /// maintains caches. /// @@ -620,7 +385,7 @@ /// before drawing. This must happen *after* `prepare` (which may create/resize /// the framebuffer attachment). Atlas renders skip the clear so previously /// rendered atlas content is preserved. - fn render_scene( + pub(crate) fn render_scene( &mut self, scene: &Scene, image_cache: &mut ImageCache, @@ -1018,7 +783,7 @@ /// Contains the WebGL programs and resources for rendering. #[derive(Debug)] -struct WebGlPrograms { +pub(crate) struct WebGlPrograms { /// Program for rendering wide tile commands. strip_program: Program, /// Uniform locations for the strip program @@ -1032,7 +797,7 @@ /// Uniform locations for the filter program. filter_uniforms: FilterPassUniforms, /// WebGL resources for rendering. - resources: WebGlResources, + pub(crate) resources: WebGlResources, /// Dimensions of the rendering target. render_size: RenderSize, /// Whether the last config buffer upload had NDC Y negation enabled. @@ -1082,7 +847,7 @@ /// Contains all WebGL resources needed for rendering. #[derive(Debug)] -struct WebGlResources { +pub(crate) struct WebGlResources { /// VAO for strip rendering. strip_vao: VertexArray, /// Buffer for [`GpuStrip`] data. @@ -1092,7 +857,7 @@ /// Height of alpha texture. alpha_texture_height: u32, /// Texture array for atlas data (multiple atlases supported) - atlas_texture_array: WebGlTextureArray, + pub(crate) atlas_texture_array: WebGlTextureArray, /// Encoded paints texture for image metadata. encoded_paints_texture: Texture, /// Height of encoded paints texture. @@ -1116,7 +881,7 @@ /// Config buffer for clear program. clear_config_buffer: Buffer, - view_framebuffer_override: Option<Framebuffer>, + pub(crate) view_framebuffer_override: Option<Framebuffer>, /// Whether the depth buffer has been cleared this frame. depth_cleared_this_frame: bool, /// Pre-allocated JS array for `invalidateFramebuffer` calls. @@ -1739,7 +1504,7 @@ /// RAII guard for WebGL state management. /// Automatically saves state on creation and restores it on drop. /// Only saves/restores the state specified in the configuration. -struct WebGlStateGuard { +pub(crate) struct WebGlStateGuard { gl: WebGl2RenderingContext, config: WebGlStateConfig, original_framebuffer: Option<WebGlFramebuffer>, @@ -1754,7 +1519,7 @@ impl WebGlStateGuard { /// Create a new state guard with custom configuration. - fn with_config(gl: &WebGl2RenderingContext, config: WebGlStateConfig) -> Self { + pub(crate) fn with_config(gl: &WebGl2RenderingContext, config: WebGlStateConfig) -> Self { // Save current framebuffer binding if requested let original_framebuffer = if config.framebuffer { gl.get_parameter(WebGl2RenderingContext::FRAMEBUFFER_BINDING) @@ -1946,76 +1711,23 @@ } /// Configuration for which WebGL state to save/restore. #[derive(Debug, Default)] -struct WebGlStateConfig { +pub(crate) struct WebGlStateConfig { /// Save/restore framebuffer binding (`FRAMEBUFFER_BINDING`) - framebuffer: bool, + pub(crate) framebuffer: bool, /// Save/restore read framebuffer binding (`READ_FRAMEBUFFER_BINDING`) - read_framebuffer: bool, + pub(crate) read_framebuffer: bool, /// Save/restore active texture unit (`ACTIVE_TEXTURE`) - active_texture: bool, + pub(crate) active_texture: bool, /// Save/restore 2D texture binding (`TEXTURE_BINDING_2D`) - texture_2d: bool, + pub(crate) texture_2d: bool, /// Save/restore 2D array texture binding (`TEXTURE_BINDING_2D_ARRAY`) - texture_2d_array: bool, + pub(crate) texture_2d_array: bool, /// Save/restore pixel pack buffer binding (`PIXEL_PACK_BUFFER_BINDING`) - pixel_pack_buffer: bool, + pub(crate) pixel_pack_buffer: bool, /// Save/restore scissor test state - scissor: bool, + pub(crate) scissor: bool, /// Save/restore viewport - viewport: bool, -} - -#[cfg(feature = "probe")] -fn launch_probe( - gl: &WebGl2RenderingContext, - framebuffer: &Framebuffer, - width: u16, - height: u16, -) -> WebGlPendingProbe { - let pixel_pack_buffer = gl.create_buffer().unwrap(); - let byte_len = i32::from(width) * i32::from(height) * 4; - - gl.bind_buffer( - WebGl2RenderingContext::PIXEL_PACK_BUFFER, - Some(&pixel_pack_buffer), - ); - gl.buffer_data_with_i32( - WebGl2RenderingContext::PIXEL_PACK_BUFFER, - byte_len, - WebGl2RenderingContext::STREAM_READ, - ); - gl.bind_framebuffer( - WebGl2RenderingContext::FRAMEBUFFER, - Some(framebuffer.deref()), - ); - gl.read_pixels_with_i32( - 0, - 0, - i32::from(width), - i32::from(height), - WebGl2RenderingContext::RGBA, - WebGl2RenderingContext::UNSIGNED_BYTE, - 0, - ) - .unwrap(); - // Create a fence that notifies us once rendering is complete and the contents have been - // transferred from the framebuffer to the pixel pack buffer. - let sync = gl - .fence_sync(WebGl2RenderingContext::SYNC_GPU_COMMANDS_COMPLETE, 0) - .unwrap(); - // https://wikis.khronos.org/opengl/Sync_Object - // "It is important that syncs are properly flushed into the GPU's command queue. Without - // proper flushing, the sync object may never be signaled." - gl.flush(); - gl.bind_buffer(WebGl2RenderingContext::PIXEL_PACK_BUFFER, None); - - WebGlPendingProbe { - gl: gl.clone(), - sync: Some(sync), - buffer: Some(pixel_pack_buffer), - width, - height, - } + pub(crate) viewport: bool, } /// Create a WebGL shader program from vertex and fragment sources. @@ -2247,7 +1959,7 @@ } /// Create a texture with nearest neighbor sampling and clamp-to-edge wrapping. -fn create_texture(gl: &WebGl2RenderingContext) -> Texture { +pub(crate) fn create_texture(gl: &WebGl2RenderingContext) -> Texture { create_texture_inner(gl, WebGl2RenderingContext::TEXTURE_2D) } @@ -2411,7 +2123,7 @@ } /// Create an atlas texture array. -fn create_atlas_texture_array( +pub(crate) fn create_atlas_texture_array( gl: &WebGl2RenderingContext, width: u32, height: u32, @@ -2458,7 +2170,7 @@ } /// Create a framebuffer for a texture. -fn create_framebuffer_for_texture( +pub(crate) fn create_framebuffer_for_texture( gl: &WebGl2RenderingContext, texture: &WebGlTexture, ) -> Framebuffer { @@ -3227,7 +2939,7 @@ /// Wrapper for `WebGlTexture` array with known dimensions. #[derive(Debug)] -struct WebGlTextureArray { +pub(crate) struct WebGlTextureArray { /// The WebGL texture array. texture: Texture, /// The size of the texture array. @@ -3351,7 +3063,7 @@ .unwrap(); } -mod resource { +pub(crate) mod resource { use core::ops::Deref; use super::{ @@ -3359,7 +3071,7 @@ WebGlTexture, WebGlVertexArrayObject, }; - pub(super) trait GlResource { + pub(crate) trait GlResource { const LABEL: &'static str; fn create(gl: &WebGl2RenderingContext) -> Option<Self> @@ -3370,7 +3082,7 @@ } #[derive(Debug)] - pub(super) struct Resource<T: GlResource> { + pub(crate) struct Resource<T: GlResource> { gl: WebGl2RenderingContext, raw: T, } @@ -3405,7 +3117,7 @@ } #[derive(Debug)] - pub(super) struct WebGlVertexShader(WebGlShader); + pub(crate) struct WebGlVertexShader(WebGlShader); impl Deref for WebGlVertexShader { type Target = WebGlShader; @@ -3416,7 +3128,7 @@ } #[derive(Debug)] - pub(super) struct WebGlFragmentShader(WebGlShader); + pub(crate) struct WebGlFragmentShader(WebGlShader); impl Deref for WebGlFragmentShader { type Target = WebGlShader; @@ -3512,11 +3224,11 @@ } } - pub(super) type Texture = Resource<WebGlTexture>; - pub(super) type Buffer = Resource<WebGlBuffer>; - pub(super) type Framebuffer = Resource<WebGlFramebuffer>; - pub(super) type Program = Resource<WebGlProgram>; - pub(super) type VertexShader = Resource<WebGlVertexShader>; - pub(super) type FragmentShader = Resource<WebGlFragmentShader>; - pub(super) type VertexArray = Resource<WebGlVertexArrayObject>; + pub(crate) type Texture = Resource<WebGlTexture>; + pub(crate) type Buffer = Resource<WebGlBuffer>; + pub(crate) type Framebuffer = Resource<WebGlFramebuffer>; + pub(crate) type Program = Resource<WebGlProgram>; + pub(crate) type VertexShader = Resource<WebGlVertexShader>; + pub(crate) type FragmentShader = Resource<WebGlFragmentShader>; + pub(crate) type VertexArray = Resource<WebGlVertexArrayObject>; }