Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

backingmode(RETAINED) bug

Jmv38Jmv38 Mod
edited February 2015 in Bugs Posts: 3,297

There is a a small annoying bug with draw() function: when a project is run, there is a small glitch after 1/5 second, which is ok in backingmode STANDARD, but resets your picture in RETAINED mode. Its seems that the backingmode() function is really applied 1/5s after the beginning of the project start.

@Simeon I can easily manage that by delaying the computations start with a tween.delay(), but since it is a bug, maybe you want to solve it? Or will you let it 'as is'? I would like to know your choice on that, to adapt my code to this choice. Thanks.

Below is some code to reproduce the bug:
[edit] changed a couple colors to make it even more obvious.


-- test backing function setup() ball = {x=0, y=HEIGHT/2, r=50} tween(1, ball, {x=WIDTH}, {loop=tween.loop.pingpong} ) stroke(0) strokeWidth(2) fill(255) -- choose one of these by commenting one of the lines draw = draw1 -- STANDARD backing mode draw = draw2 -- or RETAINED -- this is to init backingmode during setup (doesnt help) --draw() end function draw1() if backingMode() ~= STANDARD then backingMode(STANDARD) return end background(255, 0, 20, 255) ellipse(ball.x,ball.y,ball.r) end function draw2() if backingMode() ~= RETAINED then backingMode(RETAINED) background(255, 0, 20, 255) return end ellipse(ball.x,ball.y,ball.r) end -- this is to init backingmode before setup (doesnt help either) -- draw2()

Comments

  • SimeonSimeon Admin Mod
    Posts: 5,626

    Thanks @jmv38. I'd like to fix this if possible.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    thanks for your answer.
    Then i'll wait for it to be solved, and just make a simple workaround meanwhile.

  • SimeonSimeon Admin Mod
    edited February 2015 Posts: 5,626

    @Jmv38 so this is actually a tricky thing to guarantee under a multithreaded model, but I have implemented a fix that you can use to guarantee a retained backing on frame 1.

    In the fixed version, calling backingMode(RETAINED) in the global scope (e.g., outside of setup()) will ensure that you get a retained backing mode by the time setup() is called. This will be in the next beta build.

    However, calling backingMode(RETAINED) from inside draw() or setup() will not happen immediately. This is because of the teardown and setup required to set (or unset) retained backing mode.

    Basically what is happening is your draw() function is happening on a render thread, and it is possible to "queue up" multiple draw() functions for performance reasons. However, the backingMode function needs to completely reset OpenGL in order to work, so it also needs to "queue up" after the last scheduled draw function.

    This means that often there will be 1 or 2 frames before Codea gets around to evaluating your backingMode call.

    Here's a diagram:

    Lua                      Renderer
    
    draw()                   Frame #1
      backingMode()---|
      background() ---|-----> glClear 
                      | 
                      |       Finish Frame #1
                      |
    draw()            |      Frame #2 (already queued by the time backingMode is called!)
                      |       Finish Frame #2
                      | 
                      |-----> TEARDOWN
                              SETUP NEW BACKING
    
    draw()                   Frame #3
                              Finish Frame #3
    

    Because of this new asynchronous behaviour, it might be better to update the API to backingMode to accept an optional callback when the backing mode is set, in case you need to do your initial drawing, e.g.,

    function setup()
        backingMode( RETAINED, function()
            -- Backing mode has been set, do some drawing
            background(255,0,0)
    
            -- Some static content
        end)
    end
    

    Alternatively we could have a global function that Codea calls when the mode is changed:

    function backingModeChanged( newMode )
        if newMode == RETAINED then
            -- Do some initial drawing 
        end
    end
    

    (Similar to orientationChanged global callback)

  • Jmv38Jmv38 Mod
    Posts: 3,297

    wow, there is so much under the hood, good thing you manage all that for us and we have only to understand lua...
    I would go for the the backingMode( mode, optionalCallback, ...) calling optionnalCallback(...), because this increase complexity only for those who want to go that far, and keep things unchanged for others. And it does not introduce another function. And this callback mechanism is very standard in Codea now.
    Thanks.

  • Jmv38Jmv38 Mod
    edited February 2015 Posts: 3,297

    Btw, is there any chance to get access to the frame grabber some day? Today the only way to save currently displayed image is to draw() into an image with setContext(img), but this does not fit well with backingmode(retained).
    Another usage would be to capture some pictures from the internal web browser, this is not possible today. This is not very useful for making games, this is more for the fun of using Codea as an allmighty tool for ipad.
    Thanks.

  • SimeonSimeon Admin Mod
    edited February 2015 Posts: 5,626

    Actually @Jmv38 it's possible to write the backingMode + callback option directly in Codea now, so I won't implement the callback option as an official API.

    (EDIT: However the next version does implement backingMode() support for the global scope, which is good for anyone that just needs to set backingMode once at the start of their project.)

    Here is a version of your example which should work:

    function backingModeDraw( mode, callback )
        backingMode( mode )
    
        local oldDraw = draw
        draw = function()
            callback()        
            oldDraw()
            draw = oldDraw
        end
    end
    
    function setup()
        ball = {x=0, y=HEIGHT/2, r=50}
        tween(1, ball, {x=WIDTH}, {loop=tween.loop.pingpong} )
        stroke(0)
        strokeWidth(2)
        fill(255)
    end
    
    function draw()
        if backingMode() ~= RETAINED then 
            backingModeDraw(RETAINED, function()
                background(255,0,20,255)
            end)
    
            return
        end
    
        ellipse(ball.x,ball.y,ball.r)
    end
    

    (This basically "hijacks" the draw function for one frame after it is called.)

  • Jmv38Jmv38 Mod
    edited February 2015 Posts: 3,297

    nice idea, but unforunately it doesnt work: the background color is called before the baking mode is actually changed, so when i run your code i get a black background. Did it work for you?

  • SimeonSimeon Admin Mod
    edited February 2015 Posts: 5,626

    @Jmv38 actually I think that is due to a separate bug I just fixed — the layout system was queuing up a layer backing change as well as the backingMode call. It no longer does that.

    My fix above should work in the next beta version.

  • Jmv38Jmv38 Mod
    edited February 2015 Posts: 3,297

    however this works (edit: simultaneous posts, i couldnt read your above comment when i posted that)

    function backingModeDraw( mode, callback )
        backingMode( mode )
    
        local oldDraw = draw
        local countdown = 15
        draw = function()
            if countdown == 0 then
                callback()        
                draw = oldDraw
            end
            countdown = countdown - 1
        end
    end
    
    function setup()
        ball = {x=0, y=HEIGHT/2, r=50}
        tween(1, ball, {x=WIDTH}, {loop=tween.loop.pingpong} )
        stroke(0)
        strokeWidth(2)
        fill(255)
    end
    
    function draw()
        if backingMode() ~= RETAINED then 
            backingModeDraw(RETAINED, function()
                background(255,0,20,255)
            end)
    
            return
        end
    
        ellipse(ball.x,ball.y,ball.r)
    end
    
  • Jmv38Jmv38 Mod
    Posts: 3,297

    i was surprised i had to set the countdown as high as 15. the value of 10 sometimes works, sometimes not.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    next beta: i am still under ios 7.x, sorry for that :-\"

  • SimeonSimeon Admin Mod
    edited February 2015 Posts: 5,626

    @Jmv38 yeah, that's not due to the renderer queuing up frames — it can only queue up a maximum of 2 frames (otherwise touches would potentially lag).

    What is happening is another layout is being triggered after displaying the viewer (after about 10 frames it seems), which is causing the OpenGL layer to be set again, which queues up a teardown / setup. When it tears down the backing mode is still retained, but because you only ever set the background colour once it never gets set back to red after the layout causes the reset. This extra layout has been eliminated.

    The next build should be out shortly, in which you do not need the countdown and can use my solution. You will also be able to set the backing mode from global scope (much simpler for most uses).

  • Jmv38Jmv38 Mod
    edited February 2015 Posts: 3,297

    Btw, one more thing: this forum and your reactivity are really a great part of Codea experience. Some months ago i tried to switch to pythonista, because my son was starting python courses, but after a couple months i kind of stopped: it is a great environement, but the forum is 10x less active and enthousistic than this one, so after a while programming felt like... work, not fun. While writing programs under Codea and sharing them is just so much fun!
    Thank you for that!

  • SimeonSimeon Admin Mod
    Posts: 5,626

    Thanks! I had a lot of fun trying to solve this bug as well.

    I'll put the next beta on the old TestFlight as well so you can try it. (It shuts down on the 26th of February, so there is still some time to use it.)

  • SimeonSimeon Admin Mod
    Posts: 5,626

    @Jmv38 let me know if you are able to use the beta and if it solves the issue.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    i will

  • Jmv38Jmv38 Mod
    Posts: 3,297

    @simeon i've installed the lastest beta, and it works without the countdown now. Great job!

  • SimeonSimeon Admin Mod
    edited February 2015 Posts: 5,626

    @Jmv38 the following should also work now

    backingMode(RETAINED)
    
    function setup()
        background(255,0,20,255)
        ball = {x=0, y=HEIGHT/2, r=50}
        tween(1, ball, {x=WIDTH}, {loop=tween.loop.pingpong} )
        stroke(0)
        strokeWidth(2)
        fill(255)
    end
    
    function draw()
        ellipse(ball.x,ball.y,ball.r)
    end
    
  • Jmv38Jmv38 Mod
    Posts: 3,297

    yes it does.

  • dave1707dave1707 Mod
    Posts: 9,279

    @Simeon Where is the proper place to put backingMode(RETAINED). Here's some code I posted in another discussion about the RETAINED problem. If I run this code with RETAINED before setup(), I get the numbers 1 thru 20 displayed. If I put RETAINED in the setup() function, the number 1 doesn't show.


    displayMode(FULLSCREEN) backingMode(RETAINED) -- when run, always shows the numbers 1 thru 20 function setup() --backingMode(RETAINED) -- when run, doesnt show the number 1 count=0 fill(255) end function draw() if count<20 then count=count+1 text(count,WIDTH/2,HEIGHT-count*30) end end
  • SimeonSimeon Admin Mod
    Posts: 5,626

    @dave1707 if you need a permanent and immutable backing store in your project, I would suggest putting it in the global scope. If you need to switch between RETAINED and STANDARD while running, I would recommend the solution I posted above (I.e., it waits one draw cycle before kicking in, calling your initial drawing at the correct time).

  • I used the following code to fix the backingMode(RETAINED) problem in all of my projects. The code below also helps with other graphical glitches that occur in the first second after pressing the play button in the editor. :)

    function draw()
    
        if ElapsedTime > 0.5 then
           <insert all of your drawing code here>
        end
    
    end
    
  • dave1707dave1707 Mod
    Posts: 9,279

    Here another way to do it. You can change _a to another variable name if you want to. But what I don't like about this is the extra code that's executed each draw cycle. By skipping the 1 draw cycle at the start, I didn't loose any frames in the RETAINED testing I did. This isn't needed if RETAINED is set before setup() starts, but is used when RETAINED is in setup().

    function draw()
    
        if not _a then _a=true return end                -- skips 1 draw cycle
    
        --add drawing code here
    
    end
    
  • @dave1707 Just combine that with your recent discovery about a function rewriting itself:

    function draw()
        function draw()
            -- put all your drawing code here
        end
    end
    
  • dave1707dave1707 Mod
    edited February 2015 Posts: 9,279

    @LoopSpace That works and there isn't the extra code thats needed to be executed in draw(). It might be a little confusing for someone that hasn't seen the nested functions.

  • Posts: 61

    It's a bit disenchanting when a newbie drops onto a system bug after a few hours experimenting with Codea.
    Dave gave me a 'backingMode(RETAINED)' solution to my problem and looking into it I came across this thread. This forum has to be the most important supplement to the 'manual' even though most of it goes way over a newbies head.

  • SimeonSimeon Admin Mod
    Posts: 5,626

    @Steep what system bug are you referring to?

  • dave1707dave1707 Mod
    Posts: 9,279

    @Steep If you put backingMode(RETAINED) before setup(), then there isn't a problem with it. If you put it within the setup() function, then the first and/or second draw isn't retained. That requires at least 2 draw call to be ignored. Until it gets fixed, if in can be, you'll have to work around it.

  • Posts: 61

    Simeon - http://codea.io/talk/discussion/comment/57152/#Comment_57152
    As a relative newbie to Codea, Lua and the iPad I expect to code within the functions setup and draw. Debugging code is a problem in any event and where the solution involves calls outside those two functions it becomes a nightmare to newbies.
    I use 'bug' as a general term for coding problems.

  • Jmv38Jmv38 Mod
    edited February 2015 Posts: 3,297

    @Steep puting code outside setup and draw is weird for a newbee indeed, i remember i was there too. However, this is not weird at all for lua people. Check the web for lua, you'll see tons of code. Codea is built on top of lua, so it is not going to narrow lua usage. So you'll have to learn lua, but slowly is ok.
    Oh, btw, codea is really almost bug free, compared to many other platforms! I really enjoy that, when there is a bug in my code, 99.9% of times it comes from me, not codea!

  • SimeonSimeon Admin Mod
    Posts: 5,626

    @Steep backingMode is a very special case. Either you can have it functioning in setup — but then the entire setup() function must be evaluated prior to drawing (so you can't actually draw in setup).

    This used to be the case in an older version of Codea, however many people preferred to have the ability to draw in the setup function, especially using setContext to pre-render images for use later.

    Anything that touches how iOS is actually going to display your view prior to presenting it will not work immediately in setup(). I think only backingMode and supportedOrientations fit this description.

  • Posts: 61

    As a. newbie I can only describe that as a bug.
    There are three possible causes. Me, my iPad, Codea or Lua.
    I'm thankful for help that I get from this forum.

Sign In or Register to comment.