GL_OUT_OF_MEMORY using A-frame to display stereoscopic images - three.js

I've created a proof-of-concept SPA (source code / demo) which loads stereoscopic images from a web server and renders them in stereoscopic 3D using the aframe-stereo-component for A-frame.
Testing this on a Quest 2 in the Oculus Browser and Firefox Reality, this works fine: each image appears in 3D when viewed in immersive VR mode via WebXR.
However, after scrolling through a number of images (usually 8 to 12), the slideshow stops working.
In Firefox Reality, this manifests by the app just freezing and becoming unresponsive.
In Oculus Browser, the image description is displayed but the image area remains black.
Connecting the Quest 2 to a PC with adb and inspecting the page using Chrome DevTools, I can see the following output when this occurs:
23:07:59.195 [.WebGL-0x55930300]GL ERROR :GL_OUT_OF_MEMORY : glTexImage2D:
23:07:59.195 [.WebGL-0x55930300]GL ERROR :GL_INVALID_OPERATION : glGenerateMipmap: Can not generate mips
23:07:59.195 [.WebGL-0x55930300]RENDER WARNING: texture bound to texture unit 0 is not renderable. It might be non-power-of-2 or have incompatible texture filtering (maybe)?
23:08:03.340 [.WebGL-0x55930300]GL ERROR :GL_OUT_OF_MEMORY : glTexImage2D:
23:08:03.340 [.WebGL-0x55930300]GL ERROR :GL_INVALID_OPERATION : glGenerateMipmap: Can not generate mips
23:08:03.340 [.WebGL-0x55930300]GL ERROR :GL_OUT_OF_MEMORY : glTexImage2D:
23:08:03.340 [.WebGL-0x55930300]GL ERROR :GL_INVALID_OPERATION : glGenerateMipmap: Can not generate mips
23:08:03.499 WebGL: too many errors, no more errors will be reported to the console for this context.
Admittedly the images being loaded are large JPS files (2 to 3Mb) and each image is rendered twice: once for each eye.
However, an <img> element is used to load the image content by adding it to the DOM and when the next image is requested, this is removed and nulled to invalidate the image content so in theory only one image should be loaded at any one time.
Therefore I'm assuming there must be a memory leak somewhere which is causing the memory to get filled up after a number of images have been loaded but I'm not sure where it's leaking.
I'm not able to reproduce the issue on a PC using Chrome/Firefox but this is probably because it has more memory than the Quest 2.
Relevant source code extracts:
<a-scene id="scene" vr-mode-ui="enabled: false" >
<a-assets id="assets"></a-assets>
<a-plane id="left-image"
material="repeat:0.5 1"
scale="2 1 1"
position="0 0 -1"
stereo="eye:left"
>
</a-plane>
<a-plane id="right-image"
material="repeat:0.5 1; offset: 0.5 0"
scale="2 1 1"
position="0 0 -1"
stereo="eye: right"
></a-plane>
</a-scene>
const stereoImageId = 'fullsize-image'
const _stereoImage = '#'+stereoImageId;
const leye = $('#left-image')[0];
const reye = $('#right-image')[0];
const $assets = $('#assets');
const removeStereoImage = function(){
let $stereoImage = $(_stereoImage);
if($stereoImage.length){
let stereoImage = $stereoImage[0];
stereoImage.onload = stereoImage.onerror = null;
$stereoImage.attr('src', '').remove();
$stereoImage = stereoImage = null;
}
};
const unloadStereoImage = function(){
removeStereoImage();
leye.setAttribute("material", "src", '');
reye.setAttribute("material", "src", '');
setStereoImageVisibility(false);
};
const setStereoImageVisibility = function(visible){
leye.setAttribute("visible", visible)
reye.setAttribute("visible", visible)
};
const showImg = function(url, description){
function onImgLoaded(){
leye.setAttribute("material", "src", _stereoImage);
reye.setAttribute("material", "src", _stereoImage);
setStereoImageVisibility(true);
}
unloadStereoImage();
let stereoImage = document.createElement('img');
stereoImage.setAttribute('id', stereoImageId);
stereoImage.setAttribute('crossorigin', "anonymous");
stereoImage.setAttribute('src', url);
stereoImage.onload = onImgLoaded;
$(stereoImage).appendTo($assets);
};
I've also raised this as an issue in the source code repo

The asset management system (a-assets) is intended to help with preloading assets, not throwing them in and out at runtime.
Removing the <img> element from the assets won't dispose the texture (which is cached within the material system).
You can access and clear the cache, but I'd try managing the images 'manually',
loading the image via new THREE.Texture(src) like the material system does
apply the material with
element.getObject3D("mesh").material.map = texture;
material.needsUpdate = true;
remove the old texture with texture.dispose()
Thought that the textures are too big, but I guess each one would crash.

Related

Error retrieving image from URL with SpreadsheetApp.newCellImage() builder

My application is trying to insert images to google drive sheet using google app script.
It works fine...
but it hang up intermittently with the response error from google script:
Exception: Error retrieving image from URL or bad URL:
https://drive.google.com/uc?id=1uvjg9_ZZg2sI5RYbPMEn6xXJhhnwuFyq&export=download.
The code is :
var fpm_mon_image = file_from_folder(id_folder_images , image_AR ) ; // get "image_AR" from folder
var url_mon_image = fpm_mon_image.getDownloadUrl() ;
var image = SpreadsheetApp.newCellImage().setSourceUrl(url_mon_image).setAltTextTitle(titre).toBuilder().build() ;
Utilities.sleep(1000); // for testing ...
SpreadsheetApp.flush();
Utilities.sleep(1000);
var rangeIma= fpm_sp.getRange(scal_li_SP +1, 2) ;
rangeIma.setValue(image).setVerticalAlignment('bottom') ; // stop here with error above
It works fine 5, 10 times then it hang up 2, 3, 5 times and then works fine again.... (I start loosing my hairs ;-))
I tried :
var srcfile = DriveApp.getFileById(id_mon_image);
srcfile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
var image = fpm_sp.insertImage(srcfile.getBlob(), my_col , my_row);
but the image in not inserted in a cell...
Could you help please ?
Many thanks.
Unfortunately, I cannot replicate your situation of Exception: Error retrieving image from URL or bad URL:. So, although I'm not sure about your actual situation, how about the following modification?
Modified script:
Before you use this script, please enable Drive API at Advanced Google services.
var fileId = "###"; // Please set the file ID of your image.
var url = Drive.Files.get(fileId).thumbnailLink.replace(/\=s.+/, "=s512");
var title = "sample title";
var image = SpreadsheetApp.newCellImage().setSourceUrl(url).setAltTextTitle(title).build();
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("A1").setValue(image);
When this script is run, a thumbnail link is retrieved from the image file using Drive API. And, the image size of the image is changed. And, the image is put to the cell "A1" of the active sheet.
In this case, if the issue of image size occurs, this issue can be removed. And, if the issue of sharing the file occurs, this issue can be also removed. By this, I thought that your issue might be able to be removed.

How to use p5.loadImage in Vue 3

The p5 docs state how to load an image from the assets folder.
However I am using 'instance mode' in my Vue 3 setup and any attempt to use loadImage is resulting in the return of an Event object of type"error" and no explanation.
I have tried:
const script = p5 => {
...
this.assets.rocket = p5.loadImage('./assets/rocket.jpg')
this.assets.rocket = p5.loadImage('~assets/rocket.jpg')
this.assets.rocket = p5.loadImage('#/assets/rocket.jpg')
}
and so on.
I know the image exists as <img src="./assets/rocket.jpg"> renders the image correctly.
What have I missed?
(Everything else regarding p5 in instance mode appears to be working fine.)
Try p5.loadImage(require('#/assets/rocket.jpg'))?
Importing image should be a Webpack responsibility, not p5.js

createGraphics in Instance Mode in p5js

I'm new to p5.
My aim is to display ASCII value of the key I type and also leave a trail of vertical lines whose distance from left is 200+the ASCII value of the key,
which can be done using createGraphics() (adding an additional canvas layer on top with same dimensions as original and drawing on that additional canvas layer)
But the code doesn't seem to work and also it is not displaying any errors in the console.
const c5=function(p){
let pg;
p.setup=function(){
p.createCanvas(600,400);
pg=p.createGraphics(600,400);
};
p.draw=function(){
p.background(200);
p.textAlign(p.CENTER,p.TOP);
p.textSize(20);
p.text('ASCII Value : '+p.keyCode,300,100);
pg.line(200+p.keyCode,200,200+p.keyCode,300);//shift right by 200
};
};
The first issue is that you have to tell the engine that the thing you name p is actually a p5 instance. You can construct a p5 object using new p5(...) as follows:
const c5 = new p5(function(p) {
p.setup = function(){
...
};
p.draw = function(){
...
};
});
You then correctly fill up your pg graphic object with vertical lines. However, you do not "draw" it on your original canvas. You can do so using the p5.js image() function (see also the example shown in the createGraphics() documentation).
I've made a working example in the p5.js editor here.
Your code is very close. You are creating the graphic object and drawing to it but you also need to display it as an image to your canvas. In your code snippet you are also missing the call to create the new p5js object but that may be just a copy paste error.
Here is a working snippet of your code with the call to draw the image. I also moved the key detection logic to keyPressed so the logic only runs when a key is pressed.
Also notice that running the logic inside of keyPressed allows the sketch to handle keys such as f5 by returning false and preventing default behavior. In a real application we would need to be very careful about overriding default behavior. Here we assume that the user wants to know the key code of the f5 key and will be ok with the page not reloading. In a real application that might not be the case.
const c5=function(p){
let pg;
p.setup=function(){
p.createCanvas(600,400);
pg=p.createGraphics(600,400);
};
p.draw=function(){
};
p.keyPressed = function() {
p.background(200);
p.textAlign(p.CENTER,p.TOP);
p.textSize(20);
p.text('ASCII Value : '+p.key + " " +p.keyCode,300,100);
pg.line(200+p.keyCode,200,200+p.keyCode,300);//shift right by 200
p.image(pg, 0, 0);
return false; // prevent default
}
};
var myp5 = new p5(c5)
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>

Storyboard animation only displays last frame

I have to create an animation for a Windows Phone 8.1 app. I want to change the ImageSource property of my Page.Background to display an animated splash when the app launches.
I used this sample as a model: http://www.codeproject.com/Tips/823622/Images-Animation-By-Using-Story-Board-in-Windows-P
In my code, the animation lasts 8 seconds for 80 images, but the screen stays black for 8 seconds, then only displays the last frame.
private void setupStoryboard()
{
storyboard = new Storyboard();
var animation = new ObjectAnimationUsingKeyFrames();
Storyboard.SetTarget(animation, BackgroundBrush);
Storyboard.SetTargetProperty(animation, "ImageSource");
storyboard.Children.Add(animation);
for (int i = 0; i <= 80; i++)
{
var keyframe = new DiscreteObjectKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(100 * i)), //Time Interval
Value = String.Format("ms-appx:///Assets/Animation/splash{0:D2}.png", i) //Source of images
};
animation.KeyFrames.Add(keyframe);
}
Resources.Add("Storyboard", storyboard); //Add story board to resources
}
This code is called from OnNavigatedTo, and I have a button to call storyboard.Begin();
This problem appears on simulator and device.
Update: The problem persists with a greater interval (1000ms), and smaller images (30KB jpg instead of 240KB png)
Update: Same problem with an <Image> tag
Update: Still not solved, but I managed to obtain the desired effect. I created a DispatcherTimer to manually change the image. Unfortunately, this creates a nasty flickering effect. I had to add a second identical image on top of the first, and alternate between the two: imageA loads source1, imageB loads source2, imageA loads source3, ImageB loads source4...
Mission completed, with duct tape.

PhantomJS - Rendering fails to show all images

I have a phantomjs script that is stepping through the pages of my site.
For each page, I use page = new WebPage() and then page.close() after finishing with the page. (This is a simplified description of the process, and I'm using PhantomJS version 1.9.7.)
While on each page, I use page.renderBase64('PNG') one or more times, and add the results to an array.
When I'm all done, I build a new page and cycle through the array of images, adding each to the page using <img src="data:image/png;base64,.......image.data.......">.
When done, I use page.render(...) to make a PDF file.
This is all working great... except that the images stop appearing in the PDF after about the 20th image - the rest just show as 4x4 pixel black dots
For troubleshooting this...
I've changed the render to output a PNG file, and have the same
problem after the 19th or 20th image.
I've outputted the raw HTML. I
can open that in Chrome, and all the images are visible.
Any ideas why the rendering would be failing?
Solved the issue. Turns out that PhantomJS was still preparing the images when the render was executed. Moving the render into the onLoadFinished handler, as illustrated below, solved the issue. Before, the page.render was being called immediately after the page.content = assignment.
For those interested in doing something similar, here's the gist of the process we are doing:
var htmlForAllPages = [];
then, as we load each page in PhantomJS:
var img = page.renderBase64('PNG');
...
htmlForAllPages.push('<img src="data:image/png;base64,' + img + '">');
...
When done, the final PDF is created... We have a template file ready, with all the required HTML and CSS etc. and simply insert our generated HTML into it:
var fs = require('fs');
var template = fs.read('DocumentationTemplate.html');
var finalHtml = template.replace('INSERTBODYHERE', htmlForAllPages.join('\n'));
var pdfPage = new WebPage();
pdfPage.onLoadFinished = function() {
pdfPage.render('Final.pdf');
pdfPage.close();
};
pdfPage.content = finalHtml;

Resources