mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Auto merge of #21683 - servo:webgl, r=jdm
Properly support PACK_ALIGNMENT in WebGL 1 <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21683) <!-- Reviewable:end -->
This commit is contained in:
commit
60b926ade4
16 changed files with 560 additions and 2847 deletions
|
@ -3,7 +3,6 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use ::gl_context::GLContextFactory;
|
use ::gl_context::GLContextFactory;
|
||||||
use ::webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThreadObserver, WebGLThread};
|
|
||||||
use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver};
|
use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver};
|
||||||
use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler};
|
use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler};
|
||||||
use canvas_traits::webgl::DOMToTextureCommand;
|
use canvas_traits::webgl::DOMToTextureCommand;
|
||||||
|
@ -12,8 +11,8 @@ use euclid::Size2D;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
use servo_config::prefs::PREFS;
|
use servo_config::prefs::PREFS;
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThread};
|
||||||
use webrender;
|
use webrender;
|
||||||
use webrender_api;
|
use webrender_api;
|
||||||
|
|
||||||
|
@ -37,7 +36,6 @@ impl WebGLThreads {
|
||||||
gl_factory,
|
gl_factory,
|
||||||
webrender_api_sender,
|
webrender_api_sender,
|
||||||
webvr_compositor.map(|c| WebVRRenderWrapper(c)),
|
webvr_compositor.map(|c| WebVRRenderWrapper(c)),
|
||||||
PhantomData,
|
|
||||||
);
|
);
|
||||||
let output_handler = if PREFS.is_dom_to_texture_enabled() {
|
let output_handler = if PREFS.is_dom_to_texture_enabled() {
|
||||||
Some(Box::new(OutputHandler::new(
|
Some(Box::new(OutputHandler::new(
|
||||||
|
@ -112,27 +110,6 @@ impl WebGLExternalImageApi for WebGLExternalImages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Custom observer used in a `WebGLThread`.
|
|
||||||
impl WebGLThreadObserver for PhantomData<()> {
|
|
||||||
fn on_context_create(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>) {
|
|
||||||
debug!(
|
|
||||||
"WebGLContext created (ctx_id: {:?} texture_id: {:?} size: {:?}",
|
|
||||||
ctx_id, texture_id, size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_context_resize(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>) {
|
|
||||||
debug!(
|
|
||||||
"WebGLContext resized (ctx_id: {:?} texture_id: {:?} size: {:?}",
|
|
||||||
ctx_id, texture_id, size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_context_delete(&mut self, ctx_id: WebGLContextId) {
|
|
||||||
debug!("WebGLContext deleted (ctx_id: {:?})", ctx_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper to send WebVR commands used in `WebGLThread`.
|
/// Wrapper to send WebVR commands used in `WebGLThread`.
|
||||||
struct WebVRRenderWrapper(Box<WebVRRenderHandler>);
|
struct WebVRRenderWrapper(Box<WebVRRenderHandler>);
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl Default for GLState {
|
||||||
|
|
||||||
/// A WebGLThread manages the life cycle and message multiplexing of
|
/// A WebGLThread manages the life cycle and message multiplexing of
|
||||||
/// a set of WebGLContexts living in the same thread.
|
/// a set of WebGLContexts living in the same thread.
|
||||||
pub struct WebGLThread<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> {
|
pub struct WebGLThread<VR: WebVRRenderHandler + 'static> {
|
||||||
/// Factory used to create a new GLContext shared with the WR/Main thread.
|
/// Factory used to create a new GLContext shared with the WR/Main thread.
|
||||||
gl_factory: GLContextFactory,
|
gl_factory: GLContextFactory,
|
||||||
/// Channel used to generate/update or delete `webrender_api::ImageKey`s.
|
/// Channel used to generate/update or delete `webrender_api::ImageKey`s.
|
||||||
|
@ -62,17 +62,16 @@ pub struct WebGLThread<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver
|
||||||
next_webgl_id: usize,
|
next_webgl_id: usize,
|
||||||
/// Handler user to send WebVR commands.
|
/// Handler user to send WebVR commands.
|
||||||
webvr_compositor: Option<VR>,
|
webvr_compositor: Option<VR>,
|
||||||
/// Generic observer that listens WebGLContext creation, resize or removal events.
|
|
||||||
observer: OB,
|
|
||||||
/// Texture ids and sizes used in DOM to texture outputs.
|
/// Texture ids and sizes used in DOM to texture outputs.
|
||||||
dom_outputs: FnvHashMap<webrender_api::PipelineId, DOMToTextureData>,
|
dom_outputs: FnvHashMap<webrender_api::PipelineId, DOMToTextureData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR, OB> {
|
impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
|
||||||
pub fn new(gl_factory: GLContextFactory,
|
pub fn new(
|
||||||
webrender_api_sender: webrender_api::RenderApiSender,
|
gl_factory: GLContextFactory,
|
||||||
webvr_compositor: Option<VR>,
|
webrender_api_sender: webrender_api::RenderApiSender,
|
||||||
observer: OB) -> Self {
|
webvr_compositor: Option<VR>,
|
||||||
|
) -> Self {
|
||||||
WebGLThread {
|
WebGLThread {
|
||||||
gl_factory,
|
gl_factory,
|
||||||
webrender_api: webrender_api_sender.create_api(),
|
webrender_api: webrender_api_sender.create_api(),
|
||||||
|
@ -81,25 +80,25 @@ impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR,
|
||||||
bound_context_id: None,
|
bound_context_id: None,
|
||||||
next_webgl_id: 0,
|
next_webgl_id: 0,
|
||||||
webvr_compositor,
|
webvr_compositor,
|
||||||
observer: observer,
|
|
||||||
dom_outputs: Default::default(),
|
dom_outputs: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `WebGLThread` and returns a Sender to
|
/// Creates a new `WebGLThread` and returns a Sender to
|
||||||
/// communicate with it.
|
/// communicate with it.
|
||||||
pub fn start(gl_factory: GLContextFactory,
|
pub fn start(
|
||||||
webrender_api_sender: webrender_api::RenderApiSender,
|
gl_factory: GLContextFactory,
|
||||||
webvr_compositor: Option<VR>,
|
webrender_api_sender: webrender_api::RenderApiSender,
|
||||||
observer: OB)
|
webvr_compositor: Option<VR>,
|
||||||
-> WebGLSender<WebGLMsg> {
|
) -> WebGLSender<WebGLMsg> {
|
||||||
let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap();
|
let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap();
|
||||||
let result = sender.clone();
|
let result = sender.clone();
|
||||||
thread::Builder::new().name("WebGLThread".to_owned()).spawn(move || {
|
thread::Builder::new().name("WebGLThread".to_owned()).spawn(move || {
|
||||||
let mut renderer = WebGLThread::new(gl_factory,
|
let mut renderer = WebGLThread::new(
|
||||||
webrender_api_sender,
|
gl_factory,
|
||||||
webvr_compositor,
|
webrender_api_sender,
|
||||||
observer);
|
webvr_compositor,
|
||||||
|
);
|
||||||
let webgl_chan = WebGLChan(sender);
|
let webgl_chan = WebGLChan(sender);
|
||||||
loop {
|
loop {
|
||||||
let msg = receiver.recv().unwrap();
|
let msg = receiver.recv().unwrap();
|
||||||
|
@ -248,8 +247,6 @@ impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR,
|
||||||
gl_sync: None,
|
gl_sync: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.observer.on_context_create(id, texture_id, size);
|
|
||||||
|
|
||||||
Ok((id, limits, share_mode))
|
Ok((id, limits, share_mode))
|
||||||
},
|
},
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
|
@ -271,8 +268,6 @@ impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR,
|
||||||
match data.ctx.resize(size) {
|
match data.ctx.resize(size) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let (real_size, texture_id, _) = data.ctx.get_info();
|
let (real_size, texture_id, _) = data.ctx.get_info();
|
||||||
self.observer.on_context_resize(context_id, texture_id, real_size);
|
|
||||||
|
|
||||||
let info = self.cached_context_info.get_mut(&context_id).unwrap();
|
let info = self.cached_context_info.get_mut(&context_id).unwrap();
|
||||||
// Update webgl texture size. Texture id may change too.
|
// Update webgl texture size. Texture id may change too.
|
||||||
info.texture_id = texture_id;
|
info.texture_id = texture_id;
|
||||||
|
@ -313,9 +308,7 @@ impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release GL context.
|
// Release GL context.
|
||||||
if self.contexts.remove(&context_id).is_some() {
|
self.contexts.remove(&context_id);
|
||||||
self.observer.on_context_delete(context_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing a GLContext may make the current bound context_id dirty.
|
// Removing a GLContext may make the current bound context_id dirty.
|
||||||
self.bound_context_id = None;
|
self.bound_context_id = None;
|
||||||
|
@ -581,7 +574,7 @@ impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> Drop for WebGLThread<VR, OB> {
|
impl<VR: WebVRRenderHandler + 'static> Drop for WebGLThread<VR> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Call remove_context functions in order to correctly delete WebRender image keys.
|
// Call remove_context functions in order to correctly delete WebRender image keys.
|
||||||
let context_ids: Vec<WebGLContextId> = self.contexts.keys().map(|id| *id).collect();
|
let context_ids: Vec<WebGLContextId> = self.contexts.keys().map(|id| *id).collect();
|
||||||
|
@ -607,14 +600,6 @@ struct WebGLContextInfo {
|
||||||
gl_sync: Option<gl::GLsync>,
|
gl_sync: Option<gl::GLsync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait used to observe events in a WebGL Thread.
|
|
||||||
/// Used in webrender::ExternalImageHandler when multiple WebGL threads are used.
|
|
||||||
pub trait WebGLThreadObserver: Send + 'static {
|
|
||||||
fn on_context_create(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>);
|
|
||||||
fn on_context_resize(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>);
|
|
||||||
fn on_context_delete(&mut self, ctx_id: WebGLContextId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This trait is used as a bridge between the `WebGLThreads` implementation and
|
/// This trait is used as a bridge between the `WebGLThreads` implementation and
|
||||||
/// the WR ExternalImageHandler API implemented in the `WebGLExternalImageHandler` struct.
|
/// the WR ExternalImageHandler API implemented in the `WebGLExternalImageHandler` struct.
|
||||||
/// `WebGLExternalImageHandler<T>` takes care of type conversions between WR and WebGL info (e.g keys, uvs).
|
/// `WebGLExternalImageHandler<T>` takes care of type conversions between WR and WebGL info (e.g keys, uvs).
|
||||||
|
|
|
@ -533,7 +533,6 @@ parameters! {
|
||||||
FrontFace = gl::FRONT_FACE,
|
FrontFace = gl::FRONT_FACE,
|
||||||
GenerateMipmapHint = gl::GENERATE_MIPMAP_HINT,
|
GenerateMipmapHint = gl::GENERATE_MIPMAP_HINT,
|
||||||
GreenBits = gl::GREEN_BITS,
|
GreenBits = gl::GREEN_BITS,
|
||||||
PackAlignment = gl::PACK_ALIGNMENT,
|
|
||||||
RedBits = gl::RED_BITS,
|
RedBits = gl::RED_BITS,
|
||||||
SampleBuffers = gl::SAMPLE_BUFFERS,
|
SampleBuffers = gl::SAMPLE_BUFFERS,
|
||||||
Samples = gl::SAMPLES,
|
Samples = gl::SAMPLES,
|
||||||
|
@ -554,7 +553,6 @@ parameters! {
|
||||||
StencilValueMask = gl::STENCIL_VALUE_MASK,
|
StencilValueMask = gl::STENCIL_VALUE_MASK,
|
||||||
StencilWritemask = gl::STENCIL_WRITEMASK,
|
StencilWritemask = gl::STENCIL_WRITEMASK,
|
||||||
SubpixelBits = gl::SUBPIXEL_BITS,
|
SubpixelBits = gl::SUBPIXEL_BITS,
|
||||||
UnpackAlignment = gl::UNPACK_ALIGNMENT,
|
|
||||||
}),
|
}),
|
||||||
Int2(ParameterInt2 {
|
Int2(ParameterInt2 {
|
||||||
MaxViewportDims = gl::MAX_VIEWPORT_DIMS,
|
MaxViewportDims = gl::MAX_VIEWPORT_DIMS,
|
||||||
|
|
|
@ -149,7 +149,9 @@ pub struct WebGLRenderingContext {
|
||||||
canvas: Dom<HTMLCanvasElement>,
|
canvas: Dom<HTMLCanvasElement>,
|
||||||
#[ignore_malloc_size_of = "Defined in canvas_traits"]
|
#[ignore_malloc_size_of = "Defined in canvas_traits"]
|
||||||
last_error: Cell<Option<WebGLError>>,
|
last_error: Cell<Option<WebGLError>>,
|
||||||
|
texture_packing_alignment: Cell<u8>,
|
||||||
texture_unpacking_settings: Cell<TextureUnpacking>,
|
texture_unpacking_settings: Cell<TextureUnpacking>,
|
||||||
|
// TODO(nox): Should be Cell<u8>.
|
||||||
texture_unpacking_alignment: Cell<u32>,
|
texture_unpacking_alignment: Cell<u32>,
|
||||||
bound_framebuffer: MutNullableDom<WebGLFramebuffer>,
|
bound_framebuffer: MutNullableDom<WebGLFramebuffer>,
|
||||||
bound_renderbuffer: MutNullableDom<WebGLRenderbuffer>,
|
bound_renderbuffer: MutNullableDom<WebGLRenderbuffer>,
|
||||||
|
@ -193,7 +195,7 @@ impl WebGLRenderingContext {
|
||||||
|
|
||||||
result.map(|ctx_data| {
|
result.map(|ctx_data| {
|
||||||
let max_combined_texture_image_units = ctx_data.limits.max_combined_texture_image_units;
|
let max_combined_texture_image_units = ctx_data.limits.max_combined_texture_image_units;
|
||||||
WebGLRenderingContext {
|
Self {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
webgl_sender: ctx_data.sender,
|
webgl_sender: ctx_data.sender,
|
||||||
webrender_image: Cell::new(None),
|
webrender_image: Cell::new(None),
|
||||||
|
@ -203,6 +205,7 @@ impl WebGLRenderingContext {
|
||||||
limits: ctx_data.limits,
|
limits: ctx_data.limits,
|
||||||
canvas: Dom::from_ref(canvas),
|
canvas: Dom::from_ref(canvas),
|
||||||
last_error: Cell::new(None),
|
last_error: Cell::new(None),
|
||||||
|
texture_packing_alignment: Cell::new(4),
|
||||||
texture_unpacking_settings: Cell::new(TextureUnpacking::CONVERT_COLORSPACE),
|
texture_unpacking_settings: Cell::new(TextureUnpacking::CONVERT_COLORSPACE),
|
||||||
texture_unpacking_alignment: Cell::new(4),
|
texture_unpacking_alignment: Cell::new(4),
|
||||||
bound_framebuffer: MutNullableDom::new(None),
|
bound_framebuffer: MutNullableDom::new(None),
|
||||||
|
@ -1223,6 +1226,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
let unpack = self.texture_unpacking_settings.get();
|
let unpack = self.texture_unpacking_settings.get();
|
||||||
return BooleanValue(unpack.contains(TextureUnpacking::PREMULTIPLY_ALPHA));
|
return BooleanValue(unpack.contains(TextureUnpacking::PREMULTIPLY_ALPHA));
|
||||||
}
|
}
|
||||||
|
constants::PACK_ALIGNMENT => {
|
||||||
|
return UInt32Value(self.texture_packing_alignment.get() as u32);
|
||||||
|
},
|
||||||
|
constants::UNPACK_ALIGNMENT => {
|
||||||
|
return UInt32Value(self.texture_unpacking_alignment.get());
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2517,39 +2526,20 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
let mut texture_settings = self.texture_unpacking_settings.get();
|
let mut texture_settings = self.texture_unpacking_settings.get();
|
||||||
match param_name {
|
match param_name {
|
||||||
constants::UNPACK_FLIP_Y_WEBGL => {
|
constants::UNPACK_FLIP_Y_WEBGL => {
|
||||||
if param_value != 0 {
|
texture_settings.set(TextureUnpacking::FLIP_Y_AXIS, param_value != 0);
|
||||||
texture_settings.insert(TextureUnpacking::FLIP_Y_AXIS)
|
|
||||||
} else {
|
|
||||||
texture_settings.remove(TextureUnpacking::FLIP_Y_AXIS)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.texture_unpacking_settings.set(texture_settings);
|
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
constants::UNPACK_PREMULTIPLY_ALPHA_WEBGL => {
|
constants::UNPACK_PREMULTIPLY_ALPHA_WEBGL => {
|
||||||
if param_value != 0 {
|
texture_settings.set(TextureUnpacking::PREMULTIPLY_ALPHA, param_value != 0);
|
||||||
texture_settings.insert(TextureUnpacking::PREMULTIPLY_ALPHA)
|
|
||||||
} else {
|
|
||||||
texture_settings.remove(TextureUnpacking::PREMULTIPLY_ALPHA)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.texture_unpacking_settings.set(texture_settings);
|
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
|
constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
|
||||||
match param_value as u32 {
|
let convert = match param_value as u32 {
|
||||||
constants::BROWSER_DEFAULT_WEBGL
|
constants::BROWSER_DEFAULT_WEBGL => true,
|
||||||
=> texture_settings.insert(TextureUnpacking::CONVERT_COLORSPACE),
|
constants::NONE => false,
|
||||||
constants::NONE
|
|
||||||
=> texture_settings.remove(TextureUnpacking::CONVERT_COLORSPACE),
|
|
||||||
_ => return self.webgl_error(InvalidEnum),
|
_ => return self.webgl_error(InvalidEnum),
|
||||||
}
|
};
|
||||||
|
texture_settings.set(TextureUnpacking::CONVERT_COLORSPACE, convert);
|
||||||
self.texture_unpacking_settings.set(texture_settings);
|
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
constants::UNPACK_ALIGNMENT |
|
constants::UNPACK_ALIGNMENT => {
|
||||||
constants::PACK_ALIGNMENT => {
|
|
||||||
match param_value {
|
match param_value {
|
||||||
1 | 2 | 4 | 8 => (),
|
1 | 2 | 4 | 8 => (),
|
||||||
_ => return self.webgl_error(InvalidValue),
|
_ => return self.webgl_error(InvalidValue),
|
||||||
|
@ -2557,8 +2547,20 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
self.texture_unpacking_alignment.set(param_value as u32);
|
self.texture_unpacking_alignment.set(param_value as u32);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
constants::PACK_ALIGNMENT => {
|
||||||
|
match param_value {
|
||||||
|
1 | 2 | 4 | 8 => (),
|
||||||
|
_ => return self.webgl_error(InvalidValue),
|
||||||
|
}
|
||||||
|
// We never actually change the actual value on the GL side
|
||||||
|
// because it's better to receive the pixels without the padding
|
||||||
|
// and then write the result at the right place in ReadPixels.
|
||||||
|
self.texture_packing_alignment.set(param_value as u8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_ => return self.webgl_error(InvalidEnum),
|
_ => return self.webgl_error(InvalidEnum),
|
||||||
}
|
}
|
||||||
|
self.texture_unpacking_settings.set(texture_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
|
||||||
|
@ -2570,105 +2572,115 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn ReadPixels(&self, x: i32, y: i32, width: i32, height: i32, format: u32, pixel_type: u32,
|
fn ReadPixels(&self, x: i32, y: i32, width: i32, height: i32, format: u32, pixel_type: u32,
|
||||||
mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>) {
|
mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>) {
|
||||||
let (array_type, data) = match *pixels {
|
let pixels = handle_potential_webgl_error!(
|
||||||
// Spec: If data is null then an INVALID_VALUE error is generated.
|
self,
|
||||||
None => return self.webgl_error(InvalidValue),
|
pixels.as_mut().ok_or(InvalidValue),
|
||||||
// The typed array is rooted and we should have a unique reference to it,
|
return
|
||||||
// so retrieving its mutable slice is safe here
|
);
|
||||||
Some(ref mut data) => (data.get_array_type(), unsafe { data.as_mut_slice() }),
|
|
||||||
};
|
|
||||||
|
|
||||||
handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
|
|
||||||
|
|
||||||
match array_type {
|
|
||||||
Type::Uint8 => (),
|
|
||||||
_ => return self.webgl_error(InvalidOperation),
|
|
||||||
}
|
|
||||||
|
|
||||||
// From the WebGL specification, 5.14.12 Reading back pixels
|
|
||||||
//
|
|
||||||
// "Only two combinations of format and type are
|
|
||||||
// accepted. The first is format RGBA and type
|
|
||||||
// UNSIGNED_BYTE. The second is an implementation-chosen
|
|
||||||
// format. The values of format and type for this format
|
|
||||||
// may be determined by calling getParameter with the
|
|
||||||
// symbolic constants IMPLEMENTATION_COLOR_READ_FORMAT
|
|
||||||
// and IMPLEMENTATION_COLOR_READ_TYPE, respectively. The
|
|
||||||
// implementation-chosen format may vary depending on the
|
|
||||||
// format of the currently bound rendering
|
|
||||||
// surface. Unsupported combinations of format and type
|
|
||||||
// will generate an INVALID_OPERATION error."
|
|
||||||
//
|
|
||||||
// To avoid having to support general format packing math, we
|
|
||||||
// always report RGBA/UNSIGNED_BYTE as our only supported
|
|
||||||
// format.
|
|
||||||
if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
|
|
||||||
return self.webgl_error(InvalidOperation);
|
|
||||||
}
|
|
||||||
let cpp = 4;
|
|
||||||
|
|
||||||
// "If pixels is non-null, but is not large enough to
|
|
||||||
// retrieve all of the pixels in the specified rectangle
|
|
||||||
// taking into account pixel store modes, an
|
|
||||||
// INVALID_OPERATION error is generated."
|
|
||||||
let stride = match width.checked_mul(cpp) {
|
|
||||||
Some(stride) => stride,
|
|
||||||
_ => return self.webgl_error(InvalidOperation),
|
|
||||||
};
|
|
||||||
|
|
||||||
match height.checked_mul(stride) {
|
|
||||||
Some(size) if size <= data.len() as i32 => {}
|
|
||||||
_ => return self.webgl_error(InvalidOperation),
|
|
||||||
}
|
|
||||||
|
|
||||||
// "For any pixel lying outside the frame buffer, the
|
|
||||||
// corresponding destination buffer range remains
|
|
||||||
// untouched; see Reading Pixels Outside the
|
|
||||||
// Framebuffer."
|
|
||||||
let mut x = x;
|
|
||||||
let mut y = y;
|
|
||||||
let mut width = width;
|
|
||||||
let mut height = height;
|
|
||||||
let mut dst_offset = 0;
|
|
||||||
|
|
||||||
if x < 0 {
|
|
||||||
dst_offset += cpp * -x;
|
|
||||||
width += x;
|
|
||||||
x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if y < 0 {
|
|
||||||
dst_offset += stride * -y;
|
|
||||||
height += y;
|
|
||||||
y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if width < 0 || height < 0 {
|
if width < 0 || height < 0 {
|
||||||
return self.webgl_error(InvalidValue);
|
return self.webgl_error(InvalidValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.get_current_framebuffer_size() {
|
if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
|
||||||
Some((fb_width, fb_height)) => {
|
return self.webgl_error(InvalidOperation);
|
||||||
if x + width > fb_width {
|
}
|
||||||
width = fb_width - x;
|
|
||||||
}
|
if pixels.get_array_type() != Type::Uint8 {
|
||||||
if y + height > fb_height {
|
return self.webgl_error(InvalidOperation);
|
||||||
height = fb_height - y;
|
}
|
||||||
}
|
|
||||||
}
|
handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
|
||||||
_ => return self.webgl_error(InvalidOperation),
|
let (fb_width, fb_height) = handle_potential_webgl_error!(
|
||||||
|
self,
|
||||||
|
self.get_current_framebuffer_size().ok_or(InvalidOperation),
|
||||||
|
return
|
||||||
|
);
|
||||||
|
|
||||||
|
if width == 0 || height == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes_per_pixel = 4;
|
||||||
|
|
||||||
|
let row_len = handle_potential_webgl_error!(
|
||||||
|
self,
|
||||||
|
width.checked_mul(bytes_per_pixel).ok_or(InvalidOperation),
|
||||||
|
return
|
||||||
|
);
|
||||||
|
|
||||||
|
let pack_alignment = self.texture_packing_alignment.get() as i32;
|
||||||
|
let dest_padding = match row_len % pack_alignment {
|
||||||
|
0 => 0,
|
||||||
|
remainder => pack_alignment - remainder,
|
||||||
};
|
};
|
||||||
|
let dest_stride = row_len + dest_padding;
|
||||||
|
|
||||||
|
let full_rows_len = handle_potential_webgl_error!(
|
||||||
|
self,
|
||||||
|
dest_stride.checked_mul(height - 1).ok_or(InvalidOperation),
|
||||||
|
return
|
||||||
|
);
|
||||||
|
let required_dest_len = handle_potential_webgl_error!(
|
||||||
|
self,
|
||||||
|
full_rows_len.checked_add(row_len).ok_or(InvalidOperation),
|
||||||
|
return
|
||||||
|
);
|
||||||
|
|
||||||
|
let dest = unsafe { pixels.as_mut_slice() };
|
||||||
|
if dest.len() < required_dest_len as usize {
|
||||||
|
return self.webgl_error(InvalidOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut src_x = x;
|
||||||
|
let mut src_y = y;
|
||||||
|
let mut src_width = width;
|
||||||
|
let mut src_height = height;
|
||||||
|
let mut dest_offset = 0;
|
||||||
|
|
||||||
|
if src_x < 0 {
|
||||||
|
if src_width <= -src_x {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dest_offset += bytes_per_pixel * -src_x;
|
||||||
|
src_width += src_x;
|
||||||
|
src_x = 0;
|
||||||
|
}
|
||||||
|
if src_y < 0 {
|
||||||
|
if src_height <= -src_y {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dest_offset += row_len * -src_y;
|
||||||
|
src_height += src_y;
|
||||||
|
src_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if src_x + src_width > fb_width {
|
||||||
|
src_width = fb_width - src_x;
|
||||||
|
}
|
||||||
|
if src_y + src_height > fb_height {
|
||||||
|
src_height = fb_height - src_y;
|
||||||
|
}
|
||||||
|
|
||||||
let (sender, receiver) = ipc::bytes_channel().unwrap();
|
let (sender, receiver) = ipc::bytes_channel().unwrap();
|
||||||
self.send_command(WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, sender));
|
self.send_command(WebGLCommand::ReadPixels(
|
||||||
|
src_x,
|
||||||
|
src_y,
|
||||||
|
src_width,
|
||||||
|
src_height,
|
||||||
|
format,
|
||||||
|
pixel_type,
|
||||||
|
sender,
|
||||||
|
));
|
||||||
|
|
||||||
let result = receiver.recv().unwrap();
|
let src = receiver.recv().unwrap();
|
||||||
|
let src_row_len = (src_width * bytes_per_pixel) as usize;
|
||||||
for i in 0..height {
|
for i in 0..src_height {
|
||||||
for j in 0..(width * cpp) {
|
let dest_start = (dest_offset + i * dest_stride) as usize;
|
||||||
data[(dst_offset + i * stride + j) as usize] =
|
let dest_end = dest_start + src_row_len;
|
||||||
result[(i * width * cpp + j) as usize];
|
let src_start = i as usize * src_row_len;
|
||||||
}
|
let src_end = src_start + src_row_len;
|
||||||
|
dest[dest_start..dest_end].copy_from_slice(&src[src_start..src_end]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
[webgl1-idlharness.any.worker.html]
|
[webgl1-idlharness.any.worker.html]
|
||||||
[webgl1-idlharness]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[WebGLRenderingContext interface: constant VERTEX_ATTRIB_ARRAY_ENABLED on interface prototype object]
|
[WebGLRenderingContext interface: constant VERTEX_ATTRIB_ARRAY_ENABLED on interface prototype object]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -2443,9 +2440,6 @@
|
||||||
|
|
||||||
|
|
||||||
[webgl1-idlharness.any.html]
|
[webgl1-idlharness.any.html]
|
||||||
[webgl1-idlharness]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[WebGLRenderingContext interface: operation isContextLost()]
|
[WebGLRenderingContext interface: operation isContextLost()]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
[webgl1-idlharness.window.html]
|
|
||||||
[webgl1-idlharness]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[WebGLRenderingContext interface: operation isContextLost()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
[webgl2-idlharness.any.html]
|
[webgl2-idlharness.any.html]
|
||||||
[webgl2-idlharness]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[WebGL2RenderingContext interface: constant DRAW_BUFFER0 on interface prototype object]
|
[WebGL2RenderingContext interface: constant DRAW_BUFFER0 on interface prototype object]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -2086,9 +2083,6 @@
|
||||||
|
|
||||||
|
|
||||||
[webgl2-idlharness.any.worker.html]
|
[webgl2-idlharness.any.worker.html]
|
||||||
[webgl2-idlharness]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[WebGL2RenderingContext interface: constant DRAW_BUFFER0 on interface prototype object]
|
[WebGL2RenderingContext interface: constant DRAW_BUFFER0 on interface prototype object]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -36364,7 +36364,7 @@
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"conformance/rendering/framebuffer-switch.html": [
|
"conformance/rendering/framebuffer-switch.html": [
|
||||||
"943f571b3b15f9794e2a9e3f89fcdadf1d49b6dd",
|
"fc8c3dc5dac8a2ece2b286325bf8f1b227f3aee6",
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"conformance/rendering/framebuffer-texture-clear.html": [
|
"conformance/rendering/framebuffer-texture-clear.html": [
|
||||||
|
@ -36372,7 +36372,7 @@
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"conformance/rendering/framebuffer-texture-switch.html": [
|
"conformance/rendering/framebuffer-texture-switch.html": [
|
||||||
"5b677f79ad9378638624dab7c001f8381eed5e87",
|
"04d03a0bc2e2f834762f0bb47e484a38323213f8",
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"conformance/rendering/gl-clear.html": [
|
"conformance/rendering/gl-clear.html": [
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
[read-pixels-pack-alignment.html]
|
|
||||||
[WebGL test #17: pixel should be 255,102,0,255. Was 0,0,0,0.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[WebGL test #33: pixel should be 255,102,0,255. Was 0,0,0,0.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[WebGL test #53: pixel should be 255,102,0,255. Was 0,0,0,0.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[WebGL test #61: pixel should be 255,102,0,255. Was 0,0,0,0.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[framebuffer-switch.html]
|
|
||||||
bug: https://github.com/servo/servo/issues/8984
|
|
||||||
expected: ERROR
|
|
|
@ -1,3 +0,0 @@
|
||||||
[framebuffer-texture-switch.html]
|
|
||||||
bug: https://github.com/servo/servo/issues/8984
|
|
||||||
expected: ERROR
|
|
|
@ -1,4 +1,5 @@
|
||||||
[out-of-bounds-index-buffers.html]
|
[out-of-bounds-index-buffers.html]
|
||||||
|
bug: https://github.com/servo/servo/issues/20599
|
||||||
[WebGL test #2: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 0,0,255,255]
|
[WebGL test #2: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 0,0,255,255]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -44,6 +44,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
description("Test framebuffer switching. The test switches between two framebuffers, copying rendering results from one to the other.");
|
description("Test framebuffer switching. The test switches between two framebuffers, copying rendering results from one to the other.");
|
||||||
var wtu = WebGLTestUtils;
|
var wtu = WebGLTestUtils;
|
||||||
|
var canvas = document.getElementById("canvas");
|
||||||
|
|
||||||
var gl = wtu.create3DContext("canvas");
|
var gl = wtu.create3DContext("canvas");
|
||||||
var program = wtu.setupTexturedQuad(gl);
|
var program = wtu.setupTexturedQuad(gl);
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
description("Test framebuffer texture attachment switching. The test uses one framebuffer object and switches its color attachment.");
|
description("Test framebuffer texture attachment switching. The test uses one framebuffer object and switches its color attachment.");
|
||||||
var wtu = WebGLTestUtils;
|
var wtu = WebGLTestUtils;
|
||||||
|
var canvas = document.getElementById("canvas");
|
||||||
|
|
||||||
var gl = wtu.create3DContext("canvas");
|
var gl = wtu.create3DContext("canvas");
|
||||||
var program = wtu.setupTexturedQuad(gl);
|
var program = wtu.setupTexturedQuad(gl);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue