Yes you have the right idea. Depending on how you write; using objects or top-down, you need to expose your texturemanager to your grid.
Always use getmessage and check the texture port first. Everything I create is an object so I am including portions of my own code so that you can get
and idea of how I do it. Works great for me. Some sluggishness on 2 boxes but there is nothing you can do about that except keep the cache to a minimum
say about 4 images in/out at a time
and throttle down the speed a couple of notches. Doing those two things I have smoother results than the home screen grids. On the 3 box it is great hardly
ever see a loading bitmap. The incoming textures do not bog the system down at all. The user can scroll as fast as they want
My grid object is created as followsFunction NewVGrid( a_config As Object ) As Object
vg = CreateObject( "roAssociativeArray" )
vg.NMLChannel = m.NMLChannel
vg.Screen = m.NMLChannel.Screen
vg.Port = m.NMLChannel.MSGPort
vg.Is4200 = m.NMLChannel.Is4200
vg.Timer = CreateObject( "roTimeSpan" )
' Reference to the global cachemanager
vg.CacheManager = m.NMLCacheManager
' Reference to cachemanagers texture manager
vg.TManager = vg.CacheManager.TManager
' Reference to cachemanagers Texture async list
vg.TMAsyncList = vg.CacheManager.TMAsyncList
' Reference to cachemanagers Transfer async list
vg.UTAsyncList = vg.CacheManager.UTAsyncList
' Reference to the texturemanager's port
vg.TMPort = vg.CacheManager.TMPort
' Reference to the cachemanager URLTransfer Port
vg.UTPort = vg.CacheManager.UTPort
...............
Now that I have references to everything I need I can use them. Here is a portion of my event loop:
Function vg_event_loop( a_lastKey = -1 As Integer ) As Integer
while( l_running )
' Check the texture queue if something there, process it
' Only draw if idle, otherwise something else is drawing
l_tmsg = m.TMPort.GetMessage()
if l_tmsg <> Invalid then m.ReceiveTexture( l_tmsg, m.GridIsIdle )
' Check the main port
l_msg = m.Port.GetMessage()
if type( l_msg ) = "roUniversalControlEvent"
...............................
Here is a portion of the code that is used for scrolling. This is the dynamic portion where I use a cache cloud to control memoryFunction vg_scroll_down_dynamic( a_frames = 16 As Integer ) As Void
' If only one row (viewer) do not scroll
if m.DataRowCount = 1 then return
' Moving Downward: Get the index at the bottom of the display ( not the invisible row )
' I do no bounds checking here. If everything is OK in set then you don't have a problem
' if you start tinkering with those values then you may give yourself a headache finding it
l_displayIndex = m.GridRowCount - 2
' Get the current gridrowindex and increment, wrap around if needed
m.GridRowIndex = m.DisplayStack[ l_displayIndex, 0 ] + 1
if m.GridRowIndex >= m.GridRowCount then m.GridRowIndex = 0
' Do the same for the datarowindex
m.DataRowIndex = m.DisplayStack[ l_displayIndex, 1 ] + 1
if m.DataRowIndex >= m.DataRowCount then m.DataRowIndex = 0
' Now push and pop the display stack
' This MUST be in the following order to work: Push onto bottom, then remove the top
m.DisplayStack.Push( [ m.GridRowIndex, m.DataRowIndex ] )
l_removedRow = m.DisplayStack.Shift()
' Dynamic scrolling has a cache cloud following it at about 4 rows at each end
' As a stack it pushes and pops as well. It is responsible for loading and unloading new
' textures as the user scrolls.
m.MCacheRowIndex = m.MCache[ m.MCacheRowCount - 1 ] + 1
if m.MCacheRowIndex >= m.DataRowCount then m.MCacheRowIndex = 0
l_dataRowIndex = m.MCache.Shift()
m.MCache.Push( m.MCacheRowIndex )
' Set the removed row's grid x, y coordinates to -1 (no longer in the display stack)
for each l_gridCol in m.GridRows[ l_removedRow[0] ]
l_texture = l_gridCol.Data
if l_texture <> Invalid
l_texture.GridX = -1
l_texture.GridY = -1
end if
end for
' Release texture resources for the removed memory cache row
for each l_dataCol in m.DataRows[ l_dataRowIndex ]
l_texture = l_dataCol.Data
' Not a filler row at the bottom
if l_texture <> Invalid
' If this is a valid request
if l_texture.TRequest <> Invalid
' Get the state - wish I did not have to do this each time its another function call
l_state = l_texture.TRequest.GetState()
'( 99.999 % percent true even with the fastest paging ) unload or cancel
' My question is still out to Joel. If a Unload will do then I don't need to check anything
' Just unload and it will figure itself out without me having to always check the state. Function calls are expensive
if l_state = m.STATE_READY
m.TManager.UnloadBitmap( l_texture.GridFileURL )
else if l_state < m.STATE_READY
m.TManager.CancelRequest( l_texture.TRequest )
end if
end if
' Set allreference to invalid to make sure everything is released
l_texture.Bitmap = Invalid
l_texture.TRequest = Invalid
l_texture.ResendCount = 0
end if
end for
' Request textures for the new memory cache row
for each l_dataCol in m.DataRows[ m.MCacheRowIndex ]
l_texture = l_dataCol.Data
' Not a dummy row and we have not already sent this one - ( usually always invalid on the TRequest )
if l_texture <> Invalid and l_texture.TRequest = Invalid
' Create the texture request and assign to the items TRequest member
l_texture.TRequest = CreateObject( "roTextureRequest", l_texture.GridFileURL )
' Add the item into the async list
m.TMAsyncList.AddItem( l_texture.TRequest.GetID(), l_texture )
' Asynchronously request the texture and return
m.TManager.RequestTexture( l_texture.TRequest )
end if
end for
' Draw the new row
m.DrawRow()
' Ease the row into view
l_offset = 0
l_prevset = 0
l_offdiff = 0
' Indicate that this function will call DrawAll for any incoming textures
' Any key-release event will reset to true
m.GridIsIdle = False
for l_i = 1 to a_frames
' Must check the texture queue beginning here. If not you may get a problem.
' Does not affect speed at all with or without its only on 2 boxes that bog down even if you removed all the code ( I have done it)
l_tmsg = m.TMPort.GetMessage()
if l_tmsg <> Invalid then m.ReceiveTexture( l_tmsg )
l_offset = int( m.RowHeight * l_i / a_frames )
l_offdiff = l_offset - l_prevset
m.BufferRegion.Offset( 0, l_offdiff, 0, 0 )
m.DrawAll()
l_prevset = l_offset
end for
End Function
This is the portion that receives the textures and if they are still in the display stack will pop them right in
You will notice that I check the texture queue even in my easing function. Need to do this if the user can fast
scroll as NOT removing the textures (which immediatedly start to bombard the queue) fast enough can create
status codes of 3 with invalid bitmaps but it recovers on its own eventually sending a good one so you see
I don't do much with them. By putting the checking in the scrolling function I have never seen one since' This function receives all textures for the grid
Function vg_receive_texture( a_tmsg as DYNAMIC, a_isDrawAll = False As Boolean ) As Boolean
' Get the returned state
l_state = a_tmsg.GetState()
' If return state is Ready, Failed, or Cancelled - either case, remove it from the list
if l_state = m.STATE_READY or l_state >= m.STATE_FAILED
' Removed the received texture from the TMAsyncList. But do not increment the received count
l_texture = m.TMAsyncList.RemoveItem( a_tmsg.GetID(), False )
' There SHOULD be one in there
if l_texture = Invalid
return m.NMLChannel.AppendError( " State Code: " + l_state.ToStr() + " Bitmap: " + type( a_tmsg.GetBitmap() ) )
end if
' Get the bitmap, if state is ready and bitmap is valid then process and return TRUE
l_texture.Bitmap = a_tmsg.GetBitmap()
if l_state = m.STATE_READY and l_texture.Bitmap <> Invalid
' Receive completed successuflly
' So increment the count. This lets the preloaders know when to exit
m.TMAsyncList.ReceiveCount = m.TMAsyncList.ReceiveCount + 1
' If the position is within the displaystack (currently displayed portion of the grid)
' Otherwise it is not in the display area and the drawrow function will write it when
' the user scrolls to it
if l_texture.GridX >= 0
' Draw the image into its position in the grid. This is the popping in effect
m.BufferBitmap.DrawObject( l_texture.GridX, l_texture.GridY, l_bitmap )
' If true then dump the buffer to the screen (m.compositor.drawall + m.Screen.SwapBuffers)
if a_isDrawAll then m.DrawAll()
end if
return True
else if l_state = m.STATE_FAILED
' Resend any errors. Normally will not occur unless a network problem. I don't even check the resend count
' If there is problem it is channel wide ( aka no internet or server down )
l_texture.ResendCount = l_texture.ResendCount + 1
l_texture.TRequest = CreateObject( "roTextureRequest", l_texture.GridFileURL )
m.TMAsyncList.AddItem( l_texture.TRequest.GetID(), l_texture )
m.TManager.RequestTexture( l_texture.TRequest )
' Set the global error
l_str = "vg_receive_texture: Resend Texture Request. State : " + l_state.ToStr()
l_str = l_str + " Bitmap: " + type( a_tmsg.GetBitmap() ) + " ResendCount: " + l_texture.ResendCount.ToStr()
l_str = l_str + " URI: " + l_texture.GridFileURL
return m.NMLChannel.SetError( 300, l_str )
' If debug is turned on lets see what the anomalies are - generally states of 3 with invalid bitmaps
' This can occur with the dylnamic allocation when the textures are not removed from the queue fast enough
else if m.NMLChannel.DEBUG
l_str = "vg_receive_texture: Unhandled Message. State: " + l_state.ToStr() + " Bitmap: " + type( a_tmsg.GetBitmap() )
l_str = l_str + " URI: " + l_texture.GridFileURL
print l_str
end if
end if
' Otherwise it is some other code such as downloading if it even exists. Never seen it
return True
End Function
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )