Auto merge of #25519 - szeged:mmatyas__webgl_fns_uniforms, r=jdm

Add support for WebGL2 unsigned uniform operations

This adds support for the WebGL2 `uniform[1234]ui` and `uniform[1234]uiv` operations.

See: https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8

<!-- Please describe your changes on the following line: -->

cc @jdm @zakorgy @imiklos

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] There are tests for these changes

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2020-01-15 10:45:07 -05:00 committed by GitHub
commit f9174a4717
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 643 additions and 134 deletions

View file

@ -11,6 +11,7 @@ use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
use crate::dom::bindings::codegen::UnionTypes::Float32ArrayOrUnrestrictedFloatSequence;
use crate::dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
use crate::dom::bindings::codegen::UnionTypes::Int32ArrayOrLongSequence;
use crate::dom::bindings::codegen::UnionTypes::Uint32ArrayOrUnsignedLongSequence;
use crate::dom::bindings::error::{ErrorResult, Fallible};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
@ -25,7 +26,8 @@ use crate::dom::webglprogram::WebGLProgram;
use crate::dom::webglquery::WebGLQuery;
use crate::dom::webglrenderbuffer::WebGLRenderbuffer;
use crate::dom::webglrenderingcontext::{
LayoutCanvasWebGLRenderingContextHelpers, Size2DExt, WebGLRenderingContext,
uniform_get, uniform_typed, LayoutCanvasWebGLRenderingContextHelpers, Size2DExt,
WebGLRenderingContext,
};
use crate::dom::webglsampler::{WebGLSampler, WebGLSamplerValue};
use crate::dom::webglshader::WebGLShader;
@ -48,7 +50,7 @@ use js::jsapi::{JSObject, Type};
use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value};
use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue};
use js::rust::CustomAutoRooterGuard;
use js::typedarray::{ArrayBufferView, CreateWith, Uint32Array};
use js::typedarray::{ArrayBufferView, CreateWith, Uint32, Uint32Array};
use script_layout_interface::HTMLCanvasDataSource;
use std::cell::Cell;
use std::cmp;
@ -388,6 +390,49 @@ impl WebGL2RenderingContext {
.copy_from_slice(&src[src_start..src_start + src_row_bytes as usize]);
}
}
fn uniform_vec_section(
&self,
vec: Uint32ArrayOrUnsignedLongSequence,
offset: u32,
length: u32,
uniform_size: usize,
uniform_location: &WebGLUniformLocation,
) -> WebGLResult<Vec<u32>> {
let vec = match vec {
Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
};
let offset = offset as usize;
if offset > vec.len() {
return Err(InvalidValue);
}
let length = if length > 0 {
length as usize
} else {
vec.len() - offset
};
if offset + length > vec.len() {
return Err(InvalidValue);
}
let vec = if offset == 0 && length == vec.len() {
vec
} else {
vec[offset..offset + length].to_vec()
};
if vec.len() < uniform_size || vec.len() % uniform_size != 0 {
return Err(InvalidValue);
}
if uniform_location.size().is_none() && vec.len() != uniform_size {
return Err(InvalidOperation);
}
Ok(vec)
}
}
impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
@ -1474,6 +1519,57 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
self.base.Uniform1iv(location, v)
}
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
fn Uniform1ui(&self, location: Option<&WebGLUniformLocation>, val: u32) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL | constants::UNSIGNED_INT => (),
_ => return Err(InvalidOperation),
}
self.base
.send_command(WebGLCommand::Uniform1ui(location.id(), val));
Ok(())
});
}
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
fn Uniform1uiv(
&self,
location: Option<&WebGLUniformLocation>,
val: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL |
constants::UNSIGNED_INT |
constants::SAMPLER_2D |
constants::SAMPLER_CUBE => {},
_ => return Err(InvalidOperation),
}
let val = self.uniform_vec_section(val, src_offset, src_length, 1, location)?;
match location.type_() {
constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
for &v in val
.iter()
.take(cmp::min(location.size().unwrap_or(1) as usize, val.len()))
{
if v >= self.base.limits().max_combined_texture_image_units {
return Err(InvalidValue);
}
}
},
_ => {},
}
self.base
.send_command(WebGLCommand::Uniform1uiv(location.id(), val));
Ok(())
});
}
/// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn Uniform1fv(
&self,
@ -1507,6 +1603,39 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
self.base.Uniform2iv(location, v)
}
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
fn Uniform2ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
_ => return Err(InvalidOperation),
}
self.base
.send_command(WebGLCommand::Uniform2ui(location.id(), x, y));
Ok(())
});
}
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
fn Uniform2uiv(
&self,
location: Option<&WebGLUniformLocation>,
val: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
_ => return Err(InvalidOperation),
}
let val = self.uniform_vec_section(val, src_offset, src_length, 2, location)?;
self.base
.send_command(WebGLCommand::Uniform2uiv(location.id(), val));
Ok(())
});
}
/// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) {
self.base.Uniform3f(location, x, y, z)
@ -1531,6 +1660,39 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
self.base.Uniform3iv(location, v)
}
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
fn Uniform3ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
_ => return Err(InvalidOperation),
}
self.base
.send_command(WebGLCommand::Uniform3ui(location.id(), x, y, z));
Ok(())
});
}
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
fn Uniform3uiv(
&self,
location: Option<&WebGLUniformLocation>,
val: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
_ => return Err(InvalidOperation),
}
let val = self.uniform_vec_section(val, src_offset, src_length, 3, location)?;
self.base
.send_command(WebGLCommand::Uniform3uiv(location.id(), val));
Ok(())
});
}
/// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) {
self.base.Uniform4i(location, x, y, z, w)
@ -1541,6 +1703,39 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
self.base.Uniform4iv(location, v)
}
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
fn Uniform4ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32, w: u32) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
_ => return Err(InvalidOperation),
}
self.base
.send_command(WebGLCommand::Uniform4ui(location.id(), x, y, z, w));
Ok(())
});
}
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
fn Uniform4uiv(
&self,
location: Option<&WebGLUniformLocation>,
val: Uint32ArrayOrUnsignedLongSequence,
src_offset: u32,
src_length: u32,
) {
self.base.with_location(location, |location| {
match location.type_() {
constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
_ => return Err(InvalidOperation),
}
let val = self.uniform_vec_section(val, src_offset, src_length, 4, location)?;
self.base
.send_command(WebGLCommand::Uniform4uiv(location.id(), val));
Ok(())
});
}
/// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) {
self.base.Uniform4f(location, x, y, z, w)
@ -1585,14 +1780,37 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
self.base.UniformMatrix4fv(location, transpose, v)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8
#[allow(unsafe_code)]
fn GetUniform(
&self,
cx: JSContext,
program: &WebGLProgram,
location: &WebGLUniformLocation,
) -> JSVal {
self.base.GetUniform(cx, program, location)
handle_potential_webgl_error!(
self.base,
self.base.uniform_check_program(program, location),
return NullValue()
);
let triple = (&*self.base, program.id(), location.id());
match location.type_() {
constants::UNSIGNED_INT => {
UInt32Value(uniform_get(triple, WebGLCommand::GetUniformUint))
},
constants::UNSIGNED_INT_VEC2 => unsafe {
uniform_typed::<Uint32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformUint2))
},
constants::UNSIGNED_INT_VEC3 => unsafe {
uniform_typed::<Uint32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformUint3))
},
constants::UNSIGNED_INT_VEC4 => unsafe {
uniform_typed::<Uint32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformUint4))
},
_ => self.base.GetUniform(cx, program, location),
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9

View file

@ -114,6 +114,27 @@ fn has_invalid_blend_constants(arg1: u32, arg2: u32) -> bool {
}
}
pub fn uniform_get<T, F>(triple: (&WebGLRenderingContext, WebGLProgramId, i32), f: F) -> T
where
F: FnOnce(WebGLProgramId, i32, WebGLSender<T>) -> WebGLCommand,
T: for<'de> Deserialize<'de> + Serialize,
{
let (sender, receiver) = webgl_channel().unwrap();
triple.0.send_command(f(triple.1, triple.2, sender));
receiver.recv().unwrap()
}
#[allow(unsafe_code)]
pub unsafe fn uniform_typed<T>(cx: *mut JSContext, value: &[T::Element]) -> JSVal
where
T: TypedArrayElementCreator,
{
rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
<TypedArray<T, *mut JSObject>>::create(cx, CreateWith::Slice(&value), rval.handle_mut())
.unwrap();
ObjectValue(rval.get())
}
bitflags! {
/// Set of bitflags for texture unpacking (texImage2d, etc...)
#[derive(JSTraceable, MallocSizeOf)]
@ -401,7 +422,7 @@ impl WebGLRenderingContext {
Ok(())
}
fn with_location<F>(&self, location: Option<&WebGLUniformLocation>, f: F)
pub fn with_location<F>(&self, location: Option<&WebGLUniformLocation>, f: F)
where
F: FnOnce(&WebGLUniformLocation) -> WebGLResult<()>,
{
@ -1214,6 +1235,25 @@ impl WebGLRenderingContext {
pub fn current_program(&self) -> Option<DomRoot<WebGLProgram>> {
self.current_program.get()
}
pub fn uniform_check_program(
&self,
program: &WebGLProgram,
location: &WebGLUniformLocation,
) -> WebGLResult<()> {
self.validate_ownership(program)?;
if program.is_deleted() ||
!program.is_linked() ||
self.context_id() != location.context_id() ||
program.id() != location.program_id() ||
program.link_generation() != location.link_generation()
{
return Err(InvalidOperation);
}
Ok(())
}
}
#[cfg(not(feature = "webgl_backtrace"))]
@ -3548,88 +3588,60 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
program: &WebGLProgram,
location: &WebGLUniformLocation,
) -> JSVal {
handle_potential_webgl_error!(self, self.validate_ownership(program), return NullValue());
if program.is_deleted() ||
!program.is_linked() ||
self.context_id() != location.context_id() ||
program.id() != location.program_id() ||
program.link_generation() != location.link_generation()
{
self.webgl_error(InvalidOperation);
return NullValue();
}
fn get<T, F>(triple: (&WebGLRenderingContext, WebGLProgramId, i32), f: F) -> T
where
F: FnOnce(WebGLProgramId, i32, WebGLSender<T>) -> WebGLCommand,
T: for<'de> Deserialize<'de> + Serialize,
{
let (sender, receiver) = webgl_channel().unwrap();
triple.0.send_command(f(triple.1, triple.2, sender));
receiver.recv().unwrap()
}
handle_potential_webgl_error!(
self,
self.uniform_check_program(program, location),
return NullValue()
);
let triple = (self, program.id(), location.id());
unsafe fn typed<T>(cx: *mut JSContext, value: &[T::Element]) -> JSVal
where
T: TypedArrayElementCreator,
{
rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
<TypedArray<T, *mut JSObject>>::create(
cx,
CreateWith::Slice(&value),
rval.handle_mut(),
)
.unwrap();
ObjectValue(rval.get())
}
match location.type_() {
constants::BOOL => BooleanValue(get(triple, WebGLCommand::GetUniformBool)),
constants::BOOL => BooleanValue(uniform_get(triple, WebGLCommand::GetUniformBool)),
constants::BOOL_VEC2 => unsafe {
rooted!(in(*cx) let mut rval = NullValue());
get(triple, WebGLCommand::GetUniformBool2).to_jsval(*cx, rval.handle_mut());
uniform_get(triple, WebGLCommand::GetUniformBool2).to_jsval(*cx, rval.handle_mut());
rval.get()
},
constants::BOOL_VEC3 => unsafe {
rooted!(in(*cx) let mut rval = NullValue());
get(triple, WebGLCommand::GetUniformBool3).to_jsval(*cx, rval.handle_mut());
uniform_get(triple, WebGLCommand::GetUniformBool3).to_jsval(*cx, rval.handle_mut());
rval.get()
},
constants::BOOL_VEC4 => unsafe {
rooted!(in(*cx) let mut rval = NullValue());
get(triple, WebGLCommand::GetUniformBool4).to_jsval(*cx, rval.handle_mut());
uniform_get(triple, WebGLCommand::GetUniformBool4).to_jsval(*cx, rval.handle_mut());
rval.get()
},
constants::INT | constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
Int32Value(get(triple, WebGLCommand::GetUniformInt))
Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt))
},
constants::INT_VEC2 => unsafe {
typed::<Int32>(*cx, &get(triple, WebGLCommand::GetUniformInt2))
uniform_typed::<Int32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformInt2))
},
constants::INT_VEC3 => unsafe {
typed::<Int32>(*cx, &get(triple, WebGLCommand::GetUniformInt3))
uniform_typed::<Int32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformInt3))
},
constants::INT_VEC4 => unsafe {
typed::<Int32>(*cx, &get(triple, WebGLCommand::GetUniformInt4))
uniform_typed::<Int32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformInt4))
},
constants::FLOAT => {
DoubleValue(uniform_get(triple, WebGLCommand::GetUniformFloat) as f64)
},
constants::FLOAT => DoubleValue(get(triple, WebGLCommand::GetUniformFloat) as f64),
constants::FLOAT_VEC2 => unsafe {
typed::<Float32>(*cx, &get(triple, WebGLCommand::GetUniformFloat2))
uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat2))
},
constants::FLOAT_VEC3 => unsafe {
typed::<Float32>(*cx, &get(triple, WebGLCommand::GetUniformFloat3))
uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat3))
},
constants::FLOAT_VEC4 | constants::FLOAT_MAT2 => unsafe {
typed::<Float32>(*cx, &get(triple, WebGLCommand::GetUniformFloat4))
uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat4))
},
constants::FLOAT_MAT3 => unsafe {
typed::<Float32>(*cx, &get(triple, WebGLCommand::GetUniformFloat9))
uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat9))
},
constants::FLOAT_MAT4 => unsafe {
typed::<Float32>(*cx, &get(triple, WebGLCommand::GetUniformFloat16))
uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat16))
},
_ => panic!("wrong uniform type"),
}

View file

@ -15,7 +15,7 @@ typedef unsigned long long GLuint64;
// interface WebGLVertexArrayObject : WebGLObject {
// };
// typedef ([AllowShared] Uint32Array or sequence<GLuint>) Uint32List;
typedef (/*[AllowShared]*/ Uint32Array or sequence<GLuint>) Uint32List;
interface mixin WebGL2RenderingContextBase
{
@ -425,10 +425,10 @@ interface mixin WebGL2RenderingContextBase
// [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram program, DOMString name);
/* Uniforms */
// void uniform1ui(WebGLUniformLocation? location, GLuint v0);
// void uniform2ui(WebGLUniformLocation? location, GLuint v0, GLuint v1);
// void uniform3ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2);
// void uniform4ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
void uniform1ui(WebGLUniformLocation? location, GLuint v0);
void uniform2ui(WebGLUniformLocation? location, GLuint v0, GLuint v1);
void uniform3ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2);
void uniform4ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
// void uniform1fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0,
// optional GLuint srcLength = 0);
@ -448,14 +448,14 @@ interface mixin WebGL2RenderingContextBase
// void uniform4iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0,
// optional GLuint srcLength = 0);
// void uniform1uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0,
// optional GLuint srcLength = 0);
// void uniform2uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0,
// optional GLuint srcLength = 0);
// void uniform3uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0,
// optional GLuint srcLength = 0);
// void uniform4uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0,
// optional GLuint srcLength = 0);
void uniform1uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0,
optional GLuint srcLength = 0);
void uniform2uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0,
optional GLuint srcLength = 0);
void uniform3uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0,
optional GLuint srcLength = 0);
void uniform4uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0,
optional GLuint srcLength = 0);
// void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data,
// optional GLuint srcOffset = 0, optional GLuint srcLength = 0);