From e34104c6b43045d8bb2afffea7f4008296b62a34 Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 02:44:40 -0400 Subject: [PATCH 1/9] fix(core): destroy the Vulkan swapchain and its image views on drop AshGraphics::drop destroyed the surface, device and instance but left the swapchain, its image views and the semaphores. A leaked swapchain keeps the ANativeWindow connected to Vulkan, so a GL fallback on the same window failed with EGL_BAD_ALLOC ("already connected to another API"), and a new Vulkan surface on it failed too. device_wait_idle no longer panics in drop after a device loss. --- crates/canvas-core/src/gpu/vulkan.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/canvas-core/src/gpu/vulkan.rs b/crates/canvas-core/src/gpu/vulkan.rs index 77a5d7ad6..46b299476 100644 --- a/crates/canvas-core/src/gpu/vulkan.rs +++ b/crates/canvas-core/src/gpu/vulkan.rs @@ -30,11 +30,22 @@ pub struct AshGraphics { impl Drop for AshGraphics { fn drop(&mut self) { unsafe { - self.device.device_wait_idle().unwrap(); + // A lost device fails this; teardown goes ahead regardless. + let _ = self.device.device_wait_idle(); - if let Some(surface) = self.surface { + for view in self.swap_chain_image_view.take().unwrap_or_default() { + self.device.destroy_image_view(view, None); + } + // Before the surface: a live swapchain keeps the window connected to Vulkan, and + // anything that later presents to it (a GL fallback) fails to connect. + if let Some(swap_chain) = self.swap_chain.take() { + self.swap_chain_loader.destroy_swapchain(swap_chain, None); + } + if let Some(surface) = self.surface.take() { self.surface_loader.destroy_surface(surface, None); } + self.device.destroy_semaphore(self.image_available_semaphore, None); + self.device.destroy_semaphore(self.present_semaphore, None); self.device.destroy_device(None); self.instance.destroy_instance(None); From 869221f0e688fc736e321b53ca77904152563d9a Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 02:44:53 -0400 Subject: [PATCH 2/9] fix(canvas): hand a % size to MasonKit on Android and iOS too A canvas's default width/height is 100%, which its layout params can't express, so under a MasonKit parent the canvas was measured to its surface while the surface followed the layout: three.js canvases stuck at 1px and shader canvases grew to the page's height. The Windows host already handed % sizes to the parent's _setChildPercentSize; Android and iOS now do the same, through a shared setParentPercentSize that also clears a % the size no longer has. --- packages/canvas/Canvas/index.android.ts | 6 +++-- packages/canvas/Canvas/index.ios.ts | 4 +++- packages/canvas/Canvas/napi-host.ts | 15 +++---------- packages/canvas/Canvas/utils.ts | 29 +++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/packages/canvas/Canvas/index.android.ts b/packages/canvas/Canvas/index.android.ts index 66183d331..fc2869e4f 100644 --- a/packages/canvas/Canvas/index.android.ts +++ b/packages/canvas/Canvas/index.android.ts @@ -6,7 +6,7 @@ import { WebGL2RenderingContext } from '../WebGL2/WebGL2RenderingContext'; import { Application, View, Screen, ImageSource, Utils, widthProperty, heightProperty, isUserInteractionEnabledProperty } from '@nativescript/core'; import { GPUCanvasContext } from '../WebGPU'; import { ImageBitmapRenderingContext } from '../ImageBitmapRenderingContext'; -import { handleContextOptions, microtask, CanvasContextType } from './utils'; +import { handleContextOptions, microtask, CanvasContextType, setParentPercentSize } from './utils'; export function createSVGMatrix(): DOMMatrix { return new DOMMatrix(); @@ -325,18 +325,20 @@ export class Canvas extends CanvasBase { [widthProperty.setNative](value) { this.__setLayoutLength(widthProperty.setNative, value); + setParentPercentSize(this, true, value); this.__setSurfaceWidth(fromCssLength(value)); } [heightProperty.setNative](value) { this.__setLayoutLength(heightProperty.setNative, value); + setParentPercentSize(this, false, value); this.__setSurfaceHeight(fromCssLength(value)); } /** * A fixed CSS size also goes to the view's layout params, as for any view: a parent that * measures the canvas natively (MasonKit) would otherwise size it to its surface. % and auto - * keep the layout params the canvas was given. + * keep the layout params the canvas was given; a % also goes to such a parent to resolve. */ private __setLayoutLength(setNative: symbol, value: any) { if (typeof value === 'number' || value?.unit === 'dip' || value?.unit === 'px') { diff --git a/packages/canvas/Canvas/index.ios.ts b/packages/canvas/Canvas/index.ios.ts index 69e11f480..47093448b 100644 --- a/packages/canvas/Canvas/index.ios.ts +++ b/packages/canvas/Canvas/index.ios.ts @@ -6,7 +6,7 @@ import { WebGL2RenderingContext } from '../WebGL2/WebGL2RenderingContext'; import { ImageSource, Utils, Screen, isUserInteractionEnabledProperty, widthProperty, heightProperty } from '@nativescript/core'; import { GPUCanvasContext } from '../WebGPU'; import { ImageBitmapRenderingContext } from '../ImageBitmapRenderingContext'; -import { handleContextOptions, microtask, CanvasContextType } from './utils'; +import { handleContextOptions, microtask, CanvasContextType, setParentPercentSize } from './utils'; declare var NSCCanvas, NSCCanvasListener; export function createSVGMatrix(): DOMMatrix { @@ -192,6 +192,7 @@ export class Canvas extends CanvasBase { } [widthProperty.setNative](value: any) { + setParentPercentSize(this, true, value); this.__setSurfaceWidth(fromCssLength(value)); } @@ -260,6 +261,7 @@ export class Canvas extends CanvasBase { } [heightProperty.setNative](value: any) { + setParentPercentSize(this, false, value); this.__setSurfaceHeight(fromCssLength(value)); } diff --git a/packages/canvas/Canvas/napi-host.ts b/packages/canvas/Canvas/napi-host.ts index ebf9142e1..182b9c57f 100644 --- a/packages/canvas/Canvas/napi-host.ts +++ b/packages/canvas/Canvas/napi-host.ts @@ -15,7 +15,7 @@ import { WebGL2RenderingContext } from '../WebGL2/WebGL2RenderingContext'; import { GPUCanvasContext } from '../WebGPU'; import { ImageBitmapRenderingContext } from '../ImageBitmapRenderingContext'; import { ImageSource, Screen, Utils, widthProperty, heightProperty } from '@nativescript/core'; -import { handleContextOptions, microtask, CanvasContextType } from './utils'; +import { handleContextOptions, microtask, CanvasContextType, isPercentLength, setParentPercentSize } from './utils'; import { Helpers } from '../helpers'; export function createSVGMatrix(): DOMMatrix { @@ -54,15 +54,6 @@ export const enum CanvasFit { ScaleDown = 4, } -function isPercentLength(value: any) { - return (typeof value === 'object' && value?.unit === '%') || (typeof value === 'string' && value.trim().endsWith('%')); -} - -/** A % length as a 0-1 fraction. */ -function percentFraction(value: any): number { - return typeof value === 'string' ? parseFloat(value) / 100 : value.value; -} - function isFixedLength(value: any) { return value?.unit === 'px' || value?.unit === 'dip'; } @@ -261,13 +252,13 @@ export abstract class NapiCanvas extends CanvasBase { // handed the % size (`_setChildPercentSize`), to resolve against its containing block. [widthProperty.setNative](value: any) { (Object.getPrototypeOf(NapiCanvas.prototype) as any)[widthProperty.setNative]?.call(this, isPercentLength(value) ? 'auto' : value); - if (isPercentLength(value)) (this.parent as any)?._setChildPercentSize?.(this, true, percentFraction(value)); + setParentPercentSize(this, true, value); this.__setSurfaceWidth(fromCssLength(value)); } [heightProperty.setNative](value: any) { (Object.getPrototypeOf(NapiCanvas.prototype) as any)[heightProperty.setNative]?.call(this, isPercentLength(value) ? 'auto' : value); - if (isPercentLength(value)) (this.parent as any)?._setChildPercentSize?.(this, false, percentFraction(value)); + setParentPercentSize(this, false, value); this.__setSurfaceHeight(fromCssLength(value)); } diff --git a/packages/canvas/Canvas/utils.ts b/packages/canvas/Canvas/utils.ts index e79bcfa50..6d3b99766 100644 --- a/packages/canvas/Canvas/utils.ts +++ b/packages/canvas/Canvas/utils.ts @@ -95,3 +95,32 @@ export function removeItemFromArray(array: any[], item) { } export const microtask: (cb: () => void) => void = typeof queueMicrotask === 'function' ? queueMicrotask : (cb) => Promise.resolve().then(cb); + +export function isPercentLength(value: any) { + return (typeof value === 'object' && value?.unit === '%') || (typeof value === 'string' && value.trim().endsWith('%')); +} + +/** A % length as a 0-1 fraction. */ +export function percentFraction(value: any): number { + return typeof value === 'string' ? parseFloat(value) / 100 : value.value; +} + +const percentAxes_ = Symbol('percentAxes'); + +/** + * Hands a % width/height to a parent that lays out its children itself (MasonKit), so it resolves + * against the containing block rather than the canvas being sized to its surface. A size that + * stops being a % clears it again. + */ +export function setParentPercentSize(view: any, horizontal: boolean, value: any) { + const axis = horizontal ? 1 : 2; + const axes = view[percentAxes_] ?? 0; + if (isPercentLength(value)) { + if (view.parent?._setChildPercentSize?.(view, horizontal, percentFraction(value))) { + view[percentAxes_] = axes | axis; + } + } else if (axes & axis) { + view.parent?._setChildPercentSize?.(view, horizontal, null); + view[percentAxes_] = axes & ~axis; + } +} From 3ecb6ca648b08de3eb471d7d0402e28047ce99bd Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 02:45:02 -0400 Subject: [PATCH 3/9] perf(android): create WebGL EGL contexts at low GPU priority With EGL_IMG_context_priority, the UI's own rendering is scheduled ahead of a WebGL scene that saturates the GPU. 2D canvas contexts keep the default. --- crates/canvas-core/src/gpu/gl/android.rs | 39 +++++++++++++++++------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/crates/canvas-core/src/gpu/gl/android.rs b/crates/canvas-core/src/gpu/gl/android.rs index 94073805a..a398d5e1f 100644 --- a/crates/canvas-core/src/gpu/gl/android.rs +++ b/crates/canvas-core/src/gpu/gl/android.rs @@ -16,7 +16,7 @@ use glutin::config::{ Api, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, ConfigTemplateBuilder, }; use glutin::context::AsRawContext; -use glutin::context::{ContextApi, RawContext, Version}; +use glutin::context::{ContextApi, ContextAttributesBuilder, Priority, RawContext, Version}; use glutin::display::AsRawDisplay; use glutin::display::{GetGlDisplay, RawDisplay}; use glutin::prelude::GlSurface; @@ -116,6 +116,17 @@ pub(crate) enum SurfaceHelper { Pixmap(Surface), } + +/// WebGL contexts (not a 2D canvas's) run at low GPU priority where the driver allows it, so the +/// UI's own rendering is scheduled ahead of a scene that saturates the GPU. +fn webgl_priority(builder: ContextAttributesBuilder, is_canvas: bool) -> ContextAttributesBuilder { + if is_canvas { + builder + } else { + builder.with_priority(Priority::Low) + } +} + #[derive(Debug, Clone)] pub struct GLContextRaw { surface: RawSurface, @@ -629,6 +640,7 @@ impl GLContext { .map(|v| SurfaceHelper::Window(v)) .ok(); + let context_attrs_is_canvas = context_attrs.get_is_canvas(); let mut context_attrs = glutin::context::ContextAttributesBuilder::new() .with_context_api(ContextApi::Gles(Some( if context_attrs.get_is_canvas() | context_attrs.get_gl_legacy() { @@ -644,7 +656,8 @@ impl GLContext { } } - let context_attrs = context_attrs.build(Some(window)); + let context_attrs = webgl_priority(context_attrs, context_attrs_is_canvas) + .build(Some(window)); let context = display .create_context(&config, &context_attrs) @@ -1077,15 +1090,19 @@ impl GLContext { .map(SurfaceHelper::Pbuffer) .ok(); - let context_attrs = glutin::context::ContextAttributesBuilder::new() - .with_context_api(ContextApi::Gles(Some( - if context_attrs.get_is_canvas() | context_attrs.get_gl_legacy() { - Version::new(2, 0) - } else { - Version::new(3, 0) - }, - ))) - .build(None); + let context_attrs = webgl_priority( + glutin::context::ContextAttributesBuilder::new().with_context_api( + ContextApi::Gles(Some( + if context_attrs.get_is_canvas() | context_attrs.get_gl_legacy() { + Version::new(2, 0) + } else { + Version::new(3, 0) + }, + )), + ), + context_attrs.get_is_canvas(), + ) + .build(None); let context = display .create_context(&config, &context_attrs) From 4291a06d304f95a819e4cf19e69f5a11720eecda Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 02:45:26 -0400 Subject: [PATCH 4/9] feat(webgl): run Android WebGL contexts on a dedicated thread WebGL calls ran on the JS thread, which on Android is the UI thread, and the per-frame present (eglSwapBuffers) blocked it whenever the GPU fell behind: with a heavy scene on screen the UI dropped to a few frames a second. A threaded context (the default, getContext(..., { threaded: false }) opts out) is created, used, presented and dropped on one shared "nsc-webgl" thread, like threaded 2D: - The canvas-c FFI reaches the context through WebGLState::post/sync. Calls that return nothing are queued, copying any data they borrow; calls that return a value, read into the caller's memory or read another canvas run on the thread while the caller waits. An unthreaded context (iOS, offscreen) runs both inline, as before, so the V8 bindings are unchanged. - The present is queued. A context with a frame queued behind an unpresented one counts as behind, and requestAnimationFrame is held back a frame while any is, rather than queueing more work; a context also waits once it has three presents queued. - Surface updates from the view's callbacks are queued (blocking them can deadlock the buffer queue); detaching from a window waits, as the window goes away on return. - Extensions, Bitmap uploads, snapshots, 2D drawImage/createPattern and WebGPU copies from a WebGL canvas go through the context's thread, and the Java GL helpers for video run there via Utils.nativeRunWithWebGL. - Context creation waits for the thread, so the init natives are no longer @FastNative. --- .../org_nativescript_canvas_NSCCanvas.rs | 96 +- ...escript_canvas_NSCWebGLRenderingContext.rs | 28 +- crates/canvas-android/src/lib.rs | 8 +- crates/canvas-android/src/utils/gl/mod.rs | 48 +- crates/canvas-c/src/c2d/context.rs | 63 +- crates/canvas-c/src/webgl/gl.rs | 2141 +++++++++-------- crates/canvas-c/src/webgl/gl2.rs | 2126 ++++++++-------- crates/canvas-c/src/webgl/mod.rs | 2 + crates/canvas-c/src/webgl/thread.rs | 168 ++ crates/canvas-c/src/webgpu/gpu_queue.rs | 4 +- packages/canvas/Canvas/index.android.ts | 69 + .../java/org/nativescript/canvas/NSCCanvas.kt | 21 +- .../java/org/nativescript/canvas/Utils.kt | 47 +- packages/canvas/typings/android.d.ts | 3 + 14 files changed, 2735 insertions(+), 2089 deletions(-) create mode 100644 crates/canvas-c/src/webgl/thread.rs diff --git a/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCCanvas.rs b/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCCanvas.rs index 7e1a16645..250bfa1e6 100644 --- a/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCCanvas.rs +++ b/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCCanvas.rs @@ -250,6 +250,7 @@ pub extern "system" fn nativeInitWebGL( desynchronized: jboolean, xr_compatible: jboolean, version: jint, + threaded: jboolean, ) -> jlong { unsafe { let interface = env.get_native_interface(); @@ -261,7 +262,12 @@ pub extern "system" fn nativeInitWebGL( return 0; } if let Ok(power_preference) = PowerPreference::try_from(power_preference) { - let context = canvas_c::canvas_native_webgl_create( + let create = if threaded == JNI_TRUE { + canvas_c::canvas_native_webgl_create_threaded + } else { + canvas_c::canvas_native_webgl_create + }; + let context = create( window.ptr().as_ptr() as _, window.width(), window.height(), @@ -308,6 +314,7 @@ pub extern "system" fn nativeInitWebGLNoSurface( desynchronized: jboolean, xr_compatible: jboolean, version: jint, + threaded: jboolean, ) -> jlong { if version == 2 && !GLContext::has_gl2support() { return 0; @@ -317,7 +324,12 @@ pub extern "system" fn nativeInitWebGLNoSurface( let height = height.max(1); if let Ok(power_preference) = PowerPreference::try_from(power_preference) { - let context = canvas_c::canvas_native_webgl_create_no_window( + let create = if threaded == JNI_TRUE { + canvas_c::canvas_native_webgl_create_no_window_threaded + } else { + canvas_c::canvas_native_webgl_create_no_window + }; + let context = create( width, height, version as i32, @@ -458,19 +470,23 @@ pub extern "system" fn nativeUpdateWebGLSurface( return; } let context = context as *mut WebGLState; - let context = unsafe { &mut *context }; + let context = unsafe { &*context }; unsafe { if let Some(window) = NativeWindow::from_surface(env.get_native_interface(), surface) { - // NativeWindow::ptr() is NonNull; the as *mut c_void cast - // preserves non-nullness, but guard defensively to avoid a panic. - let Some(nn_ptr) = NonNull::new(window.ptr().as_ptr() as _) else { return }; - context.get_inner_mut().set_window_surface( - window.width(), - window.height(), - nn_ptr, - ); - context.get_inner().make_current(); drop(env); + let (width, height) = (window.width(), window.height()); + // Queued, not waited on: this runs from the view's surface callbacks, and blocking them + // on a thread that may be waiting for a buffer from this window can deadlock its queue. + // `window` holds a reference until the job is done with it. + context.post(move |state| { + // NativeWindow::ptr() is NonNull; the as *mut c_void cast + // preserves non-nullness, but guard defensively to avoid a panic. + let Some(nn_ptr) = NonNull::new(window.ptr().as_ptr() as _) else { + return; + }; + state.set_window_surface(width, height, nn_ptr); + state.make_current(); + }); } } } @@ -616,9 +632,12 @@ fn native_update_gl_no_surface(width: jint, height: jint, context: jlong) { return; } let context = context as *mut WebGLState; - let context = unsafe { &mut *context }; - context.get_inner().make_current(); - context.get_inner_mut().resize_pbuffer(width, height); + let context = unsafe { &*context }; + // Waited on: when the window is going away this is how the context lets go of it. + context.sync(|state| { + state.make_current(); + state.resize_pbuffer(width, height); + }); } #[no_mangle] @@ -676,7 +695,8 @@ pub extern "system" fn nativeMakeWebGLCurrent(gl_context: jlong) -> jboolean { } let gl_context = gl_context as *mut WebGLState; let gl_context = unsafe { &*gl_context }; - if gl_context.get_inner().make_current() { + // A threaded context is made current on its own thread, where everything that uses it runs. + if gl_context.sync(|state| state.make_current()) { return JNI_TRUE; } JNI_FALSE @@ -693,7 +713,8 @@ pub extern "system" fn nativeMakeWebGLCurrentNormal( } let gl_context = gl_context as *mut WebGLState; let gl_context = unsafe { &*gl_context }; - if gl_context.get_inner().make_current() { + // A threaded context is made current on its own thread, where everything that uses it runs. + if gl_context.sync(|state| state.make_current()) { return JNI_TRUE; } JNI_FALSE @@ -851,7 +872,7 @@ pub extern "system" fn nativeWriteCurrentWebGLContextToBitmap( } let context = context as *mut WebGLState; - let context = unsafe { &mut *context }; + let context = unsafe { &*context }; unsafe { crate::utils::image::bitmap_handler( @@ -859,24 +880,27 @@ pub extern "system" fn nativeWriteCurrentWebGLContextToBitmap( bitmap, Box::new(move |cb| { if let Some((image_data, info)) = cb { - context.get_inner().make_current(); - // Use checked arithmetic — width/height are u32 and can overflow on multiply. - let buf_size = (info.width() as usize) - .checked_mul(info.height() as usize) - .and_then(|n| n.checked_mul(4)); - let Some(buf_size) = buf_size else { return }; - let mut buf = vec![0u8; buf_size]; - gl_bindings::Flush(); - gl_bindings::ReadPixels( - 0, - 0, - info.width() as i32, - info.height() as i32, - gl_bindings::RGBA as std::os::raw::c_uint, - gl_bindings::UNSIGNED_BYTE as std::os::raw::c_uint, - buf.as_mut_ptr() as *mut c_void, - ); - image_data.copy_from_slice(buf.as_slice()); + // The bitmap stays locked while this waits for the read. + context.sync(|state| { + state.make_current(); + // Use checked arithmetic — width/height are u32 and can overflow on multiply. + let buf_size = (info.width() as usize) + .checked_mul(info.height() as usize) + .and_then(|n| n.checked_mul(4)); + let Some(buf_size) = buf_size else { return }; + let mut buf = vec![0u8; buf_size]; + gl_bindings::Flush(); + gl_bindings::ReadPixels( + 0, + 0, + info.width() as i32, + info.height() as i32, + gl_bindings::RGBA as std::os::raw::c_uint, + gl_bindings::UNSIGNED_BYTE as std::os::raw::c_uint, + buf.as_mut_ptr() as *mut c_void, + ); + image_data.copy_from_slice(buf.as_slice()); + }); } }), ) diff --git a/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCWebGLRenderingContext.rs b/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCWebGLRenderingContext.rs index 857c87e26..1786f735e 100644 --- a/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCWebGLRenderingContext.rs +++ b/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCWebGLRenderingContext.rs @@ -23,21 +23,23 @@ pub extern "system" fn nativeTexImage2D( } let context = context as *mut WebGLState; - let context = unsafe { &mut *context }; + let context = unsafe { &*context }; let bytes = crate::utils::image::get_bytes_from_bitmap(&env, bitmap); if let Some((bytes, info)) = bytes { - context.get_inner().make_current(); let width = info.width(); let height = info.height(); - unsafe { + let stride = info.stride(); + // The bitmap's bytes are a copy, so the upload can queue. + context.post(move |state| unsafe { + state.make_current(); if flip_y == JNI_TRUE { let mut buffer = bytes; canvas_webgl::utils::gl::flip_in_place( buffer.as_mut_ptr(), buffer.len(), - info.stride() as usize, + stride as usize, height as usize, ); @@ -65,7 +67,7 @@ pub extern "system" fn nativeTexImage2D( bytes.as_ptr() as *const c_void, ); } - } + }); } } @@ -88,21 +90,23 @@ pub extern "system" fn nativeTexSubImage2D( } let context = context as *mut WebGLState; - let context = unsafe { &mut *context }; + let context = unsafe { &*context }; let bytes = crate::utils::image::get_bytes_from_bitmap(&env, bitmap); if let Some((bytes, info)) = bytes { - unsafe { - context.get_inner().make_current(); - let width = info.width(); - let height = info.height(); + let width = info.width(); + let height = info.height(); + let stride = info.stride(); + // The bitmap's bytes are a copy, so the upload can queue. + context.post(move |state| unsafe { + state.make_current(); if flip_y == JNI_TRUE { let mut buffer = bytes; canvas_webgl::utils::gl::flip_in_place( buffer.as_mut_ptr(), buffer.len(), - info.stride() as usize, + stride as usize, height as usize, ); @@ -130,6 +134,6 @@ pub extern "system" fn nativeTexSubImage2D( bytes.as_ptr() as *const c_void, ); } - } + }); } } diff --git a/crates/canvas-android/src/lib.rs b/crates/canvas-android/src/lib.rs index 96381ddd2..04b492b8b 100644 --- a/crates/canvas-android/src/lib.rs +++ b/crates/canvas-android/src/lib.rs @@ -111,8 +111,8 @@ pub extern "system" fn JNI_OnLoad(vm: JavaVM, _reserved: *const c_void) -> jint let canvas_signatures = if ret >= ANDROID_O { let mut ret = vec![ - "(Landroid/view/Surface;ZZZZIZZZZZI)J", - "(IIZZZZIZZZZZI)J", + "(Landroid/view/Surface;ZZZZIZZZZZIZ)J", + "(IIZZZZIZZZZZIZ)J", "(IILandroid/view/Surface;ZFIFII)J", "(Landroid/view/Surface;J)V", "(Landroid/view/Surface;IIJ)V", @@ -149,8 +149,8 @@ pub extern "system" fn JNI_OnLoad(vm: JavaVM, _reserved: *const c_void) -> jint ret } else { let mut ret = vec![ - "!(Landroid/view/Surface;ZZZZIZZZZZI)J", - "!(IIZZZZIZZZZZI)J", + "(Landroid/view/Surface;ZZZZIZZZZZIZ)J", + "(IIZZZZIZZZZZIZ)J", "!(IILandroid/view/Surface;ZFIFII)J", "!(Landroid/view/Surface;J)V", "!(Landroid/view/Surface;IIJ)V", diff --git a/crates/canvas-android/src/utils/gl/mod.rs b/crates/canvas-android/src/utils/gl/mod.rs index ebea48ba2..e8bdb32bf 100644 --- a/crates/canvas-android/src/utils/gl/mod.rs +++ b/crates/canvas-android/src/utils/gl/mod.rs @@ -26,14 +26,58 @@ pub unsafe extern "system" fn Java_org_nativescript_canvas_Utils_nativeMakeState if state.is_null() { return JNI_FALSE; } - let state = &mut *state; + let state = &*state; - if state.get_inner().make_current() { + if state.sync(|state| state.make_current()) { return JNI_TRUE; } JNI_FALSE } +/// `canvas_native_webgl_contexts_behind`, for the requestAnimationFrame hold-back. +#[no_mangle] +pub extern "system" fn Java_org_nativescript_canvas_Utils_nativeWebGLContextsBehind( + _: JNIEnv, + _: jni::objects::JClass, +) -> jint { + canvas_c::canvas_native_webgl_contexts_behind() as jint +} + +/// Runs `block` (a `Runnable`) with `state` current, on the thread that owns it, and waits for it: +/// a threaded WebGL context can only be current on the WebGL thread, so Java that draws with it +/// (SurfaceTexture, GLES) has to run there too. +#[no_mangle] +pub unsafe extern "system" fn Java_org_nativescript_canvas_Utils_nativeRunWithWebGL( + mut env: JNIEnv, + _: jni::objects::JClass, + state: jlong, + block: jni::objects::JObject, +) { + if state == 0 { + return; + } + let state = &*(state as *const WebGLState); + if !state.is_threaded() { + state.sync(|state| state.make_current()); + let _ = env.call_method(&block, "run", "()V", &[]); + return; + } + let (Ok(block), Some(vm)) = (env.new_global_ref(block), crate::JVM.get()) else { + return; + }; + state.sync(|state| { + state.make_current(); + if let Ok(mut env) = vm.attach_current_thread_permanently() { + let _ = env.call_method(block.as_obj(), "run", "()V", &[]); + // Surface it here: nothing on this thread would otherwise. + if env.exception_check().unwrap_or(false) { + let _ = env.exception_describe(); + let _ = env.exception_clear(); + } + } + }); +} + /// Makes the EGL context of a 2D canvas context current on the calling thread. /// `context` is a pointer to `CanvasRenderingContext2D`. #[no_mangle] diff --git a/crates/canvas-c/src/c2d/context.rs b/crates/canvas-c/src/c2d/context.rs index 4793b9845..e4b5ca9b1 100644 --- a/crates/canvas-c/src/c2d/context.rs +++ b/crates/canvas-c/src/c2d/context.rs @@ -2452,19 +2452,18 @@ pub extern "C" fn canvas_native_context_draw_image_dx_dy_webgl( assert!(!source.is_null()); // let context = unsafe { &*context }; - let source = unsafe { &mut *source }; - let (width, height) = { - let state = source.get_inner_mut(); + let source = unsafe { &*source }; + // Read where the context lives: a threaded one is only touched on the WebGL thread. + let ((width, height), pixels) = source.sync(|state| { state.make_current(); let (w, h) = state.get_dimensions(); - (w as f32, h as f32) - }; - - let pixels = canvas_native_context_read_webgl_pixels( - &mut source.0, - gl_bindings::RGBA as i32, - gl_bindings::UNSIGNED_BYTE as i32, - ); + let pixels = canvas_native_context_read_webgl_pixels( + state, + gl_bindings::RGBA as i32, + gl_bindings::UNSIGNED_BYTE as i32, + ); + ((w as f32, h as f32), pixels) + }); let ptr = pixels.2.as_ptr(); let size = pixels.2.len(); @@ -2484,19 +2483,18 @@ pub extern "C" fn canvas_native_context_draw_image_dx_dy_dw_dh_webgl( assert!(!source.is_null()); // let context = unsafe { &*context }; - let source = unsafe { &mut *source }; - let (width, height) = { - let state = source.get_inner_mut(); + let source = unsafe { &*source }; + // Read where the context lives: a threaded one is only touched on the WebGL thread. + let ((width, height), pixels) = source.sync(|state| { state.make_current(); let (w, h) = state.get_dimensions(); - (w as f32, h as f32) - }; - - let pixels = canvas_native_context_read_webgl_pixels( - &mut source.0, - gl_bindings::RGBA as i32, - gl_bindings::UNSIGNED_BYTE as i32, - ); + let pixels = canvas_native_context_read_webgl_pixels( + state, + gl_bindings::RGBA as i32, + gl_bindings::UNSIGNED_BYTE as i32, + ); + ((w as f32, h as f32), pixels) + }); let ptr = pixels.2.as_ptr(); let size = pixels.2.len(); @@ -2523,19 +2521,18 @@ pub extern "C" fn canvas_native_context_draw_image_webgl( assert!(!source.is_null()); // let context = unsafe { &*context }; - let source = unsafe { &mut *source }; - let (width, height) = { - let state = source.get_inner_mut(); + let source = unsafe { &*source }; + // Read where the context lives: a threaded one is only touched on the WebGL thread. + let ((width, height), pixels) = source.sync(|state| { state.make_current(); let (w, h) = state.get_dimensions(); - (w as f32, h as f32) - }; - - let pixels = canvas_native_context_read_webgl_pixels( - &mut source.0, - gl_bindings::RGBA as i32, - gl_bindings::UNSIGNED_BYTE as i32, - ); + let pixels = canvas_native_context_read_webgl_pixels( + state, + gl_bindings::RGBA as i32, + gl_bindings::UNSIGNED_BYTE as i32, + ); + ((w as f32, h as f32), pixels) + }); let ptr = pixels.2.as_ptr(); let size = pixels.2.len(); diff --git a/crates/canvas-c/src/webgl/gl.rs b/crates/canvas-c/src/webgl/gl.rs index cefccc0d4..193c86ba9 100644 --- a/crates/canvas-c/src/webgl/gl.rs +++ b/crates/canvas-c/src/webgl/gl.rs @@ -13,7 +13,8 @@ use canvas_webgl::prelude::WebGLVersion; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_void}; use std::ptr::NonNull; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::cell::UnsafeCell; +use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering}; /* GL */ @@ -147,25 +148,27 @@ pub extern "C" fn canvas_native_context_create_pattern_webgl( let source = unsafe { &*source }; context.remove_if_current(); - let state = source.get_inner(); - state.make_current(); - let width = state.get_drawing_buffer_width(); - let height = state.get_drawing_buffer_height(); - - let mut buf = vec![0u8; (width * height * 4) as usize]; - - unsafe { - gl_bindings::Flush(); - gl_bindings::ReadPixels( - 0, - 0, - width, - height, - gl_bindings::RGBA, - gl_bindings::UNSIGNED_BYTE, - buf.as_mut_ptr() as *mut c_void, - ); - } + let (width, height, buf) = source.sync(|state| { + state.make_current(); + let width = state.get_drawing_buffer_width(); + let height = state.get_drawing_buffer_height(); + + let mut buf = vec![0u8; (width * height * 4) as usize]; + + unsafe { + gl_bindings::Flush(); + gl_bindings::ReadPixels( + 0, + 0, + width, + height, + gl_bindings::RGBA, + gl_bindings::UNSIGNED_BYTE, + buf.as_mut_ptr() as *mut c_void, + ); + } + (width, height, buf) + }); #[cfg(feature = "gl")] { @@ -189,15 +192,14 @@ pub extern "C" fn canvas_native_context_create_pattern_webgl( #[no_mangle] pub extern "C" fn canvas_native_webgl_make_current(state: *mut WebGLState) -> bool { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - state.get_inner().make_current() + let state = unsafe { &*state }; + state.sync(|state| state.make_current()) } #[no_mangle] pub extern "C" fn canvas_native_webgl_swap_buffers(state: *mut WebGLState) -> bool { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - state.get_inner().swap_buffers() + unsafe { &*state }.present() } #[no_mangle] @@ -205,8 +207,7 @@ pub extern "C" fn canvas_native_webgl_make_current_and_swap_buffers( state: *mut WebGLState, ) -> bool { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - state.get_inner().make_current_and_swap_buffers() + unsafe { &*state }.present() } /* GL */ @@ -228,52 +229,144 @@ pub extern "C" fn canvas_native_webgl_to_data_url( assert!(!format.is_null()); let format = unsafe { CStr::from_ptr(format) }; let format = format.to_string_lossy(); - let state = unsafe { &mut *state }; + let state = unsafe { &*state }; + state.sync(|state| { + let info = state; + info.make_current(); + let width = info.drawing_buffer_width(); + let height = info.drawing_buffer_height(); + // gl_bindings::PixelStorei(gl_bindings::UNPACK_ALIGNMENT, 1); + let mut buffer = vec![0u8; (width * height * 4) as usize]; + unsafe { + gl_bindings::ReadPixels( + 0, + 0, + width, + height, + gl_bindings::RGBA, + gl_bindings::UNSIGNED_BYTE, + buffer.as_mut_ptr() as *mut c_void, + ); + } - let info = state.get_inner(); - info.make_current(); - let width = info.drawing_buffer_width(); - let height = info.drawing_buffer_height(); - // gl_bindings::PixelStorei(gl_bindings::UNPACK_ALIGNMENT, 1); - let mut buffer = vec![0u8; (width * height * 4) as usize]; - unsafe { - gl_bindings::ReadPixels( - 0, - 0, + CString::new(canvas_2d::bytes_to_data_url( width, height, - gl_bindings::RGBA, - gl_bindings::UNSIGNED_BYTE, - buffer.as_mut_ptr() as *mut c_void, - ); - } - - CString::new(canvas_2d::bytes_to_data_url( - width, - height, - buffer.as_slice(), - format.as_ref(), - quality, - )) - .unwrap() - .into_raw() + buffer.as_slice(), + format.as_ref(), + quality, + )) + .unwrap() + .into_raw() + }) } +/// A WebGL context. A threaded one is built, used and dropped on the WebGL thread (`thread`), and +/// its inner state is only ever touched there: calls reach it through `post` and `sync`. #[derive(Debug)] -pub struct WebGLState(pub(crate) canvas_webgl::prelude::WebGLState, AtomicUsize); +pub struct WebGLState { + inner: UnsafeCell, + refs: AtomicUsize, + threaded: bool, + /// Presents queued and not yet made (threaded only). + in_flight: AtomicU32, +} + +/// Presents a threaded context may have queued before `present` waits for the oldest. +const MAX_QUEUED_FRAMES: u32 = 3; + +#[derive(Clone, Copy)] +struct StatePtr(*const WebGLState); + +// A threaded state is only dereferenced on the WebGL thread, and dropped there after every job +// that holds one of these. +unsafe impl Send for StatePtr {} impl WebGLState { pub fn get_dimensions(&self) -> (i32, i32) { - self.0.get_dimensions() + self.sync(|state| state.get_dimensions()) } pub fn get_version(&self) -> i32 { - match self.0.get_version() { + match self.sync(|state| state.get_version()) { WebGLVersion::V1 => 1, WebGLVersion::V2 => 2, WebGLVersion::NONE => 0, } } + + pub fn is_threaded(&self) -> bool { + self.threaded + } + + #[allow(clippy::mut_from_ref)] + fn inner_mut(&self) -> &mut canvas_webgl::prelude::WebGLState { + debug_assert!(!self.threaded || crate::webgl::thread::on_gl_thread()); + unsafe { &mut *self.inner.get() } + } + + /// Runs `f` against the context. A threaded context queues it behind earlier calls and + /// returns at once, so it must own what it uses: copy anything borrowed from the caller. + pub fn post(&self, f: impl FnOnce(&mut canvas_webgl::prelude::WebGLState) + Send + 'static) { + if !self.threaded { + return f(self.inner_mut()); + } + let state = StatePtr(self); + crate::webgl::thread::post(move || { + let state = state; + f(unsafe { &*state.0 }.inner_mut()) + }); + } + + /// Runs `f` against the context and returns its result; a threaded context runs it after every + /// queued call while the caller waits. + pub fn sync(&self, f: impl FnOnce(&mut canvas_webgl::prelude::WebGLState) -> R) -> R { + if !self.threaded { + return f(self.inner_mut()); + } + crate::webgl::thread::sync(|| f(self.inner_mut())) + } + + /// Ends a frame. A threaded context queues the present, waiting only once it has + /// `MAX_QUEUED_FRAMES` queued: something drawing outside requestAnimationFrame, which the JS + /// side holds back, would otherwise queue frames without limit. + pub fn present(&self) -> bool { + if !self.threaded { + return self.inner_mut().make_current_and_swap_buffers(); + } + if self.in_flight.load(Ordering::Acquire) >= MAX_QUEUED_FRAMES { + crate::webgl::thread::sync(|| ()); + } + crate::webgl::thread::frame_queued(self.in_flight.fetch_add(1, Ordering::AcqRel)); + let state = StatePtr(self); + crate::webgl::thread::post(move || { + let state = state; + let state = unsafe { &*state.0 }; + state.inner_mut().make_current_and_swap_buffers(); + crate::webgl::thread::frame_presented(state.in_flight.fetch_sub(1, Ordering::AcqRel)); + }); + true + } + + /// The drawing buffer as an image (`canvas_native_webgl_read_drawing_buffer`), read on the + /// thread that owns the context. + pub(crate) fn read_drawing_buffer(&self) -> (i32, i32, Vec) { + self.sync(|state| canvas_webgl::webgl::canvas_native_webgl_read_drawing_buffer(state)) + } + + /// Builds a context on the WebGL thread with `create`, which then owns it; falls back to + /// building it here if that thread couldn't start. + pub fn create_threaded(create: impl FnOnce() -> Option) -> *mut WebGLState { + if !crate::webgl::thread::available() { + return create().map_or(std::ptr::null_mut(), |state| Box::into_raw(Box::new(state))); + } + crate::webgl::thread::sync(|| { + create().map_or(std::ptr::null_mut(), |mut state| { + state.threaded = true; + Box::into_raw(Box::new(state)) + }) + }) + } } #[no_mangle] @@ -281,7 +374,7 @@ pub extern "C" fn canvas_native_webgl_state_reference(state: *const WebGLState) if state.is_null() { return; } - unsafe { &*state }.1.fetch_add(1, Ordering::Relaxed); + unsafe { &*state }.refs.fetch_add(1, Ordering::Relaxed); } #[no_mangle] @@ -289,14 +382,30 @@ pub extern "C" fn canvas_native_webgl_state_destroy(state: *mut WebGLState) { if state.is_null() { return; } - if unsafe { &*state }.1.fetch_sub(1, Ordering::AcqRel) == 1 { + if unsafe { &*state }.refs.fetch_sub(1, Ordering::AcqRel) != 1 { + return; + } + if unsafe { &*state }.threaded { + // Queued after every job that uses it. Not waited on: a view lets go of its window first + // (the no-surface update, which does wait), so nothing depends on this having run. + let state = StatePtr(state); + crate::webgl::thread::post(move || { + let state = state; + let _ = unsafe { Box::from_raw(state.0 as *mut WebGLState) }; + }); + } else { let _ = unsafe { Box::from_raw(state) }; } } impl WebGLState { fn wrap(state: canvas_webgl::prelude::WebGLState) -> Self { - Self(state, AtomicUsize::new(1)) + Self { + inner: UnsafeCell::new(state), + refs: AtomicUsize::new(1), + threaded: false, + in_flight: AtomicU32::new(0), + } } } @@ -449,12 +558,14 @@ impl WebGLState { ), ) } + /// The context itself, for a caller already on the thread that owns it (any thread for an + /// unthreaded one). Everything else goes through `post` or `sync`. pub fn get_inner(&self) -> &canvas_webgl::prelude::WebGLState { - &self.0 + self.inner_mut() } pub fn get_inner_mut(&mut self) -> &mut canvas_webgl::prelude::WebGLState { - &mut self.0 + self.inner_mut() } } @@ -583,12 +694,43 @@ impl WebGLShaderPrecisionFormat { } } -pub struct WebGLExtension(Option>); +/// `.1`: its context is threaded, so the typed extension's calls go to the WebGL thread too. +pub struct WebGLExtension(Option>, bool); impl WebGLExtension { pub fn new(extension: Option>) -> Self { - Self(extension) + Self(extension, false) + } +} + +/// Runs an extension call where its context lives: see `WebGLState::sync`. +fn ext_sync(threaded: bool, f: impl FnOnce() -> R) -> R { + if threaded { + crate::webgl::thread::sync(f) + } else { + f() + } +} + +#[derive(Clone, Copy)] +struct ExtPtr(*const T); + +// Only dereferenced on the WebGL thread, by a job that runs before the extension can be dropped: +// see `ext_post`. +unsafe impl Send for ExtPtr {} + +/// Queues an extension call behind its context's earlier calls. The JS wrapper keeps the extension +/// alive while its context lives, and the context is destroyed on the WebGL thread after queued +/// jobs, so the extension outlives the job. +fn ext_post(threaded: bool, ext: &T, f: impl FnOnce(&T) + Send + 'static) { + if !threaded { + return f(ext); } + let ext = ExtPtr(ext as *const T); + crate::webgl::thread::post(move || { + let ext = ext; + f(unsafe { &*ext.0 }) + }); } #[no_mangle] @@ -612,36 +754,41 @@ impl WebGLExtension { } pub fn into_ext_disjoint_timer_query(self) -> Box { + let threaded = self.1; let ext = Box::into_raw(self.0.unwrap()); let ext = unsafe { Box::from_raw(ext as *mut canvas_webgl::prelude::EXT_disjoint_timer_query) }; - Box::new(EXT_disjoint_timer_query(*ext)) + Box::new(EXT_disjoint_timer_query(*ext, threaded)) } pub fn into_angle_instanced_arrays(self) -> Box { + let threaded = self.1; let ext = Box::into_raw(self.0.unwrap()); let ext = unsafe { Box::from_raw(ext as *mut canvas_webgl::prelude::ANGLE_instanced_arrays) }; - Box::new(ANGLE_instanced_arrays(*ext)) + Box::new(ANGLE_instanced_arrays(*ext, threaded)) } pub fn into_lose_context(self) -> Box { + let threaded = self.1; let ext = Box::into_raw(self.0.unwrap()); let ext = unsafe { Box::from_raw(ext as *mut canvas_webgl::prelude::WEBGL_lose_context) }; - Box::new(WEBGL_lose_context(*ext)) + Box::new(WEBGL_lose_context(*ext, threaded)) } pub fn into_draw_buffers(self) -> Box { + let threaded = self.1; let ext = Box::into_raw(self.0.unwrap()); let ext = unsafe { Box::from_raw(ext as *mut canvas_webgl::prelude::WEBGL_draw_buffers) }; - Box::new(WEBGL_draw_buffers(*ext)) + Box::new(WEBGL_draw_buffers(*ext, threaded)) } pub fn into_oes_vertex_array_object(self) -> Box { + let threaded = self.1; let ext = Box::into_raw(self.0.unwrap()); let ext = unsafe { Box::from_raw(ext as *mut canvas_webgl::prelude::OES_vertex_array_object) }; - Box::new(OES_vertex_array_object(*ext)) + Box::new(OES_vertex_array_object(*ext, threaded)) } } @@ -670,7 +817,7 @@ pub extern "C" fn canvas_native_webgl_EXT_color_buffer_half_float_destroy( } #[allow(non_camel_case_types)] -pub struct EXT_disjoint_timer_query(canvas_webgl::prelude::EXT_disjoint_timer_query); +pub struct EXT_disjoint_timer_query(canvas_webgl::prelude::EXT_disjoint_timer_query, bool); #[no_mangle] pub extern "C" fn canvas_native_webgl_EXT_disjoint_timer_query_destroy( @@ -796,7 +943,7 @@ pub extern "C" fn canvas_native_webgl_OES_texture_half_float_linear_destroy( } #[allow(non_camel_case_types)] -pub struct OES_vertex_array_object(canvas_webgl::prelude::OES_vertex_array_object); +pub struct OES_vertex_array_object(canvas_webgl::prelude::OES_vertex_array_object, bool); #[no_mangle] pub extern "C" fn canvas_native_webgl_OES_vertex_array_object_destroy( @@ -900,7 +1047,7 @@ pub extern "C" fn canvas_native_webgl_WEBGL_compressed_texture_pvrtc_destroy( } #[allow(non_camel_case_types)] -pub struct WEBGL_lose_context(canvas_webgl::prelude::WEBGL_lose_context); +pub struct WEBGL_lose_context(canvas_webgl::prelude::WEBGL_lose_context, bool); #[no_mangle] pub extern "C" fn canvas_native_webgl_WEBGL_lose_context_destroy(value: *mut WEBGL_lose_context) { @@ -911,7 +1058,7 @@ pub extern "C" fn canvas_native_webgl_WEBGL_lose_context_destroy(value: *mut WEB } #[allow(non_camel_case_types)] -pub struct ANGLE_instanced_arrays(canvas_webgl::prelude::ANGLE_instanced_arrays); +pub struct ANGLE_instanced_arrays(canvas_webgl::prelude::ANGLE_instanced_arrays, bool); #[no_mangle] pub extern "C" fn canvas_native_webgl_ANGLE_instanced_arrays_destroy( @@ -935,7 +1082,7 @@ pub extern "C" fn canvas_native_webgl_WEBGL_depth_texture_destroy(value: *mut WE } #[allow(non_camel_case_types)] -pub struct WEBGL_draw_buffers(canvas_webgl::prelude::WEBGL_draw_buffers); +pub struct WEBGL_draw_buffers(canvas_webgl::prelude::WEBGL_draw_buffers, bool); #[no_mangle] pub extern "C" fn canvas_native_webgl_WEBGL_draw_buffers_destroy(value: *mut WEBGL_draw_buffers) { @@ -1417,13 +1564,13 @@ pub extern "C" fn canvas_native_webgl_state_get_unpack_colorspace_conversion_web state: *mut WebGLState, ) -> i32 { let state = unsafe { &*state }; - state.get_inner().get_unpack_colorspace_conversion_webgl() + state.sync(|state| state.get_unpack_colorspace_conversion_webgl()) } #[no_mangle] pub extern "C" fn canvas_native_webgl_state_get_flip_y(state: *mut WebGLState) -> bool { let state = unsafe { &*state }; - state.get_inner().get_flip_y() + state.sync(|state| state.get_flip_y()) } #[no_mangle] @@ -1431,7 +1578,7 @@ pub extern "C" fn canvas_native_webgl_state_get_premultiplied_alpha( state: *mut WebGLState, ) -> bool { let state = unsafe { &*state }; - state.get_inner().get_premultiplied_alpha() + state.sync(|state| state.get_premultiplied_alpha()) } #[no_mangle] @@ -1439,7 +1586,7 @@ pub extern "C" fn canvas_native_webgl_state_get_drawing_buffer_width( state: *mut WebGLState, ) -> i32 { let state = unsafe { &*state }; - state.get_inner().get_drawing_buffer_width() + state.sync(|state| state.get_drawing_buffer_width()) } #[no_mangle] @@ -1447,7 +1594,7 @@ pub extern "C" fn canvas_native_webgl_state_get_drawing_buffer_height( state: *mut WebGLState, ) -> i32 { let state = unsafe { &*state }; - state.get_inner().get_drawing_buffer_height() + state.sync(|state| state.get_drawing_buffer_height()) } /* WebGLState */ @@ -1458,7 +1605,7 @@ pub extern "C" fn canvas_native_webgl_ext_disjoint_timer_query_create_query_ext( query: *const EXT_disjoint_timer_query, ) -> u32 { let query = unsafe { &*query }; - query.0.create_query_ext() + ext_sync(query.1, || query.0.create_query_ext()) } #[no_mangle] @@ -1467,7 +1614,7 @@ pub extern "C" fn canvas_native_webgl_ext_disjoint_timer_query_delete_query_ext( query: *const EXT_disjoint_timer_query, ) { let query = unsafe { &*query }; - query.0.delete_query_ext(value) + ext_post(query.1, query, move |ext| ext.0.delete_query_ext(value)); } #[no_mangle] @@ -1476,7 +1623,7 @@ pub extern "C" fn canvas_native_webgl_ext_disjoint_timer_query_is_query_ext( query: *const EXT_disjoint_timer_query, ) -> bool { let query = unsafe { &*query }; - query.0.is_query_ext(value) + ext_sync(query.1, || query.0.is_query_ext(value)) } #[no_mangle] @@ -1486,7 +1633,9 @@ pub extern "C" fn canvas_native_webgl_ext_disjoint_timer_query_begin_query_ext( query: *const EXT_disjoint_timer_query, ) { let query = unsafe { &*query }; - query.0.begin_query_ext(target, value) + ext_post(query.1, query, move |ext| { + ext.0.begin_query_ext(target, value) + }); } #[no_mangle] @@ -1495,7 +1644,7 @@ pub extern "C" fn canvas_native_webgl_ext_disjoint_timer_query_end_query_ext( query: *const EXT_disjoint_timer_query, ) { let query = unsafe { &*query }; - query.0.end_query_ext(target) + ext_post(query.1, query, move |ext| ext.0.end_query_ext(target)); } #[no_mangle] @@ -1505,7 +1654,9 @@ pub extern "C" fn canvas_native_webgl_ext_disjoint_timer_query_query_counter_ext query: *const EXT_disjoint_timer_query, ) { let query = unsafe { &*query }; - query.0.query_counter_ext(value, target) + ext_post(query.1, query, move |ext| { + ext.0.query_counter_ext(value, target) + }); } #[no_mangle] @@ -1515,7 +1666,7 @@ pub extern "C" fn canvas_native_webgl_ext_disjoint_timer_query_get_query_ext( query: *const EXT_disjoint_timer_query, ) -> i32 { let query = unsafe { &*query }; - query.0.get_query_ext(target, pname) + ext_sync(query.1, || query.0.get_query_ext(target, pname)) } #[no_mangle] @@ -1525,9 +1676,11 @@ pub extern "C" fn canvas_native_webgl_ext_disjoint_timer_query_get_query_object_ query: *const EXT_disjoint_timer_query, ) -> *mut WebGLResult { let query = unsafe { &*query }; - Box::into_raw(Box::new(WebGLResult( - query.0.get_query_object_ext(target, pname), - ))) + ext_sync(query.1, || { + Box::into_raw(Box::new(WebGLResult( + query.0.get_query_object_ext(target, pname), + ))) + }) } /* EXT_disjoint_timer_query */ @@ -1542,9 +1695,10 @@ pub extern "C" fn canvas_native_webgl_angle_instanced_arrays_draw_arrays_instanc arrays: *const ANGLE_instanced_arrays, ) { let arrays = unsafe { &*arrays }; - arrays - .0 - .draw_arrays_instanced_angle(mode, first, count, primcount) + ext_post(arrays.1, arrays, move |ext| { + ext.0 + .draw_arrays_instanced_angle(mode, first, count, primcount) + }); } #[no_mangle] @@ -1557,9 +1711,10 @@ pub extern "C" fn canvas_native_webgl_angle_instanced_arrays_draw_elements_insta arrays: *const ANGLE_instanced_arrays, ) { let arrays = unsafe { &*arrays }; - arrays - .0 - .draw_elements_instanced_angle(mode, count, type_, offset, primcount) + ext_post(arrays.1, arrays, move |ext| { + ext.0 + .draw_elements_instanced_angle(mode, count, type_, offset, primcount) + }); } #[no_mangle] @@ -1569,7 +1724,9 @@ pub extern "C" fn canvas_native_webgl_angle_instanced_arrays_vertex_attrib_divis arrays: *const ANGLE_instanced_arrays, ) { let arrays = unsafe { &*arrays }; - arrays.0.vertex_attrib_divisor_angle(index, divisor) + ext_post(arrays.1, arrays, move |ext| { + ext.0.vertex_attrib_divisor_angle(index, divisor) + }); } /* ANGLE_instanced_arrays */ @@ -1579,8 +1736,7 @@ pub extern "C" fn canvas_native_webgl_lose_context_lose_context( context: *const WEBGL_lose_context, ) { let context = unsafe { &*context }; - - context.0.lose_context() + ext_post(context.1, context, move |ext| ext.0.lose_context()); } #[no_mangle] @@ -1588,7 +1744,7 @@ pub extern "C" fn canvas_native_webgl_lose_context_restore_context( context: *const WEBGL_lose_context, ) { let context = unsafe { &*context }; - context.0.restore_context() + ext_post(context.1, context, move |ext| ext.0.restore_context()); } /* WEBGL_lose_context */ @@ -1603,7 +1759,10 @@ pub extern "C" fn canvas_native_webgl_draw_buffers_draw_buffers_webgl( assert!(!context.is_null()); let buffers = unsafe { std::slice::from_raw_parts(buffers, size) }; let context = unsafe { &*context }; - context.0.draw_buffers_webgl(buffers); + let buffers = buffers.to_vec(); + ext_post(context.1, context, move |ext| { + ext.0.draw_buffers_webgl(&buffers) + }); } /* WEBGL_draw_buffers */ @@ -1615,7 +1774,7 @@ pub extern "C" fn canvas_native_webgl_oes_vertex_array_object_create_vertex_arra object: *const OES_vertex_array_object, ) -> u32 { let object = unsafe { &*object }; - object.0.create_vertex_array_oes() + ext_sync(object.1, || object.0.create_vertex_array_oes()) } #[no_mangle] @@ -1624,7 +1783,9 @@ pub extern "C" fn canvas_native_webgl_oes_vertex_array_object_delete_vertex_arra object: *const OES_vertex_array_object, ) { let object = unsafe { &*object }; - object.0.delete_vertex_array_oes(array_object) + ext_post(object.1, object, move |ext| { + ext.0.delete_vertex_array_oes(array_object) + }); } #[no_mangle] @@ -1633,7 +1794,7 @@ pub extern "C" fn canvas_native_webgl_oes_vertex_array_object_is_vertex_array_oe object: *const OES_vertex_array_object, ) -> bool { let object = unsafe { &*object }; - object.0.is_vertex_array_oes(array_object) + ext_sync(object.1, || object.0.is_vertex_array_oes(array_object)) } #[no_mangle] @@ -1642,7 +1803,9 @@ pub extern "C" fn canvas_native_webgl_oes_vertex_array_object_bind_vertex_array_ object: *const OES_vertex_array_object, ) { let object = unsafe { &*object }; - object.0.bind_vertex_array_oes(array_object) + ext_post(object.1, object, move |ext| { + ext.0.bind_vertex_array_oes(array_object) + }); } /* OES_vertex_array_object */ @@ -1785,6 +1948,95 @@ pub extern "C" fn canvas_native_webgl_create_no_window( } } +/// `canvas_native_webgl_create`, for a context that lives on the WebGL thread. +#[cfg(target_os = "android")] +pub extern "C" fn canvas_native_webgl_create_threaded( + view: *mut c_void, + width: i32, + height: i32, + version: i32, + alpha: bool, + antialias: bool, + depth: bool, + fail_if_major_performance_caveat: bool, + power_preference: i32, + premultiplied_alpha: bool, + preserve_drawing_buffer: bool, + stencil: bool, + desynchronized: bool, + xr_compatible: bool, +) -> *mut WebGLState { + let (Some(version), Some(power_preference)) = ( + WebGLVersion::try_from(version).ok(), + PowerPreference::try_from(power_preference).ok(), + ) else { + return std::ptr::null_mut(); + }; + WebGLState::create_threaded(|| { + WebGLState::new_with_view( + view, + width, + height, + version, + alpha, + antialias, + depth, + fail_if_major_performance_caveat, + power_preference, + premultiplied_alpha, + preserve_drawing_buffer, + stencil, + desynchronized, + xr_compatible, + false, + ) + }) +} + +/// `canvas_native_webgl_create_no_window`, for a context that lives on the WebGL thread. +#[cfg(target_os = "android")] +pub extern "C" fn canvas_native_webgl_create_no_window_threaded( + width: i32, + height: i32, + version: i32, + alpha: bool, + antialias: bool, + depth: bool, + fail_if_major_performance_caveat: bool, + power_preference: i32, + premultiplied_alpha: bool, + preserve_drawing_buffer: bool, + stencil: bool, + desynchronized: bool, + xr_compatible: bool, + is_canvas: bool, +) -> *mut WebGLState { + let (Some(version), Some(power_preference)) = ( + WebGLVersion::try_from(version).ok(), + PowerPreference::try_from(power_preference).ok(), + ) else { + return std::ptr::null_mut(); + }; + WebGLState::create_threaded(|| { + canvas_native_webgl_create_no_window_internal( + width, + height, + version, + alpha, + antialias, + depth, + fail_if_major_performance_caveat, + power_preference, + premultiplied_alpha, + preserve_drawing_buffer, + stencil, + desynchronized, + xr_compatible, + is_canvas, + ) + }) +} + /// Windows: a WebGL context whose drawing buffer can be shown in a `SwapChainPanel` /// (`canvas_native_webgl_attach_swap_chain_panel`). Null when ANGLE is unavailable. #[cfg(target_os = "windows")] @@ -1913,7 +2165,7 @@ pub extern "C" fn canvas_native_webgl_present(state: *mut WebGLState) -> bool { } #[cfg(not(target_os = "windows"))] { - state.get_inner().make_current_and_swap_buffers() + state.present() } } @@ -1970,8 +2222,8 @@ pub(crate) fn canvas_native_webgl_create_no_window_internal( #[no_mangle] pub extern "C" fn canvas_native_webgl_active_texture(texture: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_active_texture(texture, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_active_texture(texture, state)) } #[no_mangle] @@ -1980,8 +2232,10 @@ pub extern "C" fn canvas_native_webgl_attach_shader( shader: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_attach_shader(program, shader, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_attach_shader(program, shader, state) + }) } #[no_mangle] @@ -1995,14 +2249,11 @@ pub extern "C" fn canvas_native_webgl_bind_attrib_location( return; } let name = unsafe { CStr::from_ptr(name) }; - let name = name.to_string_lossy(); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_bind_attrib_location( - program, - index, - name.as_ref(), - state.get_inner_mut(), - ) + let name = name.to_string_lossy().into_owned(); + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_bind_attrib_location(program, index, &name, state) + }) } #[no_mangle] @@ -2011,8 +2262,10 @@ pub extern "C" fn canvas_native_webgl_bind_buffer( buffer: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_bind_buffer(target, buffer, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_bind_buffer(target, buffer, state) + }) } #[no_mangle] @@ -2021,12 +2274,10 @@ pub extern "C" fn canvas_native_webgl_bind_frame_buffer( framebuffer: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_bind_frame_buffer( - target, - framebuffer, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_bind_frame_buffer(target, framebuffer, state) + }) } #[no_mangle] @@ -2035,12 +2286,10 @@ pub extern "C" fn canvas_native_webgl_bind_render_buffer( renderbuffer: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_bind_render_buffer( - target, - renderbuffer, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_bind_render_buffer(target, renderbuffer, state) + }) } #[no_mangle] @@ -2049,8 +2298,10 @@ pub extern "C" fn canvas_native_webgl_bind_texture( texture: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_bind_texture(target, texture, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_bind_texture(target, texture, state) + }) } #[no_mangle] @@ -2061,14 +2312,10 @@ pub extern "C" fn canvas_native_webgl_blend_color( alpha: f32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_blend_color( - red, - green, - blue, - alpha, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_blend_color(red, green, blue, alpha, state) + }) } #[no_mangle] @@ -2077,18 +2324,18 @@ pub extern "C" fn canvas_native_webgl_blend_equation_separate( mode_alpha: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_blend_equation_separate( - mode_rgb, - mode_alpha, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_blend_equation_separate( + mode_rgb, mode_alpha, state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_blend_equation(mode: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_blend_equation(mode, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_blend_equation(mode, state)) } #[no_mangle] @@ -2099,14 +2346,12 @@ pub extern "C" fn canvas_native_webgl_blend_func_separate( dst_alpha: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_blend_func_separate( - src_rgb, - dst_rgb, - src_alpha, - dst_alpha, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_blend_func_separate( + src_rgb, dst_rgb, src_alpha, dst_alpha, state, + ) + }) } #[no_mangle] @@ -2115,8 +2360,10 @@ pub extern "C" fn canvas_native_webgl_blend_func( dfactor: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_blend_func(sfactor, dfactor, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_blend_func(sfactor, dfactor, state) + }) } #[no_mangle] @@ -2129,13 +2376,11 @@ pub extern "C" fn canvas_native_webgl_buffer_data( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data( - target, - src_data, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data(target, &src_data, usage, state) + }) } #[no_mangle] @@ -2148,13 +2393,11 @@ pub extern "C" fn canvas_native_webgl_buffer_data_i8( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data_i8( - target, - src_data, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data_i8(target, &src_data, usage, state) + }) } #[no_mangle] @@ -2167,13 +2410,11 @@ pub extern "C" fn canvas_native_webgl_buffer_data_u16( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data_u16( - target, - src_data, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data_u16(target, &src_data, usage, state) + }) } #[no_mangle] @@ -2186,13 +2427,11 @@ pub extern "C" fn canvas_native_webgl_buffer_data_i16( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data_i16( - target, - src_data, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data_i16(target, &src_data, usage, state) + }) } #[no_mangle] @@ -2205,13 +2444,11 @@ pub extern "C" fn canvas_native_webgl_buffer_data_u32( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data_u32( - target, - src_data, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data_u32(target, &src_data, usage, state) + }) } #[no_mangle] @@ -2224,13 +2461,11 @@ pub extern "C" fn canvas_native_webgl_buffer_data_i32( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data_i32( - target, - src_data, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data_i32(target, &src_data, usage, state) + }) } #[no_mangle] @@ -2242,13 +2477,11 @@ pub extern "C" fn canvas_native_webgl_buffer_data_f32( state: *mut WebGLState, ) { let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data_f32( - target, - src_data, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data_f32(target, &src_data, usage, state) + }) } #[no_mangle] @@ -2260,13 +2493,11 @@ pub extern "C" fn canvas_native_webgl_buffer_data_f64( state: *mut WebGLState, ) { let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data_f64( - target, - src_data, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data_f64(target, &src_data, usage, state) + }) } #[no_mangle] @@ -2276,13 +2507,10 @@ pub extern "C" fn canvas_native_webgl_buffer_data_none( usage: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_data_none( - target, - size, - usage, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_data_none(target, size, usage, state) + }) } #[no_mangle] @@ -2295,13 +2523,11 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data( - target, - offset, - src_data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data(target, offset, &src_data, state) + }) } #[no_mangle] @@ -2314,13 +2540,13 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data_i8( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_i8( - target, - offset, - src_data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_i8( + target, offset, &src_data, state, + ) + }) } #[no_mangle] @@ -2333,13 +2559,13 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data_i16( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_i16( - target, - offset, - src_data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_i16( + target, offset, &src_data, state, + ) + }) } #[no_mangle] @@ -2352,13 +2578,13 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data_u16( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_u16( - target, - offset, - src_data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_u16( + target, offset, &src_data, state, + ) + }) } #[no_mangle] @@ -2371,13 +2597,13 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data_i32( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_i32( - target, - offset, - src_data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_i32( + target, offset, &src_data, state, + ) + }) } #[no_mangle] @@ -2390,13 +2616,13 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data_u32( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_u32( - target, - offset, - src_data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_u32( + target, offset, &src_data, state, + ) + }) } #[no_mangle] @@ -2409,13 +2635,13 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data_f32( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_f32( - target, - offset, - src_data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_f32( + target, offset, &src_data, state, + ) + }) } #[no_mangle] @@ -2428,13 +2654,13 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data_f64( ) { assert!(!state.is_null()); let src_data = unsafe { std::slice::from_raw_parts(src_data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_f64( - target, - offset, - src_data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src_data = src_data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_f64( + target, offset, &src_data, state, + ) + }) } #[no_mangle] @@ -2443,12 +2669,10 @@ pub extern "C" fn canvas_native_webgl_buffer_sub_data_none( offset: isize, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_none( - target, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_buffer_sub_data_none(target, offset, state) + }) } #[no_mangle] @@ -2456,17 +2680,16 @@ pub extern "C" fn canvas_native_webgl_check_frame_buffer_status( target: u32, state: *mut WebGLState, ) -> u32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_check_frame_buffer_status( - target, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_check_frame_buffer_status(target, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_clear(mask: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_clear(mask, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_clear(mask, state)) } #[no_mangle] @@ -2477,26 +2700,22 @@ pub extern "C" fn canvas_native_webgl_clear_color( alpha: f32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_clear_color( - red, - green, - blue, - alpha, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_clear_color(red, green, blue, alpha, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_clear_depth(depth: f32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_clear_depth(depth, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_clear_depth(depth, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_clear_stencil(stencil: i32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_clear_stencil(stencil, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_clear_stencil(stencil, state)) } #[no_mangle] @@ -2507,14 +2726,10 @@ pub extern "C" fn canvas_native_webgl_color_mask( alpha: bool, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_color_mask( - red, - green, - blue, - alpha, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_color_mask(red, green, blue, alpha, state) + }) } #[no_mangle] @@ -2524,8 +2739,8 @@ pub extern "C" fn canvas_native_webgl_commit(_: *mut WebGLState) { #[no_mangle] pub extern "C" fn canvas_native_webgl_compile_shader(shader: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_compile_shader(shader, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_compile_shader(shader, state)) } #[no_mangle] @@ -2542,17 +2757,20 @@ pub extern "C" fn canvas_native_webgl_compressed_tex_image2d( ) { assert!(!state.is_null()); let pixels = unsafe { std::slice::from_raw_parts(pixels, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_compressed_tex_image2d( - target, - level, - internalformat, - width, - height, - border, - pixels, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let pixels = pixels.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_compressed_tex_image2d( + target, + level, + internalformat, + width, + height, + border, + &pixels, + state, + ) + }) } #[no_mangle] @@ -2565,16 +2783,18 @@ pub extern "C" fn canvas_native_webgl_compressed_tex_image2d_none( border: i32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_compressed_tex_image2d_none( - target, - level, - internalformat, - width, - height, - border, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_compressed_tex_image2d_none( + target, + level, + internalformat, + width, + height, + border, + state, + ) + }) } #[no_mangle] @@ -2592,18 +2812,13 @@ pub extern "C" fn canvas_native_webgl_compressed_tex_sub_image2d( ) { assert!(!state.is_null()); let pixels = unsafe { std::slice::from_raw_parts(pixels, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_compressed_tex_sub_image2d( - target, - level, - xoffset, - yoffset, - width, - height, - format, - pixels, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let pixels = pixels.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_compressed_tex_sub_image2d( + target, level, xoffset, yoffset, width, height, format, &pixels, state, + ) + }) } #[no_mangle] @@ -2618,18 +2833,20 @@ pub extern "C" fn canvas_native_webgl_copy_tex_image2d( border: i32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_copy_tex_image2d( - target, - level, - internalformat, - x, - y, - width, - height, - border, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_copy_tex_image2d( + target, + level, + internalformat, + x, + y, + width, + height, + border, + state, + ) + }) } #[no_mangle] @@ -2644,42 +2861,36 @@ pub extern "C" fn canvas_native_webgl_copy_tex_sub_image2d( height: i32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_copy_tex_sub_image2d( - target, - level, - xoffset, - yoffset, - x, - y, - width, - height, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_copy_tex_sub_image2d( + target, level, xoffset, yoffset, x, y, width, height, state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_create_buffer(state: *mut WebGLState) -> u32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_create_buffer(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_create_buffer(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_create_framebuffer(state: *mut WebGLState) -> u32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_create_framebuffer(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_create_framebuffer(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_create_program(state: *mut WebGLState) -> u32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_create_program(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_create_program(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_create_renderbuffer(state: *mut WebGLState) -> u32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_create_renderbuffer(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_create_renderbuffer(state)) } #[no_mangle] @@ -2687,26 +2898,26 @@ pub extern "C" fn canvas_native_webgl_create_shader( shader_type: u32, state: *mut WebGLState, ) -> u32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_create_shader(shader_type, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_create_shader(shader_type, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_create_texture(state: *mut WebGLState) -> u32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_create_texture(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_create_texture(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_cull_face(mode: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_cull_face(mode, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_cull_face(mode, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_delete_buffer(buffer: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_delete_buffer(buffer, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_delete_buffer(buffer, state)) } #[no_mangle] @@ -2714,14 +2925,16 @@ pub extern "C" fn canvas_native_webgl_delete_framebuffer( frame_buffer: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_delete_framebuffer(frame_buffer, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_delete_framebuffer(frame_buffer, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_delete_program(program: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_delete_program(program, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_delete_program(program, state)) } #[no_mangle] @@ -2729,41 +2942,42 @@ pub extern "C" fn canvas_native_webgl_delete_renderbuffer( render_buffer: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_delete_renderbuffer( - render_buffer, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_delete_renderbuffer(render_buffer, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_delete_shader(shader: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_delete_shader(shader, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_delete_shader(shader, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_delete_texture(texture: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_delete_texture(texture, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_delete_texture(texture, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_depth_func(func: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_depth_func(func, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_depth_func(func, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_depth_mask(flag: bool, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_depth_mask(flag, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_depth_mask(flag, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_depth_range(z_near: f32, z_far: f32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_depth_range(z_near, z_far, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_depth_range(z_near, z_far, state) + }) } #[no_mangle] @@ -2772,14 +2986,16 @@ pub extern "C" fn canvas_native_webgl_detach_shader( shader: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_detach_shader(program, shader, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_detach_shader(program, shader, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_disable(cap: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_disable(cap, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_disable(cap, state)) } #[no_mangle] @@ -2787,11 +3003,10 @@ pub extern "C" fn canvas_native_webgl_disable_vertex_attrib_array( index: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_disable_vertex_attrib_array( - index, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_disable_vertex_attrib_array(index, state) + }) } #[no_mangle] @@ -2801,9 +3016,10 @@ pub extern "C" fn canvas_native_webgl_draw_arrays( count: i32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_draw_arrays(mode, first, count, state.get_inner_mut()) - // Flush Context + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_draw_arrays(mode, first, count, state) + }) } #[no_mangle] @@ -2814,21 +3030,22 @@ pub extern "C" fn canvas_native_webgl_draw_elements( offset: isize, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_draw_elements( - mode, - count, - element_type, - offset, - state.get_inner_mut(), - ) - // Flush Context + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_draw_elements( + mode, + count, + element_type, + offset, + state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_enable(cap: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_enable(cap, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_enable(cap, state)) } #[no_mangle] @@ -2836,23 +3053,22 @@ pub extern "C" fn canvas_native_webgl_enable_vertex_attrib_array( index: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_enable_vertex_attrib_array( - index, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_enable_vertex_attrib_array(index, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_finish(state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_finish(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_finish(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_flush(state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_flush(state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_flush(state)) } #[no_mangle] @@ -2863,14 +3079,16 @@ pub extern "C" fn canvas_native_webgl_framebuffer_renderbuffer( renderbuffer: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_framebuffer_renderbuffer( - target, - attachment, - renderbuffertarget, - renderbuffer, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_framebuffer_renderbuffer( + target, + attachment, + renderbuffertarget, + renderbuffer, + state, + ) + }) } #[no_mangle] @@ -2882,27 +3100,24 @@ pub extern "C" fn canvas_native_webgl_framebuffer_texture2d( level: i32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_framebuffer_texture2d( - target, - attachment, - textarget, - texture, - level, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_framebuffer_texture2d( + target, attachment, textarget, texture, level, state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_front_face(mode: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_front_face(mode, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_front_face(mode, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_generate_mipmap(target: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_generate_mipmap(target, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_generate_mipmap(target, state)) } #[no_mangle] @@ -2911,14 +3126,13 @@ pub extern "C" fn canvas_native_webgl_get_active_attrib( index: u32, state: *mut WebGLState, ) -> *mut WebGLActiveInfo { - let state = unsafe { &mut *state }; - let info = canvas_webgl::webgl::canvas_native_webgl_get_active_attrib( - program, - index, - state.get_inner_mut(), - ); + let state = unsafe { &*state }; + state.sync(|state| { + let info = + canvas_webgl::webgl::canvas_native_webgl_get_active_attrib(program, index, state); - Box::into_raw(Box::new(WebGLActiveInfo(info))) + Box::into_raw(Box::new(WebGLActiveInfo(info))) + }) } #[no_mangle] @@ -2927,14 +3141,13 @@ pub extern "C" fn canvas_native_webgl_get_active_uniform( index: u32, state: *mut WebGLState, ) -> *mut WebGLActiveInfo { - let state = unsafe { &mut *state }; - let info = canvas_webgl::webgl::canvas_native_webgl_get_active_uniform( - program, - index, - state.get_inner_mut(), - ); + let state = unsafe { &*state }; + state.sync(|state| { + let info = + canvas_webgl::webgl::canvas_native_webgl_get_active_uniform(program, index, state); - Box::into_raw(Box::new(WebGLActiveInfo(info))) + Box::into_raw(Box::new(WebGLActiveInfo(info))) + }) } #[no_mangle] @@ -2942,13 +3155,12 @@ pub extern "C" fn canvas_native_webgl_get_attached_shaders( program: u32, state: *mut WebGLState, ) -> *mut U32Buffer { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(U32Buffer::from( - canvas_webgl::webgl::canvas_native_webgl_get_attached_shaders( - program, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(U32Buffer::from( + canvas_webgl::webgl::canvas_native_webgl_get_attached_shaders(program, state), + ))) + }) } #[no_mangle] @@ -2960,12 +3172,10 @@ pub extern "C" fn canvas_native_webgl_get_attrib_location( assert!(!state.is_null()); let name = unsafe { CStr::from_ptr(name) }; let name = name.to_string_lossy(); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_get_attrib_location( - program, - name.as_ref(), - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_get_attrib_location(program, name.as_ref(), state) + }) } #[no_mangle] @@ -2974,28 +3184,28 @@ pub extern "C" fn canvas_native_webgl_get_buffer_parameter( pname: u32, state: *mut WebGLState, ) -> i32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_get_buffer_parameter( - target, - pname, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_get_buffer_parameter(target, pname, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_get_context_attributes( state: *mut WebGLState, ) -> *mut ContextAttributes { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(ContextAttributes( - canvas_webgl::webgl::canvas_native_webgl_get_context_attributes(state.get_inner()), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(ContextAttributes( + canvas_webgl::webgl::canvas_native_webgl_get_context_attributes(state), + ))) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_get_error(state: *mut WebGLState) -> u32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_get_error(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_get_error(state)) } #[no_mangle] @@ -3006,13 +3216,10 @@ pub extern "C" fn canvas_native_webgl_get_extension( assert!(!state.is_null()); let name = unsafe { CStr::from_ptr(name) }; let name = name.to_string_lossy(); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLExtension( - canvas_webgl::webgl::canvas_native_webgl_get_extension( - name.as_ref(), - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + let extension = state + .sync(|state| canvas_webgl::webgl::canvas_native_webgl_get_extension(name.as_ref(), state)); + Box::into_raw(Box::new(WebGLExtension(extension, state.is_threaded()))) } #[no_mangle] @@ -3022,15 +3229,14 @@ pub extern "C" fn canvas_native_webgl_get_framebuffer_attachment_parameter( pname: u32, state: *mut WebGLState, ) -> *mut WebGLFramebufferAttachmentParameter { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLFramebufferAttachmentParameter( - canvas_webgl::webgl::canvas_native_webgl_get_framebuffer_attachment_parameter( - target, - attachment, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLFramebufferAttachmentParameter( + canvas_webgl::webgl::canvas_native_webgl_get_framebuffer_attachment_parameter( + target, attachment, pname, state, + ), + ))) + }) } #[no_mangle] @@ -3066,10 +3272,12 @@ pub extern "C" fn canvas_native_webgl_get_parameter( pname: u32, state: *mut WebGLState, ) -> *mut WebGLResult { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl::canvas_native_webgl_get_parameter(pname, state.get_inner_mut()), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl::canvas_native_webgl_get_parameter(pname, state), + ))) + }) } #[no_mangle] @@ -3077,15 +3285,12 @@ pub extern "C" fn canvas_native_webgl_get_program_info_log( program: u32, state: *mut WebGLState, ) -> *const c_char { - let state = unsafe { &mut *state }; - CString::new( - canvas_webgl::webgl::canvas_native_webgl_get_program_info_log( - program, - state.get_inner_mut(), - ), - ) - .unwrap() - .into_raw() + let state = unsafe { &*state }; + state.sync(|state| { + CString::new(canvas_webgl::webgl::canvas_native_webgl_get_program_info_log(program, state)) + .unwrap() + .into_raw() + }) } #[no_mangle] @@ -3094,14 +3299,12 @@ pub extern "C" fn canvas_native_webgl_get_program_parameter( pname: u32, state: *mut WebGLState, ) -> *mut WebGLResult { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl::canvas_native_webgl_get_program_parameter( - program, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl::canvas_native_webgl_get_program_parameter(program, pname, state), + ))) + }) } #[no_mangle] @@ -3110,12 +3313,10 @@ pub extern "C" fn canvas_native_webgl_get_renderbuffer_parameter( pname: u32, state: *mut WebGLState, ) -> i32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_get_renderbuffer_parameter( - target, - pname, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_get_renderbuffer_parameter(target, pname, state) + }) } #[no_mangle] @@ -3123,12 +3324,12 @@ pub extern "C" fn canvas_native_webgl_get_shader_info_log( shader: u32, state: *mut WebGLState, ) -> *const c_char { - let state = unsafe { &mut *state }; - CString::new( - canvas_webgl::webgl::canvas_native_webgl_get_shader_info_log(shader, state.get_inner_mut()), - ) - .unwrap() - .into_raw() + let state = unsafe { &*state }; + state.sync(|state| { + CString::new(canvas_webgl::webgl::canvas_native_webgl_get_shader_info_log(shader, state)) + .unwrap() + .into_raw() + }) } #[no_mangle] @@ -3137,14 +3338,12 @@ pub extern "C" fn canvas_native_webgl_get_shader_parameter( pname: u32, state: *mut WebGLState, ) -> *mut WebGLResult { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl::canvas_native_webgl_get_shader_parameter( - shader, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl::canvas_native_webgl_get_shader_parameter(shader, pname, state), + ))) + }) } #[no_mangle] @@ -3153,14 +3352,16 @@ pub extern "C" fn canvas_native_webgl_get_shader_precision_format( precision_type: u32, state: *mut WebGLState, ) -> *mut WebGLShaderPrecisionFormat { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLShaderPrecisionFormat( - canvas_webgl::webgl::canvas_native_webgl_get_shader_precision_format( - shader_type, - precision_type, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLShaderPrecisionFormat( + canvas_webgl::webgl::canvas_native_webgl_get_shader_precision_format( + shader_type, + precision_type, + state, + ), + ))) + }) } #[no_mangle] @@ -3168,34 +3369,38 @@ pub extern "C" fn canvas_native_webgl_get_shader_source( shader: u32, state: *mut WebGLState, ) -> *const c_char { - let state = unsafe { &mut *state }; - CString::new(canvas_webgl::webgl::canvas_native_webgl_get_shader_source( - shader, - state.get_inner_mut(), - )) - .unwrap() - .into_raw() + let state = unsafe { &*state }; + state.sync(|state| { + CString::new(canvas_webgl::webgl::canvas_native_webgl_get_shader_source( + shader, state, + )) + .unwrap() + .into_raw() + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_get_supported_extensions( state: *mut WebGLState, ) -> *mut StringBuffer { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(StringBuffer::from( - canvas_webgl::webgl::canvas_native_webgl_get_supported_extensions(state.get_inner_mut()), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(StringBuffer::from( + canvas_webgl::webgl::canvas_native_webgl_get_supported_extensions(state), + ))) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_get_supported_extensions_to_string( state: *mut WebGLState, ) -> *const c_char { - let state = unsafe { &mut *state }; - let ret = - canvas_webgl::webgl::canvas_native_webgl_get_supported_extensions(state.get_inner_mut()); + let state = unsafe { &*state }; + state.sync(|state| { + let ret = canvas_webgl::webgl::canvas_native_webgl_get_supported_extensions(state); - CString::new(ret.join(",").to_string()).unwrap().into_raw() + CString::new(ret.join(",").to_string()).unwrap().into_raw() + }) } #[no_mangle] @@ -3204,8 +3409,10 @@ pub extern "C" fn canvas_native_webgl_get_tex_parameter( pname: u32, state: *mut WebGLState, ) -> i32 { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_get_tex_parameter(target, pname, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_get_tex_parameter(target, pname, state) + }) } #[no_mangle] @@ -3218,12 +3425,10 @@ pub extern "C" fn canvas_native_webgl_get_uniform_location( assert!(!name.is_null()); let name = unsafe { CStr::from_ptr(name) }; let name = name.to_string_lossy(); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_get_uniform_location( - program, - name.as_ref(), - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_get_uniform_location(program, name.as_ref(), state) + }) } #[no_mangle] @@ -3232,15 +3437,12 @@ pub extern "C" fn canvas_native_webgl_get_uniform( location: i32, state: *mut WebGLState, ) -> *mut WebGLResult { - let state = unsafe { &mut *state }; - - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl::canvas_native_webgl_get_uniform( - program, - location, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl::canvas_native_webgl_get_uniform(program, location, state), + ))) + }) } #[no_mangle] @@ -3249,12 +3451,10 @@ pub extern "C" fn canvas_native_webgl_get_vertex_attrib_offset( pname: u32, state: *mut WebGLState, ) -> usize { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_get_vertex_attrib_offset( - index, - pname, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_get_vertex_attrib_offset(index, pname, state) + }) } #[no_mangle] @@ -3263,14 +3463,12 @@ pub extern "C" fn canvas_native_webgl_get_vertex_attrib( pname: u32, state: *mut WebGLState, ) -> *mut WebGLResult { - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl::canvas_native_webgl_get_vertex_attrib( - index, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl::canvas_native_webgl_get_vertex_attrib(index, pname, state), + ))) + }) } #[no_mangle] @@ -3278,26 +3476,30 @@ pub extern "C" fn canvas_native_webgl_get_is_context_lost(state: *mut WebGLState if state.is_null() { return false; } - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_get_is_context_lost(state.get_inner_mut()) + let state = unsafe { &*state }; + // Only Android threads a context, and loss is only tracked on Windows: skip the round trip. + if state.is_threaded() { + return false; + } + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_get_is_context_lost(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_hint(target: u32, mode: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_hint(target, mode, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_hint(target, mode, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_is_buffer(buffer: u32, state: *mut WebGLState) -> bool { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_is_buffer(buffer, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_is_buffer(buffer, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_is_enabled(cap: u32, state: *mut WebGLState) -> bool { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_is_enabled(cap, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_is_enabled(cap, state)) } #[no_mangle] @@ -3305,14 +3507,14 @@ pub extern "C" fn canvas_native_webgl_is_framebuffer( framebuffer: u32, state: *mut WebGLState, ) -> bool { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_is_framebuffer(framebuffer, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_is_framebuffer(framebuffer, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_is_program(program: u32, state: *mut WebGLState) -> bool { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_is_program(program, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_is_program(program, state)) } #[no_mangle] @@ -3320,38 +3522,41 @@ pub extern "C" fn canvas_native_webgl_is_renderbuffer( renderbuffer: u32, state: *mut WebGLState, ) -> bool { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_is_renderbuffer(renderbuffer, state.get_inner_mut()) + let state = unsafe { &*state }; + state + .sync(|state| canvas_webgl::webgl::canvas_native_webgl_is_renderbuffer(renderbuffer, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_is_shader(shader: u32, state: *mut WebGLState) -> bool { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_is_shader(shader, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_is_shader(shader, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_is_texture(texture: u32, state: *mut WebGLState) -> bool { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_is_texture(texture, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl::canvas_native_webgl_is_texture(texture, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_line_width(width: f32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_line_width(width, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_line_width(width, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_link_program(program: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_link_program(program, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_link_program(program, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_pixel_storei(pname: u32, param: i32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_pixel_storei(pname, param, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_pixel_storei(pname, param, state) + }) } #[no_mangle] @@ -3361,8 +3566,10 @@ pub extern "C" fn canvas_native_webgl_polygon_offset( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_polygon_offset(factor, units, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_polygon_offset(factor, units, state) + }) } #[no_mangle] @@ -3378,18 +3585,13 @@ pub extern "C" fn canvas_native_webgl_read_pixels_u8( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; let pixels = unsafe { std::slice::from_raw_parts_mut(pixels, size) }; - canvas_webgl::webgl::canvas_native_webgl_read_pixels_u8( - x, - y, - width, - height, - format, - pixel_type, - pixels, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_read_pixels_u8( + x, y, width, height, format, pixel_type, pixels, state, + ) + }) } #[no_mangle] @@ -3405,18 +3607,13 @@ pub extern "C" fn canvas_native_webgl_read_pixels_u16( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; let pixels = unsafe { std::slice::from_raw_parts_mut(pixels, size) }; - canvas_webgl::webgl::canvas_native_webgl_read_pixels_u16( - x, - y, - width, - height, - format, - pixel_type, - pixels, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_read_pixels_u16( + x, y, width, height, format, pixel_type, pixels, state, + ) + }) } #[no_mangle] @@ -3432,18 +3629,13 @@ pub extern "C" fn canvas_native_webgl_read_pixels_f32( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; let pixels = unsafe { std::slice::from_raw_parts_mut(pixels, size) }; - canvas_webgl::webgl::canvas_native_webgl_read_pixels_f32( - x, - y, - width, - height, - format, - pixel_type, - pixels, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_read_pixels_f32( + x, y, width, height, format, pixel_type, pixels, state, + ) + }) } #[no_mangle] @@ -3455,14 +3647,16 @@ pub extern "C" fn canvas_native_webgl_renderbuffer_storage( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_renderbuffer_storage( - target, - internal_format, - width, - height, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_renderbuffer_storage( + target, + internal_format, + width, + height, + state, + ) + }) } #[no_mangle] @@ -3472,8 +3666,10 @@ pub extern "C" fn canvas_native_webgl_sample_coverage( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_sample_coverage(value, invert, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_sample_coverage(value, invert, state) + }) } #[no_mangle] @@ -3485,8 +3681,10 @@ pub extern "C" fn canvas_native_webgl_scissor( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_scissor(x, y, width, height, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_scissor(x, y, width, height, state) + }) } #[no_mangle] @@ -3498,13 +3696,11 @@ pub extern "C" fn canvas_native_webgl_shader_source( assert!(!state.is_null()); assert!(!source.is_null()); let source = unsafe { CStr::from_ptr(source) }; - let source = source.to_string_lossy(); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_shader_source( - shader, - source.as_ref(), - state.get_inner_mut(), - ) + let source = source.to_string_lossy().into_owned(); + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_shader_source(shader, &source, state) + }) } #[no_mangle] @@ -3515,13 +3711,10 @@ pub extern "C" fn canvas_native_webgl_stencil_func( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_stencil_func( - func, - reference, - mask, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_stencil_func(func, reference, mask, state) + }) } #[no_mangle] @@ -3532,21 +3725,19 @@ pub extern "C" fn canvas_native_webgl_stencil_func_separate( mask: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_stencil_func_separate( - face, - func, - reference, - mask, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_stencil_func_separate( + face, func, reference, mask, state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_stencil_mask(mask: u32, state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_stencil_mask(mask, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_stencil_mask(mask, state)) } #[no_mangle] @@ -3556,12 +3747,10 @@ pub extern "C" fn canvas_native_webgl_stencil_mask_separate( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_stencil_mask_separate( - face, - mask, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_stencil_mask_separate(face, mask, state) + }) } #[no_mangle] @@ -3573,7 +3762,9 @@ pub extern "C" fn canvas_native_webgl_stencil_op( ) { assert!(!state.is_null()); let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_stencil_op(fail, zfail, zpass, state.get_inner()) + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_stencil_op(fail, zfail, zpass, state) + }) } #[no_mangle] @@ -3584,14 +3775,12 @@ pub extern "C" fn canvas_native_webgl_stencil_op_separate( zpass: u32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_stencil_op_separate( - face, - fail, - zfail, - zpass, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_stencil_op_separate( + face, fail, zfail, zpass, state, + ) + }) } #[no_mangle] @@ -3603,15 +3792,17 @@ pub extern "C" fn canvas_native_webgl_tex_image2d_image_none( image_type: i32, state: *mut WebGLState, ) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_tex_image2d_image_none( - target, - level, - internalformat, - format, - image_type, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_image2d_image_none( + target, + level, + internalformat, + format, + image_type, + state, + ) + }) } #[no_mangle] @@ -3627,7 +3818,7 @@ pub extern "C" fn canvas_native_webgl_tex_image2d_canvas2d( assert!(!canvas.is_null()); assert!(!state.is_null()); let canvas = unsafe { &mut *canvas }; - let state = unsafe { &mut *state }; + let state = unsafe { &*state }; let (width, height) = canvas.context.dimensions(); @@ -3635,20 +3826,20 @@ pub extern "C" fn canvas_native_webgl_tex_image2d_canvas2d( canvas.read_pixels_into(bytes.as_mut_slice(), (0, 0), (width as i32, height as i32)); - state.0.make_current(); - - canvas_webgl::webgl::canvas_native_webgl_tex_image2d( - target, - level, - internalformat, - width as i32, - height as i32, - 0, - format, - image_type, - bytes.as_slice(), - state.get_inner_mut(), - ); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_image2d( + target, + level, + internalformat, + width as i32, + height as i32, + 0, + format, + image_type, + bytes.as_slice(), + state, + ) + }); } #[no_mangle] @@ -3663,21 +3854,23 @@ pub extern "C" fn canvas_native_webgl_tex_image2d_webgl( ) { assert!(!state.is_null()); assert!(!webgl.is_null()); - let state = unsafe { &mut *state }; - let webgl = unsafe { &mut *webgl }; - let mut pixels = canvas_webgl::webgl::canvas_native_webgl_read_webgl_pixels(&mut webgl.0, &mut state.0); - canvas_webgl::webgl::canvas_native_webgl_tex_image2d( - target, - level, - internalformat, - pixels.0, - pixels.1, - 0, - format, - image_type, - pixels.2.as_mut_slice(), - state.get_inner_mut(), - ); + let state = unsafe { &*state }; + let webgl = unsafe { &*webgl }; + let (width, height, pixels) = webgl.read_drawing_buffer(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_image2d( + target, + level, + internalformat, + width, + height, + 0, + format, + image_type, + pixels.as_slice(), + state, + ) + }); } #[no_mangle] @@ -3695,20 +3888,23 @@ pub extern "C" fn canvas_native_webgl_tex_image2d( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; let buf = unsafe { std::slice::from_raw_parts(buf, size) }; - canvas_webgl::webgl::canvas_native_webgl_tex_image2d( - target, - level, - internalformat, - width, - height, - border, - format, - image_type, - buf, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let buf = buf.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_image2d( + target, + level, + internalformat, + width, + height, + border, + format, + image_type, + &buf, + state, + ) + }) } #[no_mangle] @@ -3724,18 +3920,20 @@ pub extern "C" fn canvas_native_webgl_tex_image2d_none( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_tex_image2d_none( - target, - level, - internalformat, - width, - height, - border, - format, - image_type, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_image2d_none( + target, + level, + internalformat, + width, + height, + border, + format, + image_type, + state, + ) + }) } #[no_mangle] @@ -3751,16 +3949,18 @@ pub extern "C" fn canvas_native_webgl_tex_image2d_image_asset( assert!(!state.is_null()); assert!(!image_asset.is_null()); let image_asset = unsafe { &*image_asset }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_tex_image2d_asset( - target, - level, - internalformat, - format, - image_type, - &image_asset.0, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_tex_image2d_asset( + target, + level, + internalformat, + format, + image_type, + &image_asset.0, + state, + ) + }) } #[no_mangle] @@ -3772,7 +3972,9 @@ pub extern "C" fn canvas_native_webgl_tex_parameterf( ) { assert!(!state.is_null()); let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_tex_parameterf(target, pname, param, state.get_inner()) + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_parameterf(target, pname, param, state) + }) } #[no_mangle] @@ -3784,7 +3986,9 @@ pub extern "C" fn canvas_native_webgl_tex_parameteri( ) { assert!(!state.is_null()); let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_tex_parameteri(target, pname, param, state.get_inner()) + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_parameteri(target, pname, param, state) + }) } #[no_mangle] @@ -3802,16 +4006,11 @@ pub extern "C" fn canvas_native_webgl_tex_sub_image2d_asset( assert!(!asset.is_null()); let asset = unsafe { &*asset }; let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d_asset( - target, - level, - xoffset, - yoffset, - format, - image_type, - &asset.0, - state.get_inner(), - ) + state.sync(|state| { + canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d_asset( + target, level, xoffset, yoffset, format, image_type, &asset.0, state, + ) + }) } #[no_mangle] @@ -3827,7 +4026,7 @@ pub extern "C" fn canvas_native_webgl_tex_sub_image2d_canvas2d( ) { assert!(!state.is_null()); assert!(!canvas.is_null()); - let state = unsafe { &mut *state }; + let state = unsafe { &*state }; let canvas = unsafe { &mut *canvas }; let (width, height) = canvas.context.dimensions(); @@ -3836,20 +4035,20 @@ pub extern "C" fn canvas_native_webgl_tex_sub_image2d_canvas2d( canvas.read_pixels_into(bytes.as_mut_slice(), (0, 0), (width as i32, height as i32)); - state.0.make_current(); - - canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d( - target, - level, - xoffset, - yoffset, - width as i32, - height as i32, - format, - image_type, - bytes.as_slice(), - state.get_inner_mut(), - ); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d( + target, + level, + xoffset, + yoffset, + width as i32, + height as i32, + format, + image_type, + bytes.as_slice(), + state, + ) + }); } #[no_mangle] @@ -3865,31 +4064,23 @@ pub extern "C" fn canvas_native_webgl_tex_sub_image2d_webgl( ) { assert!(!state.is_null()); assert!(!webgl.is_null()); - let state = unsafe { &mut *state }; - let webgl = unsafe { &mut *webgl }; - { - let inner = &mut state.0; - inner.remove_if_current(); - } - let source = webgl.get_inner(); - source.make_current(); - let width = source.drawing_buffer_width(); - let height = source.drawing_buffer_height(); - - let mut pixels = canvas_webgl::webgl::canvas_native_webgl_read_webgl_pixels(&mut webgl.0, &mut state.0); - - canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d( - target, - level, - xoffset, - yoffset, - width, - height, - format, - image_type, - pixels.2.as_mut_slice(), - state.get_inner_mut(), - ); + let state = unsafe { &*state }; + let webgl = unsafe { &*webgl }; + let (width, height, pixels) = webgl.read_drawing_buffer(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d( + target, + level, + xoffset, + yoffset, + width, + height, + format, + image_type, + pixels.as_slice(), + state, + ) + }); } #[no_mangle] @@ -3908,19 +4099,13 @@ pub extern "C" fn canvas_native_webgl_tex_sub_image2d( ) { assert!(!state.is_null()); let buf = unsafe { std::slice::from_raw_parts(buf, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d( - target, - level, - xoffset, - yoffset, - width, - height, - format, - image_type, - buf, - state.get_inner(), - ) + let state = unsafe { &*state }; + let buf = buf.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d( + target, level, xoffset, yoffset, width, height, format, image_type, &buf, state, + ) + }) } #[no_mangle] @@ -3937,26 +4122,19 @@ pub extern "C" fn canvas_native_webgl_tex_sub_image2d_offset( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d_offset( - target, - level, - xoffset, - yoffset, - width, - height, - format, - image_type, - offset, - state.get_inner(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_sub_image2d_offset( + target, level, xoffset, yoffset, width, height, format, image_type, offset, state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_uniform1f(location: i32, v0: f32, state: *mut WebGLState) { assert!(!state.is_null()); let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform1f(location, v0, state.get_inner()) + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_uniform1f(location, v0, state)) } #[no_mangle] @@ -3969,14 +4147,17 @@ pub extern "C" fn canvas_native_webgl_uniform1fv( assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform1fv(location, value, state.get_inner()) + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform1fv(location, &value, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_uniform1i(location: i32, v0: i32, state: *mut WebGLState) { assert!(!state.is_null()); let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform1i(location, v0, state.get_inner()) + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_uniform1i(location, v0, state)) } #[no_mangle] @@ -3989,7 +4170,10 @@ pub extern "C" fn canvas_native_webgl_uniform1iv( assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform1iv(location, value, state.get_inner()) + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform1iv(location, &value, state) + }) } #[no_mangle] @@ -4001,7 +4185,9 @@ pub extern "C" fn canvas_native_webgl_uniform2f( ) { assert!(!state.is_null()); let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform2f(location, v0, v1, state.get_inner()) + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform2f(location, v0, v1, state) + }) } #[no_mangle] @@ -4014,8 +4200,10 @@ pub extern "C" fn canvas_native_webgl_uniform2fv( assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; let state = unsafe { &*state }; - - canvas_webgl::webgl::canvas_native_webgl_uniform2fv(location, value, state.get_inner()) + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform2fv(location, &value, state) + }) } #[no_mangle] @@ -4027,7 +4215,9 @@ pub extern "C" fn canvas_native_webgl_uniform2i( ) { assert!(!state.is_null()); let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform2i(location, v0, v1, state.get_inner()) + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform2i(location, v0, v1, state) + }) } #[no_mangle] @@ -4040,7 +4230,10 @@ pub extern "C" fn canvas_native_webgl_uniform2iv( assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform2iv(location, value, state.get_inner()) + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform2iv(location, &value, state) + }) } #[no_mangle] @@ -4052,8 +4245,10 @@ pub extern "C" fn canvas_native_webgl_uniform3f( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform3f(location, v0, v1, v2, state.get_inner()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform3f(location, v0, v1, v2, state) + }) } #[no_mangle] @@ -4066,7 +4261,10 @@ pub extern "C" fn canvas_native_webgl_uniform3fv( assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform3fv(location, value, state.get_inner()) + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform3fv(location, &value, state) + }) } #[no_mangle] @@ -4078,8 +4276,10 @@ pub extern "C" fn canvas_native_webgl_uniform3i( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform3i(location, v0, v1, v2, state.get_inner()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform3i(location, v0, v1, v2, state) + }) } #[no_mangle] @@ -4092,7 +4292,10 @@ pub extern "C" fn canvas_native_webgl_uniform3iv( assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; let state = unsafe { &*state }; - canvas_webgl::webgl::canvas_native_webgl_uniform3iv(location, value, state.get_inner()) + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform3iv(location, &value, state) + }) } #[no_mangle] @@ -4105,8 +4308,10 @@ pub extern "C" fn canvas_native_webgl_uniform4f( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform4f(location, v0, v1, v2, v3, state.get_inner()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform4f(location, v0, v1, v2, v3, state) + }) } #[no_mangle] @@ -4118,8 +4323,11 @@ pub extern "C" fn canvas_native_webgl_uniform4fv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform4fv(location, value, state.get_inner_mut()) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform4fv(location, &value, state) + }) } #[no_mangle] @@ -4132,15 +4340,10 @@ pub extern "C" fn canvas_native_webgl_uniform4i( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform4i( - location, - v0, - v1, - v2, - v3, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform4i(location, v0, v1, v2, v3, state) + }) } #[no_mangle] @@ -4152,8 +4355,11 @@ pub extern "C" fn canvas_native_webgl_uniform4iv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform4iv(location, value, state.get_inner_mut()) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform4iv(location, &value, state) + }) } #[no_mangle] @@ -4166,13 +4372,13 @@ pub extern "C" fn canvas_native_webgl_uniform_matrix2fv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform_matrix2fv( - location, - transpose, - value, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform_matrix2fv( + location, transpose, &value, state, + ) + }) } #[no_mangle] @@ -4185,13 +4391,13 @@ pub extern "C" fn canvas_native_webgl_uniform_matrix3fv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform_matrix3fv( - location, - transpose, - value, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform_matrix3fv( + location, transpose, &value, state, + ) + }) } #[no_mangle] @@ -4204,34 +4410,38 @@ pub extern "C" fn canvas_native_webgl_uniform_matrix4fv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_uniform_matrix4fv( - location, - transpose, - value, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_uniform_matrix4fv( + location, transpose, &value, state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_use_program(program: u32, state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_use_program(program, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl::canvas_native_webgl_use_program(program, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl_validate_program(program: u32, state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_validate_program(program, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_validate_program(program, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl_vertex_attrib1f(index: u32, v0: f32, state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib1f(index, v0, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib1f(index, v0, state) + }) } #[no_mangle] @@ -4243,8 +4453,11 @@ pub extern "C" fn canvas_native_webgl_vertex_attrib1fv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib1fv(index, value, state.get_inner_mut()) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib1fv(index, &value, state) + }) } #[no_mangle] @@ -4255,8 +4468,10 @@ pub extern "C" fn canvas_native_webgl_vertex_attrib2f( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib2f(index, v0, v1, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib2f(index, v0, v1, state) + }) } #[no_mangle] @@ -4268,8 +4483,11 @@ pub extern "C" fn canvas_native_webgl_vertex_attrib2fv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib2fv(index, value, state.get_inner_mut()) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib2fv(index, &value, state) + }) } #[no_mangle] @@ -4281,14 +4499,10 @@ pub extern "C" fn canvas_native_webgl_vertex_attrib3f( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib3f( - index, - v0, - v1, - v2, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib3f(index, v0, v1, v2, state) + }) } #[no_mangle] @@ -4300,8 +4514,11 @@ pub extern "C" fn canvas_native_webgl_vertex_attrib3fv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib3fv(index, value, state.get_inner_mut()) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib3fv(index, &value, state) + }) } #[no_mangle] @@ -4314,15 +4531,10 @@ pub extern "C" fn canvas_native_webgl_vertex_attrib4f( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib4f( - index, - v0, - v1, - v2, - v3, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib4f(index, v0, v1, v2, v3, state) + }) } #[no_mangle] @@ -4334,8 +4546,11 @@ pub extern "C" fn canvas_native_webgl_vertex_attrib4fv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib4fv(index, value, state.get_inner_mut()) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib4fv(index, &value, state) + }) } #[no_mangle] @@ -4349,16 +4564,12 @@ pub extern "C" fn canvas_native_webgl_vertex_attrib_pointer( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_vertex_attrib_pointer( - index, - size, - d_type, - normalized, - stride, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_vertex_attrib_pointer( + index, size, d_type, normalized, stride, offset, state, + ) + }) } #[no_mangle] @@ -4370,6 +4581,8 @@ pub extern "C" fn canvas_native_webgl_viewport( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl::canvas_native_webgl_viewport(x, y, width, height, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_viewport(x, y, width, height, state) + }) } diff --git a/crates/canvas-c/src/webgl/gl2.rs b/crates/canvas-c/src/webgl/gl2.rs index f0b586d2b..178cf5a8a 100644 --- a/crates/canvas-c/src/webgl/gl2.rs +++ b/crates/canvas-c/src/webgl/gl2.rs @@ -38,8 +38,10 @@ pub extern "C" fn canvas_native_webgl2_indexed_parameter_get_is_buffer( #[no_mangle] pub extern "C" fn canvas_native_webgl2_begin_query(target: u32, id: u32, state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_begin_query(target, id, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_begin_query(target, id, state) + }) } #[no_mangle] @@ -48,11 +50,10 @@ pub extern "C" fn canvas_native_webgl2_begin_transform_feedback( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_begin_transform_feedback( - primitive_mode, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_begin_transform_feedback(primitive_mode, state) + }) } #[no_mangle] @@ -63,13 +64,10 @@ pub extern "C" fn canvas_native_webgl2_bind_buffer_base( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_bind_buffer_base( - target, - index, - buffer, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_bind_buffer_base(target, index, buffer, state) + }) } #[no_mangle] @@ -82,15 +80,12 @@ pub extern "C" fn canvas_native_webgl2_bind_buffer_range( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_bind_buffer_range( - target, - index, - buffer, - offset, - size, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_bind_buffer_range( + target, index, buffer, offset, size, state, + ) + }) } #[no_mangle] @@ -100,8 +95,10 @@ pub extern "C" fn canvas_native_webgl2_bind_sampler( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_bind_sampler(unit, sampler, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_bind_sampler(unit, sampler, state) + }) } #[no_mangle] @@ -111,12 +108,10 @@ pub extern "C" fn canvas_native_webgl2_bind_transform_feedback( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_bind_sampler( - target, - transform_feedback, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_bind_sampler(target, transform_feedback, state) + }) } #[no_mangle] @@ -125,11 +120,10 @@ pub extern "C" fn canvas_native_webgl2_bind_vertex_array( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_bind_vertex_array( - vertex_array, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_bind_vertex_array(vertex_array, state) + }) } #[no_mangle] @@ -147,20 +141,12 @@ pub extern "C" fn canvas_native_webgl2_blit_framebuffer( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_blit_framebuffer( - src_x0, - src_y0, - src_x1, - src_y1, - dst_x0, - dst_y0, - dst_x1, - dst_y1, - mask, - filter, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_blit_framebuffer( + src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter, state, + ) + }) } #[no_mangle] @@ -172,14 +158,12 @@ pub extern "C" fn canvas_native_webgl2_clear_bufferfi( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_clear_bufferfi( - buffer, - drawbuffer, - depth, - stencil, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_clear_bufferfi( + buffer, drawbuffer, depth, stencil, state, + ) + }) } #[no_mangle] @@ -192,13 +176,13 @@ pub extern "C" fn canvas_native_webgl2_clear_bufferfv( ) { assert!(!state.is_null()); let values = unsafe { std::slice::from_raw_parts(values, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_clear_bufferfv( - buffer, - drawbuffer, - values, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let values = values.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_clear_bufferfv( + buffer, drawbuffer, &values, state, + ) + }) } #[no_mangle] @@ -211,13 +195,13 @@ pub extern "C" fn canvas_native_webgl2_clear_bufferiv( ) { assert!(!state.is_null()); let values = unsafe { std::slice::from_raw_parts(values, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_clear_bufferiv( - buffer, - drawbuffer, - values, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let values = values.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_clear_bufferiv( + buffer, drawbuffer, &values, state, + ) + }) } #[no_mangle] @@ -230,13 +214,13 @@ pub extern "C" fn canvas_native_webgl2_clear_bufferuiv( ) { assert!(!state.is_null()); let values = unsafe { std::slice::from_raw_parts(values, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_clear_bufferuiv( - buffer, - drawbuffer, - values, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let values = values.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_clear_bufferuiv( + buffer, drawbuffer, &values, state, + ) + }) } #[no_mangle] @@ -248,14 +232,16 @@ pub extern "C" fn canvas_native_webgl2_client_wait_sync( ) -> u32 { assert!(!state.is_null()); assert!(!sync.is_null()); - let state = unsafe { &mut *state }; - let sync = unsafe { &*sync }; - canvas_webgl::webgl2::canvas_native_webgl2_client_wait_sync( - &sync.0, - flags, - timeout as c_ulong, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + let sync = unsafe { &*sync }; + canvas_webgl::webgl2::canvas_native_webgl2_client_wait_sync( + &sync.0, + flags, + timeout as c_ulong, + state, + ) + }) } #[no_mangle] @@ -272,19 +258,21 @@ pub extern "C" fn canvas_native_webgl2_compressed_tex_image3d_none( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_compressed_tex_image3d_none( - target, - level, - internalformat, - width, - height, - depth, - border, - image_size, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_compressed_tex_image3d_none( + target, + level, + internalformat, + width, + height, + depth, + border, + image_size, + offset, + state, + ) + }) } #[no_mangle] @@ -304,20 +292,23 @@ pub extern "C" fn canvas_native_webgl2_compressed_tex_image3d( ) { assert!(!state.is_null()); let src = unsafe { std::slice::from_raw_parts(src, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_compressed_tex_image3d( - target, - level, - internalformat, - width, - height, - depth, - border, - src, - src_offset, - src_length_override, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src = src.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_compressed_tex_image3d( + target, + level, + internalformat, + width, + height, + depth, + border, + &src, + src_offset, + src_length_override, + state, + ) + }) } @@ -337,21 +328,13 @@ pub extern "C" fn canvas_native_webgl2_compressed_tex_sub_image3d_none( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_compressed_tex_sub_image3d_none( - target, - level, - xoffset, - yoffset, - zoffset, - width, - height, - depth, - format, - image_size, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_compressed_tex_sub_image3d_none( + target, level, xoffset, yoffset, zoffset, width, height, depth, format, image_size, + offset, state, + ) + }) } #[no_mangle] @@ -373,22 +356,25 @@ pub extern "C" fn canvas_native_webgl2_compressed_tex_sub_image3d( ) { assert!(!state.is_null()); let src = unsafe { std::slice::from_raw_parts(src, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_compressed_tex_sub_image3d( - target, - level, - xoffset, - yoffset, - zoffset, - width, - height, - depth, - format, - src, - src_offset, - src_length_override, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let src = src.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_compressed_tex_sub_image3d( + target, + level, + xoffset, + yoffset, + zoffset, + width, + height, + depth, + format, + &src, + src_offset, + src_length_override, + state, + ) + }) } #[no_mangle] @@ -401,15 +387,17 @@ pub extern "C" fn canvas_native_webgl2_copy_buffer_sub_data( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_copy_buffer_sub_data( - read_target, - write_target, - read_offset, - write_offset, - size, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_copy_buffer_sub_data( + read_target, + write_target, + read_offset, + write_offset, + size, + state, + ) + }) } #[no_mangle] @@ -426,54 +414,49 @@ pub extern "C" fn canvas_native_webgl2_copy_tex_sub_image3d( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_copy_tex_sub_image3d( - target, - level, - xoffset, - yoffset, - zoffset, - x, - y, - width, - height, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_copy_tex_sub_image3d( + target, level, xoffset, yoffset, zoffset, x, y, width, height, state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_create_query(state: *mut WebGLState) -> u32 { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_create_query(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl2::canvas_native_webgl2_create_query(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_create_sampler(state: *mut WebGLState) -> u32 { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_create_sampler(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl2::canvas_native_webgl2_create_sampler(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_create_transform_feedback(state: *mut WebGLState) -> u32 { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_create_transform_feedback(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl2::canvas_native_webgl2_create_transform_feedback(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_create_vertex_array(state: *mut WebGLState) -> u32 { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_create_vertex_array(state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl2::canvas_native_webgl2_create_vertex_array(state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_delete_query_with_query(id: u32, state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_delete_query_with_query(id, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_delete_query_with_query(id, state) + }) } #[no_mangle] @@ -482,11 +465,10 @@ pub extern "C" fn canvas_native_webgl2_delete_sampler_with_sampler( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_delete_sampler_with_sampler( - sampler, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_delete_sampler_with_sampler(sampler, state) + }) } #[no_mangle] @@ -505,9 +487,11 @@ pub extern "C" fn canvas_native_webgl2_delete_sync_with_sync( ) { assert!(!state.is_null()); assert!(!sync.is_null()); - let state = unsafe { &mut *state }; - let sync = unsafe { &*sync }; - canvas_webgl::webgl2::canvas_native_webgl2_delete_sync_with_sync(&sync.0, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| { + let sync = unsafe { &*sync }; + canvas_webgl::webgl2::canvas_native_webgl2_delete_sync_with_sync(&sync.0, state) + }) } #[no_mangle] @@ -516,11 +500,13 @@ pub extern "C" fn canvas_native_webgl2_delete_transform_feedback( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_delete_transform_feedback( - transform_feedback, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_delete_transform_feedback( + transform_feedback, + state, + ) + }) } #[no_mangle] @@ -529,11 +515,13 @@ pub extern "C" fn canvas_native_webgl2_delete_vertex_array_with_vertex_array( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_delete_vertex_array_with_vertex_array( - vertex_array, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_delete_vertex_array_with_vertex_array( + vertex_array, + state, + ) + }) } #[no_mangle] @@ -545,14 +533,16 @@ pub extern "C" fn canvas_native_webgl2_draw_arrays_instanced( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_draw_arrays_instanced( - mode, - first, - count, - instance_count, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_draw_arrays_instanced( + mode, + first, + count, + instance_count, + state, + ) + }) } #[no_mangle] @@ -563,8 +553,10 @@ pub extern "C" fn canvas_native_webgl2_draw_buffers( ) { assert!(!state.is_null()); let buffers = unsafe { std::slice::from_raw_parts(buffers, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_draw_buffers(buffers, state.get_inner_mut()) + let state = unsafe { &*state }; + let buffers = buffers.to_vec(); + state + .post(move |state| canvas_webgl::webgl2::canvas_native_webgl2_draw_buffers(&buffers, state)) } #[no_mangle] @@ -577,15 +569,17 @@ pub extern "C" fn canvas_native_webgl2_draw_elements_instanced( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_draw_elements_instanced( - mode, - count, - type_, - offset, - instance_count, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_draw_elements_instanced( + mode, + count, + type_, + offset, + instance_count, + state, + ) + }) } #[no_mangle] @@ -599,30 +593,27 @@ pub extern "C" fn canvas_native_webgl2_draw_range_elements( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_draw_range_elements( - mode, - start, - end, - count, - type_, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_draw_range_elements( + mode, start, end, count, type_, offset, state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_end_query(target: u32, state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_end_query(target, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl2::canvas_native_webgl2_end_query(target, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_end_transform_feedback(state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_end_transform_feedback(state.get_inner_mut()) + let state = unsafe { &*state }; + state + .post(move |state| canvas_webgl::webgl2::canvas_native_webgl2_end_transform_feedback(state)) } #[no_mangle] @@ -632,14 +623,12 @@ pub extern "C" fn canvas_native_webgl2_fence_sync( state: *mut WebGLState, ) -> *mut WebGLSync { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLSync( - canvas_webgl::webgl2::canvas_native_webgl2_fence_sync( - condition, - flags, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLSync( + canvas_webgl::webgl2::canvas_native_webgl2_fence_sync(condition, flags, state), + ))) + }) } #[no_mangle] @@ -652,15 +641,12 @@ pub extern "C" fn canvas_native_webgl2_framebuffer_texture_layer( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_framebuffer_texture_layer( - target, - attachment, - texture, - level, - layer, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_framebuffer_texture_layer( + target, attachment, texture, level, layer, state, + ) + }) } #[no_mangle] @@ -670,16 +656,18 @@ pub extern "C" fn canvas_native_webgl2_get_active_uniform_block_name( state: *mut WebGLState, ) -> *const c_char { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - CString::new( - canvas_webgl::webgl2::canvas_native_webgl2_get_active_uniform_block_name( - program, - uniform_block_index, - state.get_inner_mut(), - ), - ) + let state = unsafe { &*state }; + state.sync(|state| { + CString::new( + canvas_webgl::webgl2::canvas_native_webgl2_get_active_uniform_block_name( + program, + uniform_block_index, + state, + ), + ) .unwrap() .into_raw() + }) } #[no_mangle] @@ -690,15 +678,17 @@ pub extern "C" fn canvas_native_webgl2_get_active_uniform_block_parameter( state: *mut WebGLState, ) -> *mut WebGLResult { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl2::canvas_native_webgl2_get_active_uniform_block_parameter( - program, - uniform_block_index, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl2::canvas_native_webgl2_get_active_uniform_block_parameter( + program, + uniform_block_index, + pname, + state, + ), + ))) + }) } #[no_mangle] @@ -711,15 +701,17 @@ pub extern "C" fn canvas_native_webgl2_get_active_uniforms( ) -> *mut WebGLResult { assert!(!state.is_null()); let uniform_indices = unsafe { std::slice::from_raw_parts(uniform_indices, size) }; - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl2::canvas_native_webgl2_get_active_uniforms( - program, - uniform_indices, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl2::canvas_native_webgl2_get_active_uniforms( + program, + uniform_indices, + pname, + state, + ), + ))) + }) } #[no_mangle] @@ -733,16 +725,18 @@ pub extern "C" fn canvas_native_webgl2_get_buffer_sub_data( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; let dst_data = unsafe { std::slice::from_raw_parts_mut(dst_data, size) }; - canvas_webgl::webgl2::canvas_native_webgl2_get_buffer_sub_data( - target, - src_byte_offset, - dst_data, - dst_offset, - length, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_get_buffer_sub_data( + target, + src_byte_offset, + dst_data, + dst_offset, + length, + state, + ) + }) } #[no_mangle] @@ -754,12 +748,14 @@ pub extern "C" fn canvas_native_webgl2_get_frag_data_location( assert!(!state.is_null()); let name = unsafe { CStr::from_ptr(name) }; let name = name.to_string_lossy(); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_get_frag_data_location( - program, - name.as_ref(), - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_get_frag_data_location( + program, + name.as_ref(), + state, + ) + }) } #[no_mangle] @@ -769,14 +765,12 @@ pub extern "C" fn canvas_native_webgl2_get_indexed_parameter( state: *mut WebGLState, ) -> *mut WebGLIndexedParameter { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLIndexedParameter( - canvas_webgl::webgl2::canvas_native_webgl2_get_indexed_parameter( - target, - index, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLIndexedParameter( + canvas_webgl::webgl2::canvas_native_webgl2_get_indexed_parameter(target, index, state), + ))) + }) } #[no_mangle] @@ -787,15 +781,17 @@ pub extern "C" fn canvas_native_webgl2_get_internalformat_parameter( state: *mut WebGLState, ) -> *mut WebGLResult { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl2::canvas_native_webgl2_get_internalformat_parameter( - target, - internalformat, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl2::canvas_native_webgl2_get_internalformat_parameter( + target, + internalformat, + pname, + state, + ), + ))) + }) } #[no_mangle] @@ -804,10 +800,12 @@ pub extern "C" fn canvas_native_webgl2_get_parameter( state: *mut WebGLState, ) -> *mut WebGLResult { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl2::canvas_native_webgl2_get_parameter(pname, state.get_inner_mut()), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl2::canvas_native_webgl2_get_parameter(pname, state), + ))) + }) } #[no_mangle] @@ -817,14 +815,12 @@ pub extern "C" fn canvas_native_webgl2_get_query_parameter( state: *mut WebGLState, ) -> *mut WebGLResult { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl2::canvas_native_webgl2_get_query_parameter( - query, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl2::canvas_native_webgl2_get_query_parameter(query, pname, state), + ))) + }) } #[no_mangle] @@ -834,10 +830,12 @@ pub extern "C" fn canvas_native_webgl2_get_query( state: *mut WebGLState, ) -> *mut WebGLResult { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl2::canvas_native_webgl2_get_query(target, pname, state.get_inner_mut()), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl2::canvas_native_webgl2_get_query(target, pname, state), + ))) + }) } #[no_mangle] @@ -847,14 +845,12 @@ pub extern "C" fn canvas_native_webgl2_get_sampler_parameter( state: *mut WebGLState, ) -> *mut WebGLResult { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl2::canvas_native_webgl2_get_sampler_parameter( - sampler, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl2::canvas_native_webgl2_get_sampler_parameter(sampler, pname, state), + ))) + }) } #[no_mangle] @@ -866,14 +862,12 @@ pub extern "C" fn canvas_native_webgl2_get_sync_parameter( assert!(!sync.is_null()); assert!(!state.is_null()); let sync = unsafe { &*sync }; - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLResult( - canvas_webgl::webgl2::canvas_native_webgl2_get_sync_parameter( - &sync.0, - pname, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLResult( + canvas_webgl::webgl2::canvas_native_webgl2_get_sync_parameter(&sync.0, pname, state), + ))) + }) } #[no_mangle] @@ -883,14 +877,14 @@ pub extern "C" fn canvas_native_webgl2_get_transform_feedback_varying( state: *mut WebGLState, ) -> *mut WebGLActiveInfo { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(WebGLActiveInfo( - canvas_webgl::webgl2::canvas_native_webgl2_get_transform_feedback_varying( - program, - index, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(WebGLActiveInfo( + canvas_webgl::webgl2::canvas_native_webgl2_get_transform_feedback_varying( + program, index, state, + ), + ))) + }) } #[no_mangle] @@ -902,12 +896,14 @@ pub extern "C" fn canvas_native_webgl2_get_uniform_block_index( assert!(!state.is_null()); let uniform_block_name = unsafe { CStr::from_ptr(uniform_block_name) }; let uniform_block_name = uniform_block_name.to_string_lossy(); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_get_uniform_block_index( - program, - uniform_block_name.as_ref(), - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_get_uniform_block_index( + program, + uniform_block_name.as_ref(), + state, + ) + }) } #[no_mangle] @@ -918,15 +914,17 @@ pub extern "C" fn canvas_native_webgl2_get_uniform_indices( state: *mut WebGLState, ) -> *mut U32Buffer { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - Box::into_raw(Box::new(U32Buffer::from( - canvas_webgl::webgl2::canvas_native_webgl2_get_uniform_indices_raw( - program, - uniform_names, - size, - state.get_inner_mut(), - ), - ))) + let state = unsafe { &*state }; + state.sync(|state| { + Box::into_raw(Box::new(U32Buffer::from( + canvas_webgl::webgl2::canvas_native_webgl2_get_uniform_indices_raw( + program, + uniform_names, + size, + state, + ), + ))) + }) } #[no_mangle] @@ -938,12 +936,15 @@ pub extern "C" fn canvas_native_webgl2_invalidate_framebuffer( ) { assert!(!state.is_null()); let attachments = unsafe { std::slice::from_raw_parts(attachments, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_invalidate_framebuffer( - target, - attachments, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let attachments = attachments.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_invalidate_framebuffer( + target, + &attachments, + state, + ) + }) } #[no_mangle] @@ -959,30 +960,33 @@ pub extern "C" fn canvas_native_webgl2_invalidate_sub_framebuffer( ) { assert!(!state.is_null()); let attachments = unsafe { std::slice::from_raw_parts(attachments, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_invalidate_sub_framebuffer( - target, - attachments, - x, - y, - width, - height, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let attachments = attachments.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_invalidate_sub_framebuffer( + target, + &attachments, + x, + y, + width, + height, + state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_is_query(query: u32, state: *mut WebGLState) -> bool { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_is_query(query, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl2::canvas_native_webgl2_is_query(query, state)) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_is_sampler(sampler: u32, state: *mut WebGLState) -> bool { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_is_sampler(sampler, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| canvas_webgl::webgl2::canvas_native_webgl2_is_sampler(sampler, state)) } #[no_mangle] @@ -991,9 +995,11 @@ pub extern "C" fn canvas_native_webgl2_is_sync( state: *mut WebGLState, ) -> bool { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - let sync = unsafe { &*sync }; - canvas_webgl::webgl2::canvas_native_webgl2_is_sync(&sync.0, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| { + let sync = unsafe { &*sync }; + canvas_webgl::webgl2::canvas_native_webgl2_is_sync(&sync.0, state) + }) } #[no_mangle] @@ -1002,11 +1008,10 @@ pub extern "C" fn canvas_native_webgl2_is_transform_feedback( state: *mut WebGLState, ) -> bool { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_is_transform_feedback( - transform_feedback, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_is_transform_feedback(transform_feedback, state) + }) } #[no_mangle] @@ -1015,22 +1020,26 @@ pub extern "C" fn canvas_native_webgl2_is_vertex_array( state: *mut WebGLState, ) -> bool { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_is_vertex_array(vertex_array, state.get_inner_mut()) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_is_vertex_array(vertex_array, state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_pause_transform_feedback(state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_pause_transform_feedback(state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_pause_transform_feedback(state) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_read_buffer(src: u32, state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_read_buffer(src, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| canvas_webgl::webgl2::canvas_native_webgl2_read_buffer(src, state)) } #[no_mangle] @@ -1043,22 +1052,26 @@ pub extern "C" fn canvas_native_webgl2_renderbuffer_storage_multisample( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_renderbuffer_storage_multisample( - target, - samples, - internal_format, - width, - height, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_renderbuffer_storage_multisample( + target, + samples, + internal_format, + width, + height, + state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_resume_transform_feedback(state: *mut WebGLState) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_resume_transform_feedback(state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_resume_transform_feedback(state) + }) } #[no_mangle] @@ -1069,13 +1082,10 @@ pub extern "C" fn canvas_native_webgl2_sampler_parameterf( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_sampler_parameterf( - sampler, - pname, - param, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_sampler_parameterf(sampler, pname, param, state) + }) } #[no_mangle] @@ -1086,13 +1096,10 @@ pub extern "C" fn canvas_native_webgl2_sampler_parameteri( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_sampler_parameteri( - sampler, - pname, - param, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_sampler_parameteri(sampler, pname, param, state) + }) } #[no_mangle] @@ -1110,20 +1117,22 @@ pub extern "C" fn canvas_native_webgl2_tex_image3d_none( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d_none( - target, - level, - internalformat, - width, - height, - depth, - border, - format, - type_, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d_none( + target, + level, + internalformat, + width, + height, + depth, + border, + format, + type_, + offset, + state, + ) + }) } #[no_mangle] @@ -1141,20 +1150,22 @@ pub extern "C" fn canvas_native_webgl2_tex_image3d_asset( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d_asset( - target, - level, - internalformat, - width, - height, - depth, - border, - format, - type_, - &asset.0, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d_asset( + target, + level, + internalformat, + width, + height, + depth, + border, + format, + type_, + &asset.0, + state, + ) + }) } #[no_mangle] @@ -1172,41 +1183,38 @@ pub extern "C" fn canvas_native_webgl2_tex_image3d_canvas2d( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; + let state = unsafe { &*state }; let canvas = unsafe { &mut *canvas }; - // let (width, height) = (canvas.context.width(), canvas.context.height()); // let snapshot = canvas.context.as_data(); - let (width, height) = canvas.context.dimensions(); let mut bytes = vec![0u8; (width * height * 4.) as usize]; canvas.read_pixels_into(bytes.as_mut_slice(), (0, 0), (width as i32, height as i32)); - // todo handle pre-multipied // let premultiply = state.get_inner().get_premultiplied_alpha(); // let buf = source_ctx.read_pixels_with_alpha_premultiply(&snapshot, format as i32, premultiply); - state.0.make_current(); - - canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d( - target, - level, - internalformat, - width as i32, - height as i32, - depth, - border, - format, - type_, - bytes.as_slice(), - state.get_inner_mut(), - ) + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d( + target, + level, + internalformat, + width as i32, + height as i32, + depth, + border, + format, + type_, + bytes.as_slice(), + state, + ) + }); } @@ -1226,33 +1234,31 @@ pub extern "C" fn canvas_native_webgl2_tex_image3d_webgl( ) { assert!(!state.is_null()); assert!(!webgl.is_null()); - let state = unsafe { &mut *state }; - let webgl = unsafe { &mut *webgl }; + let state = unsafe { &*state }; + let webgl = unsafe { &*webgl }; - - - let (width, height, bytes) = canvas_webgl::webgl::canvas_native_webgl_read_webgl_pixels(&mut webgl.0, &mut state.0); + let (width, height, bytes) = webgl.read_drawing_buffer(); // todo handle pre-multipied // let premultiply = state.get_inner().get_premultiplied_alpha(); // let buf = source_ctx.read_pixels_with_alpha_premultiply(&snapshot, format as i32, premultiply); - state.0.make_current(); - - canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d( - target, - level, - internalformat, - width as i32, - height as i32, - depth, - border, - format, - type_, - bytes.as_slice(), - state.get_inner_mut(), - ) + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d( + target, + level, + internalformat, + width as i32, + height as i32, + depth, + border, + format, + type_, + bytes.as_slice(), + state, + ) + }); } #[no_mangle] @@ -1272,20 +1278,23 @@ pub extern "C" fn canvas_native_webgl2_tex_image3d( ) { assert!(!state.is_null()); let buf = unsafe { std::slice::from_raw_parts(buf, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d( - target, - level, - internalformat, - width, - height, - depth, - border, - format, - type_, - buf, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let buf = buf.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d( + target, + level, + internalformat, + width, + height, + depth, + border, + format, + type_, + &buf, + state, + ) + }) } #[no_mangle] @@ -1306,21 +1315,24 @@ pub extern "C" fn canvas_native_webgl2_tex_image3d_offset( ) { assert!(!state.is_null()); let buf = unsafe { std::slice::from_raw_parts(buf, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d_offset( - target, - level, - internalformat, - width, - height, - depth, - border, - format, - type_, - buf, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let buf = buf.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image3d_offset( + target, + level, + internalformat, + width, + height, + depth, + border, + format, + type_, + &buf, + offset, + state, + ) + }) } #[no_mangle] @@ -1333,15 +1345,17 @@ pub extern "C" fn canvas_native_webgl2_tex_storage2d( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_storage2d( - target, - levels, - internalformat, - width, - height, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_storage2d( + target, + levels, + internalformat, + width, + height, + state, + ) + }) } #[no_mangle] @@ -1355,16 +1369,18 @@ pub extern "C" fn canvas_native_webgl2_tex_storage3d( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_storage3d( - target, - levels, - internalformat, - width, - height, - depth, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_storage3d( + target, + levels, + internalformat, + width, + height, + depth, + state, + ) + }) } #[no_mangle] @@ -1383,21 +1399,13 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d_none( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d_none( - target, - level, - xoffset, - yoffset, - zoffset, - width, - height, - depth, - format, - type_, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d_none( + target, level, xoffset, yoffset, zoffset, width, height, depth, format, type_, offset, + state, + ) + }) } #[no_mangle] @@ -1418,21 +1426,14 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d( ) { assert!(!state.is_null()); let buf = unsafe { std::slice::from_raw_parts(buf, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d( - target, - level, - xoffset, - yoffset, - zoffset, - width, - height, - depth, - format, - type_, - buf, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let buf = buf.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d( + target, level, xoffset, yoffset, zoffset, width, height, depth, format, type_, &buf, + state, + ) + }) } #[no_mangle] @@ -1451,21 +1452,13 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d_asset( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d_asset( - target, - level, - xoffset, - yoffset, - zoffset, - width, - height, - depth, - format, - type_, - &asset.0, - state.get_inner_mut(), - ); + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d_asset( + target, level, xoffset, yoffset, zoffset, width, height, depth, format, type_, + &asset.0, state, + ) + }); } #[no_mangle] @@ -1484,7 +1477,7 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d_canvas2d( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; + let state = unsafe { &*state }; let canvas = unsafe { &mut *canvas }; let (width, height) = canvas.context.dimensions(); @@ -1493,8 +1486,6 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d_canvas2d( canvas.read_pixels_into(bytes.as_mut_slice(), (0, 0), (width as i32, height as i32)); - - // canvas.make_current(); // let (width, height) = (canvas.context.width(), canvas.context.height()); // let data = canvas.context.as_data(); @@ -1503,22 +1494,22 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d_canvas2d( // let buf = source_ctx.read_pixels_with_alpha_premultiply(&snapshot, format as i32, premultiply); - state.0.make_current(); - - canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d( - target, - level, - xoffset, - yoffset, - zoffset, - width as i32, - height as i32, - depth, - format, - type_, - bytes.as_slice(), - state.get_inner_mut(), - ); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d( + target, + level, + xoffset, + yoffset, + zoffset, + width as i32, + height as i32, + depth, + format, + type_, + bytes.as_slice(), + state, + ) + }); } #[no_mangle] @@ -1537,11 +1528,10 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d_webgl( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - let webgl = unsafe { &mut *webgl }; - - let (width, height, bytes) = canvas_webgl::webgl::canvas_native_webgl_read_webgl_pixels(&mut webgl.0, &mut state.0); + let state = unsafe { &*state }; + let webgl = unsafe { &*webgl }; + let (width, height, bytes) = webgl.read_drawing_buffer(); // canvas.make_current(); // let (width, height) = (canvas.context.width(), canvas.context.height()); @@ -1551,22 +1541,22 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d_webgl( // let buf = source_ctx.read_pixels_with_alpha_premultiply(&snapshot, format as i32, premultiply); - state.0.make_current(); - - canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d( - target, - level, - xoffset, - yoffset, - zoffset, - width, - height, - depth, - format, - type_, - bytes.as_slice(), - state.get_inner_mut(), - ); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d( + target, + level, + xoffset, + yoffset, + zoffset, + width, + height, + depth, + format, + type_, + bytes.as_slice(), + state, + ) + }); } #[no_mangle] @@ -1588,22 +1578,14 @@ pub extern "C" fn canvas_native_webgl2_tex_sub_image3d_offset( ) { assert!(!state.is_null()); let buf = unsafe { std::slice::from_raw_parts(buf, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d_offset( - target, - level, - xoffset, - yoffset, - zoffset, - width, - height, - depth, - format, - type_, - buf, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let buf = buf.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_sub_image3d_offset( + target, level, xoffset, yoffset, zoffset, width, height, depth, format, type_, &buf, + offset, state, + ) + }) } #[no_mangle] @@ -1615,20 +1597,24 @@ pub extern "C" fn canvas_native_webgl2_transform_feedback_varyings( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_transform_feedback_varyings_raw( - program, - varyings, - size, - buffer_mode, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_transform_feedback_varyings_raw( + program, + varyings, + size, + buffer_mode, + state, + ) + }) } #[no_mangle] pub extern "C" fn canvas_native_webgl2_uniform1ui(location: i32, v0: u32, state: *mut WebGLState) { - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform1ui(location, v0, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform1ui(location, v0, state) + }) } #[no_mangle] @@ -1640,8 +1626,11 @@ pub extern "C" fn canvas_native_webgl2_uniform1uiv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform1uiv(location, data, state.get_inner_mut()) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform1uiv(location, &data, state) + }) } #[no_mangle] @@ -1652,8 +1641,10 @@ pub extern "C" fn canvas_native_webgl2_uniform2ui( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform2ui(location, v0, v1, state.get_inner_mut()) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform2ui(location, v0, v1, state) + }) } #[no_mangle] @@ -1665,8 +1656,11 @@ pub extern "C" fn canvas_native_webgl2_uniform2uiv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform2uiv(location, data, state.get_inner_mut()) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform2uiv(location, &data, state) + }) } #[no_mangle] @@ -1678,14 +1672,10 @@ pub extern "C" fn canvas_native_webgl2_uniform3ui( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform3ui( - location, - v0, - v1, - v2, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform3ui(location, v0, v1, v2, state) + }) } #[no_mangle] @@ -1697,8 +1687,11 @@ pub extern "C" fn canvas_native_webgl2_uniform3uiv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform3uiv(location, data, state.get_inner_mut()) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform3uiv(location, &data, state) + }) } #[no_mangle] @@ -1711,15 +1704,10 @@ pub extern "C" fn canvas_native_webgl2_uniform4ui( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform4ui( - location, - v0, - v1, - v2, - v3, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform4ui(location, v0, v1, v2, v3, state) + }) } #[no_mangle] @@ -1731,8 +1719,11 @@ pub extern "C" fn canvas_native_webgl2_uniform4uiv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform4uiv(location, data, state.get_inner_mut()) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform4uiv(location, &data, state) + }) } #[no_mangle] @@ -1743,13 +1734,15 @@ pub extern "C" fn canvas_native_webgl2_uniform_block_binding( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform_block_binding( - program, - uniform_block_index, - uniform_block_binding, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform_block_binding( + program, + uniform_block_index, + uniform_block_binding, + state, + ) + }) } #[no_mangle] @@ -1762,13 +1755,13 @@ pub extern "C" fn canvas_native_webgl2_uniform_matrix2x3fv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix2x3fv( - location, - transpose, - data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix2x3fv( + location, transpose, &data, state, + ) + }) } #[no_mangle] @@ -1781,13 +1774,13 @@ pub extern "C" fn canvas_native_webgl2_uniform_matrix2x4fv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix2x4fv( - location, - transpose, - data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix2x4fv( + location, transpose, &data, state, + ) + }) } #[no_mangle] @@ -1800,13 +1793,13 @@ pub extern "C" fn canvas_native_webgl2_uniform_matrix3x2fv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix3x2fv( - location, - transpose, - data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix3x2fv( + location, transpose, &data, state, + ) + }) } #[no_mangle] @@ -1819,13 +1812,13 @@ pub extern "C" fn canvas_native_webgl2_uniform_matrix3x4fv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix3x4fv( - location, - transpose, - data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix3x4fv( + location, transpose, &data, state, + ) + }) } #[no_mangle] @@ -1838,13 +1831,13 @@ pub extern "C" fn canvas_native_webgl2_uniform_matrix4x2fv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix4x2fv( - location, - transpose, - data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix4x2fv( + location, transpose, &data, state, + ) + }) } #[no_mangle] @@ -1857,13 +1850,13 @@ pub extern "C" fn canvas_native_webgl2_uniform_matrix4x3fv( ) { assert!(!state.is_null()); let data = unsafe { std::slice::from_raw_parts(data, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix4x3fv( - location, - transpose, - data, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let data = data.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_uniform_matrix4x3fv( + location, transpose, &data, state, + ) + }) } #[no_mangle] @@ -1873,12 +1866,10 @@ pub extern "C" fn canvas_native_webgl2_vertex_attrib_divisor( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_divisor( - index, - divisor, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_divisor(index, divisor, state) + }) } #[no_mangle] @@ -1891,15 +1882,10 @@ pub extern "C" fn canvas_native_webgl2_vertex_attrib_i4i( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_i4i( - index, - x, - y, - z, - w, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_i4i(index, x, y, z, w, state) + }) } #[no_mangle] @@ -1911,12 +1897,11 @@ pub extern "C" fn canvas_native_webgl2_vertex_attrib_i4iv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_i4iv( - index, - value, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_i4iv(index, &value, state) + }) } #[no_mangle] @@ -1929,15 +1914,10 @@ pub extern "C" fn canvas_native_webgl2_vertex_attrib_i4ui( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_i4ui( - index, - x, - y, - z, - w, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_i4ui(index, x, y, z, w, state) + }) } #[no_mangle] @@ -1949,12 +1929,11 @@ pub extern "C" fn canvas_native_webgl2_vertex_attrib_i4uiv( ) { assert!(!state.is_null()); let value = unsafe { std::slice::from_raw_parts(value, size) }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_i4uiv( - index, - value, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + let value = value.to_vec(); + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_vertex_attrib_i4uiv(index, &value, state) + }) } #[no_mangle] @@ -1973,19 +1952,21 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_image_asset( assert!(!state.is_null()); assert!(!image_asset.is_null()); let image_asset = unsafe { &*image_asset }; - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_image2d_asset( - target, - level, - internalformat, - width, - height, - border, - format, - type_, - &image_asset.0, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image2d_asset( + target, + level, + internalformat, + width, + height, + border, + format, + type_, + &image_asset.0, + state, + ) + }) } #[no_mangle] @@ -2004,21 +1985,23 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_src_data_offset( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_image2d_src_data_offset( - target, - level, - internalformat, - width, - height, - border, - format, - type_, - src_data, - src_data_size, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.sync(|state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image2d_src_data_offset( + target, + level, + internalformat, + width, + height, + border, + format, + type_, + src_data, + src_data_size, + offset, + state, + ) + }) } @@ -2036,19 +2019,21 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_offset( state: *mut WebGLState, ) { assert!(!state.is_null()); - let state = unsafe { &mut *state }; - canvas_webgl::webgl2::canvas_native_webgl2_tex_image2d_offset( - target, - level, - internalformat, - width, - height, - border, - format, - type_, - offset, - state.get_inner_mut(), - ) + let state = unsafe { &*state }; + state.post(move |state| { + canvas_webgl::webgl2::canvas_native_webgl2_tex_image2d_offset( + target, + level, + internalformat, + width, + height, + border, + format, + type_, + offset, + state, + ) + }) } @@ -2068,25 +2053,24 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_webgl( assert!(!state.is_null()); assert!(!webgl.is_null()); - let state = unsafe { &mut *state }; - let webgl = unsafe { &mut *webgl }; - let mut pixels = canvas_webgl::webgl::canvas_native_webgl_read_webgl_pixels(&mut webgl.0, &mut state.0); + let state = unsafe { &*state }; + let webgl = unsafe { &*webgl }; + let mut pixels = webgl.read_drawing_buffer(); - - state.0.make_current(); - - canvas_webgl::webgl::canvas_native_webgl_tex_image2d( - target, - level, - internalformat, - width as i32, - height as i32, - border, - format, - type_, - pixels.2.as_mut_slice(), - state.get_inner_mut(), - ); + state.post(move |state| { + canvas_webgl::webgl::canvas_native_webgl_tex_image2d( + target, + level, + internalformat, + width as i32, + height as i32, + border, + format, + type_, + pixels.2.as_mut_slice(), + state, + ) + }); } @@ -2107,7 +2091,7 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_canvas2d( assert!(!canvas.is_null()); let canvas = unsafe { &mut *canvas }; - let state = unsafe { &mut *state }; + let state = unsafe { &*state }; // canvas.make_current(); @@ -2115,68 +2099,114 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_canvas2d( let mut bytes = vec![0u8; (source_width * source_height * 4.) as usize]; - canvas.read_pixels_into(bytes.as_mut_slice(), (0, 0), (source_width as i32, source_height as i32)); - - state.0.make_current(); + canvas.read_pixels_into( + bytes.as_mut_slice(), + (0, 0), + (source_width as i32, source_height as i32), + ); - if format as u32 == gl_bindings::RGBA { - unsafe { - if state.0.get_flip_y() { - let mut buffer = bytes.to_vec(); - utils::gl::flip_in_place( - buffer.as_mut_ptr(), - buffer.len(), - (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) as usize, - source_height as usize, - ); + state.sync(|state| { + state.make_current(); + + if format as u32 == gl_bindings::RGBA { + unsafe { + if state.get_flip_y() { + let mut buffer = bytes.to_vec(); + utils::gl::flip_in_place( + buffer.as_mut_ptr(), + buffer.len(), + (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) + as usize, + source_height as usize, + ); + + gl_bindings::TexImage2D( + target as u32, + level, + internalformat, + width as i32, + height as i32, + border, + format as u32, + type_ as u32, + buffer.as_ptr() as *const c_void, + ); + + return; + } gl_bindings::TexImage2D( target as u32, level, internalformat, - width as i32, - height as i32, + source_width as i32, + source_height as i32, border, format as u32, type_ as u32, - buffer.as_ptr() as *const c_void, + bytes.as_ptr() as *const c_void, ); - - return; } - - gl_bindings::TexImage2D( - target as u32, - level, - internalformat, - source_width as i32, - source_height as i32, - border, - format as u32, - type_ as u32, - bytes.as_ptr() as *const c_void, - ); + return; } - return; - } - let integers = match format as u32 { - gl_bindings::RGBA_INTEGER => Some(canvas_core::image_asset::ImageAsset::rgba_to_rgba_integer(bytes.as_slice(), source_width as usize, source_height as usize)), - gl_bindings::RGB_INTEGER => Some(canvas_core::image_asset::ImageAsset::rgba_to_rgb_integer(bytes.as_slice(), source_width as usize, source_height as usize)), - gl_bindings::RED_INTEGER => Some(canvas_core::image_asset::ImageAsset::rgba_to_red_integer(bytes.as_slice(), source_width as usize, source_height as usize)), - gl_bindings::RG_INTEGER => Some(canvas_core::image_asset::ImageAsset::rgba_to_rg_integer(bytes.as_slice(), source_width as usize, source_height as usize)), - _ => None, - }; - - if let Some(mut integers) = integers { - unsafe { - if state.0.get_flip_y() { - utils::gl::flip_in_place_integer( - integers.as_mut_ptr(), - integers.len(), - (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) as usize, + let integers = match format as u32 { + gl_bindings::RGBA_INTEGER => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_rgba_integer( + bytes.as_slice(), + source_width as usize, source_height as usize, - ); + )) + } + gl_bindings::RGB_INTEGER => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_rgb_integer( + bytes.as_slice(), + source_width as usize, + source_height as usize, + )) + } + gl_bindings::RED_INTEGER => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_red_integer( + bytes.as_slice(), + source_width as usize, + source_height as usize, + )) + } + gl_bindings::RG_INTEGER => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_rg_integer( + bytes.as_slice(), + source_width as usize, + source_height as usize, + )) + } + _ => None, + }; + + if let Some(mut integers) = integers { + unsafe { + if state.get_flip_y() { + utils::gl::flip_in_place_integer( + integers.as_mut_ptr(), + integers.len(), + (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) + as usize, + source_height as usize, + ); + + gl_bindings::TexImage2D( + target as u32, + level, + internalformat, + width as i32, + height as i32, + border, + format as u32, + type_ as u32, + integers.as_ptr() as *const c_void, + ); + + return; + } gl_bindings::TexImage2D( target as u32, @@ -2189,45 +2219,74 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_canvas2d( type_ as u32, integers.as_ptr() as *const c_void, ); - return; } - - gl_bindings::TexImage2D( - target as u32, - level, - internalformat, - width as i32, - height as i32, - border, - format as u32, - type_ as u32, - integers.as_ptr() as *const c_void, - ); - return; } - } - let bytes = match format as u32 { - gl_bindings::RGB => Some(canvas_core::image_asset::ImageAsset::rgba_to_rgb(bytes.as_slice(), source_width as usize, source_height as usize)), - gl_bindings::LUMINANCE => Some(canvas_core::image_asset::ImageAsset::rgba_to_luminance(bytes.as_slice(), source_width as usize, source_height as usize)), - gl_bindings::LUMINANCE_ALPHA => Some(canvas_core::image_asset::ImageAsset::rgba_to_luminance_alpha(bytes.as_slice(), source_width as usize, source_height as usize)), - gl_bindings::ALPHA => Some(canvas_core::image_asset::ImageAsset::rgba_to_alpha(bytes.as_slice(), source_width as usize, source_height as usize)), - gl_bindings::RED => Some(canvas_core::image_asset::ImageAsset::rgba_to_red(bytes.as_slice(), source_width as usize, source_height as usize)), - gl_bindings::RG => Some(canvas_core::image_asset::ImageAsset::rgba_to_rg(bytes.as_slice(), source_width as usize, source_height as usize)), - _ => None, - }; - - if let Some(bytes) = bytes { - unsafe { - if state.0.get_flip_y() { - let mut buffer = bytes; - utils::gl::flip_in_place( - buffer.as_mut_ptr(), - buffer.len(), - (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) as usize, + let bytes = match format as u32 { + gl_bindings::RGB => Some(canvas_core::image_asset::ImageAsset::rgba_to_rgb( + bytes.as_slice(), + source_width as usize, + source_height as usize, + )), + gl_bindings::LUMINANCE => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_luminance( + bytes.as_slice(), + source_width as usize, source_height as usize, - ); + )) + } + gl_bindings::LUMINANCE_ALPHA => Some( + canvas_core::image_asset::ImageAsset::rgba_to_luminance_alpha( + bytes.as_slice(), + source_width as usize, + source_height as usize, + ), + ), + gl_bindings::ALPHA => Some(canvas_core::image_asset::ImageAsset::rgba_to_alpha( + bytes.as_slice(), + source_width as usize, + source_height as usize, + )), + gl_bindings::RED => Some(canvas_core::image_asset::ImageAsset::rgba_to_red( + bytes.as_slice(), + source_width as usize, + source_height as usize, + )), + gl_bindings::RG => Some(canvas_core::image_asset::ImageAsset::rgba_to_rg( + bytes.as_slice(), + source_width as usize, + source_height as usize, + )), + _ => None, + }; + + if let Some(bytes) = bytes { + unsafe { + if state.get_flip_y() { + let mut buffer = bytes; + utils::gl::flip_in_place( + buffer.as_mut_ptr(), + buffer.len(), + (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) + as usize, + source_height as usize, + ); + + gl_bindings::TexImage2D( + target as u32, + level, + internalformat, + width as i32, + height as i32, + border, + format as u32, + type_ as u32, + buffer.as_ptr() as *const c_void, + ); + + return; + } gl_bindings::TexImage2D( target as u32, @@ -2238,26 +2297,12 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_canvas2d( border, format as u32, type_ as u32, - buffer.as_ptr() as *const c_void, + bytes.as_ptr() as *const c_void, ); - return; } - - gl_bindings::TexImage2D( - target as u32, - level, - internalformat, - width as i32, - height as i32, - border, - format as u32, - type_ as u32, - bytes.as_ptr() as *const c_void, - ); - return; } - } + }) } @@ -2278,72 +2323,114 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_image_data( assert!(!image_data.is_null()); let image_data = unsafe { &*image_data }; - let state = unsafe { &mut *state }; + let state = unsafe { &*state }; let (source_width, source_height) = image_data.0.dimensions(); - state.0.make_current(); - - let bytes = image_data.0.data(); - - if format as u32 == gl_bindings::RGBA { - unsafe { - if state.0.get_flip_y() { - let mut buffer = bytes.to_vec(); - utils::gl::flip_in_place( - buffer.as_mut_ptr(), - buffer.len(), - (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) as usize, - source_height as usize, - ); + state.sync(|state| { + state.make_current(); + + let bytes = image_data.0.data(); + + if format as u32 == gl_bindings::RGBA { + unsafe { + if state.get_flip_y() { + let mut buffer = bytes.to_vec(); + utils::gl::flip_in_place( + buffer.as_mut_ptr(), + buffer.len(), + (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) + as usize, + source_height as usize, + ); + + gl_bindings::TexImage2D( + target as u32, + level, + internalformat, + width as i32, + height as i32, + border, + format as u32, + type_ as u32, + buffer.as_ptr() as *const c_void, + ); + + return; + } gl_bindings::TexImage2D( target as u32, level, internalformat, - width as i32, - height as i32, + source_width, + source_height, border, format as u32, type_ as u32, - buffer.as_ptr() as *const c_void, + bytes.as_ptr() as *const c_void, ); - - return; } - - gl_bindings::TexImage2D( - target as u32, - level, - internalformat, - source_width, - source_height, - border, - format as u32, - type_ as u32, - bytes.as_ptr() as *const c_void, - ); + return; } - return; - } - let integers = match format as u32 { - gl_bindings::RGBA_INTEGER => Some(canvas_core::image_asset::ImageAsset::rgba_to_rgba_integer(bytes, source_width as usize, source_height as usize)), - gl_bindings::RGB_INTEGER => Some(canvas_core::image_asset::ImageAsset::rgba_to_rgb_integer(bytes, source_width as usize, source_height as usize)), - gl_bindings::RED_INTEGER => Some(canvas_core::image_asset::ImageAsset::rgba_to_red_integer(bytes, source_width as usize, source_height as usize)), - gl_bindings::RG_INTEGER => Some(canvas_core::image_asset::ImageAsset::rgba_to_rg_integer(bytes, source_width as usize, source_height as usize)), - _ => None, - }; - - if let Some(mut integers) = integers { - unsafe { - if state.0.get_flip_y() { - utils::gl::flip_in_place_integer( - integers.as_mut_ptr(), - integers.len(), - (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) as usize, + let integers = match format as u32 { + gl_bindings::RGBA_INTEGER => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_rgba_integer( + bytes, + source_width as usize, source_height as usize, - ); + )) + } + gl_bindings::RGB_INTEGER => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_rgb_integer( + bytes, + source_width as usize, + source_height as usize, + )) + } + gl_bindings::RED_INTEGER => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_red_integer( + bytes, + source_width as usize, + source_height as usize, + )) + } + gl_bindings::RG_INTEGER => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_rg_integer( + bytes, + source_width as usize, + source_height as usize, + )) + } + _ => None, + }; + + if let Some(mut integers) = integers { + unsafe { + if state.get_flip_y() { + utils::gl::flip_in_place_integer( + integers.as_mut_ptr(), + integers.len(), + (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) + as usize, + source_height as usize, + ); + + gl_bindings::TexImage2D( + target as u32, + level, + internalformat, + width as i32, + height as i32, + border, + format as u32, + type_ as u32, + integers.as_ptr() as *const c_void, + ); + + return; + } gl_bindings::TexImage2D( target as u32, @@ -2356,45 +2443,74 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_image_data( type_ as u32, integers.as_ptr() as *const c_void, ); - return; } - - gl_bindings::TexImage2D( - target as u32, - level, - internalformat, - width as i32, - height as i32, - border, - format as u32, - type_ as u32, - integers.as_ptr() as *const c_void, - ); - return; } - } - let bytes = match format as u32 { - gl_bindings::RGB => Some(canvas_core::image_asset::ImageAsset::rgba_to_rgb(bytes, source_width as usize, source_height as usize)), - gl_bindings::LUMINANCE => Some(canvas_core::image_asset::ImageAsset::rgba_to_luminance(bytes, source_width as usize, source_height as usize)), - gl_bindings::LUMINANCE_ALPHA => Some(canvas_core::image_asset::ImageAsset::rgba_to_luminance_alpha(bytes, source_width as usize, source_height as usize)), - gl_bindings::ALPHA => Some(canvas_core::image_asset::ImageAsset::rgba_to_alpha(bytes, source_width as usize, source_height as usize)), - gl_bindings::RED => Some(canvas_core::image_asset::ImageAsset::rgba_to_red(bytes, source_width as usize, source_height as usize)), - gl_bindings::RG => Some(canvas_core::image_asset::ImageAsset::rgba_to_rg(bytes, source_width as usize, source_height as usize)), - _ => None, - }; - - if let Some(bytes) = bytes { - unsafe { - if state.0.get_flip_y() { - let mut buffer = bytes; - utils::gl::flip_in_place( - buffer.as_mut_ptr(), - buffer.len(), - (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) as usize, + let bytes = match format as u32 { + gl_bindings::RGB => Some(canvas_core::image_asset::ImageAsset::rgba_to_rgb( + bytes, + source_width as usize, + source_height as usize, + )), + gl_bindings::LUMINANCE => { + Some(canvas_core::image_asset::ImageAsset::rgba_to_luminance( + bytes, + source_width as usize, source_height as usize, - ); + )) + } + gl_bindings::LUMINANCE_ALPHA => Some( + canvas_core::image_asset::ImageAsset::rgba_to_luminance_alpha( + bytes, + source_width as usize, + source_height as usize, + ), + ), + gl_bindings::ALPHA => Some(canvas_core::image_asset::ImageAsset::rgba_to_alpha( + bytes, + source_width as usize, + source_height as usize, + )), + gl_bindings::RED => Some(canvas_core::image_asset::ImageAsset::rgba_to_red( + bytes, + source_width as usize, + source_height as usize, + )), + gl_bindings::RG => Some(canvas_core::image_asset::ImageAsset::rgba_to_rg( + bytes, + source_width as usize, + source_height as usize, + )), + _ => None, + }; + + if let Some(bytes) = bytes { + unsafe { + if state.get_flip_y() { + let mut buffer = bytes; + utils::gl::flip_in_place( + buffer.as_mut_ptr(), + buffer.len(), + (bytes_per_pixel(type_ as u32, format as u32) * source_width as u32) + as usize, + source_height as usize, + ); + + gl_bindings::TexImage2D( + target as u32, + level, + internalformat, + width as i32, + height as i32, + border, + format as u32, + type_ as u32, + buffer.as_ptr() as *const c_void, + ); + + return; + } gl_bindings::TexImage2D( target as u32, @@ -2405,24 +2521,10 @@ pub extern "C" fn canvas_native_webgl2_tex_image2d_image_data( border, format as u32, type_ as u32, - buffer.as_ptr() as *const c_void, + bytes.as_ptr() as *const c_void, ); - return; } - - gl_bindings::TexImage2D( - target as u32, - level, - internalformat, - width as i32, - height as i32, - border, - format as u32, - type_ as u32, - bytes.as_ptr() as *const c_void, - ); - return; } - } + }) } diff --git a/crates/canvas-c/src/webgl/mod.rs b/crates/canvas-c/src/webgl/mod.rs index a9b2ea06a..d2d04cff3 100644 --- a/crates/canvas-c/src/webgl/mod.rs +++ b/crates/canvas-c/src/webgl/mod.rs @@ -4,6 +4,8 @@ mod gl2; pub use gl2::*; mod result; pub use result::*; +pub(crate) mod thread; +pub use thread::canvas_native_webgl_contexts_behind; #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] diff --git a/crates/canvas-c/src/webgl/thread.rs b/crates/canvas-c/src/webgl/thread.rs new file mode 100644 index 000000000..287e1a54a --- /dev/null +++ b/crates/canvas-c/src/webgl/thread.rs @@ -0,0 +1,168 @@ +//! Runs every threaded WebGL context. Its GL context is created, used, presented and dropped on +//! this one thread, so the JS thread never waits on the driver: calls that return nothing queue up +//! behind each other, and the rest run here while the caller waits. One thread for all of them keeps +//! reads from one context into another (texImage2D from a WebGL canvas) on a single thread. + +use std::cell::Cell; +use std::collections::VecDeque; +use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::{mpsc, Arc, Condvar, Mutex, OnceLock}; + +type Job = Box; + +struct Worker { + queue: Mutex>, + signal: Condvar, +} + +impl Worker { + fn push(&self, job: Job) { + if let Ok(mut queue) = self.queue.lock() { + queue.push_back(job); + } + self.signal.notify_one(); + } +} + +static WORKER: OnceLock>> = OnceLock::new(); + +/// Threaded contexts with a frame queued behind another unpresented one: the GPU is behind them. +static CONTEXTS_BEHIND: AtomicU32 = AtomicU32::new(0); + +thread_local! { + static ON_GL_THREAD: Cell = const { Cell::new(false) }; +} + +fn worker() -> Option> { + WORKER + .get_or_init(|| { + let worker = Arc::new(Worker { + queue: Mutex::new(VecDeque::new()), + signal: Condvar::new(), + }); + let shared = Arc::clone(&worker); + std::thread::Builder::new() + .name("nsc-webgl".to_owned()) + .spawn(move || run(shared)) + .ok() + .map(|_| worker) + }) + .clone() +} + +fn raise_priority() { + #[cfg(target_os = "android")] + unsafe { + // THREAD_PRIORITY_DISPLAY, for this thread only. + libc::setpriority(libc::PRIO_PROCESS, 0, -4); + } + #[cfg(any( + target_os = "ios", + target_os = "tvos", + target_os = "visionos", + target_os = "macos" + ))] + unsafe { + libc::pthread_set_qos_class_self_np(libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, 0); + } +} + +fn run(worker: Arc) { + raise_priority(); + ON_GL_THREAD.with(|on| on.set(true)); + loop { + let jobs: Vec = { + let Ok(mut queue) = worker.queue.lock() else { + return; + }; + while queue.is_empty() { + queue = match worker.signal.wait(queue) { + Ok(queue) => queue, + Err(_) => return, + }; + } + queue.drain(..).collect() + }; + for job in jobs { + job(); + } + } +} + +/// Whether contexts can be threaded at all: false if the thread could not be started. +pub(crate) fn available() -> bool { + worker().is_some() +} + +pub(crate) fn on_gl_thread() -> bool { + ON_GL_THREAD.with(|on| on.get()) +} + +/// Queues `job` behind every earlier one. On the GL thread itself (a job calling back into the +/// FFI) it runs at once: it belongs to the job that is running. +pub(crate) fn post(job: impl FnOnce() + Send + 'static) { + if on_gl_thread() { + job(); + return; + } + match worker() { + Some(worker) => worker.push(Box::new(job)), + None => job(), + } +} + +struct AssertSend(T); + +// Only built by `sync`, which blocks until the value has crossed back. +unsafe impl Send for AssertSend {} + +/// Runs `f` after every queued job and returns its result, blocking the caller until then. Because +/// the caller waits, `f` may borrow from its stack and needn't be `Send`: nothing else touches what +/// it captures while it runs. +pub(crate) fn sync(f: impl FnOnce() -> R) -> R { + if on_gl_thread() { + return f(); + } + let Some(worker) = worker() else { + return f(); + }; + let (tx, rx) = mpsc::sync_channel::>(1); + let f = AssertSend(f); + let job: Box = Box::new(move || { + let f = f; + let _ = tx.send(AssertSend((f.0)())); + }); + // SAFETY: `recv` below doesn't return until the job has run and dropped its captures, so the + // borrows it holds outlive it, and AssertSend covers the thread hop. + let job: Job = unsafe { std::mem::transmute::, Job>(job) }; + worker.push(job); + match rx.recv() { + Ok(value) => value.0, + // The job was dropped without running: only if it panicked. + Err(_) => panic!("webgl thread job failed"), + } +} + +/// Frames a context may have queued before it counts as behind: one being presented, one next. +pub(crate) const BEHIND_AT: u32 = 2; + +/// A context's queued presents went from `before` to `before + 1`. +pub(crate) fn frame_queued(before: u32) { + if before + 1 == BEHIND_AT { + CONTEXTS_BEHIND.fetch_add(1, Ordering::AcqRel); + } +} + +/// A context's queued presents went from `before` to `before - 1`. +pub(crate) fn frame_presented(before: u32) { + if before == BEHIND_AT { + CONTEXTS_BEHIND.fetch_sub(1, Ordering::AcqRel); + } +} + +/// How many threaded contexts are behind (`BEHIND_AT`). The JS side holds requestAnimationFrame +/// back while any are, as a browser does when its compositor falls behind, rather than queue more. +#[no_mangle] +pub extern "C" fn canvas_native_webgl_contexts_behind() -> u32 { + CONTEXTS_BEHIND.load(Ordering::Acquire) +} diff --git a/crates/canvas-c/src/webgpu/gpu_queue.rs b/crates/canvas-c/src/webgpu/gpu_queue.rs index bc5fe1d6b..51a1ec4f4 100644 --- a/crates/canvas-c/src/webgpu/gpu_queue.rs +++ b/crates/canvas-c/src/webgpu/gpu_queue.rs @@ -283,9 +283,7 @@ pub unsafe extern "C" fn canvas_native_webgpu_queue_copy_webgl_to_texture( return; } // An image: RGBA8, top row first (flip_y then flips it, as for the other sources). - let (width, height, mut bytes) = canvas_webgl::webgl::canvas_native_webgl_read_drawing_buffer( - &mut (*(source.source as *mut crate::webgl::WebGLState)).0, - ); + let (width, height, mut bytes) = (*source.source).read_drawing_buffer(); { let destination = &*destination; diff --git a/packages/canvas/Canvas/index.android.ts b/packages/canvas/Canvas/index.android.ts index fc2869e4f..36240fa57 100644 --- a/packages/canvas/Canvas/index.android.ts +++ b/packages/canvas/Canvas/index.android.ts @@ -100,6 +100,12 @@ export class Canvas extends CanvasBase { /** Default for `getContext('2d', { threaded })`: rasterize on a shared render thread. */ static threaded2D = true; + /** + * Default for `getContext('webgl' | 'webgl2', { threaded })`: run the context on the WebGL + * thread, so GL work and presenting never block the UI thread. + */ + static threadedWebGL = true; + constructor(nativeInstance?) { super(); if (nativeInstance) { @@ -515,6 +521,14 @@ export class Canvas extends CanvasBase { return context; } + private __setThreadedWebGL(contextAttributes?: any) { + const threaded = !!(contextAttributes?.threaded ?? Canvas.threadedWebGL); + this._canvas.setThreadedWebGL?.(threaded); + if (threaded) { + holdBackAnimationFramesWhileGLBehind(); + } + } + getContext(type: string, contextAttributes?: any): CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext | GPUCanvasContext | null { if (!this._canvas) { return null; @@ -553,6 +567,7 @@ export class Canvas extends CanvasBase { } if (!this._webglContext) { const opts = { version: 1, ...defaultOpts, ...handleContextOptions(type, contextAttributes) }; + this.__setThreadedWebGL(contextAttributes); this._canvas.initContext(type, opts.alpha, false, opts.depth, opts.failIfMajorPerformanceCaveat, opts.powerPreference, opts.premultipliedAlpha, opts.preserveDrawingBuffer, opts.stencil, opts.desynchronized, opts.xrCompatible); this._webglContext = new (WebGLRenderingContext as any)(this._canvas, opts); (this._webglContext as any)._canvas = this; @@ -568,6 +583,7 @@ export class Canvas extends CanvasBase { } if (!this._webgl2Context) { const opts = { version: 2, ...defaultOpts, ...handleContextOptions(type, contextAttributes) }; + this.__setThreadedWebGL(contextAttributes); this._canvas.initContext(type, opts.alpha, false, opts.depth, opts.failIfMajorPerformanceCaveat, opts.powerPreference, opts.premultipliedAlpha, opts.preserveDrawingBuffer, opts.stencil, opts.desynchronized, opts.xrCompatible); this._webgl2Context = new (WebGL2RenderingContext as any)(this._canvas, opts); (this._webgl2Context as any)._canvas = this; @@ -631,3 +647,56 @@ export class Canvas extends CanvasBase { releasePointerCapture() {} } + +/** + * Holds requestAnimationFrame callbacks back a frame while the GPU is behind a threaded WebGL + * context (it has a frame queued behind one not yet presented), as a browser does when its + * compositor falls behind. The UI thread then stays free between the frames the GPU can take, + * instead of queueing more work for it every vsync. + * + * Installed with the first threaded context, over whatever requestAnimationFrame is global by then + * (core's, or a polyfill's). + */ +function holdBackAnimationFramesWhileGLBehind() { + const target = global as any; + const request = target.requestAnimationFrame; + const cancel = target.cancelAnimationFrame; + if (typeof request !== 'function' || request.__holdsBackForGL) { + return; + } + const utils = org.nativescript.canvas.Utils; + // An older native library: leave requestAnimationFrame alone rather than break every frame. + if (typeof utils?.webGLContextsBehind !== 'function') { + return; + } + // Our ids stay put while a held-back callback is re-requested under a new one. + const pending = new Map(); + let nextId = 1; + const requestAnimationFrame = (callback: (time: number) => void) => { + const id = nextId++; + const run = (time: number) => { + if (utils.webGLContextsBehind() > 0) { + pending.set(id, request(run)); + return; + } + pending.delete(id); + callback(time); + }; + pending.set(id, request(run)); + return id; + }; + requestAnimationFrame.__holdsBackForGL = true; + const cancelAnimationFrame = (id: number) => { + const current = pending.get(id); + if (current !== undefined) { + pending.delete(id); + cancel?.(current); + } + }; + target.requestAnimationFrame = requestAnimationFrame; + target.cancelAnimationFrame = cancelAnimationFrame; + if (target.window && target.window !== target && target.window.requestAnimationFrame === request) { + target.window.requestAnimationFrame = requestAnimationFrame; + target.window.cancelAnimationFrame = cancelAnimationFrame; + } +} diff --git a/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/NSCCanvas.kt b/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/NSCCanvas.kt index 3edb136a7..a2191eb23 100644 --- a/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/NSCCanvas.kt +++ b/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/NSCCanvas.kt @@ -589,7 +589,8 @@ class NSCCanvas : FrameLayout { stencil, desynchronized, xrCompatible, - version + version, + threadedWebGL ) engine = Engine.GL } @@ -649,7 +650,8 @@ class NSCCanvas : FrameLayout { stencil, desynchronized, xrCompatible, - version + version, + threadedWebGL ) engine = Engine.GL } @@ -697,7 +699,8 @@ class NSCCanvas : FrameLayout { stencil, desynchronized, xrCompatible, - version + version, + threadedWebGL ) engine = Engine.GL } @@ -710,6 +713,9 @@ class NSCCanvas : FrameLayout { /** Read when the 2D context is created. */ var threaded2D = false + /** Read when a WebGL context is created: it then lives on, and is drawn from, the WebGL thread. */ + var threadedWebGL = false + private fun create2DNative( width: Int, height: Int, @@ -1253,8 +1259,8 @@ class NSCCanvas : FrameLayout { @FastNative external fun nativeDetach2DSurface(context: Long) + // Not @FastNative: a threaded context waits for the WebGL thread to build it. @JvmStatic - @FastNative external fun nativeInitWebGL( surface: Surface?, alpha: Boolean, @@ -1267,11 +1273,11 @@ class NSCCanvas : FrameLayout { stencil: Boolean, desynchronized: Boolean, xrCompatible: Boolean, - version: Int + version: Int, + threaded: Boolean ): Long @JvmStatic - @FastNative external fun nativeInitWebGLNoSurface( width: Int, height: Int, @@ -1285,7 +1291,8 @@ class NSCCanvas : FrameLayout { stencil: Boolean, desynchronized: Boolean, xrCompatible: Boolean, - version: Int + version: Int, + threaded: Boolean ): Long @JvmStatic diff --git a/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt b/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt index 2e02808f9..0cae3ed47 100644 --- a/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt +++ b/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt @@ -40,6 +40,28 @@ object Utils { @JvmStatic private external fun nativeMakeStateContextCurrent(state: Long): Boolean + @JvmStatic + private external fun nativeRunWithWebGL(state: Long, block: Runnable) + + @JvmStatic + private external fun nativeWebGLContextsBehind(): Int + + /** Threaded WebGL contexts the GPU has fallen behind (a frame queued behind an unpresented one). */ + @JvmStatic + fun webGLContextsBehind(): Int = nativeWebGLContextsBehind() + + /** + * Runs [block] with the WebGL context [state] current, on the thread that owns it, and returns + * its result: a threaded context can only be current on the WebGL thread, so GLES and + * SurfaceTexture calls against it have to run there. + */ + private fun withWebGL(state: Long, block: () -> T): T { + var result: Any? = null + nativeRunWithWebGL(state) { result = block() } + @Suppress("UNCHECKED_CAST") + return result as T + } + @JvmStatic private external fun nativeMake2DContextCurrent(context: Long): Boolean @@ -186,11 +208,10 @@ object Utils { @JvmStatic - fun createSurfaceTexture(state: Long): Array { - nativeMakeStateContextCurrent(state) + fun createSurfaceTexture(state: Long): Array = withWebGL(state) { val render = TextureRender() render.surfaceCreated() - return arrayOf(SurfaceTexture(render.textureId), render) + arrayOf(SurfaceTexture(render.textureId), render) } @@ -258,12 +279,11 @@ object Utils { fun createRenderAndAttachToGLContext( state: Long, texture: SurfaceTexture, - ): TextureRender { - nativeMakeStateContextCurrent(state) + ): TextureRender = withWebGL(state) { val render = TextureRender() render.surfaceCreated() texture.attachToGLContext(render.textureId) - return render + render } @@ -272,14 +292,12 @@ object Utils { state: Long, texture: SurfaceTexture, render: TextureRender - ) { - nativeMakeStateContextCurrent(state) + ) = withWebGL(state) { texture.attachToGLContext(render.textureId) } @JvmStatic - fun detachFromGLContext(state: Long, texture: SurfaceTexture) { - nativeMakeStateContextCurrent(state) + fun detachFromGLContext(state: Long, texture: SurfaceTexture) = withWebGL(state) { texture.detachFromGLContext() } @@ -293,8 +311,7 @@ object Utils { height: Int, internalFormat: Int, format: Int - ) { - nativeMakeStateContextCurrent(state) + ) = withWebGL(state) { render.drawFrame(texture, width, height, internalFormat, format, flipY) if (render.width != width || render.height != width) { @@ -319,8 +336,7 @@ object Utils { depth: Int, border: Int, zoffset: Int, - ) { - nativeMakeStateContextCurrent(state) + ) = withWebGL(state) { render.drawFrameTexImage3D( texture, target, level, internalformat, width, height, depth, border, zoffset, flipY @@ -340,8 +356,7 @@ object Utils { zoffset: Int, width: Int, height: Int, - ) { - nativeMakeStateContextCurrent(state) + ) = withWebGL(state) { render.drawFrameTexSubImage3D( texture, target, level, xoffset, yoffset, zoffset, width, height, flipY ) diff --git a/packages/canvas/typings/android.d.ts b/packages/canvas/typings/android.d.ts index 9817cc26f..113bc976d 100644 --- a/packages/canvas/typings/android.d.ts +++ b/packages/canvas/typings/android.d.ts @@ -207,6 +207,8 @@ declare module org { public static TAG: string = 'CanvasView'; public getThreaded2D(): boolean; public setThreaded2D(value: boolean): void; + public getThreadedWebGL(): boolean; + public setThreadedWebGL(value: boolean): void; public create2DContext(alpha: boolean, antialias: boolean, depth: boolean, failIfMajorPerformanceCaveat: boolean, powerPreference: number, premultipliedAlpha: boolean, preserveDrawingBuffer: boolean, stencil: boolean, desynchronized: boolean, xrCompatible: boolean, willReadFrequently: boolean, colorSpace: number): number; public makeContextCurrent(): void; public setSurfaceView(value: org.nativescript.canvas.GLViewSV): void; @@ -890,6 +892,7 @@ declare module org { public getByteBufferFromBitmap(bitmap: globalAndroid.graphics.Bitmap): java.nio.ByteBuffer; public isEmulator(): boolean; public static createSurfaceTexture(render: number): androidNative.Array; + public static webGLContextsBehind(): number; public getBytesFromBitmap(bitmap: globalAndroid.graphics.Bitmap): androidNative.Array; public static updateTexImage(state: number, flipY: boolean, texture: globalAndroid.graphics.SurfaceTexture, render: org.nativescript.canvas.TextureRender, width: number, height: number, internalFormat: number, format: number): void; public static createRenderAndAttachToGLContext(render: number, state: globalAndroid.graphics.SurfaceTexture): org.nativescript.canvas.TextureRender; From a35a84ff2759cb5b9f92aad796e29d484d3bb359 Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 03:54:29 -0400 Subject: [PATCH 5/9] fix(2d): stop a lagging render thread merging frames without limit A threaded 2D canvas's commits merge into the frame the render thread hasn't picked up yet, and a frame only replaces the pending one if it clears the whole canvas. A canvas that fades rather than clears (trails) therefore grew its pending frame every vsync once the render thread fell behind; each replay got slower, so it fell further behind, until the GPU memory it used got the app killed. A canvas with a frame merged is now behind, which holds requestAnimationFrame back (the counter WebGL uses, now canvas_native_canvases_behind), and commit waits for the render thread once four frames have merged. --- crates/canvas-android/src/utils/gl/mod.rs | 6 ++--- crates/canvas-c/src/c2d/render_thread.rs | 27 ++++++++++++++++++- crates/canvas-c/src/webgl/mod.rs | 2 +- crates/canvas-c/src/webgl/thread.rs | 25 +++++++++++------ packages/canvas/Canvas/index.android.ts | 23 +++++++++------- .../java/org/nativescript/canvas/Utils.kt | 6 ++--- packages/canvas/typings/android.d.ts | 2 +- 7 files changed, 64 insertions(+), 27 deletions(-) diff --git a/crates/canvas-android/src/utils/gl/mod.rs b/crates/canvas-android/src/utils/gl/mod.rs index e8bdb32bf..d479fcbfe 100644 --- a/crates/canvas-android/src/utils/gl/mod.rs +++ b/crates/canvas-android/src/utils/gl/mod.rs @@ -34,13 +34,13 @@ pub unsafe extern "system" fn Java_org_nativescript_canvas_Utils_nativeMakeState JNI_FALSE } -/// `canvas_native_webgl_contexts_behind`, for the requestAnimationFrame hold-back. +/// `canvas_native_canvases_behind`, for the requestAnimationFrame hold-back. #[no_mangle] -pub extern "system" fn Java_org_nativescript_canvas_Utils_nativeWebGLContextsBehind( +pub extern "system" fn Java_org_nativescript_canvas_Utils_nativeCanvasesBehind( _: JNIEnv, _: jni::objects::JClass, ) -> jint { - canvas_c::canvas_native_webgl_contexts_behind() as jint + canvas_c::canvas_native_canvases_behind() as jint } /// Runs `block` (a `Runnable`) with `state` current, on the thread that owns it, and waits for it: diff --git a/crates/canvas-c/src/c2d/render_thread.rs b/crates/canvas-c/src/c2d/render_thread.rs index 15c57f73a..0f2eea613 100644 --- a/crates/canvas-c/src/c2d/render_thread.rs +++ b/crates/canvas-c/src/c2d/render_thread.rs @@ -2,7 +2,7 @@ //! make the JS thread wait on it. use std::collections::{HashMap, VecDeque}; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{mpsc, Arc, Condvar, Mutex, OnceLock}; use std::time::Duration; @@ -179,12 +179,20 @@ where rx.recv().ok() } +/// Frames that may merge into one this thread hasn't picked up before `commit` waits for it. +/// Merging keeps committing from blocking, but a frame that doesn't clear the whole canvas keeps +/// everything drawn before it, so an unbounded merge grows without limit and replays ever slower. +const MAX_MERGED_FRAMES: u32 = 4; + /// Drop waits for the real context to go, so its window can be released afterwards. pub struct RenderTarget { id: u64, worker: Arc, open: Mutex>, lost: Arc, + /// Frames merged into the open slot since this thread last took it. Non-zero means this canvas + /// is behind, which holds requestAnimationFrame back (`canvas_native_canvases_behind`). + merged: Arc, } impl RenderTarget { @@ -213,6 +221,7 @@ impl RenderTarget { worker, open: Mutex::new(None), lost, + merged: Arc::new(AtomicU32::new(0)), }) } @@ -225,6 +234,11 @@ impl RenderTarget { if frame.is_empty() { return; } + if self.merged.load(Ordering::Acquire) >= MAX_MERGED_FRAMES { + // Something is drawing faster than this thread can keep up, outside the + // requestAnimationFrame hold-back: wait for the pending frame to be drawn. + self.sync(|_| ()); + } let Ok(mut open) = self.open.lock() else { return; }; @@ -233,6 +247,9 @@ impl RenderTarget { if let Ok(mut pending) = slot.lock() { if let Some(queued) = pending.as_mut() { queued.append(frame.take().expect("set above")); + if self.merged.fetch_add(1, Ordering::AcqRel) == 0 { + crate::webgl::thread::canvas_behind(); + } return; } } @@ -240,8 +257,13 @@ impl RenderTarget { let slot: Slot = Arc::new(Mutex::new(frame)); *open = Some(Arc::clone(&slot)); let id = self.id; + let merged = Arc::clone(&self.merged); self.worker.push(Box::new(move |targets: &mut Targets| { let frame = slot.lock().ok().and_then(|mut pending| pending.take()); + // Taken under the slot's lock above, so a commit merging now lands in a new slot. + if merged.swap(0, Ordering::AcqRel) > 0 { + crate::webgl::thread::canvas_caught_up(); + } if let (Some(frame), Some(entry)) = (frame, targets.contexts.get_mut(&id)) { entry.context.get_context_mut().replay(frame); targets.presents.push(id); @@ -292,6 +314,9 @@ impl RenderTarget { impl Drop for RenderTarget { fn drop(&mut self) { + if self.merged.swap(0, Ordering::AcqRel) > 0 { + crate::webgl::thread::canvas_caught_up(); + } let (tx, rx) = mpsc::sync_channel::<()>(1); let id = self.id; self.worker.push(Box::new(move |targets: &mut Targets| { diff --git a/crates/canvas-c/src/webgl/mod.rs b/crates/canvas-c/src/webgl/mod.rs index d2d04cff3..7641f3ff7 100644 --- a/crates/canvas-c/src/webgl/mod.rs +++ b/crates/canvas-c/src/webgl/mod.rs @@ -5,7 +5,7 @@ pub use gl2::*; mod result; pub use result::*; pub(crate) mod thread; -pub use thread::canvas_native_webgl_contexts_behind; +pub use thread::canvas_native_canvases_behind; #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] diff --git a/crates/canvas-c/src/webgl/thread.rs b/crates/canvas-c/src/webgl/thread.rs index 287e1a54a..6ac806dd9 100644 --- a/crates/canvas-c/src/webgl/thread.rs +++ b/crates/canvas-c/src/webgl/thread.rs @@ -26,8 +26,9 @@ impl Worker { static WORKER: OnceLock>> = OnceLock::new(); -/// Threaded contexts with a frame queued behind another unpresented one: the GPU is behind them. -static CONTEXTS_BEHIND: AtomicU32 = AtomicU32::new(0); +/// Threaded canvases (WebGL here, 2D on its render thread) with a frame waiting behind another +/// that hasn't reached the screen yet: the GPU is behind them. +static CANVASES_BEHIND: AtomicU32 = AtomicU32::new(0); thread_local! { static ON_GL_THREAD: Cell = const { Cell::new(false) }; @@ -149,20 +150,28 @@ pub(crate) const BEHIND_AT: u32 = 2; /// A context's queued presents went from `before` to `before + 1`. pub(crate) fn frame_queued(before: u32) { if before + 1 == BEHIND_AT { - CONTEXTS_BEHIND.fetch_add(1, Ordering::AcqRel); + canvas_behind(); } } /// A context's queued presents went from `before` to `before - 1`. pub(crate) fn frame_presented(before: u32) { if before == BEHIND_AT { - CONTEXTS_BEHIND.fetch_sub(1, Ordering::AcqRel); + canvas_caught_up(); } } -/// How many threaded contexts are behind (`BEHIND_AT`). The JS side holds requestAnimationFrame -/// back while any are, as a browser does when its compositor falls behind, rather than queue more. +pub(crate) fn canvas_behind() { + CANVASES_BEHIND.fetch_add(1, Ordering::AcqRel); +} + +pub(crate) fn canvas_caught_up() { + CANVASES_BEHIND.fetch_sub(1, Ordering::AcqRel); +} + +/// How many threaded canvases are behind. The JS side holds requestAnimationFrame back while any +/// are, as a browser does when its compositor falls behind, rather than queue more work. #[no_mangle] -pub extern "C" fn canvas_native_webgl_contexts_behind() -> u32 { - CONTEXTS_BEHIND.load(Ordering::Acquire) +pub extern "C" fn canvas_native_canvases_behind() -> u32 { + CANVASES_BEHIND.load(Ordering::Acquire) } diff --git a/packages/canvas/Canvas/index.android.ts b/packages/canvas/Canvas/index.android.ts index 36240fa57..5fbedb1ae 100644 --- a/packages/canvas/Canvas/index.android.ts +++ b/packages/canvas/Canvas/index.android.ts @@ -509,6 +509,9 @@ export class Canvas extends CanvasBase { const threaded = !!(contextAttributes?.threaded ?? Canvas.threaded2D); this._canvas.setThreaded2D?.(threaded); + if (threaded) { + holdBackAnimationFramesWhileBehind(); + } const ctx = this._canvas.create2DContext(opts.alpha, opts.antialias, opts.depth, opts.failIfMajorPerformanceCaveat, opts.powerPreference, opts.premultipliedAlpha, opts.preserveDrawingBuffer, opts.stencil, opts.desynchronized, opts.xrCompatible, opts.willReadFrequently ?? false, opts.colorSpace ?? 0); const context = new (CanvasRenderingContext2D as any)(ctx, opts); // @ts-ignore @@ -525,7 +528,7 @@ export class Canvas extends CanvasBase { const threaded = !!(contextAttributes?.threaded ?? Canvas.threadedWebGL); this._canvas.setThreadedWebGL?.(threaded); if (threaded) { - holdBackAnimationFramesWhileGLBehind(); + holdBackAnimationFramesWhileBehind(); } } @@ -649,24 +652,24 @@ export class Canvas extends CanvasBase { } /** - * Holds requestAnimationFrame callbacks back a frame while the GPU is behind a threaded WebGL - * context (it has a frame queued behind one not yet presented), as a browser does when its - * compositor falls behind. The UI thread then stays free between the frames the GPU can take, - * instead of queueing more work for it every vsync. + * Holds requestAnimationFrame callbacks back a frame while a threaded canvas is behind (it has a + * frame waiting behind one that hasn't reached the screen), as a browser does when its compositor + * falls behind. The UI thread then stays free between the frames the GPU can take, instead of + * queueing more work for it every vsync. * * Installed with the first threaded context, over whatever requestAnimationFrame is global by then * (core's, or a polyfill's). */ -function holdBackAnimationFramesWhileGLBehind() { +function holdBackAnimationFramesWhileBehind() { const target = global as any; const request = target.requestAnimationFrame; const cancel = target.cancelAnimationFrame; - if (typeof request !== 'function' || request.__holdsBackForGL) { + if (typeof request !== 'function' || request.__holdsBack) { return; } const utils = org.nativescript.canvas.Utils; // An older native library: leave requestAnimationFrame alone rather than break every frame. - if (typeof utils?.webGLContextsBehind !== 'function') { + if (typeof utils?.canvasesBehind !== 'function') { return; } // Our ids stay put while a held-back callback is re-requested under a new one. @@ -675,7 +678,7 @@ function holdBackAnimationFramesWhileGLBehind() { const requestAnimationFrame = (callback: (time: number) => void) => { const id = nextId++; const run = (time: number) => { - if (utils.webGLContextsBehind() > 0) { + if (utils.canvasesBehind() > 0) { pending.set(id, request(run)); return; } @@ -685,7 +688,7 @@ function holdBackAnimationFramesWhileGLBehind() { pending.set(id, request(run)); return id; }; - requestAnimationFrame.__holdsBackForGL = true; + requestAnimationFrame.__holdsBack = true; const cancelAnimationFrame = (id: number) => { const current = pending.get(id); if (current !== undefined) { diff --git a/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt b/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt index 0cae3ed47..62f2de6b8 100644 --- a/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt +++ b/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt @@ -44,11 +44,11 @@ object Utils { private external fun nativeRunWithWebGL(state: Long, block: Runnable) @JvmStatic - private external fun nativeWebGLContextsBehind(): Int + private external fun nativeCanvasesBehind(): Int - /** Threaded WebGL contexts the GPU has fallen behind (a frame queued behind an unpresented one). */ + /** Threaded canvases (WebGL, 2D) with a frame waiting behind one that hasn't reached the screen. */ @JvmStatic - fun webGLContextsBehind(): Int = nativeWebGLContextsBehind() + fun canvasesBehind(): Int = nativeCanvasesBehind() /** * Runs [block] with the WebGL context [state] current, on the thread that owns it, and returns diff --git a/packages/canvas/typings/android.d.ts b/packages/canvas/typings/android.d.ts index 113bc976d..d2945dbcb 100644 --- a/packages/canvas/typings/android.d.ts +++ b/packages/canvas/typings/android.d.ts @@ -892,7 +892,7 @@ declare module org { public getByteBufferFromBitmap(bitmap: globalAndroid.graphics.Bitmap): java.nio.ByteBuffer; public isEmulator(): boolean; public static createSurfaceTexture(render: number): androidNative.Array; - public static webGLContextsBehind(): number; + public static canvasesBehind(): number; public getBytesFromBitmap(bitmap: globalAndroid.graphics.Bitmap): androidNative.Array; public static updateTexImage(state: number, flipY: boolean, texture: globalAndroid.graphics.SurfaceTexture, render: org.nativescript.canvas.TextureRender, width: number, height: number, internalFormat: number, format: number): void; public static createRenderAndAttachToGLContext(render: number, state: globalAndroid.graphics.SurfaceTexture): org.nativescript.canvas.TextureRender; From 226748acaa7f3f1f4f30df9a72923ce42081f37c Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 03:54:29 -0400 Subject: [PATCH 6/9] perf(2d): draw Android canvases into a multisampled offscreen, 8 bits a channel Without MSAA, Skia rasterized every antialiased path's coverage on the render thread and uploaded it each frame (glTexSubImage2D), which kept a busy canvas from reaching the display rate. The offscreen is now 4x multisampled where the GPU supports it. Opaque canvases used RGB565, which the web never does: a translucent fill can't converge on its colour at 5-6 bits, so faded trails left a permanent tint. Opaque surfaces are now RGBA8888 marked opaque. --- crates/canvas-2d/src/context/surface_gl.rs | 45 +++++++--------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/crates/canvas-2d/src/context/surface_gl.rs b/crates/canvas-2d/src/context/surface_gl.rs index 09ca45485..cd4071325 100644 --- a/crates/canvas-2d/src/context/surface_gl.rs +++ b/crates/canvas-2d/src/context/surface_gl.rs @@ -28,11 +28,10 @@ fn with_offscreen( ) -> (skia_safe::Surface, Option) { #[cfg(target_os = "android")] { - let (color_type, alpha_type) = if alpha { - (ColorType::RGBA8888, AlphaType::Premul) - } else { - (ColorType::RGB565, AlphaType::Opaque) - }; + // 8 bits a channel even when opaque, as on the web: at 5-6 bits (RGB565) a translucent + // fill never converges on its colour, so a trail fading out leaves a permanent tint. + let color_type = ColorType::RGBA8888; + let alpha_type = if alpha { AlphaType::Premul } else { AlphaType::Opaque }; let info = ImageInfo::new( ISize::new(window.width(), window.height()), color_type, @@ -40,11 +39,15 @@ fn with_offscreen( color_space, ); let props = window.props().clone(); + // Multisampled, so Skia draws antialiased paths on the GPU instead of rasterizing their + // coverage on this thread and uploading it every frame; on a tiled GPU the samples mostly + // stay in tile memory. + let samples = ctx.max_surface_sample_count_for_color_type(color_type).min(4); if let Some(offscreen) = gpu::surfaces::render_target( ctx, gpu::Budgeted::Yes, &info, - Some(0), + Some(samples), gpu::SurfaceOrigin::TopLeft, Some(&props), false, @@ -69,7 +72,6 @@ fn draws_to_window(gl: &canvas_core::gpu::gl::GLContext) -> bool { } } -const GR_GL_RGB565: u32 = 0x8D62; const GR_GL_RGBA8: u32 = 0x8058; #[cfg(feature = "gl")] @@ -150,11 +152,7 @@ impl Context { let mut frame_buffer = gpu::gl::FramebufferInfo::from_fboid(buffer_id[0] as u32); - if alpha { - frame_buffer.format = GR_GL_RGBA8; - } else { - frame_buffer.format = GR_GL_RGB565; - } + frame_buffer.format = GR_GL_RGBA8; let target = gpu::backend_render_targets::make_gl( (width as i32, height as i32), @@ -166,10 +164,7 @@ impl Context { skia_safe::SurfacePropsFlags::default(), PixelGeometry::Unknown, ); - let mut color_type = ColorType::RGBA8888; - if !alpha { - color_type = ColorType::RGB565; - } + let color_type = ColorType::RGBA8888; let surface = gpu::surfaces::wrap_backend_render_target( &mut ctx, @@ -310,11 +305,7 @@ impl Context { let mut window_surface = None; let mut engine = SurfaceEngine::GL; let surface = if bounds.is_empty() { - let color_type = if alpha { - ColorType::RGBA8888 - } else { - ColorType::RGB565 - }; + let color_type = ColorType::RGBA8888; let alpha_type = if alpha { AlphaType::Unpremul @@ -353,11 +344,7 @@ impl Context { let mut frame_buffer = gpu::gl::FramebufferInfo::from_fboid(buffer_id as u32); - if alpha { - frame_buffer.format = GR_GL_RGBA8; - } else { - frame_buffer.format = GR_GL_RGB565; - } + frame_buffer.format = GR_GL_RGBA8; let target = gpu::backend_render_targets::make_gl( (width as i32, height as i32), @@ -370,11 +357,7 @@ impl Context { skia_safe::SurfacePropsFlags::default(), PixelGeometry::Unknown, ); - let mut color_type = ColorType::RGBA8888; - - if !alpha { - color_type = ColorType::RGB565; - } + let color_type = ColorType::RGBA8888; let surface = gpu::surfaces::wrap_backend_render_target( &mut ctx, From 10ae9c2cdbaa51b999105019ae7e3f754fc32c65 Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 03:54:29 -0400 Subject: [PATCH 7/9] perf(webgl): skip a detaching context's queued presents Letting go of the window waits for the WebGL thread; presents queued for a window that is going away no longer each wait on the GPU first. --- .../org_nativescript_canvas_NSCCanvas.rs | 2 +- crates/canvas-c/src/webgl/gl.rs | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCCanvas.rs b/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCCanvas.rs index 250bfa1e6..dce969759 100644 --- a/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCCanvas.rs +++ b/crates/canvas-android/src/jni_compat/org_nativescript_canvas_NSCCanvas.rs @@ -634,7 +634,7 @@ fn native_update_gl_no_surface(width: jint, height: jint, context: jlong) { let context = context as *mut WebGLState; let context = unsafe { &*context }; // Waited on: when the window is going away this is how the context lets go of it. - context.sync(|state| { + context.detach(|state| { state.make_current(); state.resize_pbuffer(width, height); }); diff --git a/crates/canvas-c/src/webgl/gl.rs b/crates/canvas-c/src/webgl/gl.rs index 193c86ba9..15d1942ba 100644 --- a/crates/canvas-c/src/webgl/gl.rs +++ b/crates/canvas-c/src/webgl/gl.rs @@ -14,7 +14,7 @@ use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_void}; use std::ptr::NonNull; use std::cell::UnsafeCell; -use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; /* GL */ @@ -270,6 +270,8 @@ pub struct WebGLState { threaded: bool, /// Presents queued and not yet made (threaded only). in_flight: AtomicU32, + /// Queued presents are skipped: the context is letting go of its window (threaded only). + detaching: AtomicBool, } /// Presents a threaded context may have queued before `present` waits for the oldest. @@ -342,12 +344,24 @@ impl WebGLState { crate::webgl::thread::post(move || { let state = state; let state = unsafe { &*state.0 }; - state.inner_mut().make_current_and_swap_buffers(); + if !state.detaching.load(Ordering::Acquire) { + state.inner_mut().make_current_and_swap_buffers(); + } crate::webgl::thread::frame_presented(state.in_flight.fetch_sub(1, Ordering::AcqRel)); }); true } + /// Runs `f` (which lets go of the window) after every queued call, skipping the presents queued + /// meanwhile: they are for a window that is going away, and waiting on each would stall the + /// caller for as many frames as the GPU is behind. + pub fn detach(&self, f: impl FnOnce(&mut canvas_webgl::prelude::WebGLState) -> R) -> R { + self.detaching.store(true, Ordering::Release); + let result = self.sync(f); + self.detaching.store(false, Ordering::Release); + result + } + /// The drawing buffer as an image (`canvas_native_webgl_read_drawing_buffer`), read on the /// thread that owns the context. pub(crate) fn read_drawing_buffer(&self) -> (i32, i32, Vec) { @@ -405,6 +419,7 @@ impl WebGLState { refs: AtomicUsize::new(1), threaded: false, in_flight: AtomicU32::new(0), + detaching: AtomicBool::new(false), } } } From 31a00401284a2c144d6e1e7b4cafe8eb08c38618 Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 09:40:27 -0400 Subject: [PATCH 8/9] fix(2d): opaque surfaces are RGB888x over an RGB8 buffer RGBA8888 marked opaque still stored the alpha drawn into it, so an alpha:false canvas read back transparent where nothing opaque had been drawn (a bitmaprenderer compositing onto black), where RGB565 had read opaque. RGB888x keeps 8 bits a colour and reads alpha as 255; a wrapped opaque framebuffer is declared RGB8, which is what its config has and what Skia pairs with RGB888x. --- crates/canvas-2d/src/context/surface_gl.rs | 29 +++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/crates/canvas-2d/src/context/surface_gl.rs b/crates/canvas-2d/src/context/surface_gl.rs index cd4071325..96022407b 100644 --- a/crates/canvas-2d/src/context/surface_gl.rs +++ b/crates/canvas-2d/src/context/surface_gl.rs @@ -20,6 +20,17 @@ fn gl_interface() -> Option { } } +/// 8 bits a channel even when opaque, as on the web: at 5-6 bits (RGB565) a translucent fill never +/// converges on its colour, so a trail fading out leaves a permanent tint. Opaque is RGB888x, whose +/// alpha reads back as 255 whatever was drawn. +fn color_type_for(alpha: bool) -> ColorType { + if alpha { + ColorType::RGBA8888 + } else { + ColorType::RGB888x + } +} + fn with_offscreen( ctx: &mut gpu::DirectContext, window: skia_safe::Surface, @@ -28,9 +39,7 @@ fn with_offscreen( ) -> (skia_safe::Surface, Option) { #[cfg(target_os = "android")] { - // 8 bits a channel even when opaque, as on the web: at 5-6 bits (RGB565) a translucent - // fill never converges on its colour, so a trail fading out leaves a permanent tint. - let color_type = ColorType::RGBA8888; + let color_type = color_type_for(alpha); let alpha_type = if alpha { AlphaType::Premul } else { AlphaType::Opaque }; let info = ImageInfo::new( ISize::new(window.width(), window.height()), @@ -73,6 +82,8 @@ fn draws_to_window(gl: &canvas_core::gpu::gl::GLContext) -> bool { } const GR_GL_RGBA8: u32 = 0x8058; +/// An opaque config's buffer, which RGB888x wraps. +const GR_GL_RGB8: u32 = 0x8051; #[cfg(feature = "gl")] impl Context { @@ -152,7 +163,7 @@ impl Context { let mut frame_buffer = gpu::gl::FramebufferInfo::from_fboid(buffer_id[0] as u32); - frame_buffer.format = GR_GL_RGBA8; + frame_buffer.format = if alpha { GR_GL_RGBA8 } else { GR_GL_RGB8 }; let target = gpu::backend_render_targets::make_gl( (width as i32, height as i32), @@ -164,7 +175,7 @@ impl Context { skia_safe::SurfacePropsFlags::default(), PixelGeometry::Unknown, ); - let color_type = ColorType::RGBA8888; + let color_type = color_type_for(alpha); let surface = gpu::surfaces::wrap_backend_render_target( &mut ctx, @@ -305,12 +316,12 @@ impl Context { let mut window_surface = None; let mut engine = SurfaceEngine::GL; let surface = if bounds.is_empty() { - let color_type = ColorType::RGBA8888; + let color_type = color_type_for(alpha); let alpha_type = if alpha { AlphaType::Unpremul } else { - AlphaType::Premul + AlphaType::Opaque }; let mut width = width; @@ -344,7 +355,7 @@ impl Context { let mut frame_buffer = gpu::gl::FramebufferInfo::from_fboid(buffer_id as u32); - frame_buffer.format = GR_GL_RGBA8; + frame_buffer.format = if alpha { GR_GL_RGBA8 } else { GR_GL_RGB8 }; let target = gpu::backend_render_targets::make_gl( (width as i32, height as i32), @@ -357,7 +368,7 @@ impl Context { skia_safe::SurfacePropsFlags::default(), PixelGeometry::Unknown, ); - let color_type = ColorType::RGBA8888; + let color_type = color_type_for(alpha); let surface = gpu::surfaces::wrap_backend_render_target( &mut ctx, From 8164db19025f990aa050ac2468f9e48030f9cd52 Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Wed, 30 Sep 2026 10:34:08 -0400 Subject: [PATCH 9/9] perf: read the canvases-behind count from shared memory The requestAnimationFrame hold-back checked the count through a JNI call every frame. The count is now exposed to JS as an ArrayBuffer over the native counter, so the check is a memory read. It no longer depends on Android, so the iOS host holds back threaded 2D frames too. Regenerates canvas_native.h for the new export. --- crates/canvas-android/src/utils/gl/mod.rs | 9 - crates/canvas-c/src/webgl/mod.rs | 2 +- crates/canvas-c/src/webgl/thread.rs | 6 + packages/canvas/Canvas/index.android.ts | 55 +---- packages/canvas/Canvas/index.ios.ts | 5 +- packages/canvas/Canvas/utils.ts | 64 ++++++ .../platforms/ios/src/cpp/CanvasJSIModule.cpp | 10 + .../src/main/cpp/include/canvas_native.h | 196 +++++++++++++++++- .../java/org/nativescript/canvas/Utils.kt | 7 - .../CanvasNative/include/canvas_native.h | 18 ++ packages/canvas/typings/android.d.ts | 1 - 11 files changed, 298 insertions(+), 75 deletions(-) diff --git a/crates/canvas-android/src/utils/gl/mod.rs b/crates/canvas-android/src/utils/gl/mod.rs index d479fcbfe..bcb482e9b 100644 --- a/crates/canvas-android/src/utils/gl/mod.rs +++ b/crates/canvas-android/src/utils/gl/mod.rs @@ -34,15 +34,6 @@ pub unsafe extern "system" fn Java_org_nativescript_canvas_Utils_nativeMakeState JNI_FALSE } -/// `canvas_native_canvases_behind`, for the requestAnimationFrame hold-back. -#[no_mangle] -pub extern "system" fn Java_org_nativescript_canvas_Utils_nativeCanvasesBehind( - _: JNIEnv, - _: jni::objects::JClass, -) -> jint { - canvas_c::canvas_native_canvases_behind() as jint -} - /// Runs `block` (a `Runnable`) with `state` current, on the thread that owns it, and waits for it: /// a threaded WebGL context can only be current on the WebGL thread, so Java that draws with it /// (SurfaceTexture, GLES) has to run there too. diff --git a/crates/canvas-c/src/webgl/mod.rs b/crates/canvas-c/src/webgl/mod.rs index 7641f3ff7..2eee33562 100644 --- a/crates/canvas-c/src/webgl/mod.rs +++ b/crates/canvas-c/src/webgl/mod.rs @@ -5,7 +5,7 @@ pub use gl2::*; mod result; pub use result::*; pub(crate) mod thread; -pub use thread::canvas_native_canvases_behind; +pub use thread::{canvas_native_canvases_behind, canvas_native_canvases_behind_address}; #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] diff --git a/crates/canvas-c/src/webgl/thread.rs b/crates/canvas-c/src/webgl/thread.rs index 6ac806dd9..190450cc9 100644 --- a/crates/canvas-c/src/webgl/thread.rs +++ b/crates/canvas-c/src/webgl/thread.rs @@ -175,3 +175,9 @@ pub(crate) fn canvas_caught_up() { pub extern "C" fn canvas_native_canvases_behind() -> u32 { CANVASES_BEHIND.load(Ordering::Acquire) } + +/// Where that count lives, for the JS side to read as memory every frame instead of calling in. +#[no_mangle] +pub extern "C" fn canvas_native_canvases_behind_address() -> *const u32 { + CANVASES_BEHIND.as_ptr() +} diff --git a/packages/canvas/Canvas/index.android.ts b/packages/canvas/Canvas/index.android.ts index 5fbedb1ae..0be2f39f9 100644 --- a/packages/canvas/Canvas/index.android.ts +++ b/packages/canvas/Canvas/index.android.ts @@ -6,7 +6,7 @@ import { WebGL2RenderingContext } from '../WebGL2/WebGL2RenderingContext'; import { Application, View, Screen, ImageSource, Utils, widthProperty, heightProperty, isUserInteractionEnabledProperty } from '@nativescript/core'; import { GPUCanvasContext } from '../WebGPU'; import { ImageBitmapRenderingContext } from '../ImageBitmapRenderingContext'; -import { handleContextOptions, microtask, CanvasContextType, setParentPercentSize } from './utils'; +import { handleContextOptions, microtask, CanvasContextType, setParentPercentSize, holdBackAnimationFramesWhileBehind } from './utils'; export function createSVGMatrix(): DOMMatrix { return new DOMMatrix(); @@ -650,56 +650,3 @@ export class Canvas extends CanvasBase { releasePointerCapture() {} } - -/** - * Holds requestAnimationFrame callbacks back a frame while a threaded canvas is behind (it has a - * frame waiting behind one that hasn't reached the screen), as a browser does when its compositor - * falls behind. The UI thread then stays free between the frames the GPU can take, instead of - * queueing more work for it every vsync. - * - * Installed with the first threaded context, over whatever requestAnimationFrame is global by then - * (core's, or a polyfill's). - */ -function holdBackAnimationFramesWhileBehind() { - const target = global as any; - const request = target.requestAnimationFrame; - const cancel = target.cancelAnimationFrame; - if (typeof request !== 'function' || request.__holdsBack) { - return; - } - const utils = org.nativescript.canvas.Utils; - // An older native library: leave requestAnimationFrame alone rather than break every frame. - if (typeof utils?.canvasesBehind !== 'function') { - return; - } - // Our ids stay put while a held-back callback is re-requested under a new one. - const pending = new Map(); - let nextId = 1; - const requestAnimationFrame = (callback: (time: number) => void) => { - const id = nextId++; - const run = (time: number) => { - if (utils.canvasesBehind() > 0) { - pending.set(id, request(run)); - return; - } - pending.delete(id); - callback(time); - }; - pending.set(id, request(run)); - return id; - }; - requestAnimationFrame.__holdsBack = true; - const cancelAnimationFrame = (id: number) => { - const current = pending.get(id); - if (current !== undefined) { - pending.delete(id); - cancel?.(current); - } - }; - target.requestAnimationFrame = requestAnimationFrame; - target.cancelAnimationFrame = cancelAnimationFrame; - if (target.window && target.window !== target && target.window.requestAnimationFrame === request) { - target.window.requestAnimationFrame = requestAnimationFrame; - target.window.cancelAnimationFrame = cancelAnimationFrame; - } -} diff --git a/packages/canvas/Canvas/index.ios.ts b/packages/canvas/Canvas/index.ios.ts index 47093448b..9168816de 100644 --- a/packages/canvas/Canvas/index.ios.ts +++ b/packages/canvas/Canvas/index.ios.ts @@ -6,7 +6,7 @@ import { WebGL2RenderingContext } from '../WebGL2/WebGL2RenderingContext'; import { ImageSource, Utils, Screen, isUserInteractionEnabledProperty, widthProperty, heightProperty } from '@nativescript/core'; import { GPUCanvasContext } from '../WebGPU'; import { ImageBitmapRenderingContext } from '../ImageBitmapRenderingContext'; -import { handleContextOptions, microtask, CanvasContextType, setParentPercentSize } from './utils'; +import { handleContextOptions, microtask, CanvasContextType, setParentPercentSize, holdBackAnimationFramesWhileBehind } from './utils'; declare var NSCCanvas, NSCCanvasListener; export function createSVGMatrix(): DOMMatrix { @@ -501,6 +501,9 @@ export class Canvas extends CanvasBase { const opts = { ...defaultOpts, ...handleContextOptions(type, options), fontColor: this.parent?.style?.color?.android || -16777216 }; const threaded = !!(options?.threaded ?? Canvas.threaded2D); + if (threaded) { + holdBackAnimationFramesWhileBehind(); + } if ('threaded2D' in this._canvas) { this._canvas.threaded2D = threaded; } diff --git a/packages/canvas/Canvas/utils.ts b/packages/canvas/Canvas/utils.ts index 6d3b99766..3f9a01efc 100644 --- a/packages/canvas/Canvas/utils.ts +++ b/packages/canvas/Canvas/utils.ts @@ -124,3 +124,67 @@ export function setParentPercentSize(view: any, horizontal: boolean, value: any) view[percentAxes_] = axes & ~axis; } } + +let behind: Uint32Array | null | undefined; + +/** The count of threaded canvases behind, read straight from native memory: this runs every frame. */ +function canvasesBehind(): Uint32Array | null { + if (behind === undefined) { + const buffer = (global as any).CanvasModule?.__canvasesBehind; + behind = buffer instanceof ArrayBuffer ? new Uint32Array(buffer) : null; + } + return behind; +} + +/** + * Holds requestAnimationFrame callbacks back a frame while a threaded canvas is behind (it has a + * frame waiting behind one that hasn't reached the screen), as a browser does when its compositor + * falls behind. The UI thread then stays free between the frames the GPU can take, instead of + * queueing more work for it every vsync. + * + * Installed with the first threaded context, over whatever requestAnimationFrame is global by then + * (core's, or a polyfill's). + */ +export function holdBackAnimationFramesWhileBehind() { + const target = global as any; + const request = target.requestAnimationFrame; + const cancel = target.cancelAnimationFrame; + if (typeof request !== 'function' || request.__holdsBack) { + return; + } + const count = canvasesBehind(); + // An older native library: leave requestAnimationFrame alone. + if (!count) { + return; + } + // Our ids stay put while a held-back callback is re-requested under a new one. + const pending = new Map(); + let nextId = 1; + const requestAnimationFrame = (callback: (time: number) => void) => { + const id = nextId++; + const run = (time: number) => { + if (count[0] > 0) { + pending.set(id, request(run)); + return; + } + pending.delete(id); + callback(time); + }; + pending.set(id, request(run)); + return id; + }; + requestAnimationFrame.__holdsBack = true; + const cancelAnimationFrame = (id: number) => { + const current = pending.get(id); + if (current !== undefined) { + pending.delete(id); + cancel?.(current); + } + }; + target.requestAnimationFrame = requestAnimationFrame; + target.cancelAnimationFrame = cancelAnimationFrame; + if (target.window && target.window !== target && target.window.requestAnimationFrame === request) { + target.window.requestAnimationFrame = requestAnimationFrame; + target.window.cancelAnimationFrame = cancelAnimationFrame; + } +} diff --git a/packages/canvas/platforms/ios/src/cpp/CanvasJSIModule.cpp b/packages/canvas/platforms/ios/src/cpp/CanvasJSIModule.cpp index bd45f0b9f..6e62c4562 100644 --- a/packages/canvas/platforms/ios/src/cpp/CanvasJSIModule.cpp +++ b/packages/canvas/platforms/ios/src/cpp/CanvasJSIModule.cpp @@ -149,6 +149,16 @@ void CanvasJSIModule::install(v8::Isolate *isolate) { GPUSupportedLimitsImpl::Init(canvasMod, isolate); GPUDeviceImpl::Init(canvasMod, isolate); GPUQueueImpl::Init(canvasMod, isolate); + + // The count of canvases behind (canvas_native_canvases_behind), as memory JS reads every + // frame without a call. The counter is static, so nothing frees it. + { + auto store = v8::ArrayBuffer::NewBackingStore( + (void *) canvas_native_canvases_behind_address(), sizeof(uint32_t), + [](void *, size_t, void *) {}, nullptr); + canvasMod->Set(context, ConvertToV8String(isolate, "__canvasesBehind"), + v8::ArrayBuffer::New(isolate, std::move(store))).FromJust(); + } v8Global->Set(context, ConvertToV8String(isolate, "CanvasModule"), canvasMod).FromJust(); canvasMod->Set(context, ConvertToV8String(isolate, "create2DContext"), diff --git a/packages/canvas/src-native/canvas-android/canvas/src/main/cpp/include/canvas_native.h b/packages/canvas/src-native/canvas-android/canvas/src/main/cpp/include/canvas_native.h index 38b344517..6f801742f 100644 --- a/packages/canvas/src-native/canvas-android/canvas/src/main/cpp/include/canvas_native.h +++ b/packages/canvas/src-native/canvas-android/canvas/src/main/cpp/include/canvas_native.h @@ -860,6 +860,9 @@ typedef struct WEBGL_lose_context WEBGL_lose_context; typedef struct WebGLActiveInfo WebGLActiveInfo; +/** + * `.1`: its context is threaded, so the typed extension's calls go to the WebGL thread too. + */ typedef struct WebGLExtension WebGLExtension; typedef struct WebGLFramebufferAttachmentParameter WebGLFramebufferAttachmentParameter; @@ -870,6 +873,10 @@ typedef struct WebGLResult WebGLResult; typedef struct WebGLShaderPrecisionFormat WebGLShaderPrecisionFormat; +/** + * A WebGL context. A threaded one is built, used and dropped on the WebGL thread (`thread`), and + * its inner state is only ever touched there: calls reach it through `post` and `sync`. + */ typedef struct WebGLState WebGLState; typedef struct WebGLSync WebGLSync; @@ -2127,6 +2134,72 @@ void canvas_native_context_resize(struct CanvasRenderingContext2D *context, float width, float height); +/** + * A 2D context on the shared Direct3D 12 device (Windows). Offscreen until + * `canvas_native_context_attach_swap_chain_panel`. Null when there is no usable device. + */ +struct CanvasRenderingContext2D *canvas_native_context_create_d3d(float width, + float height, + float density, + bool alpha, + int32_t font_color, + float ppi, + uint32_t direction, + enum CanvasColorSpace color_space); + +/** + * `canvas_native_context_create_d3d`, rasterized and presented on the shared render thread's + * Direct3D 12 device; the context returned only records. Null when that thread has no usable + * device. + */ +struct CanvasRenderingContext2D *canvas_native_context_create_d3d_threaded(float width, + float height, + float density, + bool alpha, + int32_t font_color, + float ppi, + uint32_t direction, + enum CanvasColorSpace color_space); + +/** + * Presents a D3D context in a WinUI `SwapChainPanel` (`panel`: any COM pointer to it). UI thread. + */ +bool canvas_native_context_attach_swap_chain_panel(struct CanvasRenderingContext2D *context, + void *panel); + +/** + * Presents a D3D context into a XAML `SurfaceImageSource` (any COM pointer to it, made at the + * context's size) instead of a swapchain, so it blends with the page. UI thread. + */ +bool canvas_native_context_attach_xaml_surface(struct CanvasRenderingContext2D *context, + void *source); + +/** + * Maps the context's swapchain into its panel: DIPs = pixels * scale + offset. + */ +bool canvas_native_context_set_swap_chain_transform(struct CanvasRenderingContext2D *context, + float scale_x, + float scale_y, + float offset_x, + float offset_y); + +/** + * The context's Direct3D 12 device was removed; it draws nothing until restored. + */ +bool canvas_native_context_is_lost(const struct CanvasRenderingContext2D *context); + +/** + * Moves a lost context to a new device, cleared and in its default state, shown in `panel` + * again (null offscreen). UI thread. + */ +bool canvas_native_context_restore_d3d(struct CanvasRenderingContext2D *context, void *panel); + +/** + * Removes this thread's and the render thread's shared Direct3D 12 devices, as a driver reset + * would (tests). + */ +bool canvas_native_d3d_simulate_device_removal(void); + struct CanvasRenderingContext2D *canvas_native_context_create(float width, float height, float density, @@ -2807,6 +2880,25 @@ const char *canvas_native_to_data_url(struct CanvasRenderingContext2D *context, const char *format, uint32_t quality); +/** + * Draws a video frame another device shares (canvas-media's `gpuFrame()`: a + * `CanvasD3DSharedFrame`) on the context's device, without a readback; threaded contexts draw it + * on the render thread's. `false` when it cannot (another adapter, not a D3D context), and the + * frame is then not consumed: the caller draws it some other way. + */ +bool canvas_native_context_draw_d3d_shared_frame(struct CanvasRenderingContext2D *context, + void *frame, + int32_t width, + int32_t height, + float sx, + float sy, + float sw, + float sh, + float dx, + float dy, + float dw, + float dh); + void canvas_native_image_filter_reference(const struct ImageFilter *value); void canvas_native_image_filter_release(const struct ImageFilter *value); @@ -3324,6 +3416,8 @@ void canvas_native_webgpu_buffer_map_async(const struct CanvasGPUBuffer *buffer, void (*callback)(enum CanvasGPUErrorType, char*, void*), void *callback_data); +void canvas_native_webgpu_context_detach_surface(const struct CanvasGPUCanvasContext *context); + char *canvas_native_webgpu_to_data_url_with_fallback(const struct CanvasGPUCanvasContext *context, const char *format, float quality); @@ -3358,6 +3452,31 @@ const struct CanvasGPUCanvasContext *canvas_native_webgpu_context_create(const s uint32_t height); #endif +/** + * Windows: a context presenting in a WinUI `SwapChainPanel` (any COM pointer to it). UI thread. + */ +const struct CanvasGPUCanvasContext *canvas_native_webgpu_context_create_swap_chain_panel(const struct CanvasWebGPUInstance *instance, + void *panel, + uint32_t width, + uint32_t height); + +/** + * Windows: maps the swapchain into its panel (DIPs = pixels * scale + offset). + */ +bool canvas_native_webgpu_context_set_swap_chain_transform(const struct CanvasGPUCanvasContext *context, + float scale_x, + float scale_y, + float offset_x, + float offset_y); + +/** + * Windows: a new drawing-buffer size. The surface is reconfigured in place (its swapchain is + * resized), keeping the configuration the page chose. + */ +void canvas_native_webgpu_context_resize_swap_chain_panel(const struct CanvasGPUCanvasContext *context, + uint32_t width, + uint32_t height); + #if (defined(TARGET_OS_IOS) || defined(TARGET_OS_VISION)) const struct CanvasGPUCanvasContext *canvas_native_webgpu_context_create_uiview(const struct CanvasWebGPUInstance *instance, void *view, @@ -3986,6 +4105,11 @@ uint64_t canvas_native_webgpu_compilation_message_get_offset(struct CanvasGPUCom uint64_t canvas_native_webgpu_compilation_message_get_length(struct CanvasGPUCompilationMessage *message); +/** + * Videos share frames only with devices on their adapter. 0 if unknown. + */ +uint64_t canvas_native_webgpu_device_get_adapter_luid(const struct CanvasGPUDevice *device); + struct CanvasGPUSupportedLimits *canvas_native_webgpu_create_limits(void); void canvas_native_webgpu_limits_release(struct CanvasGPUSupportedLimits *limits); @@ -4119,6 +4243,13 @@ bool canvas_native_image_asset_load_from_fd(const struct ImageAsset *asset, int bool canvas_native_image_asset_load_from_path(const struct ImageAsset *asset, const char *path); +/** + * Encodes the image to `path`; `format` 0 JPG, 1 PNG (`ImageAssetSaveFormat`). + */ +bool canvas_native_image_asset_save_path(const struct ImageAsset *asset, + const char *path, + uint32_t format); + bool canvas_native_image_asset_load_from_raw(const struct ImageAsset *asset, uint32_t width, uint32_t height, @@ -4425,7 +4556,7 @@ struct WebGLState *canvas_native_webgl_create(void *view, bool xr_compatible); #endif -#if !defined(TARGET_OS_ANDROID) +#if (defined(TARGET_OS_IOS) || defined(TARGET_OS_MACOS) || defined(TARGET_OS_VISION)) struct WebGLState *canvas_native_webgl_create(void *view, int32_t version, bool alpha, @@ -4455,6 +4586,56 @@ struct WebGLState *canvas_native_webgl_create_no_window(int32_t width, bool xr_compatible, bool is_canvas); +/** + * Windows: a WebGL context whose drawing buffer can be shown in a `SwapChainPanel` + * (`canvas_native_webgl_attach_swap_chain_panel`). Null when ANGLE is unavailable. + */ +struct WebGLState *canvas_native_webgl_create_d3d(int32_t width, + int32_t height, + int32_t version, + bool alpha, + bool antialias, + bool depth, + bool fail_if_major_performance_caveat, + int32_t power_preference, + bool premultiplied_alpha, + bool preserve_drawing_buffer, + bool stencil, + bool desynchronized, + bool xr_compatible); + +/** + * Windows: shows a `canvas_native_webgl_create_d3d` context in a `SwapChainPanel` (any COM + * pointer to it). UI thread. + */ +bool canvas_native_webgl_attach_swap_chain_panel(struct WebGLState *state, void *panel); + +/** + * Windows: presents a `canvas_native_webgl_create_d3d` context into a XAML `SurfaceImageSource` + * (any COM pointer to it, made at the drawing buffer's size) instead of a swapchain, so it blends + * with the page. The rows are bottom-up: the host flips the image. UI thread. + */ +bool canvas_native_webgl_attach_xaml_surface(struct WebGLState *state, void *source); + +/** + * Windows: maps the drawing buffer into its panel (DIPs = pixels * scale + offset). + */ +bool canvas_native_webgl_set_swap_chain_transform(struct WebGLState *state, + float scale_x, + float scale_y, + float offset_x, + float offset_y); + +/** + * Windows: resizes (and clears) a `canvas_native_webgl_create_d3d` drawing buffer. + */ +bool canvas_native_webgl_resize_d3d(struct WebGLState *state, int32_t width, int32_t height); + +/** + * Ends a frame: presents it where the context is on screen (Windows panels), else flushes. + */ +bool canvas_native_webgl_present(struct WebGLState *state); + void canvas_native_webgl_active_texture(uint32_t texture, struct WebGLState *state); void canvas_native_webgl_attach_shader(uint32_t program, uint32_t shader, struct WebGLState *state); @@ -4833,7 +5014,7 @@ struct WebGLResult *canvas_native_webgl_get_vertex_attrib(uint32_t index, uint32_t pname, struct WebGLState *state); -bool canvas_native_webgl_get_is_context_lost(struct WebGLState*); +bool canvas_native_webgl_get_is_context_lost(struct WebGLState *state); void canvas_native_webgl_hint(uint32_t target, uint32_t mode, struct WebGLState *state); @@ -5875,4 +6056,15 @@ void canvas_native_webgl2_tex_image2d_image_data(int32_t target, const struct ImageData *image_data, struct WebGLState *state); +/** + * How many threaded canvases are behind. The JS side holds requestAnimationFrame back while any + * are, as a browser does when its compositor falls behind, rather than queue more work. + */ +uint32_t canvas_native_canvases_behind(void); + +/** + * Where that count lives, for the JS side to read as memory every frame instead of calling in. + */ +const uint32_t *canvas_native_canvases_behind_address(void); + #endif /* CANVAS_C_H */ diff --git a/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt b/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt index 62f2de6b8..d63744ef0 100644 --- a/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt +++ b/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/Utils.kt @@ -43,13 +43,6 @@ object Utils { @JvmStatic private external fun nativeRunWithWebGL(state: Long, block: Runnable) - @JvmStatic - private external fun nativeCanvasesBehind(): Int - - /** Threaded canvases (WebGL, 2D) with a frame waiting behind one that hasn't reached the screen. */ - @JvmStatic - fun canvasesBehind(): Int = nativeCanvasesBehind() - /** * Runs [block] with the WebGL context [state] current, on the thread that owns it, and returns * its result: a threaded context can only be current on the WebGL thread, so GLES and diff --git a/packages/canvas/src-native/canvas-ios/CanvasNative/include/canvas_native.h b/packages/canvas/src-native/canvas-ios/CanvasNative/include/canvas_native.h index 0c5ed6288..6f801742f 100644 --- a/packages/canvas/src-native/canvas-ios/CanvasNative/include/canvas_native.h +++ b/packages/canvas/src-native/canvas-ios/CanvasNative/include/canvas_native.h @@ -860,6 +860,9 @@ typedef struct WEBGL_lose_context WEBGL_lose_context; typedef struct WebGLActiveInfo WebGLActiveInfo; +/** + * `.1`: its context is threaded, so the typed extension's calls go to the WebGL thread too. + */ typedef struct WebGLExtension WebGLExtension; typedef struct WebGLFramebufferAttachmentParameter WebGLFramebufferAttachmentParameter; @@ -870,6 +873,10 @@ typedef struct WebGLResult WebGLResult; typedef struct WebGLShaderPrecisionFormat WebGLShaderPrecisionFormat; +/** + * A WebGL context. A threaded one is built, used and dropped on the WebGL thread (`thread`), and + * its inner state is only ever touched there: calls reach it through `post` and `sync`. + */ typedef struct WebGLState WebGLState; typedef struct WebGLSync WebGLSync; @@ -6049,4 +6056,15 @@ void canvas_native_webgl2_tex_image2d_image_data(int32_t target, const struct ImageData *image_data, struct WebGLState *state); +/** + * How many threaded canvases are behind. The JS side holds requestAnimationFrame back while any + * are, as a browser does when its compositor falls behind, rather than queue more work. + */ +uint32_t canvas_native_canvases_behind(void); + +/** + * Where that count lives, for the JS side to read as memory every frame instead of calling in. + */ +const uint32_t *canvas_native_canvases_behind_address(void); + #endif /* CANVAS_C_H */ diff --git a/packages/canvas/typings/android.d.ts b/packages/canvas/typings/android.d.ts index d2945dbcb..32840ce0d 100644 --- a/packages/canvas/typings/android.d.ts +++ b/packages/canvas/typings/android.d.ts @@ -892,7 +892,6 @@ declare module org { public getByteBufferFromBitmap(bitmap: globalAndroid.graphics.Bitmap): java.nio.ByteBuffer; public isEmulator(): boolean; public static createSurfaceTexture(render: number): androidNative.Array; - public static canvasesBehind(): number; public getBytesFromBitmap(bitmap: globalAndroid.graphics.Bitmap): androidNative.Array; public static updateTexImage(state: number, flipY: boolean, texture: globalAndroid.graphics.SurfaceTexture, render: org.nativescript.canvas.TextureRender, width: number, height: number, internalFormat: number, format: number): void; public static createRenderAndAttachToGLContext(render: number, state: globalAndroid.graphics.SurfaceTexture): org.nativescript.canvas.TextureRender;