use std::any::TypeId;
use std::borrow::Borrow;
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque};
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::Deref;
use std::rc::Rc;
use fnv::FnvHasher;
use js_sys::{Int32Array, Promise};
use serde_derive::Serialize;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{HtmlCanvasElement, WebGl2RenderingContext as Gl};
use crate::buffer::{Buffer, BufferId, IntoBuffer, UsageHint};
use crate::extensions::Extension;
use crate::image::format::{
InternalFormat, Multisamplable, Multisample, RenderbufferFormat, TextureFormat,
};
use crate::image::renderbuffer::{Renderbuffer, RenderbufferDescriptor};
use crate::image::sampler::{
MagnificationFilter, MinificationFilter, Sampler, SamplerDescriptor, ShadowSampler,
ShadowSamplerDescriptor,
};
use crate::image::texture_2d::{Texture2D, Texture2DDescriptor};
use crate::image::texture_2d_array::{Texture2DArray, Texture2DArrayDescriptor};
use crate::image::texture_3d::{Texture3D, Texture3DDescriptor};
use crate::image::texture_cube::{TextureCube, TextureCubeDescriptor};
use crate::image::MaxMipmapLevelsExceeded;
use crate::pipeline::graphics::shader::{
FragmentShaderAllocateCommand, VertexShaderAllocateCommand,
};
use crate::pipeline::graphics::{
FragmentShader, GraphicsPipeline, GraphicsPipelineDescriptor, IndexBuffer, IndexFormat,
VertexShader,
};
use crate::pipeline::resources::{BindGroup, EncodeBindableResourceGroup};
use crate::rendering::{
DefaultDepthBuffer, DefaultDepthStencilBuffer, DefaultMultisampleRenderTarget,
DefaultRGBABuffer, DefaultRGBBuffer, DefaultRenderTarget, DefaultStencilBuffer,
MultisampleRenderTarget, MultisampleRenderTargetDescriptor, RenderTarget,
RenderTargetDescriptor,
};
use crate::runtime::executor_job::{job, ExecutorJob, JobState};
use crate::runtime::fenced::JsTimeoutFencedTaskRunner;
use crate::runtime::rendering_context::{
CreateGraphicsPipelineError, MaxColorBuffersExceeded, UnsupportedSampleCount,
};
use crate::runtime::state::DynamicState;
use crate::runtime::{
Connection, ContextOptions, Execution, PowerPreference, RenderingContext,
ShaderCompilationError, SupportedSamples,
};
use crate::task::{GpuTask, Progress};
use wasm_bindgen::__rt::core::mem::MaybeUninit;
thread_local!(static ID_GEN: IdGen = IdGen::new());
struct IdGen {
next: Cell<usize>,
}
impl IdGen {
const fn new() -> Self {
IdGen { next: Cell::new(0) }
}
fn next(&self) -> usize {
let next = self.next.get();
self.next.set(next + 1);
next
}
}
#[derive(Clone)]
pub(crate) struct ObjectIdGen {
context_id: u64,
object_id: Rc<Cell<u64>>,
}
impl ObjectIdGen {
fn new(context_id: u64) -> Self {
ObjectIdGen {
context_id,
object_id: Rc::new(Cell::new(0)),
}
}
pub(crate) fn next(&self) -> u64 {
let id = self.object_id.get();
self.object_id.set(id + 1);
let mut hasher = FnvHasher::default();
(self.context_id, id).hash(&mut hasher);
hasher.finish()
}
}
#[derive(Clone)]
pub struct SingleThreadedContext {
executor: Rc<SingleThreadedExecutor>,
id: u64,
object_id_gen: ObjectIdGen,
max_color_attachments: u8,
supported_samples_cache: Rc<RefCell<HashMap<u32, SupportedSamples>>>,
}
impl RenderingContext for SingleThreadedContext {
fn id(&self) -> u64 {
self.id
}
fn get_extension<T>(&self) -> Option<T>
where
T: Extension,
{
let executor = self.executor.deref().borrow();
let mut connection = executor.connection.deref().borrow_mut();
Extension::try_init(&mut connection, self.id)
}
fn supported_samples<F>(&self, _format: F) -> SupportedSamples
where
F: InternalFormat + Multisamplable,
{
let mut cache = self.supported_samples_cache.borrow_mut();
if let Some(supported_samples) = cache.get(&F::ID) {
*supported_samples
} else {
let executor = self.executor.deref().borrow();
let connection = executor.connection.deref().borrow();
let (gl, _) = unsafe { connection.unpack() };
let array: Int32Array = gl
.get_internalformat_parameter(Gl::RENDERBUFFER, F::ID, Gl::SAMPLES)
.unwrap()
.into();
let len = array.length();
let mut supported_samples = SupportedSamples::NONE;
for i in 0..len {
let entry = array.get_index(i) as u8;
if entry == 16 {
supported_samples |= SupportedSamples::SAMPLES_16;
} else if entry == 8 {
supported_samples |= SupportedSamples::SAMPLES_8;
} else if entry == 4 {
supported_samples |= SupportedSamples::SAMPLES_4;
} else if entry == 2 {
supported_samples |= SupportedSamples::SAMPLES_2;
}
}
cache.insert(F::ID, supported_samples);
supported_samples
}
}
fn create_bind_group<T>(&self, resources: T) -> BindGroup<T>
where
T: EncodeBindableResourceGroup,
{
let object_id = self.object_id_gen.next();
BindGroup::new(object_id, self.id, resources)
}
fn create_buffer<D, T>(&self, data: D, usage_hint: UsageHint) -> Buffer<T>
where
D: IntoBuffer<T>,
T: ?Sized,
{
let object_id = self.object_id_gen.next();
let buffer_id = BufferId { object_id };
data.into_buffer(self, buffer_id, usage_hint)
}
fn create_buffer_uninit<T>(&self, usage_hint: UsageHint) -> Buffer<MaybeUninit<T>>
where
T: 'static,
{
let object_id = self.object_id_gen.next();
let buffer_id = BufferId { object_id };
Buffer::create_uninit(self, buffer_id, usage_hint)
}
fn create_buffer_slice_uninit<T>(
&self,
len: usize,
usage_hint: UsageHint,
) -> Buffer<[MaybeUninit<T>]>
where
T: 'static,
{
let object_id = self.object_id_gen.next();
let buffer_id = BufferId { object_id };
Buffer::create_slice_uninit(self, buffer_id, len, usage_hint)
}
fn create_index_buffer<D, T>(&self, data: D, usage_hint: UsageHint) -> IndexBuffer<T>
where
D: Borrow<[T]> + 'static,
T: IndexFormat + 'static,
{
let object_id = self.object_id_gen.next();
IndexBuffer::new(self, object_id, data, usage_hint)
}
fn create_index_buffer_uninit<T>(
&self,
len: usize,
usage_hint: UsageHint,
) -> IndexBuffer<MaybeUninit<T>>
where
T: IndexFormat + 'static,
{
let object_id = self.object_id_gen.next();
IndexBuffer::new_uninit(self, object_id, len, usage_hint)
}
fn create_renderbuffer<F>(&self, descriptor: &RenderbufferDescriptor<F>) -> Renderbuffer<F>
where
F: RenderbufferFormat + 'static,
{
let object_id = self.object_id_gen.next();
Renderbuffer::new(self, object_id, descriptor)
}
fn try_create_multisample_renderbuffer<F>(
&self,
descriptor: &RenderbufferDescriptor<Multisample<F>>,
) -> Result<Renderbuffer<Multisample<F>>, UnsupportedSampleCount>
where
F: RenderbufferFormat + Multisamplable + Copy + 'static,
{
let object_id = self.object_id_gen.next();
Renderbuffer::new_multisample(self, object_id, descriptor)
}
fn try_create_vertex_shader<S>(&self, source: S) -> Result<VertexShader, ShaderCompilationError>
where
S: Borrow<str> + 'static,
{
let object_id = self.object_id_gen.next();
let allocate_command = VertexShaderAllocateCommand::new(self, object_id, source);
match self.submit(allocate_command) {
Execution::Ready(res) => res.unwrap(),
_ => unreachable!(),
}
}
fn try_create_fragment_shader<S>(
&self,
source: S,
) -> Result<FragmentShader, ShaderCompilationError>
where
S: Borrow<str> + 'static,
{
let object_id = self.object_id_gen.next();
let allocate_command = FragmentShaderAllocateCommand::new(self, object_id, source);
match self.submit(allocate_command) {
Execution::Ready(res) => res.unwrap(),
_ => unreachable!(),
}
}
fn try_create_graphics_pipeline<V, R, Tf>(
&self,
descriptor: &GraphicsPipelineDescriptor<V, R, Tf>,
) -> Result<GraphicsPipeline<V, R, Tf>, CreateGraphicsPipelineError> {
let mut connection = self.executor.connection.borrow_mut();
let object_id = self.object_id_gen.next();
GraphicsPipeline::create(self, object_id, &mut connection, descriptor)
}
fn create_render_target<C, Ds>(
&self,
descriptor: RenderTargetDescriptor<(C,), Ds>,
) -> RenderTarget<(C,), Ds> {
let RenderTargetDescriptor {
color_attachments,
depth_stencil_attachment,
context_id,
..
} = descriptor;
let object_id = self.object_id_gen.next();
context_id.verify(self.id);
RenderTarget {
color_attachments,
depth_stencil_attachment,
object_id,
context_id: self.id,
render_pass_id_gen: self.object_id_gen.clone(),
}
}
fn try_create_render_target<C, Ds>(
&self,
descriptor: RenderTargetDescriptor<C, Ds>,
) -> Result<RenderTarget<C, Ds>, MaxColorBuffersExceeded> {
let RenderTargetDescriptor {
color_attachments,
depth_stencil_attachment,
color_attachment_count,
context_id,
} = descriptor;
context_id.verify(self.id);
if color_attachment_count > self.max_color_attachments {
Err(MaxColorBuffersExceeded {
max_supported_color_buffers: self.max_color_attachments,
requested_color_buffers: color_attachment_count,
})
} else {
let object_id = self.object_id_gen.next();
Ok(RenderTarget {
color_attachments,
depth_stencil_attachment,
object_id,
context_id: self.id,
render_pass_id_gen: self.object_id_gen.clone(),
})
}
}
fn create_multisample_render_target<C, Ds>(
&self,
descriptor: MultisampleRenderTargetDescriptor<(C,), Ds>,
) -> MultisampleRenderTarget<(C,), Ds> {
let MultisampleRenderTargetDescriptor {
color_attachments,
depth_stencil_attachment,
samples,
context_id,
..
} = descriptor;
let object_id = self.object_id_gen.next();
context_id.verify(self.id);
MultisampleRenderTarget {
color_attachments,
depth_stencil_attachment,
samples,
object_id,
context_id: self.id,
render_pass_id_gen: self.object_id_gen.clone(),
}
}
fn try_create_multisample_render_target<C, Ds>(
&self,
descriptor: MultisampleRenderTargetDescriptor<C, Ds>,
) -> Result<MultisampleRenderTarget<C, Ds>, MaxColorBuffersExceeded> {
let MultisampleRenderTargetDescriptor {
color_attachments,
depth_stencil_attachment,
samples,
color_attachment_count,
context_id,
} = descriptor;
context_id.verify(self.id);
if color_attachment_count > self.max_color_attachments {
Err(MaxColorBuffersExceeded {
max_supported_color_buffers: self.max_color_attachments,
requested_color_buffers: color_attachment_count,
})
} else {
let object_id = self.object_id_gen.next();
Ok(MultisampleRenderTarget {
color_attachments,
depth_stencil_attachment,
samples,
object_id,
context_id: self.id,
render_pass_id_gen: self.object_id_gen.clone(),
})
}
}
fn try_create_texture_2d<F>(
&self,
descriptor: &Texture2DDescriptor<F>,
) -> Result<Texture2D<F>, MaxMipmapLevelsExceeded>
where
F: TextureFormat + 'static,
{
let object_id = self.object_id_gen.next();
Texture2D::new(self, object_id, descriptor)
}
fn try_create_texture_2d_array<F>(
&self,
descriptor: &Texture2DArrayDescriptor<F>,
) -> Result<Texture2DArray<F>, MaxMipmapLevelsExceeded>
where
F: TextureFormat + 'static,
{
let object_id = self.object_id_gen.next();
Texture2DArray::new(self, object_id, descriptor)
}
fn try_create_texture_3d<F>(
&self,
descriptor: &Texture3DDescriptor<F>,
) -> Result<Texture3D<F>, MaxMipmapLevelsExceeded>
where
F: TextureFormat + 'static,
{
let object_id = self.object_id_gen.next();
Texture3D::new(self, object_id, descriptor)
}
fn try_create_texture_cube<F>(
&self,
descriptor: &TextureCubeDescriptor<F>,
) -> Result<TextureCube<F>, MaxMipmapLevelsExceeded>
where
F: TextureFormat + 'static,
{
let object_id = self.object_id_gen.next();
TextureCube::new(self, object_id, descriptor)
}
fn create_sampler<Min, Mag>(
&self,
descriptor: &SamplerDescriptor<Min, Mag>,
) -> Sampler<Min, Mag>
where
Min: MinificationFilter + Copy + 'static,
Mag: MagnificationFilter + Copy + 'static,
{
let object_id = self.object_id_gen.next();
Sampler::new(self, object_id, descriptor)
}
fn create_shadow_sampler(&self, descriptor: &ShadowSamplerDescriptor) -> ShadowSampler {
let object_id = self.object_id_gen.next();
ShadowSampler::new(self, object_id, descriptor)
}
fn submit<T>(&self, task: T) -> Execution<T::Output>
where
T: GpuTask<Connection> + 'static,
{
self.executor.accept(task)
}
}
impl SingleThreadedContext {
pub unsafe fn from_webgl2_context(gl: Gl, state: DynamicState) -> Self {
let id = ID_GEN.with(|id_gen| id_gen.next());
let mut hasher = FnvHasher::default();
(TypeId::of::<Self>(), id).hash(&mut hasher);
let id = hasher.finish();
let max_color_attachments = gl
.get_parameter(Gl::MAX_COLOR_ATTACHMENTS)
.unwrap()
.as_f64()
.unwrap() as u8;
SingleThreadedContext {
executor: SingleThreadedExecutor::new(Connection::new(id, gl, state)).into(),
id,
object_id_gen: ObjectIdGen::new(id),
max_color_attachments,
supported_samples_cache: Rc::new(RefCell::new(HashMap::new())),
}
}
}
struct SingleThreadedExecutor {
connection: Rc<RefCell<Connection>>,
fenced_task_queue_runner: Rc<RefCell<JsTimeoutFencedTaskRunner>>,
buffer: Rc<RefCell<VecDeque<Box<dyn ExecutorJob>>>>,
process_buffer_closure: Rc<RefCell<Option<Closure<dyn FnMut(JsValue)>>>>,
process_buffer_promise: Promise,
}
impl SingleThreadedExecutor {
fn new(connection: Connection) -> Self {
let connection = Rc::new(RefCell::new(connection));
let fenced_task_queue_runner = Rc::new(RefCell::new(JsTimeoutFencedTaskRunner::new(
connection.clone(),
)));
let buffer: Rc<RefCell<VecDeque<Box<dyn ExecutorJob>>>> =
Rc::new(RefCell::new(VecDeque::new()));
let rc = Rc::new(RefCell::new(None));
let rc_clone = rc.clone();
let connection_clone = connection.clone();
let fenced_task_queue_runner_clone = fenced_task_queue_runner.clone();
let buffer_clone = buffer.clone();
let callback = Closure::wrap(Box::new(move |_| {
while let Some(mut job) = buffer_clone.borrow_mut().pop_front() {
if let JobState::ContinueFenced = job.progress(&mut connection_clone.borrow_mut()) {
fenced_task_queue_runner_clone.borrow_mut().schedule(job);
}
}
if Rc::strong_count(&rc_clone) == 1 {
let callback = rc_clone.borrow_mut().take();
mem::drop(callback);
}
}) as Box<dyn FnMut(JsValue)>);
*rc.borrow_mut() = Some(callback);
SingleThreadedExecutor {
connection,
fenced_task_queue_runner,
buffer,
process_buffer_closure: rc,
process_buffer_promise: Promise::resolve(&JsValue::null()),
}
}
fn accept<T>(&self, mut task: T) -> Execution<T::Output>
where
T: GpuTask<Connection> + 'static,
{
if let Ok(mut connection) = self.connection.try_borrow_mut() {
let output = task.progress(&mut connection);
mem::drop(connection);
match output {
Progress::Finished(res) => res.into(),
Progress::ContinueFenced => {
let (job, execution) = job(task);
self.fenced_task_queue_runner
.borrow_mut()
.schedule(Box::new(job));
execution
}
}
} else {
let (job, execution) = job(task);
let mut buffer = self.buffer.borrow_mut();
buffer.push_back(Box::new(job));
if buffer.len() == 1 {
let ref_cell: &RefCell<_> = self.process_buffer_closure.borrow();
let callback_ref = ref_cell.borrow();
let promise = self
.process_buffer_promise
.then(callback_ref.as_ref().unwrap());
mem::drop(promise);
}
execution
}
}
}
impl Drop for SingleThreadedExecutor {
fn drop(&mut self) {
if self.buffer.deref().borrow().len() == 0 {
let ref_cell: &RefCell<_> = self.process_buffer_closure.borrow();
let callback_ref = ref_cell.borrow();
let promise = self
.process_buffer_promise
.then(callback_ref.as_ref().unwrap());
mem::drop(promise);
}
}
}
pub unsafe fn init<O>(canvas: &HtmlCanvasElement, options: &O) -> O::Output
where
O: Options,
{
options.get_context(canvas)
}
pub trait Options {
type Output;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output;
}
impl Options for ContextOptions<DefaultMultisampleRenderTarget<DefaultRGBABuffer, ()>> {
type Output = Result<
(
SingleThreadedContext,
DefaultMultisampleRenderTarget<DefaultRGBABuffer, ()>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: true,
antialias: true,
depth: false,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: false,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let samples = gl.get_parameter(Gl::SAMPLES).unwrap().as_f64().unwrap() as u8;
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultMultisampleRenderTarget::new(
context.id(),
samples,
context.object_id_gen.clone(),
);
Ok((context, render_target))
}
}
impl Options
for ContextOptions<DefaultMultisampleRenderTarget<DefaultRGBABuffer, DefaultDepthStencilBuffer>>
{
type Output = Result<
(
SingleThreadedContext,
DefaultMultisampleRenderTarget<DefaultRGBABuffer, DefaultDepthStencilBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: true,
antialias: true,
depth: true,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: true,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let samples = gl.get_parameter(Gl::SAMPLES).unwrap().as_f64().unwrap() as u8;
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultMultisampleRenderTarget::new(
context.id(),
samples,
context.object_id_gen.clone(),
);
Ok((context, render_target))
}
}
impl Options
for ContextOptions<DefaultMultisampleRenderTarget<DefaultRGBABuffer, DefaultDepthBuffer>>
{
type Output = Result<
(
SingleThreadedContext,
DefaultMultisampleRenderTarget<DefaultRGBABuffer, DefaultDepthBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: true,
antialias: true,
depth: true,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: false,
})
.unwrap();
let gl: web_sys::WebGl2RenderingContext = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let samples = gl.get_parameter(Gl::SAMPLES).unwrap().as_f64().unwrap() as u8;
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultMultisampleRenderTarget::new(
context.id(),
samples,
context.object_id_gen.clone(),
);
Ok((context, render_target))
}
}
impl Options
for ContextOptions<DefaultMultisampleRenderTarget<DefaultRGBABuffer, DefaultStencilBuffer>>
{
type Output = Result<
(
SingleThreadedContext,
DefaultMultisampleRenderTarget<DefaultRGBABuffer, DefaultStencilBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: true,
antialias: true,
depth: false,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: true,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let samples = gl.get_parameter(Gl::SAMPLES).unwrap().as_f64().unwrap() as u8;
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultMultisampleRenderTarget::new(
context.id(),
samples,
context.object_id_gen.clone(),
);
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultMultisampleRenderTarget<DefaultRGBBuffer, ()>> {
type Output = Result<
(
SingleThreadedContext,
DefaultMultisampleRenderTarget<DefaultRGBBuffer, ()>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: false,
antialias: true,
depth: false,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: false,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let samples = gl.get_parameter(Gl::SAMPLES).unwrap().as_f64().unwrap() as u8;
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultMultisampleRenderTarget::new(
context.id(),
samples,
context.object_id_gen.clone(),
);
Ok((context, render_target))
}
}
impl Options
for ContextOptions<DefaultMultisampleRenderTarget<DefaultRGBBuffer, DefaultDepthStencilBuffer>>
{
type Output = Result<
(
SingleThreadedContext,
DefaultMultisampleRenderTarget<DefaultRGBBuffer, DefaultDepthStencilBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: false,
antialias: true,
depth: true,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: true,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let samples = gl.get_parameter(Gl::SAMPLES).unwrap().as_f64().unwrap() as u8;
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultMultisampleRenderTarget::new(
context.id(),
samples,
context.object_id_gen.clone(),
);
Ok((context, render_target))
}
}
impl Options
for ContextOptions<DefaultMultisampleRenderTarget<DefaultRGBBuffer, DefaultDepthBuffer>>
{
type Output = Result<
(
SingleThreadedContext,
DefaultMultisampleRenderTarget<DefaultRGBBuffer, DefaultDepthBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: false,
antialias: true,
depth: true,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: false,
})
.unwrap();
let gl: web_sys::WebGl2RenderingContext = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let samples = gl.get_parameter(Gl::SAMPLES).unwrap().as_f64().unwrap() as u8;
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultMultisampleRenderTarget::new(
context.id(),
samples,
context.object_id_gen.clone(),
);
Ok((context, render_target))
}
}
impl Options
for ContextOptions<DefaultMultisampleRenderTarget<DefaultRGBBuffer, DefaultStencilBuffer>>
{
type Output = Result<
(
SingleThreadedContext,
DefaultMultisampleRenderTarget<DefaultRGBBuffer, DefaultStencilBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: false,
antialias: true,
depth: false,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: true,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let samples = gl.get_parameter(Gl::SAMPLES).unwrap().as_f64().unwrap() as u8;
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultMultisampleRenderTarget::new(
context.id(),
samples,
context.object_id_gen.clone(),
);
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultRenderTarget<DefaultRGBABuffer, ()>> {
type Output = Result<
(
SingleThreadedContext,
DefaultRenderTarget<DefaultRGBABuffer, ()>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: true,
antialias: false,
depth: false,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: false,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultRenderTarget::new(context.id(), context.object_id_gen.clone());
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultRenderTarget<DefaultRGBABuffer, DefaultDepthStencilBuffer>> {
type Output = Result<
(
SingleThreadedContext,
DefaultRenderTarget<DefaultRGBABuffer, DefaultDepthStencilBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: true,
antialias: false,
depth: true,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: true,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultRenderTarget::new(context.id(), context.object_id_gen.clone());
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultRenderTarget<DefaultRGBABuffer, DefaultDepthBuffer>> {
type Output = Result<
(
SingleThreadedContext,
DefaultRenderTarget<DefaultRGBABuffer, DefaultDepthBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: true,
antialias: false,
depth: true,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: false,
})
.unwrap();
let gl: web_sys::WebGl2RenderingContext = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultRenderTarget::new(context.id(), context.object_id_gen.clone());
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultRenderTarget<DefaultRGBABuffer, DefaultStencilBuffer>> {
type Output = Result<
(
SingleThreadedContext,
DefaultRenderTarget<DefaultRGBABuffer, DefaultStencilBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: true,
antialias: false,
depth: false,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: true,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultRenderTarget::new(context.id(), context.object_id_gen.clone());
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultRenderTarget<DefaultRGBBuffer, ()>> {
type Output = Result<
(
SingleThreadedContext,
DefaultRenderTarget<DefaultRGBBuffer, ()>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: false,
antialias: false,
depth: false,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: false,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultRenderTarget::new(context.id(), context.object_id_gen.clone());
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultRenderTarget<DefaultRGBBuffer, DefaultDepthStencilBuffer>> {
type Output = Result<
(
SingleThreadedContext,
DefaultRenderTarget<DefaultRGBBuffer, DefaultDepthStencilBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: false,
antialias: false,
depth: true,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: true,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultRenderTarget::new(context.id(), context.object_id_gen.clone());
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultRenderTarget<DefaultRGBBuffer, DefaultDepthBuffer>> {
type Output = Result<
(
SingleThreadedContext,
DefaultRenderTarget<DefaultRGBBuffer, DefaultDepthBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: false,
antialias: false,
depth: true,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: false,
})
.unwrap();
let gl: web_sys::WebGl2RenderingContext = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultRenderTarget::new(context.id(), context.object_id_gen.clone());
Ok((context, render_target))
}
}
impl Options for ContextOptions<DefaultRenderTarget<DefaultRGBBuffer, DefaultStencilBuffer>> {
type Output = Result<
(
SingleThreadedContext,
DefaultRenderTarget<DefaultRGBBuffer, DefaultStencilBuffer>,
),
String,
>;
unsafe fn get_context(&self, canvas: &HtmlCanvasElement) -> Self::Output {
let options = JsValue::from_serde(&OptionsJson {
alpha: false,
antialias: false,
depth: false,
fail_if_major_performance_caveat: self.fail_if_major_performance_caveat(),
power_preference: self.power_preference(),
premultiplied_alpha: self.premultiplied_alpha(),
preserve_drawing_buffer: self.preserve_drawing_buffer(),
stencil: true,
})
.unwrap();
let gl = canvas
.get_context_with_context_options("webgl2", &options)
.map_err(|e| e.as_string().unwrap())?
.unwrap()
.unchecked_into();
let state = DynamicState::initial(&gl);
let context = SingleThreadedContext::from_webgl2_context(gl, state);
let render_target = DefaultRenderTarget::new(context.id(), context.object_id_gen.clone());
Ok((context, render_target))
}
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct OptionsJson {
alpha: bool,
antialias: bool,
depth: bool,
fail_if_major_performance_caveat: bool,
power_preference: PowerPreference,
premultiplied_alpha: bool,
preserve_drawing_buffer: bool,
stencil: bool,
}