0
votes

The following code will erase a bitmap (brush akk droplet) from another bitmap (akka
The code works great on PC and pretty ok performacewise.

When i test it on more android devices, it doesn't work. No matter if is a high end device or a slower one. I've made some tests and found out the problem is lock() and unlock() functions from BitmapData. It simply doesn't update the image on device, only once.
I've tried to remove them, but the then it lags alot. Also the performace drop is noticeable on PC too. Does anyone know a solution, where am I doing wrong?

import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.geom.Point;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Rectangle;

var m:BitmapData = new water_pattern;
var b:BitmapData = new droplet;

var bm:Bitmap = new Bitmap(m);

var bla = new blabla();
addChild(bla);
bla.addChild(bm);

function p($x,$y){


var refPoint = new Point($x-b.width/2,$y-b.height/2);
for(var i=0;i<b.width;i++)
for(var j=0;j<b.height;j++)
{
    var a:uint = (b.getPixel32(i,j)>> 24) & 0xFF;
    a=0xFF-a;
    var tp:uint = m.getPixel32(refPoint.x+i,refPoint.y+j);
    var tp_trans:uint = (tp >> 24)&0xFF;
    if(tp_trans>a){

        tp=(tp&0x00FFFFFF)|(a<<24);
        m.setPixel32(refPoint.x+i,refPoint.y+j,tp);
    }
}

//for(var k=0;k<10000000;k++){};
}
var k=1;
var md = function(e)
{
    m.lock();
    p(bm.mouseX,bm.mouseY);
    m.unlock();
};

bla.addEventListener(MouseEvent.MOUSE_DOWN,function(e)
{
    bla.addEventListener(Event.EXIT_FRAME,md);
});
bla.addEventListener(MouseEvent.MOUSE_UP,function(e)
{
    bla.removeEventListener(Event.EXIT_FRAME,md);
});

I've reworked the code :

public function draw($x, $y)
        {

            var refPoint = new Point($x - brush.width / 2, $y - brush.height / 2);
            var r:Rectangle = new Rectangle(refPoint.x, refPoint.y, brush.width, brush.height);

             var pv:Vector.<uint> = pattern.getVector(r);
             var bv:Vector.<uint> = brush.getVector(brush.rect);


             for (var i = 0; i < bv.length; i++)
                {
                    var a:uint = (bv[i]>>24) &0xFF;
                    a = 0xFF - a;
                    var tp:uint = pv[i];
                    var tp_trans:uint = (tp >> 24) & 0xFF;

                //  trace(a.toString(16) + " vs " + tp_trans.toString(16));
                    if (tp_trans > a)
                    {

                        tp = (tp & 0x00FFFFFF) | (a << 24);
                        //      trace("??>" + tp);
                        pv[i] = tp;
                    }
                }

               pattern.setVector(r, pv);
        }

Now it works, but still it is pretty slow on device. That before i saw Jeff Ward's comment, so i changed it to render mode on CPU. It works fast. The big problem is in CPU mode the game is very slow compared to GPU. Yet this script is fast on CPU but unusable slow on GPU.

So I've tried again the first code and surprise. It works. Jeff Ward, thank you, you're a genius. Now the question remains is why? Can someone please explain?

1
Are you using GPU renderMode, or direct? - Jeff Ward
Have you tried direct? I wonder if GPU render mode doesn't notice that kind of update to the underlying bitmapdata. Try bm.alpha = 0.98+0.02*Math.random(); after the unlock() statement, or perhaps re-assign the bitmapdata: bm.bitmapData = m; - Jeff Ward
Yeah, it works in direct. Though while this works like a charm the rest of my game is laggy. I don't understand to be honest how something so simple gets laggy on GPU mode and doesn't work and how it does on direct, but other stuff that were working just fine are getting laggy - Nertan Lucian
GPU and direct modes take entirely different approaches to GPU acceleration, so they'll be fast/slow in different situations. Regardless, did those tricks make GPU mode work? Or maybe bm.filters = []; You need to get GPU mode to realize you've changed the BitmapData contents, and it needs to re-upload the texture (which itself could be a laggy operation, depending on the size of the bitmap). - Jeff Ward
In GPU mode, changing any pixel in a Bitmap requires a re-upload of the full bitmap, so it's the size of Bitmap that's the limiting factor. In direct mode, it blits only the portions of the screen that have been updated. So I'd guess some parts of the game change a lot of the screen at once (slow in direct mode), and this effect changes a large bitmap, but only a little bit at a time (slow in GPU mode). - Jeff Ward

1 Answers

1
votes

For your original question, sometimes GPU mode doesn't pick up changes into the underlying bitmapdata. Try any one of these operations after your unlock() to 'hint' that it should re-upload the bitmap data:

bm.filters = [];
bm.bitmapData = m;
bm.alpha = 0.98+Math.random()*0.02;

But as you found, uploading bitmapdata can be slow. To clarify GPU/direct render modes:

In GPU mode, changing any pixel in a Bitmap requires a re-upload of the full bitmap, so it's the size of Bitmap that's the limiting factor. In direct mode, it blits only the portions of the screen that have been updated. So I'd guess some parts of the game change a lot of the screen at once (slow in direct mode), whereas this effect changes a large bitmap, but only a little bit at a time (slow in GPU mode).

You have to get creative to maximize your performance wrt GPUs:

  1. In GPU mode, split the effect into many bitmaps, and only change as few as possible for any given frame. (medium effort)
  2. Use Starling GPU-accelerated framework and Starling filters (GPU shaders) to achieve your effect (effort depends on how much you have invested in your game already), see a couple of examples