WebGL 2 When to clear the drawing buffer? - webgl2

I am currently working on a demo that calls readPixels.
This answer on SO is most of the information I can find on the preserveDrawingBuffer option.
While testing I've observed that in WebGL 2 this answer remains true - you have to set preserveDrawingBuffer to true.
Is this actually correct?
Is there an OpenGL equivalent for preserveDrawingBuffer?
Is there any way to set preserveDrawingBuffer to false and still call readPixels?
This answer makes it seem like you could call gl.flush instead.
How is preserveDrawingBuffer the same thing as flushing the context?

You don't need preserveDrawingBuffer: true to call readPixels. What you need is to call readPixels before exiting the current event.
The spec says if you call any function that affects the canvas (gl.clear, gl.drawXXX) then the browser will clear the canvas after the next composite operation. When that composite operation happens is up to the browser. It could be after it processes several mouse events or keyboard events or click events. The order is undefined. What is defined is that it won't do it until the current event exits so
render
read
const gl = document.querySelector("canvas").getContext("webgl2");
render();
read(); // read in same event
function render() {
gl.clearColor(.25, .5, .75, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
function read() {
const pixel = new Uint8Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
log(pixel);
}
function log(...args) {
const elem = document.createElement("pre");
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
<canvas></canvas>
works where as
render
setTimeout(read, 1000); // some other event
does not work
const gl = document.querySelector("canvas").getContext("webgl2");
render();
setTimeout(read, 1000); // read in other event
function render() {
gl.clearColor(.25, .5, .75, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
function read() {
const pixel = new Uint8Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
log(pixel);
}
function log(...args) {
const elem = document.createElement("pre");
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
<canvas></canvas>
Note that since it's the composite operation (the browser actually drawing the canvas on the page with the rest of the HTML) that triggers the clear, if the canvas is not on the page then it's not composited and won't be cleared.
In other words the case that didn't work above does work here
// create an offscreen canvas. Because it's offscreen it won't be composited
// and therefore will not be cleared.
const gl = document.createElement("canvas").getContext("webgl2");
render();
setTimeout(read, 1000); // read in other event
function render() {
gl.clearColor(.25, .5, .75, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
function read() {
const pixel = new Uint8Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
log(pixel);
}
function log(...args) {
const elem = document.createElement("pre");
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
If you want to call readPixels in some other event, like when the user clicks an element, then you have at least 2 options
Set preserveDrawingBuffer: true
Render again in your event
screenshotButton.addEventListener('click', () => {
render();
read();
});
From the spec section 2.2
WebGL presents its drawing buffer to the HTML page compositor immediately before a compositing operation, but only if at least one of the following has occurred since the previous compositing operation:
Context creation
Canvas resize
clear, drawArrays, or drawElements has been called while the drawing buffer is the currently bound framebuffer
Before the drawing buffer is presented for compositing the implementation shall ensure that all rendering operations have been flushed to the drawing buffer. By default, after compositing the contents of the drawing buffer shall be cleared to their default values, as shown in the table above.
This default behavior can be changed by setting the preserveDrawingBuffer attribute of the WebGLContextAttributes object. If this flag is true, the contents of the drawing buffer shall be preserved until the author either clears or overwrites them. If this flag is false, attempting to perform operations using this context as a source image after the rendering function has returned can lead to undefined behavior. This includes readPixels or toDataURL calls, or using this context as the source image of another context's texImage2D or drawImage call.

Related

AFRAME: Event on completion of dynamic adding of component

My use-case is as follows:
In a loop, entities are being created and components are being set up. This is via a json-object that is being passed to the function. The question I have is how best to get an event that the whole set of entities and their components are being initialised. The code is something like this
var parent = document.querySelector('#parent');
var ent = document.createElement('a-entity');
parent.appendChild(ent);
for(var i =0; i = components.length; i++) {
var arr = components[i];
var cl = arr[0]; // class name
var attr = arr[2]; // component name
var attrV = arr[3]; // component data
ent.setAttribute('class', cl);
AFRAME.utils.entity.setComponentProperty(ent, attr, attrV);
//ent.setAttribute(attr, attrV); tried with this too
}
console.log('loop completed')
The loop completed gets logged before the completion of the loading of some of the components. I would like to have some sort of a call back to know that all the components have been completed loaded.
There seems to be an event componentinitialized but it sends a return for only 1 component. My real requirement (not reflected in above code) is that an entity can have multiple components added.
To use the above, I may have to set this event for every component and keep track of whether it has been completed or not. Just wondering if there is a more elegant way to do it. Thanks
Entities emit the "loaded" event. It should be easier than listening for each component initialization within the entity.
Try out:
entity.addEventListener("loaded", (e) => {
console.log(e)
})
like i did here.

Activity Indicator is not visible on xaml page in xamarin.forms?

I have an activity indicator on xaml page. Initially its IsVisible property is false. I have a button on page. When user click on button it calls a web service to get data. I change the value of IsVisible property to true before calling the service so that activity indicator starts to display on page and after successful calling of service I change its value to again false so that it doesn't show any more on page.
But it is not working. I know the actual problem. When we call the web service the UI thread gets block and it doesn't show the activity indicator.
How I can enable the UI thread when web service gets called so that activity indicator can show on page until we get the data?
Try making your webservice call into an async and await it.
Depending on how you've structured things you may have to use a TaskCompletionSource as the following example demonstrates.
In this example when the button is clicked, the button is made invisible, and the ActivityIndicator is set to IsRunning=True to show it.
It then executes your long running task / webservice in the function ExecuteSomeLongTask using a TaskCompletionSource.
The reason for this is that in our button click code, we have the final lines:-
objActivityIndicator1.IsRunning = false;
objButton1.IsVisible = true;
That stop the ActivityIndicator from running and showing, and also set the button back to a visible state.
If we did not use a TaskCompletionSource these lines would execute immediately after calling the ExecuteSomeLongTask if it was a normal async method / function, and would result in the ActivityIndicator not running and the button still being visible.
Example:-
Grid objGrid = new Grid()
{
};
ActivityIndicator objActivityIndicator1 = new ActivityIndicator();
objGrid.Children.Add(objActivityIndicator1);
Button objButton1 = new Button();
objButton1.Text = "Execute webservice call.";
objButton1.Clicked += (async (o2, e2) =>
{
objButton1.IsVisible = false;
objActivityIndicator1.IsRunning = true;
//
bool blnResult = await ExecuteSomeLongTask();
//
objActivityIndicator1.IsRunning = false;
objButton1.IsVisible = true;
});
objGrid.Children.Add(objButton1);
return objGrid;
Supporting function:-
private Task<bool> ExecuteSomeLongTask()
{
TaskCompletionSource<bool> objTaskCompletionSource1 = new TaskCompletionSource<bool>();
//
Xamarin.Forms.Device.StartTimer(new TimeSpan(0, 0, 5), new Func<bool>(() =>
{
objTaskCompletionSource1.SetResult(true);
//
return false;
}));
//
return objTaskCompletionSource1.Task;
}
You need to do your work in an asynchronous way. Or in other words: Use Asnyc & Await to ensure, that you UI works well during the call.
You can find more informations in the Xamarin Docs.
async and await are new C# language features that work in conjunction
with the Task Parallel Library to make it easy to write threaded code
to perform long-running tasks without blocking the main thread of your
application.
If you need further asistance, please update your question and post your code or what you have tried so far.

How to animate a tilemap

I'm new to cocos2d and coding and I'm sure this question was asked many times already, but I want to animate a ghost in a tilemap to go up about 150 pixels when the player is next to to it. I have
`CCSprite* sprite1 = [CCSprite spriteWithFile:#"enemy.png"];
sprite1.position = ccp(464, 80);
[self addChild:sprite1];
[sprite1 runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:1 position:ccp(0, 150)],
[CCMoveBy actionWithDuration:1 position:ccp(0, -200)], nil]];`
which animates a sprite but it stays on the map. If I add it to a tilemap it will only show when the player is close. But I'm not exactly sure how to do that. Thanks in advance
Don't get it on a tilemap then, you're coding custom behavior for that specific character, work it apart from the TileMap. You'll have to work on sensors if you're planning on getting efficient code, maybe Box2D or the built in Chipmunk engine that CocosV3 has recently fully integrated, or an "easy" way out of this, if you're not planning on making a sliding scenario you could work simple coordinates and an event listener on the update method, so when the "character" reaches the point you wish the ghost to appear, well then you can make that method happen.
Wrap the custom behavior on a class that you may further reuse , maybe a class named sensorSprite , once you code the default behavior for the class, you could create methods to instantiate the object with specific coordinates to generate the sensor around, or some other cool stuff.
Here's what your class could look like.
Header File
#interface SensorSprite : CCSprite {
kCharacterState currentState;
}
-(id)initWithSpriteFrame:(CCSpriteFrame *)spriteFrame inCoordinates:(CGPoint)spawnLocation withParent:(id)theParent;
-(void)updateSensorSpriteWithCharacters:(NSMutableArray*)charactersArray;
#end
Implementation File
typedef enum {
kCharacterStateAlive,
kCharacterStateDead,
kCharacterStateAppearing,
kCharacterStateDissapearing
}kCharacterState;
#import "SensorSprite.h"
#implementation SensorSprite
-(id)initWithSpriteFrame:(CCSpriteFrame *)spriteFrame inCoordinates:(CGPoint)spawnLocation withParent:(id)theParent{
self = [CCSprite spriteWithImageNamed:#"enemy.png"];
self.position = ccp(464, 80);
[theParent addChild:self];
return self;
}
-(void)updateSensorSpriteWithCharacters:(NSMutableArray *)charactersArray {
//If you're planning on having different characters to update your state from then you should use tags.
for (int i=0; i<=charactersArray.count-1; i++) {
CCSprite *characterInArray = [charactersArray objectAtIndex:i];
if (CGRectIntersectsRect([characterInArray boundingBox], [self boundingBox])) {
//What happens when the CG Rects from this class and another one intersects.
//For making reactive to encountering different sprites, you should use tags , and on each cycle detect what sprite is the one colliding by looking at it's tag.
}
}
}
-(void)changeStateTo:(kCharacterState)theState {
if (currentState==theState) {
//It's the same state.
return;
}
switch (theState) {
case kCharacterStateAlive:
//What happens when character state alive.
break;
case kCharacterStateDead:
//What happens when character state dead
break;
case kCharacterStateAppearing:
//What happens when character state reapearing
break;
case kCharacterStateDissapearing:
//What happens when character state dissapearing.
default:
break;
}
//After doing the required we set our current state to the changed state.
currentState = theState;
}
#end
Notice my comments on the code, it could be improved in huge ways, but this should be your base to understand what's going on here.

Titanium - Webview is not rendered when is loaded by async callback

I have loop that for each item that call a async request to server, create a view with a webview for later add in a scrollview after calc the positions. In the callback, I get the server data and give to webview.
When the load method takes a little longer to perform (In a slow connection, for example), the webview is not rendered.
This happens many times in the device, but few times in the simulator. But, to simulate easily, just put a breakpoint in the load method, delaying the execution.
I suspect that there is a problem when rendering an already added webview.
I simplified the code in a one block for better understanding:
function createView(){
var view = Ti.UI.createView({
//...
});
var webview = Ti.UI.createWebView({});
view.add(webview);
//Callback to process webview content after receive the server data
view.load = function(serverData){
webview.setUrl(serverData.url);
webview.addEventListener('beforeload', function(){
webview.evalJS("var value="+serverData.value+";");
});
};
return view;
};
var views = new Array();
for(i=0;i < data.length;i++){
views[i] = createView();
//Async request to server
var req = Ti.Network.createHTTPClient();
req.onload = function(){
result = JSON.parse(this.responseText);
views[i].load(result);
}
req.open("POST", "http://myserver.com.br/myservice");
req.send({myParam: data[i].value});
}
var scrollView = Ti.UI.createScrollView({
//...
});
for(i=0;i < views.length;i++){
//Calc the positions, not relevant
views[i].width = calculedValue;
views[i].height = calculedValue;
scrollView.add(views[i]);
}
win.add(scrollView);
Im using Titanium SDK 1.8.2 and running on iPad 2 with iOS 5.0.1.
I solved the problem evaluating the javascript data on the callback instead of on event listener
view.load = function(serverData){
webview.setUrl(serverData.url);
//Not on event 'beforeload'
//webview.addEventListener('beforeload', function(){ not on
webview.evalJS("var value="+serverData.value+";");
//});
};
I think that this is related with the UI thread is not called sometimes on event listener

Flex web application: prevent framerate drop when window is invisible

So there's been a new "feature" in the flash player since version 10.1, which reduces the player's framerate to 2 fps when the application window is out of view. This is good news for performance, but it can break some functionality, such as the Timer class.
I have an application which uses a Timer to display a countdown. Given the nature of the application, it is required for the Timer to complete its countdown even if the user is not there to see it. Imagine that you need to give the user only 10 seconds to perform a task. If the user minimizes the window halfway through the counter, they can take as much time as they want and still have 5 seconds left when they return to the window. This apparently can not be avoided with the newer flash players.
In Air applications there is the backgroundFrameRate property which can be set to prevent this behavior, but this is part of the WindowedApplication class, so it seems that it is not available in a web application. Does anyone know a way to keep a constant frame rate even when the window is not visible? Thanks
Setting the wmode parameter of the embedded swf to opaque will prevent the framerate throttling.
Brian
I've not tried myself, but maybe you can try to force the framerate onDeactivate:
stage.addEventListener(Event.DEACTIVATE, onDeactivate);
function onDeactivate (e:Event):void
{
//eg myFrameRate=24
stage.frameRate = myFrameRate;
}
Let me know if this works.
Testing with:
private var numer:int = 0;
private var prevNumer:int = 0;
private var timer:Timer = new Timer( 1000, 0 )
[...]
var tf:TextField = new TextField ();
addChild (tf);
addEventListener ( Event.ENTER_FRAME, onEnterFrame )
timer.addEventListener (TimerEvent.TIMER, onTimer )
timer.start()
function onTimer ( e:TimerEvent ):void
{ tf.appendText (' ' + (numer - prevNumer)); prevNumer = numer;}
function onEnterFrame ( e:Event ):void { numer++ }
shows clearly, that when You see the flash, tf appends numbers equal to Your FPS. If timer would get changed together with FPS, You wouldn't see a difference when minimizing a window. But, coming back You see 2 2 2 2 2, that is, FPS dropped to 2.
onDeactivate solution by AsTheWormTurns doesn't work. Event is fired, but fps not changed.
wmode=opaque solution by Mr Brian Bishop doesn't work too
something obvious to try: change onEnterFrame function to set FPS:
function onEnterFrame ( e:Event ):void { numer++; stage.frameRate = 30 }
Obviously You can't set FPS when flash is not visible! Well, You can't set FPS unless You set it to 1.
Workaround to Your problem is simple, just make another timer similar to this above but with additional conditional:
function onTimer ( e:TimerEvent ):void {
if ( numer - prevNumer == 2 ) adjustOriginalTimer();
tf.appendText (' ' + (numer - prevNumer)); prevNumer = numer;
}
E: You can read about it here: http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-8000.html

Resources