Implement the basic WebGL2 buffer data operations

Adds support for `bufferData`, `bufferSubData`, `copyBufferSubData`
and `getBufferSubData`.

Reference: https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3
This commit is contained in:
Mátyás Mustoha 2019-10-17 11:34:06 +02:00
parent f626355b67
commit 4050b7f9ec
13 changed files with 510 additions and 197 deletions

View file

@ -255,7 +255,7 @@ impl WebGLRenderingContext {
&self.limits
}
fn current_vao(&self) -> DomRoot<WebGLVertexArrayObjectOES> {
pub fn current_vao(&self) -> DomRoot<WebGLVertexArrayObjectOES> {
self.current_vao.or_init(|| {
DomRoot::from_ref(
self.default_vao
@ -1004,6 +1004,10 @@ impl WebGLRenderingContext {
self.bound_buffer_array.get()
}
pub fn array_buffer_slot(&self) -> &MutNullableDom<WebGLBuffer> {
&self.bound_buffer_array
}
pub fn bound_buffer(&self, target: u32) -> WebGLResult<Option<DomRoot<WebGLBuffer>>> {
match target {
constants::ARRAY_BUFFER => Ok(self.bound_buffer_array.get()),
@ -1095,6 +1099,72 @@ impl WebGLRenderingContext {
pub fn extension_manager(&self) -> &WebGLExtensions {
&self.extension_manager
}
#[allow(unsafe_code)]
pub fn buffer_data(
&self,
target: u32,
data: Option<ArrayBufferViewOrArrayBuffer>,
usage: u32,
bound_buffer: Option<DomRoot<WebGLBuffer>>,
) {
let data = handle_potential_webgl_error!(self, data.ok_or(InvalidValue), return);
let bound_buffer =
handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
let data = unsafe {
// Safe because we don't do anything with JS until the end of the method.
match data {
ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(),
ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(),
}
};
handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, data, usage));
}
pub fn buffer_data_(
&self,
target: u32,
size: i64,
usage: u32,
bound_buffer: Option<DomRoot<WebGLBuffer>>,
) {
let bound_buffer =
handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
if size < 0 {
return self.webgl_error(InvalidValue);
}
// FIXME: Allocating a buffer based on user-requested size is
// not great, but we don't have a fallible allocation to try.
let data = vec![0u8; size as usize];
handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data, usage));
}
pub fn bind_buffer_maybe(
&self,
slot: &MutNullableDom<WebGLBuffer>,
target: u32,
buffer: Option<&WebGLBuffer>,
) {
if let Some(buffer) = buffer {
handle_potential_webgl_error!(self, self.validate_ownership(buffer), return);
if buffer.is_marked_for_deletion() {
return self.webgl_error(InvalidOperation);
}
handle_potential_webgl_error!(self, buffer.set_target_maybe(target), return);
buffer.increment_attached_counter();
}
self.send_command(WebGLCommand::BindBuffer(target, buffer.map(|b| b.id())));
if let Some(old) = slot.get() {
old.decrement_attached_counter();
}
slot.set(buffer);
}
}
#[cfg(not(feature = "webgl_backtrace"))]
@ -1585,10 +1655,6 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) {
if let Some(buffer) = buffer {
handle_potential_webgl_error!(self, self.validate_ownership(buffer), return);
}
let current_vao;
let slot = match target {
constants::ARRAY_BUFFER => &self.bound_buffer_array,
@ -1598,19 +1664,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
},
_ => return self.webgl_error(InvalidEnum),
};
if let Some(buffer) = buffer {
if buffer.is_marked_for_deletion() {
return self.webgl_error(InvalidOperation);
}
handle_potential_webgl_error!(self, buffer.set_target(target), return);
buffer.increment_attached_counter();
}
self.send_command(WebGLCommand::BindBuffer(target, buffer.map(|b| b.id())));
if let Some(old) = slot.get() {
old.decrement_attached_counter();
}
slot.set(buffer);
self.bind_buffer_maybe(&slot, target, buffer);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
@ -1701,38 +1755,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
#[allow(unsafe_code)]
fn BufferData(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) {
let data = handle_potential_webgl_error!(self, data.ok_or(InvalidValue), return);
let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return);
let bound_buffer =
handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
let data = unsafe {
// Safe because we don't do anything with JS until the end of the method.
match data {
ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(),
ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(),
}
};
handle_potential_webgl_error!(self, bound_buffer.buffer_data(data, usage));
self.buffer_data(target, data, usage, bound_buffer)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
fn BufferData_(&self, target: u32, size: i64, usage: u32) {
let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return);
let bound_buffer =
handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
if size < 0 {
return self.webgl_error(InvalidValue);
}
// FIXME: Allocating a buffer based on user-requested size is
// not great, but we don't have a fallible allocation to try.
let data = vec![0u8; size as usize];
handle_potential_webgl_error!(self, bound_buffer.buffer_data(&data, usage));
self.buffer_data_(target, size, usage, bound_buffer)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5