UNPKG

xgpu

Version:

XGPU is an extendable library for WebGPU that provides a higher-level, easy-to-use interface for building rendering engines or processing numeric data. It handles automatic data binding, buffer alignment, variable declarations in shaders, and more. XGPU i

125 lines (124 loc) 4.52 kB
// Copyright (c) 2023 Thomas Le Coz. All rights reserved. // This code is governed by an MIT license that can be found in the LICENSE file. import { XGPU } from "../../XGPU"; export class VideoTexture { mustBeTransfered = true; descriptor; useWebcodec = false; //still in beta gpuResource; /* bindgroups: an array of bindgroup that contains the VideoTexture => I need it to call its "build" function onVideoFrameCallback => a videoTexture can be contained in multiple bindgroups, that's why it's an array */ bindgroups = []; addBindgroup(bindgroup) { if (this.bindgroups.indexOf(bindgroup) === -1) { this.bindgroups.push(bindgroup); } } constructor(descriptor) { if (undefined === descriptor.format) descriptor.format = "rgba8unorm"; if (undefined === descriptor.usage) descriptor.usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT; if (undefined === descriptor.mipLevelCount) descriptor.mipLevelCount = 1; if (undefined === descriptor.sampleCount) descriptor.sampleCount = 1; if (undefined === descriptor.dimension) descriptor.dimension = "2d"; if (undefined === descriptor.viewFormats) descriptor.viewFormats = []; this.descriptor = descriptor; if (descriptor.source) this.source = descriptor.source; } clone() { return new VideoTexture(this.descriptor); } set source(video) { this.gpuResource = video; this.descriptor.source = video; this.descriptor.size = [video.width, video.height]; let nbError = 0; const frame = () => { if (!this.gpuResource) return; if (XGPU.device && this.deviceId === XGPU.deviceId) { this.bindgroups.forEach(b => b.build()); nbError = 0; } else { nbError++; } if (nbError < 30) { //i keep the video updating during 30 frame after device loss //because we don't know if we lost the device voluntarily (if we change the page ) //or because the device was lost for an external reason (in this case, the video must continue to update) video.requestVideoFrameCallback(frame); } else { video.src = undefined; //console.log("KILL VIDEO") } }; video.requestVideoFrameCallback(frame); } createDeclaration(varName, bindingId, groupId = 0) { return "@binding(" + bindingId + ") @group(" + groupId + ") var " + varName + ":texture_external;\n"; } createBindGroupLayoutEntry(bindingId) { return { binding: bindingId, visibility: GPUShaderStage.FRAGMENT, externalTexture: {}, }; } createGpuResource() { /* no code here : The HtmlVideoElement is used as gpyResource */ } update() { /* np code here : the video update itself automaticly */ this.deviceId = XGPU.deviceId; } deviceId; destroyGpuResource() { //console.log("destroyGpuResource deviceLost = ") /* if (this.gpuResource ) { this.gpuResource.src = undefined; this.gpuResource = null; }*/ if (this.videoFrame) { this.videoFrame.close(); this.videoFrame = null; } } videoFrame; createBindGroupEntry(bindingId) { if (this.useWebcodec) { if (this.videoFrame) this.videoFrame.close(); this.videoFrame = new window["VideoFrame"](this.gpuResource); } if (!this.gpuResource) throw new Error("gpuResource cannot be null. You must provide a HTMLVideoElement"); return { binding: bindingId, resource: XGPU.device.importExternalTexture({ source: this.useWebcodec ? this.videoFrame : this.gpuResource }) }; } setPipelineType(pipelineType) { if (pipelineType) { } //use to handle particular cases in descriptor relative to the nature of pipeline } }