mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
This allows keeping the VertexAttrib* calls asynchronous. Another option would be to do the validation in the apply() function, but that'd require us passing an unnecessary channel around and add extra synchronization. The counterpart of this is that it has to be updated when the context changes, but that's less problem.
162 lines
5.6 KiB
Rust
162 lines
5.6 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* 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/. */
|
|
|
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
|
|
use angle::hl::{BuiltInResources, Output, ShaderValidator};
|
|
use canvas_traits::CanvasMsg;
|
|
use dom::bindings::cell::DOMRefCell;
|
|
use dom::bindings::codegen::Bindings::WebGLShaderBinding;
|
|
use dom::bindings::global::GlobalRef;
|
|
use dom::bindings::js::Root;
|
|
use dom::bindings::reflector::reflect_dom_object;
|
|
use dom::webglobject::WebGLObject;
|
|
use ipc_channel::ipc::{self, IpcSender};
|
|
use std::cell::Cell;
|
|
use std::sync::{ONCE_INIT, Once};
|
|
use util::str::DOMString;
|
|
use webrender_traits::{WebGLCommand, WebGLParameter, WebGLResult};
|
|
|
|
#[derive(Clone, Copy, PartialEq, Debug, JSTraceable, HeapSizeOf)]
|
|
pub enum ShaderCompilationStatus {
|
|
NotCompiled,
|
|
Succeeded,
|
|
Failed,
|
|
}
|
|
|
|
#[dom_struct]
|
|
pub struct WebGLShader {
|
|
webgl_object: WebGLObject,
|
|
id: u32,
|
|
gl_type: u32,
|
|
source: DOMRefCell<Option<DOMString>>,
|
|
info_log: DOMRefCell<Option<String>>,
|
|
is_deleted: Cell<bool>,
|
|
compilation_status: Cell<ShaderCompilationStatus>,
|
|
#[ignore_heap_size_of = "Defined in ipc-channel"]
|
|
renderer: IpcSender<CanvasMsg>,
|
|
}
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
const SHADER_OUTPUT_FORMAT: Output = Output::Glsl;
|
|
|
|
#[cfg(target_os = "android")]
|
|
const SHADER_OUTPUT_FORMAT: Output = Output::Essl;
|
|
|
|
static GLSLANG_INITIALIZATION: Once = ONCE_INIT;
|
|
|
|
impl WebGLShader {
|
|
fn new_inherited(renderer: IpcSender<CanvasMsg>, id: u32, shader_type: u32) -> WebGLShader {
|
|
GLSLANG_INITIALIZATION.call_once(|| ::angle::hl::initialize().unwrap());
|
|
WebGLShader {
|
|
webgl_object: WebGLObject::new_inherited(),
|
|
id: id,
|
|
gl_type: shader_type,
|
|
source: DOMRefCell::new(None),
|
|
info_log: DOMRefCell::new(None),
|
|
is_deleted: Cell::new(false),
|
|
compilation_status: Cell::new(ShaderCompilationStatus::NotCompiled),
|
|
renderer: renderer,
|
|
}
|
|
}
|
|
|
|
pub fn maybe_new(global: GlobalRef,
|
|
renderer: IpcSender<CanvasMsg>,
|
|
shader_type: u32) -> Option<Root<WebGLShader>> {
|
|
let (sender, receiver) = ipc::channel().unwrap();
|
|
renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateShader(shader_type, sender))).unwrap();
|
|
|
|
let result = receiver.recv().unwrap();
|
|
result.map(|shader_id| WebGLShader::new(global, renderer, *shader_id, shader_type))
|
|
}
|
|
|
|
pub fn new(global: GlobalRef,
|
|
renderer: IpcSender<CanvasMsg>,
|
|
id: u32,
|
|
shader_type: u32) -> Root<WebGLShader> {
|
|
reflect_dom_object(
|
|
box WebGLShader::new_inherited(renderer, id, shader_type), global, WebGLShaderBinding::Wrap)
|
|
}
|
|
}
|
|
|
|
|
|
impl WebGLShader {
|
|
pub fn id(&self) -> u32 {
|
|
self.id
|
|
}
|
|
|
|
pub fn gl_type(&self) -> u32 {
|
|
self.gl_type
|
|
}
|
|
|
|
/// glCompileShader
|
|
pub fn compile(&self) {
|
|
if self.compilation_status.get() != ShaderCompilationStatus::NotCompiled {
|
|
debug!("Compiling already compiled shader {}", self.id);
|
|
}
|
|
|
|
if let Some(ref source) = *self.source.borrow() {
|
|
let validator = ShaderValidator::for_webgl(self.gl_type,
|
|
SHADER_OUTPUT_FORMAT,
|
|
&BuiltInResources::default()).unwrap();
|
|
match validator.compile_and_translate(&[source]) {
|
|
Ok(translated_source) => {
|
|
debug!("Shader translated: {}", translated_source);
|
|
// NOTE: At this point we should be pretty sure that the compilation in the paint thread
|
|
// will succeed.
|
|
// It could be interesting to retrieve the info log from the paint thread though
|
|
let msg = WebGLCommand::CompileShader(self.id, translated_source);
|
|
self.renderer.send(CanvasMsg::WebGL(msg)).unwrap();
|
|
self.compilation_status.set(ShaderCompilationStatus::Succeeded);
|
|
},
|
|
Err(error) => {
|
|
self.compilation_status.set(ShaderCompilationStatus::Failed);
|
|
debug!("Shader {} compilation failed: {}", self.id, error);
|
|
},
|
|
}
|
|
|
|
*self.info_log.borrow_mut() = Some(validator.info_log());
|
|
}
|
|
}
|
|
|
|
/// Mark this shader as deleted (if it wasn't previously)
|
|
/// and delete it as if calling glDeleteShader.
|
|
pub fn delete(&self) {
|
|
if !self.is_deleted.get() {
|
|
self.is_deleted.set(true);
|
|
let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteShader(self.id)));
|
|
}
|
|
}
|
|
|
|
/// glGetShaderInfoLog
|
|
pub fn info_log(&self) -> Option<String> {
|
|
self.info_log.borrow().clone()
|
|
}
|
|
|
|
/// glGetParameter
|
|
pub fn parameter(&self, param_id: u32) -> WebGLResult<WebGLParameter> {
|
|
let (sender, receiver) = ipc::channel().unwrap();
|
|
self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GetShaderParameter(self.id, param_id, sender))).unwrap();
|
|
receiver.recv().unwrap()
|
|
}
|
|
|
|
/// Get the shader source
|
|
pub fn source(&self) -> Option<DOMString> {
|
|
self.source.borrow().clone()
|
|
}
|
|
|
|
/// glShaderSource
|
|
pub fn set_source(&self, source: DOMString) {
|
|
*self.source.borrow_mut() = Some(source);
|
|
}
|
|
|
|
pub fn successfully_compiled(&self) -> bool {
|
|
self.compilation_status.get() == ShaderCompilationStatus::Succeeded
|
|
}
|
|
}
|
|
|
|
impl Drop for WebGLShader {
|
|
fn drop(&mut self) {
|
|
self.delete();
|
|
}
|
|
}
|