Mbtiles, asynctasking imagedecoding from sqlite

ok, riot failed, let’s try the good old forum with a patch. is this my first real VL thread ?

stripped project can be found here

its basically reading map tiles from a sqlite database file and tries to convert them as dx11 texture, possibly async, since this is heavy lifting.

so far so good, imagedecoding via asynctask works and spits out a spread of observables of bitmap. downside is, it kills memory.

seems like CreateBitmap is the culprit. a dispose thingi is missing.

first problemo, can’t find bitmap.dispose() as in c#. appearently this is not imported or something ? hayden and readme helped me to find it in system.drawing. not sure if this is the way to go.

anyway, let’s say you have your dispose.

and here i’m stuck. just trying to dispose right in the asynctask doesn’t work. its disposing too early. what would be best practise ? additional datatype ? why? there was a lot of bit and pieces on riot which i couldnt put together.

therefore, here is the real thing. looking forward to learn !

1 Like

Two guesses an event on loaded and dispose in the event. Second you can use bytes to dx11 texture directly, no need in bitmap. Since both things goes to c# you can try to write an class that does that and import that in VL… as I understood that’s the proper way if you miss a feature…

kind of the same approach, but foreach instead of asynctask.
note the bitmapwrapper process that has the dispose.

i’m sure there’s more elegant and powerful constructs to solve this kind of problems…

i wonder if there’s a way to get from Observable<Spread<Image>> to Spread<Observable<Image>>?

mbtilesVL.v4p (18.7 KB) VVVV.Value.MBTiles.vl (97.2 KB)

VVVV.Value.MBTiles.vl (91.2 KB)
minor general improvements

this is indeed not the easiest patch to start with. but quite some things to learn here.

first, the Cache region, very helpful when you want to react on changes and construct things that you then keep around for a while. in this case, the database connection for example:


next thing is a bit more tricky, you create images but want to dispose older ones. for that you need to keep a reference to everything that you create in the async task and needs to be disposed. for that i made a new data type that stores the image and a sequence of IDisposable that can be disposed at a later point:


this gets joined in the task:


then you have a Spread<Observable<DBResult>> which you can iterate with a ForEach loop. and put a ForEach reactive there which executes for every event (per slice). in the event, you can extract the image and pass it on (in the moment of the same event). also, per event you can dispose the DBResult from two events ago using FrameDelays:


note that the FrameDelays in the ForEach [Reactive] don’t run in the mainloop, but per event. One might be enough, i just put two for safety reasons.

this is a bit hacky, because it just assumes that no one is using the image from two events ago, but that knowledge has only the application developer.

VVVV.Value.MBTiles.vl (82.1 KB)

ah, thanks @sebl… i was also trying to put everything into one async task, but it re-creates the state on each trigger, so you cannot keep the database connection around. but the ForEach [Reactive] keeps the state.
so behold, the power of object oriented visual programming. you can just put all of the above into another background task:


with this, the only thing that is distracting the main loop is the actual data upload to the GPU.

VVVV.Value.MBTiles.vl (91.9 KB)

1 Like

you could just do it like this:

Here’s another approach I’ve used a lot for camera devices. I basically only switched the AsyncTask with a Using [Observable] region and left a rather long comment there trying to explain what’s going on.
The general idea behind it is to create the bitmap for a very short time - just when a sink is actually interested (the UploadImage node in this case). This also means that no bitmap will get created if no one is interested. Had it running now for half an hour and memory looks stable.
VVVV.Value.MBTiles.vl (72.9 KB)

PS: I should also mentioned that I simplified the MutableArray → Bitmap path by referencing mscorlib and wrapping the byte array returned by the database directly in a memory stream. I guess there’s also room for improvement here by using another ExecuteCommand overload with that DataReader or something and wrapping that one as a Stream so no big byte array allocation is necessary. But that’s probably something to be done in C# - or it’s already there and I didn’t see it. In any case should that become an issue we can talk about it separately.


ok, wow. so much highly valued response. need to go through all the feedback/solutions thouroughly and check which one suits me best. i’ll report back.

thank you all ! great support.

1 Like

Here is another idea:

Use resource providers. These were done exactly only for the purpose of creating and disposing resources at the right time. These resource providers encapsulate the idea of providing a disposable thing in the moment when somebody wants to use it. In that regard it is a lazy/pull idea. But they also encapsulate the idea of getting rid of the resource at the time the sink doesn’t “use” it anylonger. So the using region communicates both. And then there are some operators in System.Resources [Advanced] that allow to tweak the lifetime and share resources for several sinks…

The difference to the observable idea is that downstream on the consumer side you decide to pull a resource in whatever thread you want. You don’t get them pushed from upstream via observables.
VVVV.Value.MBTiles resource provider.vl (74.1 KB)

that all sounds really interesting, but the uploaded patch is not finished? or is it just some proposal?

oops, I thought the other patches were also just sketches or were they working implementations already? Yes, mine is just another proposal on what technique could get applied, but yes, unfinished :/

Ok, I made the resource provider sketch work. https://github.com/gregsn/VL.mbtiles/commits/resourceProviderTest

There still is plenty of room for experimentation though. I stuck with the async UploadImage node in beta and whenever the center changes push 25 tiles to that UploadImage node. This is done on the main thread as soon as all 25 tiles have been loaded in background, all tiles then appear in the same frame.
It would be interesting if we could have a pool or dictionary of tiles, so that when you slightly move the center most can be reused. With these resource provider share nodes we can delay disposal and maybe reuse some.
Still not sure if the mixture of observables and resource providers is necessary, maybe we could even get rid of the observables and use the other UploadImage node @Elias probably can comment on that.

Totally unrelated: i saw many Changed node, If region, S+H node combinations. This can be simplified by using cache regions. They check for wehter the inputs of the region have changed and whenever they do trigger the inside of the region. The outputs of the region get cached in a S+H manner.

I also saw that you write into a field and access that field somewhere else. Note that this potentially can produce unwanted results. You have a frame delay right there. Not sure if this is wanted.

absolutely, this is the next step. but i did the hard 25 changes test to see what can be done. of course in the end you don’t need to update all tiles at once.

thanks, i wasn’t aware of the cache region

ah ok, forgot about the principle. is there a way to send/receive a pad or do i always need a “physical” connection to a pad to stay within the frame ? this is more or less and send and receive question:)

and kinda related, i want to check if the sqliteconnection is open, there is the state method giving me enums like “open” or “closed”, how can i compare this output to check if its open. in c# its myConnection.State == ConnectionState.Open.

see first image in my answer. in the uploaded patch i also changed everything to cache regions.

that’s just an = node in VL.

still not clear how to compare it


ok got it


This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.