| // Copyright 2026 the Vello Authors | |
| // SPDX-License-Identifier: Apache-2.0 OR MIT | |
| use core::ops::Deref; | |
| use crate::{WebGlError, WebGlOperation, WebGlResourceKind, WebGlShaderStage}; | |
| use super::{ | |
| WebGl2RenderingContext, WebGlBuffer, WebGlFramebuffer, WebGlProgram, WebGlShader, WebGlTexture, | |
| WebGlVertexArrayObject, | |
| }; | |
| #[cfg(feature = "probe")] | |
| use web_sys::{WebGlRenderbuffer, WebGlSync}; | |
| pub(crate) trait GlResource { | |
| const KIND: WebGlResourceKind; | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> | |
| where | |
| Self: Sized; | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self); | |
| } | |
| #[derive(Debug)] | |
| pub(crate) struct Resource<T: GlResource> { | |
| gl: WebGl2RenderingContext, | |
| raw: T, | |
| } | |
| // `Resource` intentionally does not implement `Clone`. There should | |
| // only be a single handle to a given resources, such that it has | |
| // unique ownership and we don't end up deleting the same resource | |
| // twice. | |
| impl<T: GlResource> Resource<T> { | |
| pub(super) fn new(gl: &WebGl2RenderingContext) -> Result<Self, WebGlError> { | |
| let raw = T::create(gl).ok_or(WebGlError::OperationFailed { | |
| operation: WebGlOperation::ResourceCreation(T::KIND), | |
| message: None, | |
| })?; | |
| Ok(Self { | |
| gl: gl.clone(), | |
| raw, | |
| }) | |
| } | |
| } | |
| impl<T: GlResource> Drop for Resource<T> { | |
| fn drop(&mut self) { | |
| T::delete(&self.gl, &self.raw); | |
| } | |
| } | |
| impl<T: GlResource> Deref for Resource<T> { | |
| type Target = T; | |
| fn deref(&self) -> &Self::Target { | |
| &self.raw | |
| } | |
| } | |
| #[derive(Debug)] | |
| pub(crate) struct WebGlVertexShader(WebGlShader); | |
| impl Deref for WebGlVertexShader { | |
| type Target = WebGlShader; | |
| fn deref(&self) -> &Self::Target { | |
| &self.0 | |
| } | |
| } | |
| #[derive(Debug)] | |
| pub(crate) struct WebGlFragmentShader(WebGlShader); | |
| impl Deref for WebGlFragmentShader { | |
| type Target = WebGlShader; | |
| fn deref(&self) -> &Self::Target { | |
| &self.0 | |
| } | |
| } | |
| impl GlResource for WebGlTexture { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::Texture; | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.create_texture() | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_texture(Some(raw)); | |
| } | |
| } | |
| impl GlResource for WebGlBuffer { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::Buffer; | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.create_buffer() | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_buffer(Some(raw)); | |
| } | |
| } | |
| impl GlResource for WebGlFramebuffer { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::Framebuffer; | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.create_framebuffer() | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_framebuffer(Some(raw)); | |
| } | |
| } | |
| #[cfg(feature = "probe")] | |
| impl GlResource for WebGlRenderbuffer { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::Renderbuffer; | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.create_renderbuffer() | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_renderbuffer(Some(raw)); | |
| } | |
| } | |
| #[cfg(feature = "probe")] | |
| impl GlResource for WebGlSync { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::Sync; | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.fence_sync(WebGl2RenderingContext::SYNC_GPU_COMMANDS_COMPLETE, 0) | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_sync(Some(raw)); | |
| } | |
| } | |
| impl GlResource for WebGlProgram { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::Program; | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.create_program() | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_program(Some(raw)); | |
| } | |
| } | |
| impl GlResource for WebGlVertexShader { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::Shader(WebGlShaderStage::Vertex); | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.create_shader(WebGl2RenderingContext::VERTEX_SHADER) | |
| .map(Self) | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_shader(Some(raw)); | |
| } | |
| } | |
| impl GlResource for WebGlFragmentShader { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::Shader(WebGlShaderStage::Fragment); | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.create_shader(WebGl2RenderingContext::FRAGMENT_SHADER) | |
| .map(Self) | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_shader(Some(raw)); | |
| } | |
| } | |
| impl GlResource for WebGlVertexArrayObject { | |
| const KIND: WebGlResourceKind = WebGlResourceKind::VertexArray; | |
| fn create(gl: &WebGl2RenderingContext) -> Option<Self> { | |
| gl.create_vertex_array() | |
| } | |
| fn delete(gl: &WebGl2RenderingContext, raw: &Self) { | |
| gl.delete_vertex_array(Some(raw)); | |
| } | |
| } | |
| pub(crate) type Texture = Resource<WebGlTexture>; | |
| pub(crate) type Buffer = Resource<WebGlBuffer>; | |
| pub(crate) type Framebuffer = Resource<WebGlFramebuffer>; | |
| #[cfg(feature = "probe")] | |
| pub(crate) type Renderbuffer = Resource<WebGlRenderbuffer>; | |
| #[cfg(feature = "probe")] | |
| pub(crate) type SyncFence = Resource<WebGlSync>; | |
| pub(crate) type Program = Resource<WebGlProgram>; | |
| pub(crate) type VertexShader = Resource<WebGlVertexShader>; | |
| pub(crate) type FragmentShader = Resource<WebGlFragmentShader>; | |
| pub(crate) type VertexArray = Resource<WebGlVertexArrayObject>; |