#![allow(unsafe_code)]
use std::borrow::Cow;
use std::num::NonZeroU32;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::{slice, thread};
use bitflags::bitflags;
use byteorder::{ByteOrder, NativeEndian, WriteBytesExt};
use canvas_traits::webgl;
#[cfg(feature = "webxr")]
use canvas_traits::webgl::WebXRCommand;
use canvas_traits::webgl::{
ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, AlphaTreatment,
GLContextAttributes, GLLimits, GlType, InternalFormatIntVec, ProgramLinkInfo, TexDataType,
TexFormat, WebGLBufferId, WebGLChan, WebGLCommand, WebGLCommandBacktrace, WebGLContextId,
WebGLCreateContextResult, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLMsg,
WebGLMsgSender, WebGLProgramId, WebGLQueryId, WebGLReceiver, WebGLRenderbufferId,
WebGLSLVersion, WebGLSamplerId, WebGLSender, WebGLShaderId, WebGLSyncId, WebGLTextureId,
WebGLVersion, WebGLVertexArrayId, YAxisTreatment,
};
use euclid::default::Size2D;
use fnv::FnvHashMap;
use glow::{
self as gl, bytes_per_type, components_per_format, ActiveTransformFeedback, Context as Gl,
HasContext, NativeFramebuffer, NativeTransformFeedback, NativeUniformLocation,
NativeVertexArray, PixelUnpackData, ShaderPrecisionFormat,
};
use half::f16;
use log::{debug, error, trace, warn};
use pixels::{self, unmultiply_inplace, PixelFormat};
use surfman::chains::{PreserveBuffer, SwapChains, SwapChainsAPI};
use surfman::{
self, Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device,
GLVersion, SurfaceAccess, SurfaceInfo, SurfaceType,
};
use webrender::{RenderApi, RenderApiSender, Transaction};
use webrender_api::units::DeviceIntSize;
use webrender_api::{
DirtyRect, DocumentId, ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind,
ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey,
};
use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType};
use crate::webgl_limits::GLLimitsDetect;
#[cfg(feature = "webxr")]
use crate::webxr::{WebXRBridge, WebXRBridgeContexts, WebXRBridgeInit};
type GLuint = u32;
type GLint = i32;
fn native_uniform_location(location: i32) -> Option<NativeUniformLocation> {
location.try_into().ok().map(NativeUniformLocation)
}
pub(crate) struct GLContextData {
pub(crate) ctx: Context,
pub(crate) gl: Rc<glow::Context>,
state: GLState,
attributes: GLContextAttributes,
}
#[derive(Debug)]
pub struct GLState {
_webgl_version: WebGLVersion,
_gl_version: GLVersion,
requested_flags: ContextAttributeFlags,
color_write_mask: [bool; 4],
clear_color: (f32, f32, f32, f32),
scissor_test_enabled: bool,
stencil_write_mask: (u32, u32),
stencil_test_enabled: bool,
stencil_clear_value: i32,
depth_write_mask: bool,
depth_test_enabled: bool,
depth_clear_value: f64,
drawing_to_default_framebuffer: bool,
default_vao: Option<NativeVertexArray>,
}
impl GLState {
fn fake_no_alpha(&self) -> bool {
self.drawing_to_default_framebuffer &
!self.requested_flags.contains(ContextAttributeFlags::ALPHA)
}
fn fake_no_depth(&self) -> bool {
self.drawing_to_default_framebuffer &
!self.requested_flags.contains(ContextAttributeFlags::DEPTH)
}
fn fake_no_stencil(&self) -> bool {
self.drawing_to_default_framebuffer &
!self
.requested_flags
.contains(ContextAttributeFlags::STENCIL)
}
fn restore_invariant(&self, gl: &Gl) {
self.restore_clear_color_invariant(gl);
self.restore_scissor_invariant(gl);
self.restore_alpha_invariant(gl);
self.restore_depth_invariant(gl);
self.restore_stencil_invariant(gl);
}
fn restore_clear_color_invariant(&self, gl: &Gl) {
let (r, g, b, a) = self.clear_color;
unsafe { gl.clear_color(r, g, b, a) };
}
fn restore_scissor_invariant(&self, gl: &Gl) {
if self.scissor_test_enabled {
unsafe { gl.enable(gl::SCISSOR_TEST) };
} else {
unsafe { gl.disable(gl::SCISSOR_TEST) };
}
}
fn restore_alpha_invariant(&self, gl: &Gl) {
let [r, g, b, a] = self.color_write_mask;
if self.fake_no_alpha() {
unsafe { gl.color_mask(r, g, b, false) };
} else {
unsafe { gl.color_mask(r, g, b, a) };
}
}
fn restore_depth_invariant(&self, gl: &Gl) {
unsafe {
if self.fake_no_depth() {
gl.depth_mask(false);
gl.disable(gl::DEPTH_TEST);
} else {
gl.depth_mask(self.depth_write_mask);
if self.depth_test_enabled {
gl.enable(gl::DEPTH_TEST);
} else {
gl.disable(gl::DEPTH_TEST);
}
}
}
}
fn restore_stencil_invariant(&self, gl: &Gl) {
unsafe {
if self.fake_no_stencil() {
gl.stencil_mask(0);
gl.disable(gl::STENCIL_TEST);
} else {
let (f, b) = self.stencil_write_mask;
gl.stencil_mask_separate(gl::FRONT, f);
gl.stencil_mask_separate(gl::BACK, b);
if self.stencil_test_enabled {
gl.enable(gl::STENCIL_TEST);
} else {
gl.disable(gl::STENCIL_TEST);
}
}
}
}
}
impl Default for GLState {
fn default() -> GLState {
GLState {
_gl_version: GLVersion { major: 1, minor: 0 },
_webgl_version: WebGLVersion::WebGL1,
requested_flags: ContextAttributeFlags::empty(),
color_write_mask: [true, true, true, true],
clear_color: (0., 0., 0., 0.),
scissor_test_enabled: false,
stencil_write_mask: (0, 0),
stencil_test_enabled: false,
stencil_clear_value: 0,
depth_write_mask: true,
depth_test_enabled: false,
depth_clear_value: 1.,
default_vao: None,
drawing_to_default_framebuffer: true,
}
}
}
pub(crate) struct WebGLThread {
device: Device,
webrender_api: RenderApi,
webrender_doc: DocumentId,
contexts: FnvHashMap<WebGLContextId, GLContextData>,
cached_context_info: FnvHashMap<WebGLContextId, WebGLContextInfo>,
bound_context_id: Option<WebGLContextId>,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
receiver: crossbeam_channel::Receiver<WebGLMsg>,
sender: WebGLSender<WebGLMsg>,
webrender_swap_chains: SwapChains<WebGLContextId, Device>,
api_type: GlType,
#[cfg(feature = "webxr")]
pub webxr_bridge: WebXRBridge,
}
pub(crate) struct WebGLThreadInit {
pub webrender_api_sender: RenderApiSender,
pub webrender_doc: DocumentId,
pub external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
pub sender: WebGLSender<WebGLMsg>,
pub receiver: WebGLReceiver<WebGLMsg>,
pub webrender_swap_chains: SwapChains<WebGLContextId, Device>,
pub connection: Connection,
pub adapter: Adapter,
pub api_type: GlType,
#[cfg(feature = "webxr")]
pub webxr_init: WebXRBridgeInit,
}
const SAFE_VIEWPORT_DIMS: [u32; 2] = [1024, 1024];
impl WebGLThread {
pub(crate) fn new(
WebGLThreadInit {
webrender_api_sender,
webrender_doc,
external_images,
sender,
receiver,
webrender_swap_chains,
connection,
adapter,
api_type,
#[cfg(feature = "webxr")]
webxr_init,
}: WebGLThreadInit,
) -> Self {
WebGLThread {
device: connection
.create_device(&adapter)
.expect("Couldn't open WebGL device!"),
webrender_api: webrender_api_sender.create_api(),
webrender_doc,
contexts: Default::default(),
cached_context_info: Default::default(),
bound_context_id: None,
external_images,
sender,
receiver: receiver.into_inner(),
webrender_swap_chains,
api_type,
#[cfg(feature = "webxr")]
webxr_bridge: WebXRBridge::new(webxr_init),
}
}
pub(crate) fn run_on_own_thread(init: WebGLThreadInit) {
thread::Builder::new()
.name("WebGL".to_owned())
.spawn(move || {
let mut data = WebGLThread::new(init);
data.process();
})
.expect("Thread spawning failed");
}
fn process(&mut self) {
let webgl_chan = WebGLChan(self.sender.clone());
while let Ok(msg) = self.receiver.recv() {
let exit = self.handle_msg(msg, &webgl_chan);
if exit {
let context_ids: Vec<WebGLContextId> = self.contexts.keys().copied().collect();
for id in context_ids {
self.remove_webgl_context(id);
}
self.webrender_api.shut_down(true);
return;
}
}
}
fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool {
trace!("processing {:?}", msg);
match msg {
WebGLMsg::CreateContext(version, size, attributes, result_sender) => {
let result = self.create_webgl_context(version, size, attributes);
result_sender
.send(result.map(|(id, limits)| {
let image_key = self
.cached_context_info
.get_mut(&id)
.expect("Where's the cached context info?")
.image_key;
let data = Self::make_current_if_needed(
&self.device,
id,
&self.contexts,
&mut self.bound_context_id,
)
.expect("WebGLContext not found");
let glsl_version = Self::get_glsl_version(&data.gl);
let api_type = if data.gl.version().is_embedded {
GlType::Gles
} else {
GlType::Gl
};
if api_type != GlType::Gles {
unsafe {
const GL_POINT_SPRITE: u32 = 0x8861;
data.gl.enable(GL_POINT_SPRITE);
let err = data.gl.get_error();
if err != 0 {
warn!("Error enabling GL point sprites: {}", err);
}
data.gl.enable(gl::PROGRAM_POINT_SIZE);
let err = data.gl.get_error();
if err != 0 {
warn!("Error enabling GL program point size: {}", err);
}
}
}
WebGLCreateContextResult {
sender: WebGLMsgSender::new(id, webgl_chan.clone()),
limits,
glsl_version,
api_type,
image_key,
}
}))
.unwrap();
},
WebGLMsg::ResizeContext(ctx_id, size, sender) => {
let _ = sender.send(self.resize_webgl_context(ctx_id, size));
},
WebGLMsg::RemoveContext(ctx_id) => {
self.remove_webgl_context(ctx_id);
},
WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => {
self.handle_webgl_command(ctx_id, command, backtrace);
},
WebGLMsg::WebXRCommand(_command) => {
#[cfg(feature = "webxr")]
self.handle_webxr_command(_command);
},
WebGLMsg::SwapBuffers(swap_ids, sender, sent_time) => {
self.handle_swap_buffers(swap_ids, sender, sent_time);
},
WebGLMsg::Exit(sender) => {
if let Err(e) = sender.send(()) {
warn!("Failed to send response to WebGLMsg::Exit ({e})");
}
return true;
},
}
false
}
#[cfg(feature = "webxr")]
fn handle_webxr_command(&mut self, command: WebXRCommand) {
trace!("processing {:?}", command);
let mut contexts = WebXRBridgeContexts {
contexts: &mut self.contexts,
bound_context_id: &mut self.bound_context_id,
};
match command {
WebXRCommand::CreateLayerManager(sender) => {
let result = self
.webxr_bridge
.create_layer_manager(&mut self.device, &mut contexts);
let _ = sender.send(result);
},
WebXRCommand::DestroyLayerManager(manager_id) => {
self.webxr_bridge.destroy_layer_manager(manager_id);
},
WebXRCommand::CreateLayer(manager_id, context_id, layer_init, sender) => {
let result = self.webxr_bridge.create_layer(
manager_id,
&mut self.device,
&mut contexts,
context_id,
layer_init,
);
let _ = sender.send(result);
},
WebXRCommand::DestroyLayer(manager_id, context_id, layer_id) => {
self.webxr_bridge.destroy_layer(
manager_id,
&mut self.device,
&mut contexts,
context_id,
layer_id,
);
},
WebXRCommand::BeginFrame(manager_id, layers, sender) => {
let result = self.webxr_bridge.begin_frame(
manager_id,
&mut self.device,
&mut contexts,
&layers[..],
);
let _ = sender.send(result);
},
WebXRCommand::EndFrame(manager_id, layers, sender) => {
let result = self.webxr_bridge.end_frame(
manager_id,
&mut self.device,
&mut contexts,
&layers[..],
);
let _ = sender.send(result);
},
}
}
fn handle_webgl_command(
&mut self,
context_id: WebGLContextId,
command: WebGLCommand,
backtrace: WebGLCommandBacktrace,
) {
if self.cached_context_info.get_mut(&context_id).is_none() {
return;
}
let data = Self::make_current_if_needed_mut(
&self.device,
context_id,
&mut self.contexts,
&mut self.bound_context_id,
);
if let Some(data) = data {
WebGLImpl::apply(
&self.device,
&data.ctx,
&data.gl,
&mut data.state,
&data.attributes,
command,
backtrace,
);
}
}
fn create_webgl_context(
&mut self,
webgl_version: WebGLVersion,
requested_size: Size2D<u32>,
attributes: GLContextAttributes,
) -> Result<(WebGLContextId, webgl::GLLimits), String> {
debug!(
"WebGLThread::create_webgl_context({:?}, {:?}, {:?})",
webgl_version, requested_size, attributes
);
self.bound_context_id = None;
let requested_flags =
attributes.to_surfman_context_attribute_flags(webgl_version, self.api_type);
let flags = requested_flags |
ContextAttributeFlags::ALPHA |
ContextAttributeFlags::DEPTH |
ContextAttributeFlags::STENCIL;
let context_attributes = &ContextAttributes {
version: webgl_version.to_surfman_version(self.api_type),
flags,
};
let context_descriptor = self
.device
.create_context_descriptor(context_attributes)
.map_err(|err| format!("Failed to create context descriptor: {:?}", err))?;
let safe_size = Size2D::new(
requested_size.width.min(SAFE_VIEWPORT_DIMS[0]).max(1),
requested_size.height.min(SAFE_VIEWPORT_DIMS[1]).max(1),
);
let surface_type = SurfaceType::Generic {
size: safe_size.to_i32(),
};
let surface_access = self.surface_access();
let mut ctx = self
.device
.create_context(&context_descriptor, None)
.map_err(|err| format!("Failed to create the GL context: {:?}", err))?;
let surface = self
.device
.create_surface(&ctx, surface_access, surface_type)
.map_err(|err| format!("Failed to create the initial surface: {:?}", err))?;
self.device
.bind_surface_to_context(&mut ctx, surface)
.map_err(|err| format!("Failed to bind initial surface: {:?}", err))?;
self.device
.make_context_current(&ctx)
.map_err(|err| format!("Failed to make new context current: {:?}", err))?;
let id = WebGLContextId(
self.external_images
.lock()
.expect("Lock poisoned?")
.next_id(WebrenderImageHandlerType::WebGL)
.0,
);
self.webrender_swap_chains
.create_attached_swap_chain(id, &mut self.device, &mut ctx, surface_access)
.map_err(|err| format!("Failed to create swap chain: {:?}", err))?;
let swap_chain = self
.webrender_swap_chains
.get(id)
.expect("Failed to get the swap chain");
debug!(
"Created webgl context {:?}/{:?}",
id,
self.device.context_id(&ctx),
);
let gl = unsafe {
Rc::new(match self.api_type {
GlType::Gl => glow::Context::from_loader_function(|symbol_name| {
self.device.get_proc_address(&ctx, symbol_name)
}),
GlType::Gles => glow::Context::from_loader_function(|symbol_name| {
self.device.get_proc_address(&ctx, symbol_name)
}),
})
};
let limits = GLLimits::detect(&gl, webgl_version);
let size = clamp_viewport(&gl, requested_size);
if safe_size != size {
debug!("Resizing swap chain from {:?} to {:?}", safe_size, size);
swap_chain
.resize(&mut self.device, &mut ctx, size.to_i32())
.map_err(|err| format!("Failed to resize swap chain: {:?}", err))?;
}
let descriptor = self.device.context_descriptor(&ctx);
let descriptor_attributes = self.device.context_descriptor_attributes(&descriptor);
let gl_version = descriptor_attributes.version;
let has_alpha = requested_flags.contains(ContextAttributeFlags::ALPHA);
let image_buffer_kind = current_wr_image_buffer_kind(&self.device);
self.device.make_context_current(&ctx).unwrap();
let framebuffer = self
.device
.context_surface_info(&ctx)
.map_err(|err| format!("Failed to get context surface info: {:?}", err))?
.ok_or_else(|| "Failed to get context surface info".to_string())?
.framebuffer_object;
unsafe {
gl.bind_framebuffer(
gl::FRAMEBUFFER,
NonZeroU32::new(framebuffer).map(NativeFramebuffer),
);
gl.viewport(0, 0, size.width as i32, size.height as i32);
gl.scissor(0, 0, size.width as i32, size.height as i32);
gl.clear_color(0., 0., 0., !has_alpha as u32 as f32);
gl.clear_depth(1.);
gl.clear_stencil(0);
gl.clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
gl.clear_color(0., 0., 0., 0.);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
}
let default_vao = if let Some(vao) = WebGLImpl::create_vertex_array(&gl) {
WebGLImpl::bind_vertex_array(&gl, Some(vao.glow()));
Some(vao.glow())
} else {
None
};
let state = GLState {
_gl_version: gl_version,
_webgl_version: webgl_version,
requested_flags,
default_vao,
..Default::default()
};
debug!("Created state {:?}", state);
state.restore_invariant(&gl);
debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
self.contexts.insert(
id,
GLContextData {
ctx,
gl,
state,
attributes,
},
);
let image_key = Self::create_wr_external_image(
&mut self.webrender_api,
self.webrender_doc,
size.to_i32(),
has_alpha,
id,
image_buffer_kind,
);
self.cached_context_info
.insert(id, WebGLContextInfo { image_key });
Ok((id, limits))
}
fn resize_webgl_context(
&mut self,
context_id: WebGLContextId,
requested_size: Size2D<u32>,
) -> Result<(), String> {
let data = Self::make_current_if_needed_mut(
&self.device,
context_id,
&mut self.contexts,
&mut self.bound_context_id,
)
.expect("Missing WebGL context!");
let size = clamp_viewport(&data.gl, requested_size);
let framebuffer_rebinding_info =
FramebufferRebindingInfo::detect(&self.device, &data.ctx, &data.gl);
if let Some(swap_chain) = self.webrender_swap_chains.get(context_id) {
let alpha = data
.state
.requested_flags
.contains(ContextAttributeFlags::ALPHA);
let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32];
swap_chain
.resize(&mut self.device, &mut data.ctx, size.to_i32())
.map_err(|err| format!("Failed to resize swap chain: {:?}", err))?;
swap_chain
.clear_surface(&mut self.device, &mut data.ctx, &data.gl, clear_color)
.map_err(|err| format!("Failed to clear resized swap chain: {:?}", err))?;
} else {
error!("Failed to find swap chain");
}
framebuffer_rebinding_info.apply(&self.device, &data.ctx, &data.gl);
debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
let has_alpha = data
.state
.requested_flags
.contains(ContextAttributeFlags::ALPHA);
self.update_wr_image_for_context(context_id, size.to_i32(), has_alpha);
Ok(())
}
fn remove_webgl_context(&mut self, context_id: WebGLContextId) {
if let Some(info) = self.cached_context_info.remove(&context_id) {
let mut txn = Transaction::new();
txn.delete_image(info.image_key);
self.webrender_api.send_transaction(self.webrender_doc, txn)
}
Self::make_current_if_needed(
&self.device,
context_id,
&self.contexts,
&mut self.bound_context_id,
);
#[cfg(feature = "webxr")]
{
let webxr_context_id = webxr_api::ContextId::from(context_id);
let mut webxr_contexts = WebXRBridgeContexts {
contexts: &mut self.contexts,
bound_context_id: &mut self.bound_context_id,
};
self.webxr_bridge.destroy_all_layers(
&mut self.device,
&mut webxr_contexts,
webxr_context_id,
);
}
let mut data = match self.contexts.remove(&context_id) {
Some(data) => data,
None => return,
};
self.webrender_swap_chains
.destroy(context_id, &mut self.device, &mut data.ctx)
.unwrap();
self.device.destroy_context(&mut data.ctx).unwrap();
self.bound_context_id = None;
}
fn handle_swap_buffers(
&mut self,
context_ids: Vec<WebGLContextId>,
completed_sender: WebGLSender<u64>,
_sent_time: u64,
) {
debug!("handle_swap_buffers()");
for context_id in context_ids {
let data = Self::make_current_if_needed_mut(
&self.device,
context_id,
&mut self.contexts,
&mut self.bound_context_id,
)
.expect("Where's the GL data?");
debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
let framebuffer_rebinding_info =
FramebufferRebindingInfo::detect(&self.device, &data.ctx, &data.gl);
debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
debug!("Getting swap chain for {:?}", context_id);
let swap_chain = self
.webrender_swap_chains
.get(context_id)
.expect("Where's the swap chain?");
debug!("Swapping {:?}", context_id);
swap_chain
.swap_buffers(
&mut self.device,
&mut data.ctx,
if data.attributes.preserve_drawing_buffer {
PreserveBuffer::Yes(&data.gl)
} else {
PreserveBuffer::No
},
)
.unwrap();
debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
if !data.attributes.preserve_drawing_buffer {
debug!("Clearing {:?}", context_id);
let alpha = data
.state
.requested_flags
.contains(ContextAttributeFlags::ALPHA);
let clear_color = [0.0, 0.0, 0.0, !alpha as i32 as f32];
swap_chain
.clear_surface(&mut self.device, &mut data.ctx, &data.gl, clear_color)
.unwrap();
debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
}
debug!("Rebinding {:?}", context_id);
framebuffer_rebinding_info.apply(&self.device, &data.ctx, &data.gl);
debug_assert_eq!(unsafe { data.gl.get_error() }, gl::NO_ERROR);
let SurfaceInfo {
size,
framebuffer_object,
id,
..
} = self
.device
.context_surface_info(&data.ctx)
.unwrap()
.unwrap();
debug!(
"... rebound framebuffer {}, new back buffer surface is {:?}",
framebuffer_object, id
);
let has_alpha = data
.state
.requested_flags
.contains(ContextAttributeFlags::ALPHA);
self.update_wr_image_for_context(context_id, size, has_alpha);
}
#[allow(unused)]
let mut end_swap = 0;
completed_sender.send(end_swap).unwrap();
}
fn surface_access(&self) -> SurfaceAccess {
SurfaceAccess::GPUOnly
}
pub(crate) fn make_current_if_needed<'a>(
device: &Device,
context_id: WebGLContextId,
contexts: &'a FnvHashMap<WebGLContextId, GLContextData>,
bound_id: &mut Option<WebGLContextId>,
) -> Option<&'a GLContextData> {
let data = contexts.get(&context_id);
if let Some(data) = data {
if Some(context_id) != *bound_id {
device.make_context_current(&data.ctx).unwrap();
*bound_id = Some(context_id);
}
}
data
}
pub(crate) fn make_current_if_needed_mut<'a>(
device: &Device,
context_id: WebGLContextId,
contexts: &'a mut FnvHashMap<WebGLContextId, GLContextData>,
bound_id: &mut Option<WebGLContextId>,
) -> Option<&'a mut GLContextData> {
let data = contexts.get_mut(&context_id);
if let Some(ref data) = data {
if Some(context_id) != *bound_id {
device.make_context_current(&data.ctx).unwrap();
*bound_id = Some(context_id);
}
}
data
}
fn create_wr_external_image(
webrender_api: &mut RenderApi,
webrender_doc: DocumentId,
size: Size2D<i32>,
alpha: bool,
context_id: WebGLContextId,
image_buffer_kind: ImageBufferKind,
) -> ImageKey {
let descriptor = Self::image_descriptor(size, alpha);
let data = Self::external_image_data(context_id, image_buffer_kind);
let image_key = webrender_api.generate_image_key();
let mut txn = Transaction::new();
txn.add_image(image_key, descriptor, data, None);
webrender_api.send_transaction(webrender_doc, txn);
image_key
}
fn update_wr_image_for_context(
&mut self,
context_id: WebGLContextId,
size: Size2D<i32>,
has_alpha: bool,
) {
let info = self.cached_context_info.get(&context_id).unwrap();
let image_buffer_kind = current_wr_image_buffer_kind(&self.device);
let descriptor = Self::image_descriptor(size, has_alpha);
let image_data = Self::external_image_data(context_id, image_buffer_kind);
let mut txn = Transaction::new();
txn.update_image(info.image_key, descriptor, image_data, &DirtyRect::All);
self.webrender_api.send_transaction(self.webrender_doc, txn);
}
fn image_descriptor(size: Size2D<i32>, alpha: bool) -> ImageDescriptor {
let mut flags = ImageDescriptorFlags::empty();
flags.set(ImageDescriptorFlags::IS_OPAQUE, !alpha);
ImageDescriptor {
size: DeviceIntSize::new(size.width, size.height),
stride: None,
format: ImageFormat::BGRA8,
offset: 0,
flags,
}
}
fn external_image_data(
context_id: WebGLContextId,
image_buffer_kind: ImageBufferKind,
) -> ImageData {
let data = ExternalImageData {
id: ExternalImageId(context_id.0),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(image_buffer_kind),
};
ImageData::External(data)
}
fn get_glsl_version(gl: &Gl) -> WebGLSLVersion {
let version = unsafe { gl.get_parameter_string(gl::SHADING_LANGUAGE_VERSION) };
let mut values = version.split(&['.', ' '][..]);
let major = values
.next()
.and_then(|v| v.parse::<u32>().ok())
.unwrap_or(1);
let minor = values
.next()
.and_then(|v| v.parse::<u32>().ok())
.unwrap_or(20);
WebGLSLVersion { major, minor }
}
}
struct WebGLContextInfo {
image_key: ImageKey,
}
fn current_wr_image_buffer_kind(device: &Device) -> ImageBufferKind {
match device.surface_gl_texture_target() {
gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect,
_ => ImageBufferKind::Texture2D,
}
}
pub struct WebGLImpl;
impl WebGLImpl {
pub fn apply(
device: &Device,
ctx: &Context,
gl: &Gl,
state: &mut GLState,
attributes: &GLContextAttributes,
command: WebGLCommand,
_backtrace: WebGLCommandBacktrace,
) {
debug!("WebGLImpl::apply({:?})", command);
debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
match command {
WebGLCommand::GetContextAttributes(ref sender) => sender.send(*attributes).unwrap(),
WebGLCommand::ActiveTexture(target) => unsafe { gl.active_texture(target) },
WebGLCommand::AttachShader(program_id, shader_id) => unsafe {
gl.attach_shader(program_id.glow(), shader_id.glow())
},
WebGLCommand::DetachShader(program_id, shader_id) => unsafe {
gl.detach_shader(program_id.glow(), shader_id.glow())
},
WebGLCommand::BindAttribLocation(program_id, index, ref name) => unsafe {
gl.bind_attrib_location(program_id.glow(), index, &to_name_in_compiled_shader(name))
},
WebGLCommand::BlendColor(r, g, b, a) => unsafe { gl.blend_color(r, g, b, a) },
WebGLCommand::BlendEquation(mode) => unsafe { gl.blend_equation(mode) },
WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) => unsafe {
gl.blend_equation_separate(mode_rgb, mode_alpha)
},
WebGLCommand::BlendFunc(src, dest) => unsafe { gl.blend_func(src, dest) },
WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) => unsafe {
gl.blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha)
},
WebGLCommand::BufferData(buffer_type, ref receiver, usage) => unsafe {
gl.buffer_data_u8_slice(buffer_type, &receiver.recv().unwrap(), usage)
},
WebGLCommand::BufferSubData(buffer_type, offset, ref receiver) => unsafe {
gl.buffer_sub_data_u8_slice(buffer_type, offset as i32, &receiver.recv().unwrap())
},
WebGLCommand::CopyBufferSubData(src, dst, src_offset, dst_offset, size) => {
unsafe {
gl.copy_buffer_sub_data(
src,
dst,
src_offset as i32,
dst_offset as i32,
size as i32,
)
};
},
WebGLCommand::GetBufferSubData(buffer_type, offset, length, ref sender) => unsafe {
let ptr = gl.map_buffer_range(
buffer_type,
offset as i32,
length as i32,
gl::MAP_READ_BIT,
);
let data: &[u8] = slice::from_raw_parts(ptr as _, length);
sender.send(data).unwrap();
gl.unmap_buffer(buffer_type);
},
WebGLCommand::Clear(mask) => {
unsafe { gl.clear(mask) };
},
WebGLCommand::ClearColor(r, g, b, a) => {
state.clear_color = (r, g, b, a);
unsafe { gl.clear_color(r, g, b, a) };
},
WebGLCommand::ClearDepth(depth) => {
let value = depth.clamp(0., 1.) as f64;
state.depth_clear_value = value;
unsafe { gl.clear_depth(value) }
},
WebGLCommand::ClearStencil(stencil) => {
state.stencil_clear_value = stencil;
unsafe { gl.clear_stencil(stencil) };
},
WebGLCommand::ColorMask(r, g, b, a) => {
state.color_write_mask = [r, g, b, a];
state.restore_alpha_invariant(gl);
},
WebGLCommand::CopyTexImage2D(
target,
level,
internal_format,
x,
y,
width,
height,
border,
) => unsafe {
gl.copy_tex_image_2d(target, level, internal_format, x, y, width, height, border)
},
WebGLCommand::CopyTexSubImage2D(
target,
level,
xoffset,
yoffset,
x,
y,
width,
height,
) => unsafe {
gl.copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height)
},
WebGLCommand::CullFace(mode) => unsafe { gl.cull_face(mode) },
WebGLCommand::DepthFunc(func) => unsafe { gl.depth_func(func) },
WebGLCommand::DepthMask(flag) => {
state.depth_write_mask = flag;
state.restore_depth_invariant(gl);
},
WebGLCommand::DepthRange(near, far) => unsafe {
gl.depth_range(near.clamp(0., 1.) as f64, far.clamp(0., 1.) as f64)
},
WebGLCommand::Disable(cap) => match cap {
gl::SCISSOR_TEST => {
state.scissor_test_enabled = false;
state.restore_scissor_invariant(gl);
},
gl::DEPTH_TEST => {
state.depth_test_enabled = false;
state.restore_depth_invariant(gl);
},
gl::STENCIL_TEST => {
state.stencil_test_enabled = false;
state.restore_stencil_invariant(gl);
},
_ => unsafe { gl.disable(cap) },
},
WebGLCommand::Enable(cap) => match cap {
gl::SCISSOR_TEST => {
state.scissor_test_enabled = true;
state.restore_scissor_invariant(gl);
},
gl::DEPTH_TEST => {
state.depth_test_enabled = true;
state.restore_depth_invariant(gl);
},
gl::STENCIL_TEST => {
state.stencil_test_enabled = true;
state.restore_stencil_invariant(gl);
},
_ => unsafe { gl.enable(cap) },
},
WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) => {
let attach = |attachment| unsafe {
gl.framebuffer_renderbuffer(
target,
attachment,
renderbuffertarget,
rb.map(WebGLRenderbufferId::glow),
)
};
if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
attach(gl::DEPTH_ATTACHMENT);
attach(gl::STENCIL_ATTACHMENT);
} else {
attach(attachment);
}
},
WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) => {
let attach = |attachment| unsafe {
gl.framebuffer_texture_2d(
target,
attachment,
textarget,
texture.map(WebGLTextureId::glow),
level,
)
};
if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
attach(gl::DEPTH_ATTACHMENT);
attach(gl::STENCIL_ATTACHMENT);
} else {
attach(attachment)
}
},
WebGLCommand::FrontFace(mode) => unsafe { gl.front_face(mode) },
WebGLCommand::DisableVertexAttribArray(attrib_id) => unsafe {
gl.disable_vertex_attrib_array(attrib_id)
},
WebGLCommand::EnableVertexAttribArray(attrib_id) => unsafe {
gl.enable_vertex_attrib_array(attrib_id)
},
WebGLCommand::Hint(name, val) => unsafe { gl.hint(name, val) },
WebGLCommand::LineWidth(width) => {
unsafe { gl.line_width(width) };
if width != 1.0 {
let _ = unsafe { gl.get_error() };
}
},
WebGLCommand::PixelStorei(name, val) => unsafe { gl.pixel_store_i32(name, val) },
WebGLCommand::PolygonOffset(factor, units) => unsafe {
gl.polygon_offset(factor, units)
},
WebGLCommand::ReadPixels(rect, format, pixel_type, ref sender) => {
let len = bytes_per_type(pixel_type) *
components_per_format(format) *
rect.size.area() as usize;
let mut pixels = vec![0; len];
unsafe {
gl.pixel_store_i32(glow::PACK_ALIGNMENT, 1);
gl.read_pixels(
rect.origin.x as i32,
rect.origin.y as i32,
rect.size.width as i32,
rect.size.height as i32,
format,
pixel_type,
glow::PixelPackData::Slice(Some(&mut pixels)),
)
};
sender.send(&pixels).unwrap();
},
WebGLCommand::ReadPixelsPP(rect, format, pixel_type, offset) => unsafe {
gl.read_pixels(
rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height,
format,
pixel_type,
glow::PixelPackData::BufferOffset(offset as u32),
);
},
WebGLCommand::RenderbufferStorage(target, format, width, height) => unsafe {
gl.renderbuffer_storage(target, format, width, height)
},
WebGLCommand::RenderbufferStorageMultisample(
target,
samples,
format,
width,
height,
) => unsafe {
gl.renderbuffer_storage_multisample(target, samples, format, width, height)
},
WebGLCommand::SampleCoverage(value, invert) => unsafe {
gl.sample_coverage(value, invert)
},
WebGLCommand::Scissor(x, y, width, height) => {
unsafe { gl.scissor(x, y, width as i32, height as i32) };
},
WebGLCommand::StencilFunc(func, ref_, mask) => unsafe {
gl.stencil_func(func, ref_, mask)
},
WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) => unsafe {
gl.stencil_func_separate(face, func, ref_, mask)
},
WebGLCommand::StencilMask(mask) => {
state.stencil_write_mask = (mask, mask);
state.restore_stencil_invariant(gl);
},
WebGLCommand::StencilMaskSeparate(face, mask) => {
if face == gl::FRONT {
state.stencil_write_mask.0 = mask;
} else {
state.stencil_write_mask.1 = mask;
}
state.restore_stencil_invariant(gl);
},
WebGLCommand::StencilOp(fail, zfail, zpass) => unsafe {
gl.stencil_op(fail, zfail, zpass)
},
WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) => unsafe {
gl.stencil_op_separate(face, fail, zfail, zpass)
},
WebGLCommand::GetRenderbufferParameter(target, pname, ref chan) => {
Self::get_renderbuffer_parameter(gl, target, pname, chan)
},
WebGLCommand::CreateTransformFeedback(ref sender) => {
let value = unsafe { gl.create_transform_feedback() }.ok();
sender
.send(value.map(|ntf| ntf.0.get()).unwrap_or_default())
.unwrap()
},
WebGLCommand::DeleteTransformFeedback(id) => {
if let Some(tf) = NonZeroU32::new(id) {
unsafe { gl.delete_transform_feedback(NativeTransformFeedback(tf)) };
}
},
WebGLCommand::IsTransformFeedback(id, ref sender) => {
let value = NonZeroU32::new(id)
.map(|id| unsafe { gl.is_transform_feedback(NativeTransformFeedback(id)) })
.unwrap_or_default();
sender.send(value).unwrap()
},
WebGLCommand::BindTransformFeedback(target, id) => {
unsafe {
gl.bind_transform_feedback(
target,
NonZeroU32::new(id).map(NativeTransformFeedback),
)
};
},
WebGLCommand::BeginTransformFeedback(mode) => {
unsafe { gl.begin_transform_feedback(mode) };
},
WebGLCommand::EndTransformFeedback() => {
unsafe { gl.end_transform_feedback() };
},
WebGLCommand::PauseTransformFeedback() => {
unsafe { gl.pause_transform_feedback() };
},
WebGLCommand::ResumeTransformFeedback() => {
unsafe { gl.resume_transform_feedback() };
},
WebGLCommand::GetTransformFeedbackVarying(program, index, ref sender) => {
let ActiveTransformFeedback { size, tftype, name } =
unsafe { gl.get_transform_feedback_varying(program.glow(), index) }.unwrap();
let name = from_name_in_compiled_shader(&name);
sender.send((size, tftype, name)).unwrap();
},
WebGLCommand::TransformFeedbackVaryings(program, ref varyings, buffer_mode) => {
let varyings: Vec<String> = varyings
.iter()
.map(|varying| to_name_in_compiled_shader(varying))
.collect();
let varyings_refs: Vec<&str> = varyings.iter().map(String::as_ref).collect();
unsafe {
gl.transform_feedback_varyings(
program.glow(),
varyings_refs.as_slice(),
buffer_mode,
)
};
},
WebGLCommand::GetFramebufferAttachmentParameter(
target,
attachment,
pname,
ref chan,
) => Self::get_framebuffer_attachment_parameter(gl, target, attachment, pname, chan),
WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, ref chan) => {
Self::shader_precision_format(gl, shader_type, precision_type, chan)
},
WebGLCommand::GetExtensions(ref chan) => Self::get_extensions(gl, chan),
WebGLCommand::GetFragDataLocation(program_id, ref name, ref sender) => {
let location = unsafe {
gl.get_frag_data_location(program_id.glow(), &to_name_in_compiled_shader(name))
};
sender.send(location).unwrap();
},
WebGLCommand::GetUniformLocation(program_id, ref name, ref chan) => {
Self::uniform_location(gl, program_id, name, chan)
},
WebGLCommand::GetShaderInfoLog(shader_id, ref chan) => {
Self::shader_info_log(gl, shader_id, chan)
},
WebGLCommand::GetProgramInfoLog(program_id, ref chan) => {
Self::program_info_log(gl, program_id, chan)
},
WebGLCommand::CompileShader(shader_id, ref source) => {
Self::compile_shader(gl, shader_id, source)
},
WebGLCommand::CreateBuffer(ref chan) => Self::create_buffer(gl, chan),
WebGLCommand::CreateFramebuffer(ref chan) => Self::create_framebuffer(gl, chan),
WebGLCommand::CreateRenderbuffer(ref chan) => Self::create_renderbuffer(gl, chan),
WebGLCommand::CreateTexture(ref chan) => Self::create_texture(gl, chan),
WebGLCommand::CreateProgram(ref chan) => Self::create_program(gl, chan),
WebGLCommand::CreateShader(shader_type, ref chan) => {
Self::create_shader(gl, shader_type, chan)
},
WebGLCommand::DeleteBuffer(id) => unsafe { gl.delete_buffer(id.glow()) },
WebGLCommand::DeleteFramebuffer(id) => unsafe { gl.delete_framebuffer(id.glow()) },
WebGLCommand::DeleteRenderbuffer(id) => unsafe { gl.delete_renderbuffer(id.glow()) },
WebGLCommand::DeleteTexture(id) => unsafe { gl.delete_texture(id.glow()) },
WebGLCommand::DeleteProgram(id) => unsafe { gl.delete_program(id.glow()) },
WebGLCommand::DeleteShader(id) => unsafe { gl.delete_shader(id.glow()) },
WebGLCommand::BindBuffer(target, id) => unsafe {
gl.bind_buffer(target, id.map(WebGLBufferId::glow))
},
WebGLCommand::BindFramebuffer(target, request) => {
Self::bind_framebuffer(gl, target, request, ctx, device, state)
},
WebGLCommand::BindRenderbuffer(target, id) => unsafe {
gl.bind_renderbuffer(target, id.map(WebGLRenderbufferId::glow))
},
WebGLCommand::BindTexture(target, id) => unsafe {
gl.bind_texture(target, id.map(WebGLTextureId::glow))
},
WebGLCommand::Uniform1f(uniform_id, v) => unsafe {
gl.uniform_1_f32(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform1fv(uniform_id, ref v) => unsafe {
gl.uniform_1_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform1i(uniform_id, v) => unsafe {
gl.uniform_1_i32(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform1iv(uniform_id, ref v) => unsafe {
gl.uniform_1_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform1ui(uniform_id, v) => unsafe {
gl.uniform_1_u32(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform1uiv(uniform_id, ref v) => unsafe {
gl.uniform_1_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform2f(uniform_id, x, y) => unsafe {
gl.uniform_2_f32(native_uniform_location(uniform_id).as_ref(), x, y)
},
WebGLCommand::Uniform2fv(uniform_id, ref v) => unsafe {
gl.uniform_2_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform2i(uniform_id, x, y) => unsafe {
gl.uniform_2_i32(native_uniform_location(uniform_id).as_ref(), x, y)
},
WebGLCommand::Uniform2iv(uniform_id, ref v) => unsafe {
gl.uniform_2_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform2ui(uniform_id, x, y) => unsafe {
gl.uniform_2_u32(native_uniform_location(uniform_id).as_ref(), x, y)
},
WebGLCommand::Uniform2uiv(uniform_id, ref v) => unsafe {
gl.uniform_2_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform3f(uniform_id, x, y, z) => unsafe {
gl.uniform_3_f32(native_uniform_location(uniform_id).as_ref(), x, y, z)
},
WebGLCommand::Uniform3fv(uniform_id, ref v) => unsafe {
gl.uniform_3_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform3i(uniform_id, x, y, z) => unsafe {
gl.uniform_3_i32(native_uniform_location(uniform_id).as_ref(), x, y, z)
},
WebGLCommand::Uniform3iv(uniform_id, ref v) => unsafe {
gl.uniform_3_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform3ui(uniform_id, x, y, z) => unsafe {
gl.uniform_3_u32(native_uniform_location(uniform_id).as_ref(), x, y, z)
},
WebGLCommand::Uniform3uiv(uniform_id, ref v) => unsafe {
gl.uniform_3_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform4f(uniform_id, x, y, z, w) => unsafe {
gl.uniform_4_f32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
},
WebGLCommand::Uniform4fv(uniform_id, ref v) => unsafe {
gl.uniform_4_f32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform4i(uniform_id, x, y, z, w) => unsafe {
gl.uniform_4_i32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
},
WebGLCommand::Uniform4iv(uniform_id, ref v) => unsafe {
gl.uniform_4_i32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::Uniform4ui(uniform_id, x, y, z, w) => unsafe {
gl.uniform_4_u32(native_uniform_location(uniform_id).as_ref(), x, y, z, w)
},
WebGLCommand::Uniform4uiv(uniform_id, ref v) => unsafe {
gl.uniform_4_u32_slice(native_uniform_location(uniform_id).as_ref(), v)
},
WebGLCommand::UniformMatrix2fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_2_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::UniformMatrix3fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_3_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::UniformMatrix4fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_4_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::UniformMatrix3x2fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_3x2_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::UniformMatrix4x2fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_4x2_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::UniformMatrix2x3fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_2x3_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::UniformMatrix4x3fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_4x3_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::UniformMatrix2x4fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_2x4_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::UniformMatrix3x4fv(uniform_id, ref v) => unsafe {
gl.uniform_matrix_3x4_f32_slice(
native_uniform_location(uniform_id).as_ref(),
false,
v,
)
},
WebGLCommand::ValidateProgram(program_id) => unsafe {
gl.validate_program(program_id.glow())
},
WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) => unsafe {
gl.vertex_attrib_4_f32(attrib_id, x, y, z, w)
},
WebGLCommand::VertexAttribI(attrib_id, x, y, z, w) => unsafe {
gl.vertex_attrib_4_i32(attrib_id, x, y, z, w)
},
WebGLCommand::VertexAttribU(attrib_id, x, y, z, w) => unsafe {
gl.vertex_attrib_4_u32(attrib_id, x, y, z, w)
},
WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => unsafe {
gl.vertex_attrib_pointer_f32(
attrib_id,
size,
gl::FLOAT,
normalized,
stride,
offset as _,
)
},
WebGLCommand::VertexAttribPointer(
attrib_id,
size,
data_type,
normalized,
stride,
offset,
) => unsafe {
gl.vertex_attrib_pointer_f32(
attrib_id,
size,
data_type,
normalized,
stride,
offset as _,
)
},
WebGLCommand::SetViewport(x, y, width, height) => unsafe {
gl.viewport(x, y, width, height)
},
WebGLCommand::TexImage2D {
target,
level,
internal_format,
size,
format,
data_type,
effective_data_type,
unpacking_alignment,
alpha_treatment,
y_axis_treatment,
pixel_format,
ref data,
} => {
let pixels = prepare_pixels(
internal_format,
data_type,
size,
unpacking_alignment,
alpha_treatment,
y_axis_treatment,
pixel_format,
Cow::Borrowed(data),
);
unsafe {
gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
gl.tex_image_2d(
target,
level as i32,
internal_format.as_gl_constant() as i32,
size.width as i32,
size.height as i32,
0,
format.as_gl_constant(),
effective_data_type,
PixelUnpackData::Slice(Some(&pixels)),
);
}
},
WebGLCommand::TexImage2DPBO {
target,
level,
internal_format,
size,
format,
effective_data_type,
unpacking_alignment,
offset,
} => unsafe {
gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
gl.tex_image_2d(
target,
level as i32,
internal_format.as_gl_constant() as i32,
size.width as i32,
size.height as i32,
0,
format.as_gl_constant(),
effective_data_type,
PixelUnpackData::BufferOffset(offset as u32),
);
},
WebGLCommand::TexSubImage2D {
target,
level,
xoffset,
yoffset,
size,
format,
data_type,
effective_data_type,
unpacking_alignment,
alpha_treatment,
y_axis_treatment,
pixel_format,
ref data,
} => {
let pixels = prepare_pixels(
format,
data_type,
size,
unpacking_alignment,
alpha_treatment,
y_axis_treatment,
pixel_format,
Cow::Borrowed(data),
);
unsafe {
gl.pixel_store_i32(gl::UNPACK_ALIGNMENT, unpacking_alignment as i32);
gl.tex_sub_image_2d(
target,
level as i32,
xoffset,
yoffset,
size.width as i32,
size.height as i32,
format.as_gl_constant(),
effective_data_type,
glow::PixelUnpackData::Slice(Some(&pixels)),
);
}
},
WebGLCommand::CompressedTexImage2D {
target,
level,
internal_format,
size,
ref data,
} => unsafe {
gl.compressed_tex_image_2d(
target,
level as i32,
internal_format as i32,
size.width as i32,
size.height as i32,
0,
data.len() as i32,
data,
)
},
WebGLCommand::CompressedTexSubImage2D {
target,
level,
xoffset,
yoffset,
size,
format,
ref data,
} => {
unsafe {
gl.compressed_tex_sub_image_2d(
target,
level,
xoffset,
yoffset,
size.width as i32,
size.height as i32,
format,
glow::CompressedPixelUnpackData::Slice(data),
)
};
},
WebGLCommand::TexStorage2D(target, levels, internal_format, width, height) => unsafe {
gl.tex_storage_2d(
target,
levels as i32,
internal_format.as_gl_constant(),
width as i32,
height as i32,
)
},
WebGLCommand::TexStorage3D(target, levels, internal_format, width, height, depth) => unsafe {
gl.tex_storage_3d(
target,
levels as i32,
internal_format.as_gl_constant(),
width as i32,
height as i32,
depth as i32,
)
},
WebGLCommand::DrawingBufferWidth(ref sender) => {
let size = device
.context_surface_info(ctx)
.unwrap()
.expect("Where's the front buffer?")
.size;
sender.send(size.width).unwrap()
},
WebGLCommand::DrawingBufferHeight(ref sender) => {
let size = device
.context_surface_info(ctx)
.unwrap()
.expect("Where's the front buffer?")
.size;
sender.send(size.height).unwrap()
},
WebGLCommand::Finish(ref sender) => Self::finish(gl, sender),
WebGLCommand::Flush => unsafe { gl.flush() },
WebGLCommand::GenerateMipmap(target) => unsafe { gl.generate_mipmap(target) },
WebGLCommand::CreateVertexArray(ref chan) => {
let id = Self::create_vertex_array(gl);
let _ = chan.send(id);
},
WebGLCommand::DeleteVertexArray(id) => {
Self::delete_vertex_array(gl, id);
},
WebGLCommand::BindVertexArray(id) => {
let id = id.map(WebGLVertexArrayId::glow).or(state.default_vao);
Self::bind_vertex_array(gl, id);
},
WebGLCommand::GetParameterBool(param, ref sender) => {
let value = match param {
webgl::ParameterBool::DepthWritemask => state.depth_write_mask,
_ => unsafe { gl.get_parameter_bool(param as u32) },
};
sender.send(value).unwrap()
},
WebGLCommand::FenceSync(ref sender) => {
let value = unsafe { gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0).unwrap() };
sender.send(WebGLSyncId::from_glow(value)).unwrap();
},
WebGLCommand::IsSync(sync_id, ref sender) => {
let value = unsafe { gl.is_sync(sync_id.glow()) };
sender.send(value).unwrap();
},
WebGLCommand::ClientWaitSync(sync_id, flags, timeout, ref sender) => {
let value = unsafe { gl.client_wait_sync(sync_id.glow(), flags, timeout as _) };
sender.send(value).unwrap();
},
WebGLCommand::WaitSync(sync_id, flags, timeout) => {
unsafe { gl.wait_sync(sync_id.glow(), flags, timeout as u64) };
},
WebGLCommand::GetSyncParameter(sync_id, param, ref sender) => {
let value = unsafe { gl.get_sync_parameter_i32(sync_id.glow(), param) };
sender.send(value as u32).unwrap();
},
WebGLCommand::DeleteSync(sync_id) => {
unsafe { gl.delete_sync(sync_id.glow()) };
},
WebGLCommand::GetParameterBool4(param, ref sender) => {
let value = match param {
webgl::ParameterBool4::ColorWritemask => state.color_write_mask,
};
sender.send(value).unwrap()
},
WebGLCommand::GetParameterInt(param, ref sender) => {
let value = match param {
webgl::ParameterInt::AlphaBits if state.fake_no_alpha() => 0,
webgl::ParameterInt::DepthBits if state.fake_no_depth() => 0,
webgl::ParameterInt::StencilBits if state.fake_no_stencil() => 0,
webgl::ParameterInt::StencilWritemask => state.stencil_write_mask.0 as i32,
webgl::ParameterInt::StencilBackWritemask => state.stencil_write_mask.1 as i32,
_ => unsafe { gl.get_parameter_i32(param as u32) },
};
sender.send(value).unwrap()
},
WebGLCommand::GetParameterInt2(param, ref sender) => {
let mut value = [0; 2];
unsafe {
gl.get_parameter_i32_slice(param as u32, &mut value);
}
sender.send(value).unwrap()
},
WebGLCommand::GetParameterInt4(param, ref sender) => {
let mut value = [0; 4];
unsafe {
gl.get_parameter_i32_slice(param as u32, &mut value);
}
sender.send(value).unwrap()
},
WebGLCommand::GetParameterFloat(param, ref sender) => {
let mut value = [0.];
unsafe {
gl.get_parameter_f32_slice(param as u32, &mut value);
}
sender.send(value[0]).unwrap()
},
WebGLCommand::GetParameterFloat2(param, ref sender) => {
let mut value = [0.; 2];
unsafe {
gl.get_parameter_f32_slice(param as u32, &mut value);
}
sender.send(value).unwrap()
},
WebGLCommand::GetParameterFloat4(param, ref sender) => {
let mut value = [0.; 4];
unsafe {
gl.get_parameter_f32_slice(param as u32, &mut value);
}
sender.send(value).unwrap()
},
WebGLCommand::GetProgramValidateStatus(program, ref sender) => sender
.send(unsafe { gl.get_program_validate_status(program.glow()) })
.unwrap(),
WebGLCommand::GetProgramActiveUniforms(program, ref sender) => sender
.send(unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) })
.unwrap(),
WebGLCommand::GetCurrentVertexAttrib(index, ref sender) => {
let mut value = [0.; 4];
unsafe {
gl.get_vertex_attrib_parameter_f32_slice(
index,
gl::CURRENT_VERTEX_ATTRIB,
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetTexParameterFloat(target, param, ref sender) => {
sender
.send(unsafe { gl.get_tex_parameter_f32(target, param as u32) })
.unwrap();
},
WebGLCommand::GetTexParameterInt(target, param, ref sender) => {
sender
.send(unsafe { gl.get_tex_parameter_i32(target, param as u32) })
.unwrap();
},
WebGLCommand::GetTexParameterBool(target, param, ref sender) => {
sender
.send(unsafe { gl.get_tex_parameter_i32(target, param as u32) } != 0)
.unwrap();
},
WebGLCommand::GetInternalFormatIntVec(target, internal_format, param, ref sender) => {
match param {
InternalFormatIntVec::Samples => {
let mut count = [0; 1];
unsafe {
gl.get_internal_format_i32_slice(
target,
internal_format,
gl::NUM_SAMPLE_COUNTS,
&mut count,
)
};
assert!(count[0] >= 0);
let mut values = vec![0; count[0] as usize];
unsafe {
gl.get_internal_format_i32_slice(
target,
internal_format,
param as u32,
&mut values,
)
};
sender.send(values).unwrap()
},
}
},
WebGLCommand::TexParameteri(target, param, value) => unsafe {
gl.tex_parameter_i32(target, param, value)
},
WebGLCommand::TexParameterf(target, param, value) => unsafe {
gl.tex_parameter_f32(target, param, value)
},
WebGLCommand::LinkProgram(program_id, ref sender) => {
return sender.send(Self::link_program(gl, program_id)).unwrap();
},
WebGLCommand::UseProgram(program_id) => unsafe {
gl.use_program(program_id.map(|p| p.glow()))
},
WebGLCommand::DrawArrays { mode, first, count } => unsafe {
gl.draw_arrays(mode, first, count)
},
WebGLCommand::DrawArraysInstanced {
mode,
first,
count,
primcount,
} => unsafe { gl.draw_arrays_instanced(mode, first, count, primcount) },
WebGLCommand::DrawElements {
mode,
count,
type_,
offset,
} => unsafe { gl.draw_elements(mode, count, type_, offset as _) },
WebGLCommand::DrawElementsInstanced {
mode,
count,
type_,
offset,
primcount,
} => unsafe {
gl.draw_elements_instanced(mode, count, type_, offset as i32, primcount)
},
WebGLCommand::VertexAttribDivisor { index, divisor } => unsafe {
gl.vertex_attrib_divisor(index, divisor)
},
WebGLCommand::GetUniformBool(program_id, loc, ref sender) => {
let mut value = [0];
unsafe {
gl.get_uniform_i32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value[0] != 0).unwrap();
},
WebGLCommand::GetUniformBool2(program_id, loc, ref sender) => {
let mut value = [0; 2];
unsafe {
gl.get_uniform_i32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
let value = [value[0] != 0, value[1] != 0];
sender.send(value).unwrap();
},
WebGLCommand::GetUniformBool3(program_id, loc, ref sender) => {
let mut value = [0; 3];
unsafe {
gl.get_uniform_i32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
let value = [value[0] != 0, value[1] != 0, value[2] != 0];
sender.send(value).unwrap();
},
WebGLCommand::GetUniformBool4(program_id, loc, ref sender) => {
let mut value = [0; 4];
unsafe {
gl.get_uniform_i32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
let value = [value[0] != 0, value[1] != 0, value[2] != 0, value[3] != 0];
sender.send(value).unwrap();
},
WebGLCommand::GetUniformInt(program_id, loc, ref sender) => {
let mut value = [0];
unsafe {
gl.get_uniform_i32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value[0]).unwrap();
},
WebGLCommand::GetUniformInt2(program_id, loc, ref sender) => {
let mut value = [0; 2];
unsafe {
gl.get_uniform_i32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformInt3(program_id, loc, ref sender) => {
let mut value = [0; 3];
unsafe {
gl.get_uniform_i32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformInt4(program_id, loc, ref sender) => {
let mut value = [0; 4];
unsafe {
gl.get_uniform_i32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformUint(program_id, loc, ref sender) => {
let mut value = [0];
unsafe {
gl.get_uniform_u32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value[0]).unwrap();
},
WebGLCommand::GetUniformUint2(program_id, loc, ref sender) => {
let mut value = [0; 2];
unsafe {
gl.get_uniform_u32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformUint3(program_id, loc, ref sender) => {
let mut value = [0; 3];
unsafe {
gl.get_uniform_u32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformUint4(program_id, loc, ref sender) => {
let mut value = [0; 4];
unsafe {
gl.get_uniform_u32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformFloat(program_id, loc, ref sender) => {
let mut value = [0.];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value[0]).unwrap();
},
WebGLCommand::GetUniformFloat2(program_id, loc, ref sender) => {
let mut value = [0.; 2];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformFloat3(program_id, loc, ref sender) => {
let mut value = [0.; 3];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformFloat4(program_id, loc, ref sender) => {
let mut value = [0.; 4];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformFloat9(program_id, loc, ref sender) => {
let mut value = [0.; 9];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformFloat16(program_id, loc, ref sender) => {
let mut value = [0.; 16];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap();
},
WebGLCommand::GetUniformFloat2x3(program_id, loc, ref sender) => {
let mut value = [0.; 2 * 3];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap()
},
WebGLCommand::GetUniformFloat2x4(program_id, loc, ref sender) => {
let mut value = [0.; 2 * 4];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap()
},
WebGLCommand::GetUniformFloat3x2(program_id, loc, ref sender) => {
let mut value = [0.; 3 * 2];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap()
},
WebGLCommand::GetUniformFloat3x4(program_id, loc, ref sender) => {
let mut value = [0.; 3 * 4];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap()
},
WebGLCommand::GetUniformFloat4x2(program_id, loc, ref sender) => {
let mut value = [0.; 4 * 2];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap()
},
WebGLCommand::GetUniformFloat4x3(program_id, loc, ref sender) => {
let mut value = [0.; 4 * 3];
unsafe {
gl.get_uniform_f32(
program_id.glow(),
&NativeUniformLocation(loc as u32),
&mut value,
);
}
sender.send(value).unwrap()
},
WebGLCommand::GetUniformBlockIndex(program_id, ref name, ref sender) => {
let name = to_name_in_compiled_shader(name);
let index = unsafe { gl.get_uniform_block_index(program_id.glow(), &name) };
sender.send(index.unwrap_or(gl::INVALID_INDEX)).unwrap();
},
WebGLCommand::GetUniformIndices(program_id, ref names, ref sender) => {
let names = names
.iter()
.map(|name| to_name_in_compiled_shader(name))
.collect::<Vec<_>>();
let name_strs = names.iter().map(|name| name.as_str()).collect::<Vec<_>>();
let indices = unsafe {
gl.get_uniform_indices(program_id.glow(), &name_strs)
.iter()
.map(|index| index.unwrap_or(gl::INVALID_INDEX))
.collect()
};
sender.send(indices).unwrap();
},
WebGLCommand::GetActiveUniforms(program_id, ref indices, pname, ref sender) => {
let results =
unsafe { gl.get_active_uniforms_parameter(program_id.glow(), indices, pname) };
sender.send(results).unwrap();
},
WebGLCommand::GetActiveUniformBlockName(program_id, block_idx, ref sender) => {
let name =
unsafe { gl.get_active_uniform_block_name(program_id.glow(), block_idx) };
sender.send(name).unwrap();
},
WebGLCommand::GetActiveUniformBlockParameter(
program_id,
block_idx,
pname,
ref sender,
) => {
let size = match pname {
gl::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe {
gl.get_active_uniform_block_parameter_i32(
program_id.glow(),
block_idx,
gl::UNIFORM_BLOCK_ACTIVE_UNIFORMS,
) as usize
},
_ => 1,
};
let mut result = vec![0; size];
unsafe {
gl.get_active_uniform_block_parameter_i32_slice(
program_id.glow(),
block_idx,
pname,
&mut result,
)
};
sender.send(result).unwrap();
},
WebGLCommand::UniformBlockBinding(program_id, block_idx, block_binding) => unsafe {
gl.uniform_block_binding(program_id.glow(), block_idx, block_binding)
},
WebGLCommand::InitializeFramebuffer {
color,
depth,
stencil,
} => Self::initialize_framebuffer(gl, state, color, depth, stencil),
WebGLCommand::BeginQuery(target, query_id) => {
unsafe { gl.begin_query(target, query_id.glow()) };
},
WebGLCommand::EndQuery(target) => {
unsafe { gl.end_query(target) };
},
WebGLCommand::DeleteQuery(query_id) => {
unsafe { gl.delete_query(query_id.glow()) };
},
WebGLCommand::GenerateQuery(ref sender) => {
let id = unsafe { gl.create_query().unwrap() };
sender.send(WebGLQueryId::from_glow(id)).unwrap()
},
WebGLCommand::GetQueryState(ref sender, query_id, pname) => {
let value = unsafe { gl.get_query_parameter_u32(query_id.glow(), pname) };
sender.send(value).unwrap()
},
WebGLCommand::GenerateSampler(ref sender) => {
let id = unsafe { gl.create_sampler().unwrap() };
sender.send(WebGLSamplerId::from_glow(id)).unwrap()
},
WebGLCommand::DeleteSampler(sampler_id) => {
unsafe { gl.delete_sampler(sampler_id.glow()) };
},
WebGLCommand::BindSampler(unit, sampler_id) => {
unsafe { gl.bind_sampler(unit, Some(sampler_id.glow())) };
},
WebGLCommand::SetSamplerParameterInt(sampler_id, pname, value) => {
unsafe { gl.sampler_parameter_i32(sampler_id.glow(), pname, value) };
},
WebGLCommand::SetSamplerParameterFloat(sampler_id, pname, value) => {
unsafe { gl.sampler_parameter_f32(sampler_id.glow(), pname, value) };
},
WebGLCommand::GetSamplerParameterInt(sampler_id, pname, ref sender) => {
let value = unsafe { gl.get_sampler_parameter_i32(sampler_id.glow(), pname) };
sender.send(value).unwrap();
},
WebGLCommand::GetSamplerParameterFloat(sampler_id, pname, ref sender) => {
let value = unsafe { gl.get_sampler_parameter_f32(sampler_id.glow(), pname) };
sender.send(value).unwrap();
},
WebGLCommand::BindBufferBase(target, index, id) => {
let id = id.map(WebGLBufferId::glow);
unsafe {
gl.bind_buffer(target, id);
gl.bind_buffer(target, None);
gl.bind_buffer_base(target, index, id);
}
},
WebGLCommand::BindBufferRange(target, index, id, offset, size) => {
let id = id.map(WebGLBufferId::glow);
unsafe {
gl.bind_buffer(target, id);
gl.bind_buffer(target, None);
gl.bind_buffer_range(target, index, id, offset as i32, size as i32);
}
},
WebGLCommand::ClearBufferfv(buffer, draw_buffer, ref value) => unsafe {
gl.clear_buffer_f32_slice(buffer, draw_buffer as u32, value)
},
WebGLCommand::ClearBufferiv(buffer, draw_buffer, ref value) => unsafe {
gl.clear_buffer_i32_slice(buffer, draw_buffer as u32, value)
},
WebGLCommand::ClearBufferuiv(buffer, draw_buffer, ref value) => unsafe {
gl.clear_buffer_u32_slice(buffer, draw_buffer as u32, value)
},
WebGLCommand::ClearBufferfi(buffer, draw_buffer, depth, stencil) => unsafe {
gl.clear_buffer_depth_stencil(buffer, draw_buffer as u32, depth, stencil)
},
WebGLCommand::InvalidateFramebuffer(target, ref attachments) => unsafe {
gl.invalidate_framebuffer(target, attachments)
},
WebGLCommand::InvalidateSubFramebuffer(target, ref attachments, x, y, w, h) => unsafe {
gl.invalidate_sub_framebuffer(target, attachments, x, y, w, h)
},
WebGLCommand::FramebufferTextureLayer(target, attachment, tex_id, level, layer) => {
let tex_id = tex_id.map(WebGLTextureId::glow);
let attach = |attachment| unsafe {
gl.framebuffer_texture_layer(target, attachment, tex_id, level, layer)
};
if attachment == gl::DEPTH_STENCIL_ATTACHMENT {
attach(gl::DEPTH_ATTACHMENT);
attach(gl::STENCIL_ATTACHMENT);
} else {
attach(attachment)
}
},
WebGLCommand::ReadBuffer(buffer) => unsafe { gl.read_buffer(buffer) },
WebGLCommand::DrawBuffers(ref buffers) => unsafe { gl.draw_buffers(buffers) },
}
#[cfg(debug_assertions)]
{
let error = unsafe { gl.get_error() };
if error != gl::NO_ERROR {
error!("Last GL operation failed: {:?}", command);
if error == gl::INVALID_FRAMEBUFFER_OPERATION {
let framebuffer_bindings =
unsafe { gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING) };
debug!(
"(thread {:?}) Current draw framebuffer binding: {:?}",
::std::thread::current().id(),
framebuffer_bindings
);
}
#[cfg(feature = "webgl_backtrace")]
{
error!("Backtrace from failed WebGL API:\n{}", _backtrace.backtrace);
if let Some(backtrace) = _backtrace.js_backtrace {
error!("JS backtrace from failed WebGL API:\n{}", backtrace);
}
}
log::warn!(
"debug assertion failed! Unexpected WebGL error: 0x{:x} ({}) [{:?}]",
error,
error,
command
);
}
}
}
fn initialize_framebuffer(gl: &Gl, state: &GLState, color: bool, depth: bool, stencil: bool) {
let bits = [
(color, gl::COLOR_BUFFER_BIT),
(depth, gl::DEPTH_BUFFER_BIT),
(stencil, gl::STENCIL_BUFFER_BIT),
]
.iter()
.fold(0, |bits, &(enabled, bit)| {
bits | if enabled { bit } else { 0 }
});
unsafe {
gl.disable(gl::SCISSOR_TEST);
gl.color_mask(true, true, true, true);
gl.clear_color(0., 0., 0., 0.);
gl.depth_mask(true);
gl.clear_depth(1.);
gl.stencil_mask_separate(gl::FRONT, 0xFFFFFFFF);
gl.stencil_mask_separate(gl::BACK, 0xFFFFFFFF);
gl.clear_stencil(0);
gl.clear(bits);
}
state.restore_invariant(gl);
}
fn link_program(gl: &Gl, program: WebGLProgramId) -> ProgramLinkInfo {
unsafe { gl.link_program(program.glow()) };
let linked = unsafe { gl.get_program_link_status(program.glow()) };
if !linked {
return ProgramLinkInfo {
linked: false,
active_attribs: vec![].into(),
active_uniforms: vec![].into(),
active_uniform_blocks: vec![].into(),
transform_feedback_length: Default::default(),
transform_feedback_mode: Default::default(),
};
}
let num_active_attribs =
unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_ATTRIBUTES) };
let active_attribs = (0..num_active_attribs as u32)
.map(|i| {
let active_attribute =
unsafe { gl.get_active_attribute(program.glow(), i) }.unwrap();
let name = &active_attribute.name;
let location = if name.starts_with("gl_") {
None
} else {
unsafe { gl.get_attrib_location(program.glow(), name) }
};
ActiveAttribInfo {
name: from_name_in_compiled_shader(name),
size: active_attribute.size,
type_: active_attribute.atype,
location,
}
})
.collect::<Vec<_>>()
.into();
let num_active_uniforms =
unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORMS) };
let active_uniforms = (0..num_active_uniforms as u32)
.map(|i| {
let active_uniform = unsafe { gl.get_active_uniform(program.glow(), i) }.unwrap();
let is_array = active_uniform.name.ends_with("[0]");
let active_uniform_name = active_uniform
.name
.strip_suffix("[0]")
.unwrap_or_else(|| &active_uniform.name);
ActiveUniformInfo {
base_name: from_name_in_compiled_shader(active_uniform_name).into(),
size: if is_array {
Some(active_uniform.size)
} else {
None
},
type_: active_uniform.utype,
bind_index: None,
}
})
.collect::<Vec<_>>()
.into();
let num_active_uniform_blocks =
unsafe { gl.get_program_parameter_i32(program.glow(), gl::ACTIVE_UNIFORM_BLOCKS) };
let active_uniform_blocks = (0..num_active_uniform_blocks as u32)
.map(|i| {
let name = unsafe { gl.get_active_uniform_block_name(program.glow(), i) };
let size = unsafe {
gl.get_active_uniform_block_parameter_i32(
program.glow(),
i,
gl::UNIFORM_BLOCK_DATA_SIZE,
)
};
ActiveUniformBlockInfo { name, size }
})
.collect::<Vec<_>>()
.into();
let transform_feedback_length = unsafe {
gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_VARYINGS)
};
let transform_feedback_mode = unsafe {
gl.get_program_parameter_i32(program.glow(), gl::TRANSFORM_FEEDBACK_BUFFER_MODE)
};
ProgramLinkInfo {
linked: true,
active_attribs,
active_uniforms,
active_uniform_blocks,
transform_feedback_length,
transform_feedback_mode,
}
}
fn finish(gl: &Gl, chan: &WebGLSender<()>) {
unsafe { gl.finish() };
chan.send(()).unwrap();
}
fn shader_precision_format(
gl: &Gl,
shader_type: u32,
precision_type: u32,
chan: &WebGLSender<(i32, i32, i32)>,
) {
let ShaderPrecisionFormat {
range_min,
range_max,
precision,
} = unsafe {
gl.get_shader_precision_format(shader_type, precision_type)
.unwrap_or_else(|| {
ShaderPrecisionFormat::common_desktop_hardware(
precision_type,
gl.version().is_embedded,
)
})
};
chan.send((range_min, range_max, precision)).unwrap();
}
fn get_extensions(gl: &Gl, chan: &WebGLSender<String>) {
let mut ext_count = [0];
unsafe {
gl.get_parameter_i32_slice(gl::NUM_EXTENSIONS, &mut ext_count);
}
if unsafe { gl.get_error() } != gl::NO_ERROR {
chan.send(unsafe { gl.get_parameter_string(gl::EXTENSIONS) })
.unwrap();
return;
}
let ext_count = ext_count[0] as usize;
let mut extensions = Vec::with_capacity(ext_count);
for idx in 0..ext_count {
extensions.push(unsafe { gl.get_parameter_indexed_string(gl::EXTENSIONS, idx as u32) })
}
let extensions = extensions.join(" ");
chan.send(extensions).unwrap();
}
fn get_framebuffer_attachment_parameter(
gl: &Gl,
target: u32,
attachment: u32,
pname: u32,
chan: &WebGLSender<i32>,
) {
let parameter =
unsafe { gl.get_framebuffer_attachment_parameter_i32(target, attachment, pname) };
chan.send(parameter).unwrap();
}
fn get_renderbuffer_parameter(gl: &Gl, target: u32, pname: u32, chan: &WebGLSender<i32>) {
let parameter = unsafe { gl.get_renderbuffer_parameter_i32(target, pname) };
chan.send(parameter).unwrap();
}
fn uniform_location(gl: &Gl, program_id: WebGLProgramId, name: &str, chan: &WebGLSender<i32>) {
let location = unsafe {
gl.get_uniform_location(program_id.glow(), &to_name_in_compiled_shader(name))
};
chan.send(location.map(|l| l.0).unwrap_or_default() as i32)
.unwrap();
}
fn shader_info_log(gl: &Gl, shader_id: WebGLShaderId, chan: &WebGLSender<String>) {
let log = unsafe { gl.get_shader_info_log(shader_id.glow()) };
chan.send(log).unwrap();
}
fn program_info_log(gl: &Gl, program_id: WebGLProgramId, chan: &WebGLSender<String>) {
let log = unsafe { gl.get_program_info_log(program_id.glow()) };
chan.send(log).unwrap();
}
fn create_buffer(gl: &Gl, chan: &WebGLSender<Option<WebGLBufferId>>) {
let buffer = unsafe { gl.create_buffer() }
.ok()
.map(WebGLBufferId::from_glow);
chan.send(buffer).unwrap();
}
fn create_framebuffer(gl: &Gl, chan: &WebGLSender<Option<WebGLFramebufferId>>) {
let framebuffer = unsafe { gl.create_framebuffer() }
.ok()
.map(WebGLFramebufferId::from_glow);
chan.send(framebuffer).unwrap();
}
fn create_renderbuffer(gl: &Gl, chan: &WebGLSender<Option<WebGLRenderbufferId>>) {
let renderbuffer = unsafe { gl.create_renderbuffer() }
.ok()
.map(WebGLRenderbufferId::from_glow);
chan.send(renderbuffer).unwrap();
}
fn create_texture(gl: &Gl, chan: &WebGLSender<Option<WebGLTextureId>>) {
let texture = unsafe { gl.create_texture() }
.ok()
.map(WebGLTextureId::from_glow);
chan.send(texture).unwrap();
}
fn create_program(gl: &Gl, chan: &WebGLSender<Option<WebGLProgramId>>) {
let program = unsafe { gl.create_program() }
.ok()
.map(WebGLProgramId::from_glow);
chan.send(program).unwrap();
}
fn create_shader(gl: &Gl, shader_type: u32, chan: &WebGLSender<Option<WebGLShaderId>>) {
let shader = unsafe { gl.create_shader(shader_type) }
.ok()
.map(WebGLShaderId::from_glow);
chan.send(shader).unwrap();
}
fn create_vertex_array(gl: &Gl) -> Option<WebGLVertexArrayId> {
let vao = unsafe { gl.create_vertex_array() }
.ok()
.map(WebGLVertexArrayId::from_glow);
if vao.is_none() {
let code = unsafe { gl.get_error() };
warn!("Failed to create vertex array with error code {:x}", code);
}
vao
}
fn bind_vertex_array(gl: &Gl, vao: Option<NativeVertexArray>) {
unsafe { gl.bind_vertex_array(vao) }
debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
}
fn delete_vertex_array(gl: &Gl, vao: WebGLVertexArrayId) {
unsafe { gl.delete_vertex_array(vao.glow()) };
debug_assert_eq!(unsafe { gl.get_error() }, gl::NO_ERROR);
}
#[inline]
fn bind_framebuffer(
gl: &Gl,
target: u32,
request: WebGLFramebufferBindingRequest,
ctx: &Context,
device: &Device,
state: &mut GLState,
) {
let id = match request {
WebGLFramebufferBindingRequest::Explicit(id) => Some(id.glow()),
WebGLFramebufferBindingRequest::Default => NonZeroU32::new(
device
.context_surface_info(ctx)
.unwrap()
.expect("No surface attached!")
.framebuffer_object,
)
.map(NativeFramebuffer),
};
debug!("WebGLImpl::bind_framebuffer: {:?}", id);
unsafe { gl.bind_framebuffer(target, id) };
if (target == gl::FRAMEBUFFER) || (target == gl::DRAW_FRAMEBUFFER) {
state.drawing_to_default_framebuffer =
request == WebGLFramebufferBindingRequest::Default;
state.restore_invariant(gl);
}
}
#[inline]
fn compile_shader(gl: &Gl, shader_id: WebGLShaderId, source: &str) {
unsafe {
gl.shader_source(shader_id.glow(), source);
gl.compile_shader(shader_id.glow());
}
}
}
const ANGLE_NAME_PREFIX: &str = "_u";
fn to_name_in_compiled_shader(s: &str) -> String {
map_dot_separated(s, |s, mapped| {
mapped.push_str(ANGLE_NAME_PREFIX);
mapped.push_str(s);
})
}
fn from_name_in_compiled_shader(s: &str) -> String {
map_dot_separated(s, |s, mapped| {
mapped.push_str(if let Some(stripped) = s.strip_prefix(ANGLE_NAME_PREFIX) {
stripped
} else {
s
})
})
}
fn map_dot_separated<F: Fn(&str, &mut String)>(s: &str, f: F) -> String {
let mut iter = s.split('.');
let mut mapped = String::new();
f(iter.next().unwrap(), &mut mapped);
for s in iter {
mapped.push('.');
f(s, &mut mapped);
}
mapped
}
#[allow(clippy::too_many_arguments)]
fn prepare_pixels(
internal_format: TexFormat,
data_type: TexDataType,
size: Size2D<u32>,
unpacking_alignment: u32,
alpha_treatment: Option<AlphaTreatment>,
y_axis_treatment: YAxisTreatment,
pixel_format: Option<PixelFormat>,
mut pixels: Cow<[u8]>,
) -> Cow<[u8]> {
match alpha_treatment {
Some(AlphaTreatment::Premultiply) => {
if let Some(pixel_format) = pixel_format {
match pixel_format {
PixelFormat::BGRA8 | PixelFormat::RGBA8 => {},
_ => unimplemented!("unsupported pixel format ({:?})", pixel_format),
}
premultiply_inplace(TexFormat::RGBA, TexDataType::UnsignedByte, pixels.to_mut());
} else {
premultiply_inplace(internal_format, data_type, pixels.to_mut());
}
},
Some(AlphaTreatment::Unmultiply) => {
assert!(pixel_format.is_some());
unmultiply_inplace(pixels.to_mut());
},
None => {},
}
if let Some(pixel_format) = pixel_format {
pixels = image_to_tex_image_data(
pixel_format,
internal_format,
data_type,
pixels.into_owned(),
)
.into();
}
if y_axis_treatment == YAxisTreatment::Flipped {
pixels = flip_pixels_y(
internal_format,
data_type,
size.width as usize,
size.height as usize,
unpacking_alignment as usize,
pixels.into_owned(),
)
.into();
}
pixels
}
fn image_to_tex_image_data(
pixel_format: PixelFormat,
format: TexFormat,
data_type: TexDataType,
mut pixels: Vec<u8>,
) -> Vec<u8> {
let pixel_count = pixels.len() / 4;
match pixel_format {
PixelFormat::BGRA8 => pixels::rgba8_byte_swap_colors_inplace(&mut pixels),
PixelFormat::RGBA8 => {},
_ => unimplemented!("unsupported pixel format ({:?})", pixel_format),
}
match (format, data_type) {
(TexFormat::RGBA, TexDataType::UnsignedByte) |
(TexFormat::RGBA8, TexDataType::UnsignedByte) => pixels,
(TexFormat::RGB, TexDataType::UnsignedByte) |
(TexFormat::RGB8, TexDataType::UnsignedByte) => {
for i in 0..pixel_count {
let rgb = {
let rgb = &pixels[i * 4..i * 4 + 3];
[rgb[0], rgb[1], rgb[2]]
};
pixels[i * 3..i * 3 + 3].copy_from_slice(&rgb);
}
pixels.truncate(pixel_count * 3);
pixels
},
(TexFormat::Alpha, TexDataType::UnsignedByte) => {
for i in 0..pixel_count {
let p = pixels[i * 4 + 3];
pixels[i] = p;
}
pixels.truncate(pixel_count);
pixels
},
(TexFormat::Luminance, TexDataType::UnsignedByte) => {
for i in 0..pixel_count {
let p = pixels[i * 4];
pixels[i] = p;
}
pixels.truncate(pixel_count);
pixels
},
(TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
for i in 0..pixel_count {
let (lum, a) = {
let rgba = &pixels[i * 4..i * 4 + 4];
(rgba[0], rgba[3])
};
pixels[i * 2] = lum;
pixels[i * 2 + 1] = a;
}
pixels.truncate(pixel_count * 2);
pixels
},
(TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
for i in 0..pixel_count {
let p = {
let rgba = &pixels[i * 4..i * 4 + 4];
(rgba[0] as u16 & 0xf0) << 8 |
(rgba[1] as u16 & 0xf0) << 4 |
(rgba[2] as u16 & 0xf0) |
(rgba[3] as u16 & 0xf0) >> 4
};
NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
}
pixels.truncate(pixel_count * 2);
pixels
},
(TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
for i in 0..pixel_count {
let p = {
let rgba = &pixels[i * 4..i * 4 + 4];
(rgba[0] as u16 & 0xf8) << 8 |
(rgba[1] as u16 & 0xf8) << 3 |
(rgba[2] as u16 & 0xf8) >> 2 |
(rgba[3] as u16) >> 7
};
NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
}
pixels.truncate(pixel_count * 2);
pixels
},
(TexFormat::RGB, TexDataType::UnsignedShort565) => {
for i in 0..pixel_count {
let p = {
let rgb = &pixels[i * 4..i * 4 + 3];
(rgb[0] as u16 & 0xf8) << 8 |
(rgb[1] as u16 & 0xfc) << 3 |
(rgb[2] as u16 & 0xf8) >> 3
};
NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
}
pixels.truncate(pixel_count * 2);
pixels
},
(TexFormat::RGBA, TexDataType::Float) | (TexFormat::RGBA32f, TexDataType::Float) => {
let mut rgbaf32 = Vec::<u8>::with_capacity(pixel_count * 16);
for rgba8 in pixels.chunks(4) {
rgbaf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
rgbaf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
rgbaf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
rgbaf32.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
}
rgbaf32
},
(TexFormat::RGB, TexDataType::Float) | (TexFormat::RGB32f, TexDataType::Float) => {
let mut rgbf32 = Vec::<u8>::with_capacity(pixel_count * 12);
for rgba8 in pixels.chunks(4) {
rgbf32.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
rgbf32.write_f32::<NativeEndian>(rgba8[1] as f32).unwrap();
rgbf32.write_f32::<NativeEndian>(rgba8[2] as f32).unwrap();
}
rgbf32
},
(TexFormat::Alpha, TexDataType::Float) | (TexFormat::Alpha32f, TexDataType::Float) => {
for rgba8 in pixels.chunks_mut(4) {
let p = rgba8[3] as f32;
NativeEndian::write_f32(rgba8, p);
}
pixels
},
(TexFormat::Luminance, TexDataType::Float) |
(TexFormat::Luminance32f, TexDataType::Float) => {
for rgba8 in pixels.chunks_mut(4) {
let p = rgba8[0] as f32;
NativeEndian::write_f32(rgba8, p);
}
pixels
},
(TexFormat::LuminanceAlpha, TexDataType::Float) |
(TexFormat::LuminanceAlpha32f, TexDataType::Float) => {
let mut data = Vec::<u8>::with_capacity(pixel_count * 8);
for rgba8 in pixels.chunks(4) {
data.write_f32::<NativeEndian>(rgba8[0] as f32).unwrap();
data.write_f32::<NativeEndian>(rgba8[3] as f32).unwrap();
}
data
},
(TexFormat::RGBA, TexDataType::HalfFloat) |
(TexFormat::RGBA16f, TexDataType::HalfFloat) => {
let mut rgbaf16 = Vec::<u8>::with_capacity(pixel_count * 8);
for rgba8 in pixels.chunks(4) {
rgbaf16
.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits())
.unwrap();
rgbaf16
.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits())
.unwrap();
rgbaf16
.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits())
.unwrap();
rgbaf16
.write_u16::<NativeEndian>(f16::from_f32(rgba8[3] as f32).to_bits())
.unwrap();
}
rgbaf16
},
(TexFormat::RGB, TexDataType::HalfFloat) | (TexFormat::RGB16f, TexDataType::HalfFloat) => {
let mut rgbf16 = Vec::<u8>::with_capacity(pixel_count * 6);
for rgba8 in pixels.chunks(4) {
rgbf16
.write_u16::<NativeEndian>(f16::from_f32(rgba8[0] as f32).to_bits())
.unwrap();
rgbf16
.write_u16::<NativeEndian>(f16::from_f32(rgba8[1] as f32).to_bits())
.unwrap();
rgbf16
.write_u16::<NativeEndian>(f16::from_f32(rgba8[2] as f32).to_bits())
.unwrap();
}
rgbf16
},
(TexFormat::Alpha, TexDataType::HalfFloat) |
(TexFormat::Alpha16f, TexDataType::HalfFloat) => {
for i in 0..pixel_count {
let p = f16::from_f32(pixels[i * 4 + 3] as f32).to_bits();
NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
}
pixels.truncate(pixel_count * 2);
pixels
},
(TexFormat::Luminance, TexDataType::HalfFloat) |
(TexFormat::Luminance16f, TexDataType::HalfFloat) => {
for i in 0..pixel_count {
let p = f16::from_f32(pixels[i * 4] as f32).to_bits();
NativeEndian::write_u16(&mut pixels[i * 2..i * 2 + 2], p);
}
pixels.truncate(pixel_count * 2);
pixels
},
(TexFormat::LuminanceAlpha, TexDataType::HalfFloat) |
(TexFormat::LuminanceAlpha16f, TexDataType::HalfFloat) => {
for rgba8 in pixels.chunks_mut(4) {
let lum = f16::from_f32(rgba8[0] as f32).to_bits();
let a = f16::from_f32(rgba8[3] as f32).to_bits();
NativeEndian::write_u16(&mut rgba8[0..2], lum);
NativeEndian::write_u16(&mut rgba8[2..4], a);
}
pixels
},
_ => unreachable!("Unsupported formats {:?} {:?}", format, data_type),
}
}
fn premultiply_inplace(format: TexFormat, data_type: TexDataType, pixels: &mut [u8]) {
match (format, data_type) {
(TexFormat::RGBA, TexDataType::UnsignedByte) => {
pixels::rgba8_premultiply_inplace(pixels);
},
(TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
for la in pixels.chunks_mut(2) {
la[0] = pixels::multiply_u8_color(la[0], la[1]);
}
},
(TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
for rgba in pixels.chunks_mut(2) {
if NativeEndian::read_u16(rgba) & 1 == 0 {
NativeEndian::write_u16(rgba, 0);
}
}
},
(TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
for rgba in pixels.chunks_mut(2) {
let pix = NativeEndian::read_u16(rgba);
let extend_to_8_bits = |val| (val | val << 4) as u8;
let r = extend_to_8_bits(pix >> 12 & 0x0f);
let g = extend_to_8_bits(pix >> 8 & 0x0f);
let b = extend_to_8_bits(pix >> 4 & 0x0f);
let a = extend_to_8_bits(pix & 0x0f);
NativeEndian::write_u16(
rgba,
((pixels::multiply_u8_color(r, a) & 0xf0) as u16) << 8 |
((pixels::multiply_u8_color(g, a) & 0xf0) as u16) << 4 |
((pixels::multiply_u8_color(b, a) & 0xf0) as u16) |
((a & 0x0f) as u16),
);
}
},
_ => {},
}
}
fn flip_pixels_y(
internal_format: TexFormat,
data_type: TexDataType,
width: usize,
height: usize,
unpacking_alignment: usize,
pixels: Vec<u8>,
) -> Vec<u8> {
let cpp = (data_type.element_size() * internal_format.components() /
data_type.components_per_element()) as usize;
let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
let mut flipped = Vec::<u8>::with_capacity(pixels.len());
for y in 0..height {
let flipped_y = height - 1 - y;
let start = flipped_y * stride;
flipped.extend_from_slice(&pixels[start..(start + width * cpp)]);
flipped.extend(vec![0u8; stride - width * cpp]);
}
flipped
}
fn clamp_viewport(gl: &Gl, size: Size2D<u32>) -> Size2D<u32> {
let mut max_viewport = [i32::MAX, i32::MAX];
let mut max_renderbuffer = [i32::MAX];
unsafe {
gl.get_parameter_i32_slice(gl::MAX_VIEWPORT_DIMS, &mut max_viewport);
gl.get_parameter_i32_slice(gl::MAX_RENDERBUFFER_SIZE, &mut max_renderbuffer);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
}
Size2D::new(
size.width
.min(max_viewport[0] as u32)
.min(max_renderbuffer[0] as u32)
.max(1),
size.height
.min(max_viewport[1] as u32)
.min(max_renderbuffer[0] as u32)
.max(1),
)
}
trait ToSurfmanVersion {
fn to_surfman_version(self, api_type: GlType) -> GLVersion;
}
impl ToSurfmanVersion for WebGLVersion {
fn to_surfman_version(self, api_type: GlType) -> GLVersion {
if api_type == GlType::Gles {
return GLVersion::new(3, 0);
}
match self {
WebGLVersion::WebGL1 => GLVersion::new(2, 1),
WebGLVersion::WebGL2 => GLVersion::new(3, 2),
}
}
}
trait SurfmanContextAttributeFlagsConvert {
fn to_surfman_context_attribute_flags(
&self,
webgl_version: WebGLVersion,
api_type: GlType,
) -> ContextAttributeFlags;
}
impl SurfmanContextAttributeFlagsConvert for GLContextAttributes {
fn to_surfman_context_attribute_flags(
&self,
webgl_version: WebGLVersion,
api_type: GlType,
) -> ContextAttributeFlags {
let mut flags = ContextAttributeFlags::empty();
flags.set(ContextAttributeFlags::ALPHA, self.alpha);
flags.set(ContextAttributeFlags::DEPTH, self.depth);
flags.set(ContextAttributeFlags::STENCIL, self.stencil);
if (webgl_version == WebGLVersion::WebGL1) && (api_type == GlType::Gl) {
flags.set(ContextAttributeFlags::COMPATIBILITY_PROFILE, true);
}
flags
}
}
bitflags! {
struct FramebufferRebindingFlags: u8 {
const REBIND_READ_FRAMEBUFFER = 0x1;
const REBIND_DRAW_FRAMEBUFFER = 0x2;
}
}
struct FramebufferRebindingInfo {
flags: FramebufferRebindingFlags,
viewport: [GLint; 4],
}
impl FramebufferRebindingInfo {
fn detect(device: &Device, context: &Context, gl: &Gl) -> FramebufferRebindingInfo {
unsafe {
let (mut read_framebuffer, mut draw_framebuffer) = ([0], [0]);
gl.get_parameter_i32_slice(gl::READ_FRAMEBUFFER_BINDING, &mut read_framebuffer);
gl.get_parameter_i32_slice(gl::DRAW_FRAMEBUFFER_BINDING, &mut draw_framebuffer);
let context_surface_framebuffer = device
.context_surface_info(context)
.unwrap()
.unwrap()
.framebuffer_object;
let mut flags = FramebufferRebindingFlags::empty();
if context_surface_framebuffer == read_framebuffer[0] as GLuint {
flags.insert(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER);
}
if context_surface_framebuffer == draw_framebuffer[0] as GLuint {
flags.insert(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER);
}
let mut viewport = [0; 4];
gl.get_parameter_i32_slice(gl::VIEWPORT, &mut viewport);
FramebufferRebindingInfo { flags, viewport }
}
}
fn apply(self, device: &Device, context: &Context, gl: &Gl) {
if self.flags.is_empty() {
return;
}
let context_surface_framebuffer = device
.context_surface_info(context)
.unwrap()
.unwrap()
.framebuffer_object;
if self
.flags
.contains(FramebufferRebindingFlags::REBIND_READ_FRAMEBUFFER)
{
unsafe {
gl.bind_framebuffer(
gl::READ_FRAMEBUFFER,
NonZeroU32::new(context_surface_framebuffer).map(NativeFramebuffer),
)
};
}
if self
.flags
.contains(FramebufferRebindingFlags::REBIND_DRAW_FRAMEBUFFER)
{
unsafe {
gl.bind_framebuffer(
gl::DRAW_FRAMEBUFFER,
NonZeroU32::new(context_surface_framebuffer).map(NativeFramebuffer),
)
};
}
unsafe {
gl.viewport(
self.viewport[0],
self.viewport[1],
self.viewport[2],
self.viewport[3],
)
};
}
}