Skip to content

Commit

Permalink
Fix program and shader lifetime cycle
Browse files Browse the repository at this point in the history
  • Loading branch information
nox committed Jul 31, 2018
1 parent 43463e8 commit a0fc4c9
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 63 deletions.
79 changes: 51 additions & 28 deletions components/script/dom/webglprogram.rs
Expand Up @@ -25,7 +25,8 @@ use std::cell::{Cell, Ref};
pub struct WebGLProgram {
webgl_object: WebGLObject,
id: WebGLProgramId,
is_deleted: Cell<bool>,
is_in_use: Cell<bool>,
marked_for_deletion: Cell<bool>,
link_called: Cell<bool>,
linked: Cell<bool>,
link_generation: Cell<u64>,
Expand All @@ -40,9 +41,10 @@ impl WebGLProgram {
Self {
webgl_object: WebGLObject::new_inherited(context),
id: id,
is_deleted: Cell::new(false),
link_called: Cell::new(false),
linked: Cell::new(false),
is_in_use: Default::default(),
marked_for_deletion: Default::default(),
link_called: Default::default(),
linked: Default::default(),
link_generation: Default::default(),
fragment_shader: Default::default(),
vertex_shader: Default::default(),
Expand Down Expand Up @@ -73,36 +75,59 @@ impl WebGLProgram {
}

/// glDeleteProgram
pub fn delete(&self) {
if !self.is_deleted.get() {
self.is_deleted.set(true);
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::DeleteProgram(self.id));

if let Some(shader) = self.fragment_shader.get() {
shader.decrement_attached_counter();
}
pub fn mark_for_deletion(&self) {
if self.marked_for_deletion.get() {
return;
}
self.marked_for_deletion.set(true);
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::DeleteProgram(self.id));
if self.is_deleted() {
self.detach_shaders();
}
}

if let Some(shader) = self.vertex_shader.get() {
shader.decrement_attached_counter();
}
pub fn in_use(&self, value: bool) {
if self.is_in_use.get() == value {
return;
}
self.is_in_use.set(value);
if self.is_deleted() {
self.detach_shaders();
}
}

fn detach_shaders(&self) {
assert!(self.is_deleted());
if let Some(shader) = self.fragment_shader.get() {
shader.decrement_attached_counter();
self.fragment_shader.set(None);
}
if let Some(shader) = self.vertex_shader.get() {
shader.decrement_attached_counter();
self.vertex_shader.set(None);
}
}

pub fn is_in_use(&self) -> bool {
self.is_in_use.get()
}

pub fn is_marked_for_deletion(&self) -> bool {
self.marked_for_deletion.get()
}

pub fn is_deleted(&self) -> bool {
self.is_deleted.get()
self.marked_for_deletion.get() && !self.is_in_use.get()
}

pub fn is_linked(&self) -> bool {
self.linked.get()
}

/// glLinkProgram
pub fn link(&self) -> WebGLResult<()> {
if self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
pub fn link(&self) -> WebGLResult<()> {
self.linked.set(false);
self.link_generation.set(self.link_generation.get().checked_add(1).unwrap());
*self.active_attribs.borrow_mut() = Box::new([]);
Expand Down Expand Up @@ -214,10 +239,7 @@ impl WebGLProgram {
let shader_slot = match shader.gl_type() {
constants::FRAGMENT_SHADER => &self.fragment_shader,
constants::VERTEX_SHADER => &self.vertex_shader,
_ => {
error!("detachShader: Unexpected shader type");
return Err(WebGLError::InvalidValue);
}
_ => return Err(WebGLError::InvalidValue),
};

match shader_slot.get() {
Expand Down Expand Up @@ -389,7 +411,7 @@ impl WebGLProgram {
}

pub fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> {
if self.is_deleted.get() {
if self.marked_for_deletion.get() {
return Err(WebGLError::InvalidValue);
}
Ok(match (self.vertex_shader.get(), self.fragment_shader.get()) {
Expand All @@ -408,7 +430,8 @@ impl WebGLProgram {

impl Drop for WebGLProgram {
fn drop(&mut self) {
self.delete();
self.in_use(false);
self.mark_for_deletion();
}
}

Expand Down
29 changes: 20 additions & 9 deletions components/script/dom/webglrenderingcontext.rs
Expand Up @@ -2354,19 +2354,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn DeleteProgram(&self, program: Option<&WebGLProgram>) {
if let Some(program) = program {
handle_potential_webgl_error!(self, self.validate_ownership(program), return);
// FIXME: We should call glUseProgram(0), but
// WebGLCommand::UseProgram() doesn't take an Option
// currently. This is also a problem for useProgram(null)
handle_object_deletion!(self, self.current_program, program, None);
program.delete()
program.mark_for_deletion()
}
}

// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn DeleteShader(&self, shader: Option<&WebGLShader>) {
if let Some(shader) = shader {
handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
shader.delete()
shader.mark_for_deletion()
}
}

Expand Down Expand Up @@ -2700,8 +2696,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
unsafe fn GetProgramParameter(&self, _: *mut JSContext, program: &WebGLProgram, param: u32) -> JSVal {
handle_potential_webgl_error!(self, self.validate_ownership(program), return NullValue());
if program.is_deleted() {
self.webgl_error(InvalidOperation);
return NullValue();
}
match param {
constants::DELETE_STATUS => BooleanValue(program.is_deleted()),
constants::DELETE_STATUS => BooleanValue(program.is_marked_for_deletion()),
constants::LINK_STATUS => BooleanValue(program.is_linked()),
constants::VALIDATE_STATUS => {
// FIXME(nox): This could be cached on the DOM side when we call validateProgram
Expand Down Expand Up @@ -2733,7 +2733,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
unsafe fn GetShaderParameter(&self, _: *mut JSContext, shader: &WebGLShader, param: u32) -> JSVal {
handle_potential_webgl_error!(self, self.validate_ownership(shader), return NullValue());
if shader.is_deleted() && !shader.is_attached() {
if shader.is_deleted() {
self.webgl_error(InvalidValue);
return NullValue();
}
Expand Down Expand Up @@ -2903,7 +2903,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {

// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn IsShader(&self, shader: Option<&WebGLShader>) -> bool {
shader.map_or(false, |s| !s.is_deleted() || s.is_attached())
shader.map_or(false, |s| !s.is_deleted())
}

// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
Expand Down Expand Up @@ -3166,6 +3166,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn LinkProgram(&self, program: &WebGLProgram) {
handle_potential_webgl_error!(self, self.validate_ownership(program), return);
if program.is_deleted() {
return self.webgl_error(InvalidValue);
}
handle_potential_webgl_error!(self, program.link());
}

Expand Down Expand Up @@ -3721,6 +3724,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
if program.is_deleted() || !program.is_linked() {
return self.webgl_error(InvalidOperation);
}
if program.is_in_use() {
return;
}
program.in_use(true);
}
match self.current_program.get() {
Some(ref current) if program != Some(&**current) => current.in_use(false),
_ => {}
}
self.send_command(WebGLCommand::UseProgram(program.map(|p| p.id())));
self.current_program.set(program);
Expand Down
17 changes: 8 additions & 9 deletions components/script/dom/webglshader.rs
Expand Up @@ -38,7 +38,7 @@ pub struct WebGLShader {
gl_type: u32,
source: DomRefCell<DOMString>,
info_log: DomRefCell<DOMString>,
is_deleted: Cell<bool>,
marked_for_deletion: Cell<bool>,
attached_counter: Cell<u32>,
compilation_status: Cell<ShaderCompilationStatus>,
}
Expand All @@ -58,7 +58,7 @@ impl WebGLShader {
gl_type: shader_type,
source: Default::default(),
info_log: Default::default(),
is_deleted: Cell::new(false),
marked_for_deletion: Cell::new(false),
attached_counter: Cell::new(0),
compilation_status: Cell::new(ShaderCompilationStatus::NotCompiled),
}
Expand Down Expand Up @@ -101,7 +101,7 @@ impl WebGLShader {
limits: &GLLimits,
ext: &WebGLExtensions,
) -> WebGLResult<()> {
if self.is_deleted.get() && !self.is_attached() {
if self.marked_for_deletion.get() && !self.is_attached() {
return Err(WebGLError::InvalidValue);
}
if self.compilation_status.get() != ShaderCompilationStatus::NotCompiled {
Expand Down Expand Up @@ -183,17 +183,17 @@ impl WebGLShader {
/// Mark this shader as deleted (if it wasn't previously)
/// and delete it as if calling glDeleteShader.
/// Currently does not check if shader is attached
pub fn delete(&self) {
if !self.is_deleted.get() {
self.is_deleted.set(true);
pub fn mark_for_deletion(&self) {
if !self.marked_for_deletion.get() {
self.marked_for_deletion.set(true);
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::DeleteShader(self.id));
}
}

pub fn is_deleted(&self) -> bool {
self.is_deleted.get()
self.marked_for_deletion.get() && !self.is_attached()
}

pub fn is_attached(&self) -> bool {
Expand Down Expand Up @@ -231,7 +231,6 @@ impl WebGLShader {

impl Drop for WebGLShader {
fn drop(&mut self) {
assert_eq!(self.attached_counter.get(), 0);
self.delete();
self.mark_for_deletion();
}
}

This file was deleted.

This file was deleted.

0 comments on commit a0fc4c9

Please sign in to comment.