mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Always use a WebGLVertexArrayObject to handle vertex attribs
This lets us clean up how buffers are reference-counted.
This commit is contained in:
parent
3e09d7270a
commit
fba6d801a1
6 changed files with 442 additions and 561 deletions
|
@ -2,39 +2,44 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use canvas_traits::webgl::WebGLVertexArrayId;
|
||||
use canvas_traits::webgl::{ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVertexArrayId};
|
||||
use dom::bindings::cell::DomRefCell;
|
||||
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
|
||||
use dom::bindings::codegen::Bindings::WebGLVertexArrayObjectOESBinding;
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::reflector::{DomObject, reflect_dom_object};
|
||||
use dom::bindings::root::{DomRoot, MutNullableDom};
|
||||
use dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
use dom::webglbuffer::WebGLBuffer;
|
||||
use dom::webglobject::WebGLObject;
|
||||
use dom::webglrenderingcontext::{VertexAttribs, WebGLRenderingContext};
|
||||
use dom::webglrenderingcontext::WebGLRenderingContext;
|
||||
use dom_struct::dom_struct;
|
||||
use std::cell::Cell;
|
||||
use ref_filter_map::ref_filter_map;
|
||||
use std::cell::{Cell, Ref};
|
||||
|
||||
#[dom_struct]
|
||||
pub struct WebGLVertexArrayObjectOES {
|
||||
webgl_object_: WebGLObject,
|
||||
id: WebGLVertexArrayId,
|
||||
id: Option<WebGLVertexArrayId>,
|
||||
ever_bound: Cell<bool>,
|
||||
is_deleted: Cell<bool>,
|
||||
vertex_attribs: VertexAttribs,
|
||||
bound_buffer_element_array: MutNullableDom<WebGLBuffer>,
|
||||
vertex_attribs: DomRefCell<Box<[VertexAttribData]>>,
|
||||
element_array_buffer: MutNullableDom<WebGLBuffer>,
|
||||
}
|
||||
|
||||
impl WebGLVertexArrayObjectOES {
|
||||
fn new_inherited(context: &WebGLRenderingContext, id: WebGLVertexArrayId) -> Self {
|
||||
fn new_inherited(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self {
|
||||
let max_vertex_attribs = context.limits().max_vertex_attribs as usize;
|
||||
Self {
|
||||
webgl_object_: WebGLObject::new_inherited(context),
|
||||
id: id,
|
||||
ever_bound: Cell::new(false),
|
||||
is_deleted: Cell::new(false),
|
||||
vertex_attribs: VertexAttribs::new(context.limits().max_vertex_attribs),
|
||||
bound_buffer_element_array: MutNullableDom::new(None),
|
||||
id,
|
||||
ever_bound: Default::default(),
|
||||
is_deleted: Default::default(),
|
||||
vertex_attribs: DomRefCell::new(vec![Default::default(); max_vertex_attribs].into()),
|
||||
element_array_buffer: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(context: &WebGLRenderingContext, id: WebGLVertexArrayId) -> DomRoot<Self> {
|
||||
pub fn new(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> DomRoot<Self> {
|
||||
reflect_dom_object(
|
||||
Box::new(WebGLVertexArrayObjectOES::new_inherited(context, id)),
|
||||
&*context.global(),
|
||||
|
@ -42,11 +47,7 @@ impl WebGLVertexArrayObjectOES {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn vertex_attribs(&self) -> &VertexAttribs {
|
||||
&self.vertex_attribs
|
||||
}
|
||||
|
||||
pub fn id(&self) -> WebGLVertexArrayId {
|
||||
pub fn id(&self) -> Option<WebGLVertexArrayId> {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
@ -54,8 +55,25 @@ impl WebGLVertexArrayObjectOES {
|
|||
self.is_deleted.get()
|
||||
}
|
||||
|
||||
pub fn set_deleted(&self) {
|
||||
self.is_deleted.set(true)
|
||||
pub fn delete(&self) {
|
||||
assert!(self.id.is_some());
|
||||
if self.is_deleted.get() {
|
||||
return;
|
||||
}
|
||||
self.is_deleted.set(true);
|
||||
|
||||
self.upcast::<WebGLObject>()
|
||||
.context()
|
||||
.send_command(WebGLCommand::DeleteVertexArray(self.id.unwrap()));
|
||||
|
||||
for attrib_data in &**self.vertex_attribs.borrow() {
|
||||
if let Some(buffer) = attrib_data.buffer() {
|
||||
buffer.decrement_attached_counter();
|
||||
}
|
||||
}
|
||||
if let Some(buffer) = self.element_array_buffer.get() {
|
||||
buffer.decrement_attached_counter();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ever_bound(&self) -> bool {
|
||||
|
@ -66,11 +84,202 @@ impl WebGLVertexArrayObjectOES {
|
|||
self.ever_bound.set(true);
|
||||
}
|
||||
|
||||
pub fn bound_buffer_element_array(&self) -> Option<DomRoot<WebGLBuffer>> {
|
||||
self.bound_buffer_element_array.get()
|
||||
pub fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> {
|
||||
&self.element_array_buffer
|
||||
}
|
||||
|
||||
pub fn set_bound_buffer_element_array(&self, buffer: Option<&WebGLBuffer>) {
|
||||
self.bound_buffer_element_array.set(buffer);
|
||||
pub fn get_vertex_attrib(&self, index: u32) -> Option<Ref<VertexAttribData>> {
|
||||
ref_filter_map(self.vertex_attribs.borrow(), |attribs| attribs.get(index as usize))
|
||||
}
|
||||
|
||||
pub fn vertex_attrib_pointer(
|
||||
&self,
|
||||
index: u32,
|
||||
size: i32,
|
||||
type_: u32,
|
||||
normalized: bool,
|
||||
stride: i32,
|
||||
offset: i64,
|
||||
) -> WebGLResult<()> {
|
||||
let mut attribs = self.vertex_attribs.borrow_mut();
|
||||
let data = attribs.get_mut(index as usize).ok_or(WebGLError::InvalidValue)?;
|
||||
|
||||
if size < 1 || size > 4 {
|
||||
return Err(WebGLError::InvalidValue);
|
||||
}
|
||||
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#BUFFER_OFFSET_AND_STRIDE
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#VERTEX_STRIDE
|
||||
if stride < 0 || stride > 255 || offset < 0 {
|
||||
return Err(WebGLError::InvalidValue);
|
||||
}
|
||||
let bytes_per_component: i32 = match type_ {
|
||||
constants::BYTE | constants::UNSIGNED_BYTE => 1,
|
||||
constants::SHORT | constants::UNSIGNED_SHORT => 2,
|
||||
constants::FLOAT => 4,
|
||||
_ => return Err(WebGLError::InvalidEnum),
|
||||
};
|
||||
if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 {
|
||||
return Err(WebGLError::InvalidOperation);
|
||||
}
|
||||
|
||||
let context = self.upcast::<WebGLObject>().context();
|
||||
let buffer = context.array_buffer().ok_or(WebGLError::InvalidOperation)?;
|
||||
buffer.increment_attached_counter();
|
||||
context.send_command(WebGLCommand::VertexAttribPointer(
|
||||
index,
|
||||
size,
|
||||
type_,
|
||||
normalized,
|
||||
stride,
|
||||
offset as u32,
|
||||
));
|
||||
if let Some(old) = data.buffer() {
|
||||
old.decrement_attached_counter();
|
||||
}
|
||||
|
||||
*data = VertexAttribData {
|
||||
enabled_as_array: data.enabled_as_array,
|
||||
size: size as u8,
|
||||
type_,
|
||||
bytes_per_vertex: size as u8 * bytes_per_component as u8,
|
||||
normalized,
|
||||
stride: stride as u8,
|
||||
offset: offset as u32,
|
||||
buffer: Some(Dom::from_ref(&*buffer)),
|
||||
divisor: data.divisor,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vertex_attrib_divisor(&self, index: u32, value: u32) {
|
||||
self.vertex_attribs.borrow_mut()[index as usize].divisor = value;
|
||||
}
|
||||
|
||||
pub fn enabled_vertex_attrib_array(&self, index: u32, value: bool) {
|
||||
self.vertex_attribs.borrow_mut()[index as usize].enabled_as_array = value;
|
||||
}
|
||||
|
||||
pub fn unbind_buffer(&self, buffer: &WebGLBuffer) {
|
||||
for attrib in &mut **self.vertex_attribs.borrow_mut() {
|
||||
if let Some(b) = attrib.buffer() {
|
||||
if b.id() != buffer.id() {
|
||||
continue;
|
||||
}
|
||||
b.decrement_attached_counter();
|
||||
}
|
||||
attrib.buffer = None;
|
||||
}
|
||||
if self.element_array_buffer.get().map_or(false, |b| buffer == &*b) {
|
||||
buffer.decrement_attached_counter();
|
||||
self.element_array_buffer.set(None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_for_draw(
|
||||
&self,
|
||||
required_len: u32,
|
||||
instance_count: u32,
|
||||
active_attribs: &[ActiveAttribInfo],
|
||||
) -> WebGLResult<()> {
|
||||
// TODO(nox): Cache limits per VAO.
|
||||
let attribs = self.vertex_attribs.borrow();
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2
|
||||
if attribs.iter().any(|data| data.enabled_as_array && data.buffer.is_none()) {
|
||||
return Err(WebGLError::InvalidOperation);
|
||||
}
|
||||
let mut has_active_attrib = false;
|
||||
let mut has_divisor_0 = false;
|
||||
for active_info in active_attribs {
|
||||
if active_info.location < 0 {
|
||||
continue;
|
||||
}
|
||||
has_active_attrib = true;
|
||||
let attrib = &attribs[active_info.location as usize];
|
||||
if attrib.divisor == 0 {
|
||||
has_divisor_0 = true;
|
||||
}
|
||||
if !attrib.enabled_as_array {
|
||||
continue;
|
||||
}
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6
|
||||
if required_len > 0 && instance_count > 0 {
|
||||
let max_vertices = attrib.max_vertices();
|
||||
if attrib.divisor == 0 {
|
||||
if max_vertices < required_len {
|
||||
return Err(WebGLError::InvalidOperation);
|
||||
}
|
||||
} else if max_vertices.checked_mul(attrib.divisor).map_or(false, |v| v < instance_count) {
|
||||
return Err(WebGLError::InvalidOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
if has_active_attrib && !has_divisor_0 {
|
||||
return Err(WebGLError::InvalidOperation);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WebGLVertexArrayObjectOES {
|
||||
fn drop(&mut self) {
|
||||
if self.id.is_some() {
|
||||
self.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||
#[must_root]
|
||||
pub struct VertexAttribData {
|
||||
pub enabled_as_array: bool,
|
||||
pub size: u8,
|
||||
pub type_: u32,
|
||||
bytes_per_vertex: u8,
|
||||
pub normalized: bool,
|
||||
pub stride: u8,
|
||||
pub offset: u32,
|
||||
pub buffer: Option<Dom<WebGLBuffer>>,
|
||||
pub divisor: u32,
|
||||
}
|
||||
|
||||
impl Default for VertexAttribData {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled_as_array: false,
|
||||
size: 4,
|
||||
type_: constants::FLOAT,
|
||||
bytes_per_vertex: 16,
|
||||
normalized: false,
|
||||
stride: 0,
|
||||
offset: 0,
|
||||
buffer: None,
|
||||
divisor: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VertexAttribData {
|
||||
pub fn buffer(&self) -> Option<&WebGLBuffer> {
|
||||
self.buffer.as_ref().map(|b| &**b)
|
||||
}
|
||||
|
||||
fn max_vertices(&self) -> u32 {
|
||||
let capacity = (self.buffer().unwrap().capacity() as u32).saturating_sub(self.offset);
|
||||
if capacity < self.bytes_per_vertex as u32 {
|
||||
0
|
||||
} else if self.stride == 0 {
|
||||
capacity / self.bytes_per_vertex as u32
|
||||
} else if self.stride < self.bytes_per_vertex {
|
||||
(capacity - (self.bytes_per_vertex - self.stride) as u32) / self.stride as u32
|
||||
} else {
|
||||
let mut max = capacity / self.stride as u32;
|
||||
if capacity % self.stride as u32 >= self.bytes_per_vertex as u32 {
|
||||
max += 1;
|
||||
}
|
||||
max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue