invalidating bitmaps/sprites

I have a carousel of large images. In order to optimize the amount of available memory I am trying to remove the offscreen sprites from the compositor and then invalidate them. However after i have done so using the r2d2_bitmaps command on port 8080 still shows the bitmaps in the graphics memory. What are the steps for removing a sprite and it’s corresponding bitmap from memory?

the relevant functions are below


'avalanche is called from a videoplayer, ads is json from the default getads interface

function avalanche(ads) as boolean
	av = setup(ads)
	if av.load("http://vmg-us.s3.amazonaws.com/demo/designlab/av-demo-app/source/rokucontent.json")
    	if av.events()
    		return true
    	end if
    end if
End function

function setup(ads) as object
    this = {
        port:      CreateObject("roMessagePort")
        index: {x: 0, y: [0]}
        paused:    false 
        fonts:     CreateObject("roFontRegistry") 'global font registry
        canvas:    CreateObject("roImageCanvas") 'user interface
        compositor:    CreateObject("roCompositor") 'user interface
        clock: CreateObject("roTimespan")
        adclock: CreateObject("roTimespan")
        player:    []
        tobedrawn: []
        ads: ads
        bitmaps : []
        introplayed: false
        interacted : false
        load:      load
        downloading: {}
        videocontrolcanvas: []
        videocontroldata: []
        initialblack: initialblack
        spriteinator: spriteinator
        canvinator: canvinator
        videocontrols: videocontrols
        track:     track
        events:    events
        sprites:   []
        canvasitems: []
        imagecachecontrol: imagecachecontrol
        canTranslate: canTranslate
        translate: translate
        bounds: bounds
        frames: []
        adtimer: adtimer
        crumblabel: crumblabel
        crumblabeldata: []
        adtimerdata: []
        close: close
        get: get
        fade: fade
        selector: selector
        selected: 0
        action: action
        play: play
        openstore: openstore
        writeessay: writeessay
        waitforfont: []
        drawing: 0
        cachefull: false
    }

    this.canvas.SetMessagePort(this.port)
    this.canvas.SetLayer(0, { Color: "#000000" })
    loading = []
    loading.Push({
        Text: "Creating Your Interactive Ad Experience..."
        TextAttrs: { font:this.fonts.Get("Syfy Medium", 12, False, False), color: "#ffffff" }
        TargetRect: {x: 530, y: 250, w: 300, h:200} 
    })  
    this.canvas.SetLayer(1, loading)
    this.displaymode = "canvas"
    this.canvas.Show()
	return this 
end function

function spriteinator(file as string, sprite) as object
                tempfile = "tmp:/"+ file.tokenize("/")[int(file.tokenize("/").count() - 1)].tostr()
            
                if sprite.meta.delta <> invalid
                    delta = sprite.meta.delta
                    x = (sprite.meta.offset.tokenize("|")[0].toint() - (sprite.meta.dimension.tokenize("|")[0].toint() * m.index.x * delta.tokenize("|")[0].tofloat()))
                else
                    x = sprite.meta.offset.tokenize("|")[0].toint()

                end if

                bitmap = CreateObject("roBitmap", tempfile)
                m.bitmaps.push(bitmap)

                sprite = m.compositor.newsprite(x, sprite.meta.offset.tokenize("|")[1].toint(), createobject("roregion", bitmap, 0, 0, sprite.meta.dimension.tokenize("|")[0].toint(), sprite.meta.dimension.tokenize("|")[1].toint()), sprite.meta.offset.tokenize("|")[2].toint())
                if sprite = invalid
                	m.cachefull = true
                end if
                return sprite

end function

function imagecachecontrol() 
    if m.cacherefresh = m.index or m.cacherefresh = invalid
    this = {carousel:0,x:0,y:0,grid:[],filecheck:CreateObject("roFileSystem")}
    while m.cachefull <> true
        for each sprite in m.sprites
                if sprite.meta.imgsrc <> invalid
                    this.tempfile = "tmp:/"+ sprite.meta.imgsrc.tokenize("/")[int(sprite.meta.imgsrc.tokenize("/").count() - 1)].tostr()
                end if
                this.carouselwidth = m.canvas.getcanvasrect().w
                this.carouselheight = m.canvas.getcanvasrect().h

                this.gridx = sprite.meta.offset.tokenize("|")[0].toint() / this.carouselwidth
             if sprite.meta.delta <> invalid
                    if sprite.meta.delta.tokenize("|")[0].toFloat() <> 0 
                        this.gridx = this.gridx / sprite.meta.delta.tokenize("|")[0].toFloat()
                    end if
            end if
                this.gridy = sprite.meta.offset.tokenize("|")[1].toint() / this.carouselheight
            
            if sprite.meta.delta <> invalid
                if sprite.meta.delta.tokenize("|")[1].toFloat() <> 0
                    this.gridy = int(this.gridy / sprite.meta.delta.tokenize("|")[1].toFloat())
                end if
            end if
            if sprite.meta.position <> invalid
                this.gridx = sprite.meta.position.toint()
            end if
                square = [this.gridx, this.gridy]
                if sprite.meta.contentmodel <> "videocontrol"
    	            if (square[0] = m.drawing) or (sprite.meta.contentmodel = "crumb" or sprite.meta.contentmodel = "crumb-behind")
                        ?"drawing sprites for panel" + m.drawing.tostr()
                        if sprite.meta.scrollabletext<>invalid 
                            if m.fonts.getfont(sprite.meta.scrollabletext.tokenize("|")[5], sprite.meta.scrollabletext.tokenize("|")[6].toint(), False, False) <> invalid
                                sprite.sprite = m.writeEssay(sprite)
                            else
                                m.waitforfont.push(sprite.meta)
                            end if
                        end if

    	                if (this.filecheck.exists(this.tempfile) = false) and (sprite.meta.imgsrc <> invalid)
    			             m.get(sprite.meta.imgsrc, sprite.meta)

                            ?"Downloading"
                            ?sprite.meta

    	                else if (sprite.sprite = invalid) and (this.filecheck.exists(this.tempfile) = true) and (sprite.meta.imgsrc <> invalid)
                            if m.frames.count() = 0 
    	                        sprite.sprite = m.spriteinator(this.tempfile, sprite)
                            else 
                                m.tobedrawn.push([this.tempfile, sprite])
                            end if
    	                end if
    	            end if
    	         end if
        end for
    if m.events() and m.cachefull <> true
        m.drawing = m.drawing + 1
    end if
    end while

    for each sprite in m.sprites
 
       'i decided to remove all the sprites from my parent function to see if anything happened

        if sprite.sprite <> invalid 
        ?"REMOVING"
        ?sprite.meta
        stop
        sprite.sprite.remove()
        sprite.sprite = invalid
        global.rungarbagecollector()
        end if
    end for

    end if
end function

While taking only a quick scan of your code you appear to use a compositor with an imagecanvas. Compositors are used with an roScreen. Imagecanvas has its own built in way of displaying images. To release all resources you need to invalidate all created bitmaps as well. It’s generally the bitmap that you see in memory. I would reconsider using roScreen for a carousel or use the imagecanvas with correct layers set to the image URL

Generally your AA keeps a handle to your region (optional) the bitmap and the Sprite. When the Sprite is removed then both the bitmap and the Sprite inI the AA are invalidated to release memory. Memory is recovered with the next call to swapbuffers using the roScreen. I have no experience using the compositor apart from roScreen nor do I completely understand the reason for its use apart from roScreen. Sprites are positioned on an roScreen and used for collision, layering etc. exactly what to you intend to do with the Sprite

Well, to be honest when i set this up I had no idea what i was doing.

I am using the sprites to contain bitmaps, so that I can layer them, and more easily move them across the screen, and in particular create a neat parrallaxing effect ( each layer translates at a slightly different rate to create a false sense of depth) when the carousel advances.

rather than establishing a region the size of the screen for a much larger bitmap and then changing it’s offset within the region to scroll, I have several sprites, where each sprite is it’s own layer, and then I use the sprite’s moveto utility in order to scroll.

In the netflix app when you scroll down from their initial hero image, it parallaxes slightly as it fades out. I am trying to replicate something similar for each panel of the carousel, altho without the fadeout.

the carousel has a series of panels, where each covers the whole screen.

That’s a very appropriate way to use sprites. However that will only work with roScreen ( which perhaps you are using? ) not imagecanvas. Furthermore you cannot use an imagecanvas and an roScreen at the same time. They have incompatible window stacks. Notice that compositor.setdrawto takes an roScreen or bitmap handle. Either way they can only be drawn on an roScreen not an inagecanvas. Imagecanvas is also inappropriate for a carousel and it is much too slow

If you want to pull off what you describe and have a large number of bitmaps that need to be memory managed your best bet is the roScreen and the rotexturemanager

After reading further about your Netflix effects. A combination of direct screen draw and region offsetting is faster than redrawing or moving each Sprite esp in roku 1. You can create entire or partial regions within any bitmap or combination of bitmaps such as the top row of a grid bitmap and animate them drawing the region directly to the screen and offsetting the regions that you only wish to scroll normally such as rows below the top row. To pull this off you basically define your region to be animated ( this is usually a composite ), set the z order of those sprites you are animating via their regions to < 0 to hide the Sprite, call compositor.drawall to draw all sprites not in the animation, then use the roScreen drawing functions to perform your animation and then call swapbuffers. When done you simply offset the hidden sprites to their final destination, change the z order back to visible and redraw. I use this approach all the time to create complex interface. The only drawback (and not so much) is that your direct screen draw will overwrite any visible Sprite. If you need to perform layered animation then you simply get those regions and later your direct drawing as you would if you did not use compositor

Yeah, it occurred to me that this is probably the case. i will look into that.

This is in response to your previous post:

I did not post the full code, a roscreen is defined, but not until after the first round of bitmaps is downloaded. I am using rourlttansfer to manage my bitmaps, mainly because i didn’t want to have a separate get function for bitmaps and my other http get requests. Is there a benefit to using the texturemanager over an asynchronous urltransfer and then creating a bitmap from the filesystem?

There are two seperate screen stacks. I am not sure if this is the intent, but whichever screen stack is defined last ends up showing on top. So if i define a roscreen and then define a roimagecanvas it works similar to the regular screen stack. Altho both cover the whole screen.

I need a videoplayer on the screen on user initiation, however i dont want to deconstruct my roscreen / all my sprites yet. If you display the rovideoplayer on roscreen it always shows on the bottom layer below the screen and is covered by other image data. i decided to solve this by defining a new image canvas and then targeting my videoplayer and it’s ui to that. Then to return to the screen i just close the image canvas.

I have a working version that displays the carousel and video and animations as intended.

The problem l am specifically having is this: over the course of navigating through the carousel of images, images are downloaded from a server before they come into view, and then a sprite is generated to add to the carousel. Other images are deleted in order to give those space. This works fine but i have to preset the number of frames it loads in each direction.

What i want to do is this: load and create bitmaps until a bitmap returns invalid when defined. Then delete the frame of images that weren’t completely loaded. This will reduce the number of times i have to load.

Right now this fails during the main loading sequence. The bitmaps are loaded, one returns invalid, the sprites for the extra frame are removed, but the bitmaps for those sprites remain in memory. When a new bitmap is created to fadeout the load screen the memory is already full.

I think i may need to call screen.swapbuffers, maybe the bitmaps are not released until the buffer has cleared.

I cant test until tomorrow.

Looking at it this morning, it seems that i need to call screen.clear> compositor.drawall> screen.swapbuffers before invalidating my sprites, regions, and bitmaps. After doing it in this order the memory for the bitmaps used in sprites is cleared.

That’s true. It’s usually the next call. Everything you describe in your Netflix prototype can be performed with roScreen. Sprites can be hidden by setting the z order to < 0. They can even be reused by simply changing their region (SetRegion) to a new bitmap region. Using a compositor introduces a bit more complexity for implementing a video player as it always clears the screen with your call to drawall. But a full screen videoplayer can be easily implemented by setting the color value in setdrawto to transparent. A smaller video player can be carved out of a region of the window itself ( create a region using roScreen) and clear that area sandwiched between drawall and swap buffers. Panels can be created and sprited over an entire transparent window Using their own transparent region within their bitmap to expose the videoplayer. While other accessory components can take advantage of the panel bitmap real estate and create their own regions with which to draw themselves Saving resources and memory. regardless the player can only be exposed by drawing or clearing a transparent area over it with the proper alpha settings. There are some anomalies with firmware 7 that I noticed when it comes to clearing or drawing transparently to the window itself. It appears to change the alpha enable setting of the window. I don’t know if this applies to having a compositor or not. In any event depending on the setting of the window you generally will set it false to clear the transparent video region before your call to any roScreen drawing or clearing function and then set it back before you call swapbuffers otherwise any other alpha blending you have in accessory components will not be rendered properly after the swapbuffers returns

As far a texturemanager vs rourltransfer the advantage is in what you need it for. Rourltransfer is good for downloading once or infrequently a complete set of textures. If you need to manage memory because you have many bitmaps then you essentially have to create a system to manage loading And unloading. This is where the texturemanager outperforms. It is already designed to request, cancel and load bitmaps. And it does it very well. It also has the added advantage of an LRU which is pretty good so subsequent reuse of unloaded bitmaps will appear faster

That’s some very useful information. I am going to ditch the image canvas entirely, and also switch to using the texturemanager for bitmaps.

There are some anomalies with firmware 7 that I noticed when it comes to clearing or drawing transparently to the window itself. It appears to change the alpha enable setting of the window

The compositor seems to set the alpha enable of the entire screen to false if you clear it with a non-transparent color ie &hffffffff

:mrgreen:

I write 2d extensively and always have a few alpha blends somewhere on the screen so I always create my window with alpha enabled since I’m constantly needing it to be enabled. Clearing with a non-transparent does not change the setting for me only when I specifically create a region on the roScreen itself or use roScreen drawrect with transparency does the setting get changed. Like I said direct calls to roScreen are always sandwiched in between compositor.drawall and swapbuffers. I also found in the past that this worked somewhat different on devices with or without open gl. You generally had to Disable, draw then re-enable to insure everything rendered correctly with the next swapbuffers