Auto merge of #21341 - servo:webgl, r=avadacatavra

Properly check limit in gl.activeTexture()

Fixes #20531.

<!-- 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/21341)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-08-23 09:16:56 -04:00 committed by GitHub
commit a1abdde8c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 155 additions and 141 deletions

View file

@ -122,7 +122,7 @@ impl<'a> WebGLValidator for CommonTexImage2DValidator<'a> {
}
};
let texture = self.context.bound_texture_for_target(&target);
let texture = self.context.textures().active_texture_for_image_target(target);
let limits = self.context.limits();
let max_size = if target.is_cubic() {

View file

@ -10,7 +10,6 @@ use canvas_traits::webgl::{WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSen
use canvas_traits::webgl::{WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLSender};
use canvas_traits::webgl::{WebGLVersion, WebVRCommand, webgl_channel};
use canvas_traits::webgl::WebGLError::*;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
use dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants;
use dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
@ -52,7 +51,6 @@ use dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES;
use dom::window::Window;
use dom_struct::dom_struct;
use euclid::Size2D;
use fnv::FnvHashMap;
use half::f16;
use js::jsapi::{JSContext, JSObject, Type};
use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value, JSVal};
@ -135,41 +133,6 @@ bitflags! {
}
}
/// Information about the bound textures of a WebGL texture unit.
#[must_root]
#[derive(JSTraceable, MallocSizeOf)]
struct TextureUnitBindings {
bound_texture_2d: MutNullableDom<WebGLTexture>,
bound_texture_cube_map: MutNullableDom<WebGLTexture>,
}
impl TextureUnitBindings {
fn new() -> Self {
Self {
bound_texture_2d: MutNullableDom::new(None),
bound_texture_cube_map: MutNullableDom::new(None),
}
}
/// Clears the slot associated to the given texture.
/// Returns the GL target of the cleared slot, if any.
fn clear_slot(&self, texture: &WebGLTexture) -> Option<u32> {
let fields = [(&self.bound_texture_2d, constants::TEXTURE_2D),
(&self.bound_texture_cube_map, constants::TEXTURE_CUBE_MAP)];
fields.iter().find(|field| {
match field.0.get() {
Some(t) => t.id() == texture.id(),
_ => false,
}
}).and_then(|field| {
field.0.set(None);
Some(field.1)
})
}
}
#[dom_struct]
pub struct WebGLRenderingContext {
reflector_: Reflector,
@ -189,8 +152,6 @@ pub struct WebGLRenderingContext {
texture_unpacking_alignment: Cell<u32>,
bound_framebuffer: MutNullableDom<WebGLFramebuffer>,
bound_renderbuffer: MutNullableDom<WebGLRenderbuffer>,
bound_textures: DomRefCell<FnvHashMap<u32, TextureUnitBindings>>,
bound_texture_unit: Cell<u32>,
bound_buffer_array: MutNullableDom<WebGLBuffer>,
current_program: MutNullableDom<WebGLProgram>,
/// https://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Vertex_Attribute_0
@ -204,6 +165,7 @@ pub struct WebGLRenderingContext {
capabilities: Capabilities,
default_vao: DomOnceCell<WebGLVertexArrayObjectOES>,
current_vao: MutNullableDom<WebGLVertexArrayObjectOES>,
textures: Textures,
}
impl WebGLRenderingContext {
@ -229,6 +191,7 @@ impl WebGLRenderingContext {
let result = receiver.recv().unwrap();
result.map(|ctx_data| {
let max_combined_texture_image_units = ctx_data.limits.max_combined_texture_image_units;
WebGLRenderingContext {
reflector_: Reflector::new(),
webgl_sender: ctx_data.sender,
@ -242,8 +205,6 @@ impl WebGLRenderingContext {
texture_unpacking_settings: Cell::new(TextureUnpacking::CONVERT_COLORSPACE),
texture_unpacking_alignment: Cell::new(4),
bound_framebuffer: MutNullableDom::new(None),
bound_textures: DomRefCell::new(Default::default()),
bound_texture_unit: Cell::new(constants::TEXTURE0),
bound_buffer_array: MutNullableDom::new(None),
bound_renderbuffer: MutNullableDom::new(None),
current_program: MutNullableDom::new(None),
@ -254,6 +215,7 @@ impl WebGLRenderingContext {
capabilities: Default::default(),
default_vao: Default::default(),
current_vao: Default::default(),
textures: Textures::new(max_combined_texture_image_units),
}
})
}
@ -293,36 +255,6 @@ impl WebGLRenderingContext {
})
}
fn bound_texture(&self, target: u32) -> Option<DomRoot<WebGLTexture>> {
match target {
constants::TEXTURE_2D => {
self.bound_textures.borrow().get(&self.bound_texture_unit.get()).and_then(|t| {
t.bound_texture_2d.get()
})
},
constants::TEXTURE_CUBE_MAP => {
self.bound_textures.borrow().get(&self.bound_texture_unit.get()).and_then(|t| {
t.bound_texture_cube_map.get()
})
},
_ => None,
}
}
pub fn bound_texture_for_target(&self, target: &TexImageTarget) -> Option<DomRoot<WebGLTexture>> {
self.bound_textures.borrow().get(&self.bound_texture_unit.get()).and_then(|binding| {
match *target {
TexImageTarget::Texture2D => binding.bound_texture_2d.get(),
TexImageTarget::CubeMapPositiveX |
TexImageTarget::CubeMapNegativeX |
TexImageTarget::CubeMapPositiveY |
TexImageTarget::CubeMapNegativeY |
TexImageTarget::CubeMapPositiveZ |
TexImageTarget::CubeMapNegativeZ => binding.bound_texture_cube_map.get(),
}
})
}
pub fn recreate(&self, size: Size2D<i32>) {
let (sender, receiver) = webgl_channel().unwrap();
self.webgl_sender.send_resize(size, sender).unwrap();
@ -348,7 +280,7 @@ impl WebGLRenderingContext {
// Right now offscreen_gl_context generates a new FBO and the bound texture is changed
// in order to create a new render to texture attachment.
// Send a command to re-bind the TEXTURE_2D, if any.
if let Some(texture) = self.bound_texture(constants::TEXTURE_2D) {
if let Some(texture) = self.textures.active_texture_slot(constants::TEXTURE_2D).unwrap().get() {
self.send_command(WebGLCommand::BindTexture(constants::TEXTURE_2D, Some(texture.id())));
}
@ -446,17 +378,21 @@ impl WebGLRenderingContext {
handle_potential_webgl_error!(self, f(location));
}
fn tex_parameter(&self, target: u32, param: u32, value: TexParameterValue) {
let texture = match target {
constants::TEXTURE_2D |
constants::TEXTURE_CUBE_MAP => self.bound_texture(target),
_ => return self.webgl_error(InvalidEnum),
};
pub fn textures(&self) -> &Textures {
&self.textures
}
let texture = match texture {
Some(tex) => tex,
None => return self.webgl_error(InvalidOperation),
};
fn tex_parameter(&self, target: u32, param: u32, value: TexParameterValue) {
let texture_slot = handle_potential_webgl_error!(
self,
self.textures.active_texture_slot(target),
return
);
let texture = handle_potential_webgl_error!(
self,
texture_slot.get().ok_or(InvalidOperation),
return
);
if !self.extension_manager.is_get_tex_parameter_name_enabled(param) {
return self.webgl_error(InvalidEnum);
@ -1483,11 +1419,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return optional_root_object_to_js_or_null!(cx, &self.bound_renderbuffer.get());
}
constants::TEXTURE_BINDING_2D => {
let texture = self.bound_texture(constants::TEXTURE_2D);
let texture = self.textures.active_texture_slot(constants::TEXTURE_2D).unwrap().get();
return optional_root_object_to_js_or_null!(cx, texture);
}
constants::TEXTURE_BINDING_CUBE_MAP => {
let texture = self.bound_texture(constants::TEXTURE_CUBE_MAP);
let texture = self.textures.active_texture_slot(constants::TEXTURE_CUBE_MAP).unwrap().get();
return optional_root_object_to_js_or_null!(cx, texture);
}
OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES => {
@ -1655,28 +1591,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
unsafe fn GetTexParameter(&self, _cx: *mut JSContext, target: u32, pname: u32) -> JSVal {
match target {
constants::TEXTURE_2D |
constants::TEXTURE_CUBE_MAP => {},
_ => {
self.webgl_error(InvalidEnum);
return NullValue();
}
};
let texture_slot = handle_potential_webgl_error!(
self,
self.textures.active_texture_slot(target),
return NullValue()
);
let texture = handle_potential_webgl_error!(
self,
texture_slot.get().ok_or(InvalidOperation),
return NullValue()
);
if !self.extension_manager.is_get_tex_parameter_name_enabled(pname) {
self.webgl_error(InvalidEnum);
return NullValue();
}
let texture = match self.bound_texture(target) {
Some(texture) => texture,
None => {
self.webgl_error(InvalidOperation);
return NullValue();
}
};
match pname {
constants::TEXTURE_MAG_FILTER => return UInt32Value(texture.mag_filter()),
constants::TEXTURE_MIN_FILTER => return UInt32Value(texture.min_filter()),
@ -1759,7 +1689,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn ActiveTexture(&self, texture: u32) {
self.bound_texture_unit.set(texture);
handle_potential_webgl_error!(
self,
self.textures.set_active_unit_enum(texture),
return
);
self.send_command(WebGLCommand::ActiveTexture(texture));
}
@ -1929,39 +1863,33 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
}
let mut bound_textures = self.bound_textures.borrow_mut();
let binding = bound_textures.entry(self.bound_texture_unit.get())
.or_insert(TextureUnitBindings::new());
let slot = match target {
constants::TEXTURE_2D => &binding.bound_texture_2d,
constants::TEXTURE_CUBE_MAP => &binding.bound_texture_cube_map,
_ => return self.webgl_error(InvalidEnum),
};
let texture_slot = handle_potential_webgl_error!(
self,
self.textures.active_texture_slot(target),
return
);
if let Some(texture) = texture {
match texture.bind(target) {
Ok(_) => slot.set(Some(texture)),
Err(err) => return self.webgl_error(err),
}
handle_potential_webgl_error!(self, texture.bind(target), return);
} else {
slot.set(None);
// Unbind the currently bound texture
self.send_command(WebGLCommand::BindTexture(target, None));
}
texture_slot.set(texture);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
fn GenerateMipmap(&self, target: u32) {
let texture = match target {
constants::TEXTURE_2D |
constants::TEXTURE_CUBE_MAP => self.bound_texture(target),
_ => return self.webgl_error(InvalidEnum),
};
match texture {
Some(texture) => handle_potential_webgl_error!(self, texture.generate_mipmap()),
None => self.webgl_error(InvalidOperation)
}
let texture_slot = handle_potential_webgl_error!(
self,
self.textures.active_texture_slot(target),
return
);
let texture = handle_potential_webgl_error!(
self,
texture_slot.get().ok_or(InvalidOperation),
return
);
handle_potential_webgl_error!(self, texture.generate_mipmap());
}
#[allow(unsafe_code)]
@ -2367,20 +2295,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// zero."
//
// The same texture may be bound to multiple texture units.
let mut bound_unit = self.bound_texture_unit.get();
for (texture_unit, binding) in self.bound_textures.borrow().iter() {
if let Some(target) = binding.clear_slot(texture) {
if *texture_unit != bound_unit {
self.send_command(WebGLCommand::ActiveTexture(*texture_unit));
bound_unit = *texture_unit;
let mut active_unit_enum = self.textures.active_unit_enum();
for (unit_enum, slot) in self.textures.iter() {
if let Some(target) = slot.unbind(texture) {
if unit_enum != active_unit_enum {
self.send_command(WebGLCommand::ActiveTexture(unit_enum));
active_unit_enum = unit_enum;
}
self.send_command(WebGLCommand::BindTexture(target, None));
}
}
// Restore bound texture unit if it has been changed.
if self.bound_texture_unit.get() != bound_unit {
self.send_command(WebGLCommand::ActiveTexture(self.bound_texture_unit.get()));
if active_unit_enum == self.textures.active_unit_enum() {
self.send_command(WebGLCommand::ActiveTexture(
self.textures.active_unit_enum(),
));
}
// From the GLES 2.0.25 spec, page 113:
@ -2394,6 +2324,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
if let Some(fb) = self.bound_framebuffer.get() {
fb.detach_texture(texture);
}
texture.delete()
}
}
@ -3944,12 +3875,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// Get bound texture
let texture = match self.bound_texture(constants::TEXTURE_2D) {
Some(texture) => texture,
None => {
return Ok(self.webgl_error(InvalidOperation));
}
};
let texture = handle_potential_webgl_error!(
self,
self.textures.active_texture_slot(constants::TEXTURE_2D).unwrap().get().ok_or(InvalidOperation),
return Ok(())
);
let pipeline_id = source.pipeline_id().ok_or(Error::InvalidState)?;
let document_id = self.global().downcast::<Window>().ok_or(Error::InvalidState)?.webrender_document();
@ -4257,3 +4187,87 @@ capabilities! {
SCISSOR_TEST,
STENCIL_TEST,
}
#[must_root]
#[derive(JSTraceable, MallocSizeOf)]
pub struct Textures {
active_unit: Cell<u32>,
units: Box<[TextureUnit]>,
}
impl Textures {
fn new(max_combined_textures: u32) -> Self {
Self {
active_unit: Default::default(),
units: (0..max_combined_textures).map(|_| Default::default()).collect::<Vec<_>>().into(),
}
}
fn active_unit_enum(&self) -> u32 {
self.active_unit.get() + constants::TEXTURE0
}
fn set_active_unit_enum(&self, index: u32) -> WebGLResult<()> {
if index < constants::TEXTURE0 || (index - constants::TEXTURE0) as usize > self.units.len() {
return Err(InvalidEnum);
}
self.active_unit.set(index - constants::TEXTURE0);
Ok(())
}
fn active_texture_slot(&self, target: u32) -> WebGLResult<&MutNullableDom<WebGLTexture>> {
let active_unit = self.active_unit();
match target {
constants::TEXTURE_2D => Ok(&active_unit.tex_2d),
constants::TEXTURE_CUBE_MAP => Ok(&active_unit.tex_cube_map),
_ => Err(InvalidEnum),
}
}
pub fn active_texture_for_image_target(
&self,
target: TexImageTarget,
) -> Option<DomRoot<WebGLTexture>> {
let active_unit = self.active_unit();
match target {
TexImageTarget::Texture2D => active_unit.tex_2d.get(),
TexImageTarget::CubeMapPositiveX |
TexImageTarget::CubeMapNegativeX |
TexImageTarget::CubeMapPositiveY |
TexImageTarget::CubeMapNegativeY |
TexImageTarget::CubeMapPositiveZ |
TexImageTarget::CubeMapNegativeZ => active_unit.tex_cube_map.get(),
}
}
fn active_unit(&self) -> &TextureUnit {
&self.units[self.active_unit.get() as usize]
}
fn iter(&self) -> impl Iterator<Item = (u32, &TextureUnit)> {
self.units.iter().enumerate().map(|(index, unit)| (index as u32 + constants::TEXTURE0, unit))
}
}
#[must_root]
#[derive(Default, JSTraceable, MallocSizeOf)]
struct TextureUnit {
tex_2d: MutNullableDom<WebGLTexture>,
tex_cube_map: MutNullableDom<WebGLTexture>,
}
impl TextureUnit {
fn unbind(&self, texture: &WebGLTexture) -> Option<u32> {
let fields = [
(&self.tex_2d, constants::TEXTURE_2D),
(&self.tex_cube_map, constants::TEXTURE_CUBE_MAP),
];
for &(slot, target) in &fields {
if slot.get().map_or(false, |t| texture == &*t) {
slot.set(None);
return Some(target);
}
}
None
}
}