Skip to content

Commit

Permalink
Add support for WebGL2 read and draw buffer settings
Browse files Browse the repository at this point in the history
  • Loading branch information
mmatyas committed Mar 16, 2020
1 parent e8e0b70 commit 0afe27e
Show file tree
Hide file tree
Showing 17 changed files with 315 additions and 51 deletions.
2 changes: 2 additions & 0 deletions components/canvas/webgl_thread.rs
Expand Up @@ -2024,6 +2024,8 @@ impl WebGLImpl {
attach(attachment)
}
},
WebGLCommand::ReadBuffer(buffer) => gl.read_buffer(buffer),
WebGLCommand::DrawBuffers(ref buffers) => gl.draw_buffers(buffers),
}

// If debug asertions are enabled, then check the error state.
Expand Down
2 changes: 2 additions & 0 deletions components/canvas_traits/webgl.rs
Expand Up @@ -546,6 +546,8 @@ pub enum WebGLCommand {
InvalidateFramebuffer(u32, Vec<u32>),
InvalidateSubFramebuffer(u32, Vec<u32>, i32, i32, i32, i32),
FramebufferTextureLayer(u32, u32, Option<WebGLTextureId>, i32, i32),
ReadBuffer(u32),
DrawBuffers(Vec<u32>),
}

macro_rules! nonzero_type {
Expand Down
69 changes: 69 additions & 0 deletions components/script/dom/webgl2renderingcontext.rs
Expand Up @@ -96,6 +96,8 @@ pub struct WebGL2RenderingContext {
texture_pack_skip_pixels: Cell<usize>,
texture_pack_skip_rows: Cell<usize>,
enable_rasterizer_discard: Cell<bool>,
default_fb_readbuffer: Cell<u32>,
default_fb_drawbuffer: Cell<u32>,
}

fn typedarray_elem_size(typeid: Type) -> usize {
Expand Down Expand Up @@ -162,6 +164,8 @@ impl WebGL2RenderingContext {
texture_pack_skip_pixels: Cell::new(0),
texture_pack_skip_rows: Cell::new(0),
enable_rasterizer_discard: Cell::new(false),
default_fb_readbuffer: Cell::new(constants::BACK),
default_fb_drawbuffer: Cell::new(constants::BACK),
})
}

Expand Down Expand Up @@ -344,6 +348,11 @@ impl WebGL2RenderingContext {
return self.base.webgl_error(InvalidOperation);
}

let fb_slot = self.base.get_draw_framebuffer_slot();
if fb_slot.get().is_none() && self.default_fb_readbuffer.get() == constants::NONE {
return self.base.webgl_error(InvalidOperation);
}

let dst_byte_offset = {
let dst_elem_size = typedarray_elem_size(dst.get_array_type());
dst_elem_offset as usize * dst_elem_size
Expand Down Expand Up @@ -800,6 +809,26 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
&self.base.get_read_framebuffer_slot().get()
);
},
constants::READ_BUFFER => {
let buffer = match self.base.get_read_framebuffer_slot().get() {
Some(fb) => fb.read_buffer(),
None => self.default_fb_readbuffer.get(),
};
return UInt32Value(buffer);
},
constants::DRAW_BUFFER0..=constants::DRAW_BUFFER15 => {
let buffer = match self.base.get_read_framebuffer_slot().get() {
Some(fb) => {
let idx = parameter - constants::DRAW_BUFFER0;
fb.draw_buffer_i(idx as usize)
},
None if parameter == constants::DRAW_BUFFER0 => {
self.default_fb_readbuffer.get()
},
None => constants::NONE,
};
return UInt32Value(buffer);
},
_ => {},
}

Expand Down Expand Up @@ -3811,6 +3840,46 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
self.base
.renderbuffer_storage(target, samples, internal_format, width, height)
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4
fn ReadBuffer(&self, src: u32) {
match src {
constants::BACK | constants::NONE => {},
_ if self.base.valid_color_attachment_enum(src) => {},
_ => return self.base.webgl_error(InvalidEnum),
}

if let Some(fb) = self.base.get_read_framebuffer_slot().get() {
handle_potential_webgl_error!(self.base, fb.set_read_buffer(src), return)
} else {
match src {
constants::NONE | constants::BACK => {},
_ => return self.base.webgl_error(InvalidOperation),
}

self.default_fb_readbuffer.set(src);
self.base.send_command(WebGLCommand::ReadBuffer(src));
}
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11
fn DrawBuffers(&self, buffers: Vec<u32>) {
if let Some(fb) = self.base.get_draw_framebuffer_slot().get() {
handle_potential_webgl_error!(self.base, fb.set_draw_buffers(buffers), return)
} else {
if buffers.len() != 1 {
return self.base.webgl_error(InvalidOperation);
}

match buffers[0] {
constants::NONE | constants::BACK => {},
_ => return self.base.webgl_error(InvalidOperation),
}

self.default_fb_drawbuffer.set(buffers[0]);
self.base.send_command(WebGLCommand::DrawBuffers(buffers));
}
}
}

impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGL2RenderingContext> {
Expand Down
53 changes: 53 additions & 0 deletions components/script/dom/webglframebuffer.rs
Expand Up @@ -101,6 +101,8 @@ pub struct WebGLFramebuffer {
depth: DomRefCell<Option<WebGLFramebufferAttachment>>,
stencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
color_read_buffer: DomRefCell<u32>,
color_draw_buffers: DomRefCell<Vec<u32>>,
is_initialized: Cell<bool>,
// Framebuffers for XR keep a reference to the XR session.
// https://github.com/immersive-web/webxr/issues/856
Expand All @@ -121,6 +123,8 @@ impl WebGLFramebuffer {
depth: DomRefCell::new(None),
stencil: DomRefCell::new(None),
depthstencil: DomRefCell::new(None),
color_read_buffer: DomRefCell::new(constants::COLOR_ATTACHMENT0),
color_draw_buffers: DomRefCell::new(vec![constants::COLOR_ATTACHMENT0]),
is_initialized: Cell::new(false),
xr_session: Default::default(),
}
Expand Down Expand Up @@ -914,6 +918,55 @@ impl WebGLFramebuffer {
});
}

pub fn set_read_buffer(&self, buffer: u32) -> WebGLResult<()> {
let context = self.upcast::<WebGLObject>().context();

match buffer {
constants::NONE => {},
_ if context.valid_color_attachment_enum(buffer) => {},
_ => return Err(WebGLError::InvalidOperation),
};

*self.color_read_buffer.borrow_mut() = buffer;
context.send_command(WebGLCommand::ReadBuffer(buffer));
Ok(())
}

pub fn set_draw_buffers(&self, buffers: Vec<u32>) -> WebGLResult<()> {
let context = self.upcast::<WebGLObject>().context();

if buffers.len() > context.limits().max_draw_buffers as usize {
return Err(WebGLError::InvalidValue);
}

let enums_valid = buffers
.iter()
.all(|&val| val == constants::NONE || context.valid_color_attachment_enum(val));
if !enums_valid {
return Err(WebGLError::InvalidEnum);
}

let values_valid = buffers.iter().enumerate().all(|(i, &val)| {
val == constants::NONE || val == (constants::COLOR_ATTACHMENT0 + i as u32)
});
if !values_valid {
return Err(WebGLError::InvalidOperation);
}

*self.color_draw_buffers.borrow_mut() = buffers.clone();
context.send_command(WebGLCommand::DrawBuffers(buffers));
Ok(())
}

pub fn read_buffer(&self) -> u32 {
*self.color_read_buffer.borrow()
}

pub fn draw_buffer_i(&self, index: usize) -> u32 {
let buffers = &*self.color_draw_buffers.borrow();
*buffers.get(index).unwrap_or(&constants::NONE)
}

pub fn target(&self) -> Option<u32> {
self.target.get()
}
Expand Down
5 changes: 5 additions & 0 deletions components/script/dom/webglrenderingcontext.rs
Expand Up @@ -1510,6 +1510,11 @@ impl WebGLRenderingContext {

// FIXME: https://github.com/servo/servo/issues/13710
}

pub fn valid_color_attachment_enum(&self, attachment: u32) -> bool {
let last_slot = constants::COLOR_ATTACHMENT0 + self.limits().max_color_attachments - 1;
constants::COLOR_ATTACHMENT0 <= attachment && attachment <= last_slot
}
}

#[cfg(not(feature = "webgl_backtrace"))]
Expand Down
4 changes: 2 additions & 2 deletions components/script/dom/webidls/WebGL2RenderingContext.webidl
Expand Up @@ -313,7 +313,7 @@ interface mixin WebGL2RenderingContextBase
void invalidateFramebuffer(GLenum target, sequence<GLenum> attachments);
void invalidateSubFramebuffer(GLenum target, sequence<GLenum> attachments,
GLint x, GLint y, GLsizei width, GLsizei height);
// void readBuffer(GLenum src);
void readBuffer(GLenum src);

/* Renderbuffer objects */
any getInternalformatParameter(GLenum target, GLenum internalformat, GLenum pname);
Expand Down Expand Up @@ -475,7 +475,7 @@ interface mixin WebGL2RenderingContextBase
/*[AllowShared]*/ ArrayBufferView dstData, GLuint dstOffset);

/* Multiple Render Targets */
// void drawBuffers(sequence<GLenum> buffers);
void drawBuffers(sequence<GLenum> buffers);

void clearBufferfv(GLenum buffer, GLint drawbuffer, Float32List values,
optional GLuint srcOffset = 0);
Expand Down
32 changes: 13 additions & 19 deletions tests/wpt/webgl/meta/conformance2/context/methods-2.html.ini
@@ -1,52 +1,46 @@
[methods-2.html]
[WebGL test #2: Property either does not exist or is not a function: readBuffer]
expected: FAIL

[WebGL test #1: Property either does not exist or is not a function: blitFramebuffer]
expected: FAIL

[WebGL test #11: Property either does not exist or is not a function: vertexAttribI4iv]
expected: FAIL

[WebGL test #8: Property either does not exist or is not a function: compressedTexImage3D]
[WebGL test #7: Property either does not exist or is not a function: compressedTexImage3D]
expected: FAIL

[WebGL test #13: Property either does not exist or is not a function: vertexAttribI4uiv]
[WebGL test #4: Property either does not exist or is not a function: texStorage3D]
expected: FAIL

[WebGL test #16: Property either does not exist or is not a function: drawBuffers]
[WebGL test #11: Property either does not exist or is not a function: vertexAttribI4ui]
expected: FAIL

[WebGL test #3: Property either does not exist or is not a function: texImage3D]
[WebGL test #2: Property either does not exist or is not a function: texImage3D]
expected: FAIL

[WebGL test #10: Property either does not exist or is not a function: vertexAttribI4i]
[WebGL test #12: Property either does not exist or is not a function: vertexAttribI4uiv]
expected: FAIL

[WebGL test #9: Property either does not exist or is not a function: compressedTexSubImage3D]
[WebGL test #3: Property either does not exist or is not a function: texStorage2D]
expected: FAIL

[WebGL test #12: Property either does not exist or is not a function: vertexAttribI4ui]
[WebGL test #8: Property either does not exist or is not a function: compressedTexSubImage3D]
expected: FAIL

[WebGL test #0: Property either does not exist or is not a function: isContextLost]
expected: FAIL

[WebGL test #7: Property either does not exist or is not a function: copyTexSubImage3D]
[WebGL test #9: Property either does not exist or is not a function: vertexAttribI4i]
expected: FAIL

[WebGL test #14: Property either does not exist or is not a function: vertexAttribIPointer]
[WebGL test #13: Property either does not exist or is not a function: vertexAttribIPointer]
expected: FAIL

[WebGL test #4: Property either does not exist or is not a function: texStorage2D]
[WebGL test #14: Property either does not exist or is not a function: drawRangeElements]
expected: FAIL

[WebGL test #15: Property either does not exist or is not a function: drawRangeElements]
[WebGL test #5: Property either does not exist or is not a function: texSubImage3D]
expected: FAIL

[WebGL test #6: Property either does not exist or is not a function: texSubImage3D]
[WebGL test #10: Property either does not exist or is not a function: vertexAttribI4iv]
expected: FAIL

[WebGL test #5: Property either does not exist or is not a function: texStorage3D]
[WebGL test #6: Property either does not exist or is not a function: copyTexSubImage3D]
expected: FAIL

@@ -1,5 +1,5 @@
[readbuffer.html]
expected: ERROR
expected: TIMEOUT
[WebGL test #3: gl.getParameter(gl.READ_BUFFER) should be 1029 (of type number). Was null (of type object).]
expected: FAIL

Expand Down

0 comments on commit 0afe27e

Please sign in to comment.