Roku Developer Program

Join our online forum to talk to Roku developers and fellow channel creators. Ask questions, share tips with the community, and find helpful resources.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
sonnykr
Visitor

Re: Infinite wait on roUrlTransfer object


there you don't mention using roGridScreen; here you do and i think it might be important. Are you getting rid of the roGridScreen before returning to main menu or is it lingering still in some variable/global?


EnTerr; I am not using any out of the box components from Roku like roGridScreen. The grid I was talking about; is build manually using brightscript 2D api.
0 Kudos
EnTerr
Roku Guru

Re: Infinite wait on roUrlTransfer object

"sonnykr" wrote:
EnTerr; I am not using any out of the box components from Roku like roGridScreen. The grid I was talking about; is build manually using brightscript 2D api.

I see. Well then most likely there are "memory leaks" of your own doing. 20mb per grid sounds huge-mongous.

Consider that even if you invalidate the main reference to an object, as long as some live variable points to it (ref.count > 0), it will not be released and even explicit GC call won't touch it. Most enlightening probably will be to interrupt your code with ctrl-C in the debug console (telnet port 8085) and there use "var" and "bsc" to see what remains. Also, go down the invocation stack with "down" and check "var" again and so on.
0 Kudos
sonnykr
Visitor

Re: Infinite wait on roUrlTransfer object

Thanks EnTerr and NewManLiving. I kinda fixed the memory leak issue.

Coming back to roTextureManager, I feel the performance is much faster than roUrlTransfer. But I am still missing that ASYNC behavior.

I have a main while true loop for my roUniversalControlEvents. That is listening to my screen port. But for textures I have another port. Can i listen for messages from textureManager port (port.GetMessage()) from the main event loop itself by pulling the reference of textureManager port? Will that achieve async behavior?

Or can i listen from another infinite while loop and achieve the same?
0 Kudos
NewManLiving
Visitor

Re: Infinite wait on roUrlTransfer object

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 follows
Function 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 memory

Function 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 )
0 Kudos
NewManLiving
Visitor

Re: Infinite wait on roUrlTransfer object

One additional thought. Async operations are generally handled
On separate worker threads. Unfortunately, we do not have the ability
To use a separate thread and cannot segregate or partition the services
So although you can send them off and do not have to wait and poll
For them. You still have to receive them in the main thread. This is
The challenging part and the most difficult to coordinate if you have a
Lot going on. You always need to be aware that you have to check the
Queue while doing just about everything else if you employ dynamic
Texture caching
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )
0 Kudos
Need Assistance?
Welcome to the Roku Community! Feel free to search our Community for answers or post your question to get help.

Become a Roku Streaming Expert!

Share your expertise, help fellow streamers, and unlock exclusive rewards as part of the Roku Community. Learn more.