I’m working on a project with over 200 image targets, and I’m using pooling to create xrextras-named-image-target dynamically. When an image target is found, a video element is attached as a child to xrextras-named-image-target, but whenever the image target is lost, and I want to remove the child from DOM, I get continuous warnings stating:
xr-simd-24.1.2.2165.js:18 WebGL: INVALID_OPERATION: useProgram: attempt to use a deleted object"
The warning propagation stops when I find an image target again. I can’t understand the erroneous code because it’s minified:
// xr-simd.js
if (Object.keys(y).forEach((function(I) {
A[I] = function() {
for (var g, C = arguments.length, Q = new Array(C), B = 0; B < C; B++)
Q[B] = arguments[B];
var E = (g = H[I]).call.apply(g, [A].concat(Q)); // g IS EMPTY
return Y.apply(void 0, [I].concat(Q)),
E
}
}
this: WebGL2RenderingContext
B: 1
C: 1
E: undefined
Q: [WebGLProgram]
g: undefined
Problem
Everything seems to work despite the warning, but I wonder why the warning happens, and how I get rid of it.
My code
The functionality in short:
- Image target is found,
#imageTargetwith theec-named-image-targetcomponent is added to the scene viasceneEl.components.pool__imagetarget.requestEntity(),- The content of
#imagetarget-video- an a-entity that plays a video, is appended to#imageTarget, - When the image target is lost, the child element
#imagetarget-videois removed from DOM. - The warning is triggered.
<!-- index.html -->
<a-scene
xrextras-loading
pool__imagetarget="mixin: imagetarget; size: 3; dynamic: true"
xrextras-runtime-error
camera-feed-delegator
renderer="colorManagement:true; webgl2: true;"
xrweb="disableWorldTracking: true;"
>
<a-assets>
<img id="imagetarget-video-thumbnail" crossorigin="anonymous" src="./assets/img/thumbs/video-play-button.png" />
<!-- "src" is set in dynamicTarget.js when an image target is found -->
<video id="imagetarget-video-asset"
playsinline
crossorigin="anonymous"
loop="false"
src=""
></video>
<!-- ec-named-image-target is a modification of xrextras-named-image-target-->
<a-mixin id="imagetarget" ec-named-image-target></a-mixin>
</a-assets>
<!-- This entity is injected into #imageTarget in dynamicTarget.js -->
<template id="imagetarget-video">
<a-entity
ec-play-video="video: #imagetarget-video-asset; thumb: #imagetarget-video-thumbnail; canstop: true;"
geometry="primitive: plane; height: 1.38; width: 0.776;"
material="transparent: true">
></a-entity>
</template>
// index.js
import { cameraFeedDelegator } from './src/js/components/image-targeting/camera-feed-delegator.js';
import { ecNamedImageTarget } from './src/js/components/image-targeting/ec-named-image-target.js';
// Register components and primitives
AFRAME.registerComponent('camera-feed-delegator', cameraFeedDelegator);
AFRAME.registerComponent('ec-named-image-target', ecNamedImageTarget);
// ecNamedImageTarget.js - just a modification of xrextras-image-target, but with the event listener for `xrimagefound` moved to dynamicTargeting.js
const ecNamedImageTarget = {
schema: {
name: {type: 'string'},
target: {type: 'string'}
},
init() {
const {object3D} = this.el
const updateImage = ({detail}) => {
if (this.data.name === detail.name) {
object3D.position.copy(detail.position)
object3D.quaternion.copy(detail.rotation)
object3D.scale.set(detail.scale, detail.scale, detail.scale)
object3D.visible = true
}
}
const removeComponent = ({detail}) => {
if (this.data.name === detail.name) {
this.data.name = null;
object3D.visible = false;
this.el.innerHTML = ''; // THIS LINE CREATES THE ERROR, when ec-named-image-target is removed from the DOM.
this.el.sceneEl.components.pool__imagetarget.returnEntity(this.el);
}
}
this.el.sceneEl.addEventListener('xrimageupdated', updateImage);
this.el.sceneEl.addEventListener('xrimagelost', removeComponent);
},
}
export {ecNamedImageTarget}
// cameraFeedDelegator.js
const cameraFeedDelegator = {
init() {
const dynamicTargeting = new DynamicTargeting();
// sets all the target images that the app should loop through. Not relevant for this case.
dynamicTargeting.updateTargetElements()
window.addEventListener('xrimagefound', ({detail}) => {
dynamicTargeting.foundTarget(detail.name, this.el.sceneEl);
});
window.addEventListener('xrimagelost', ({detail}) => {
dynamicTargeting.lostTarget(detail.name, this.el.sceneEl);
});
}
}
export {cameraFeedDelegator}
// dynamicTargeting.js
export default class DynamicTargeting {
targets = [];
found = new Set();
constructor() {
this.videoEl = document.getElementById("imagetarget-video").content.firstElementChild;
this.videoAssetEl = document.getElementById("imagetarget-video-asset");
}
updateTargetElements(imageTargets) {
// ... code for looping through image targets, using this.found and this.targets.
}
/**
* Adds ec-named-image-target from the pool to a-scene
* Always making sure that the found image target is being scanned
*/
foundTarget(imageTargetName, sceneEl) {
this.found.add(imageTargetName);
this.targets.splice(this.targets.indexOf(imageTargetName), 1);
let imageTargetEl = sceneEl.components.pool__imagetarget.requestEntity();
imageTargetEl.innerHTML = ''; // REMOVING THE CHILD HERE doesn't trigger the error.
imageTargetEl.appendChild(childEl);
// Sets the name of the image target that is being tracked.
imageTargetEl.setAttribute('ec-named-image-target', `name: ${imageTargetName}`);
}
}
/**
* Restores this.targets if the image target is lost.
*/
lostTarget(imageTargetName, sceneEl) {
this.found.delete(name);
this.targets.push(name);
}
}