Creating Textures in Multithread

HI,

I am running two threads in my code.
Main thread will display Image textures and another thread will just create vector of same textures while main thread is displaying the images. But when I am trying to create Image texture in another thread it is giving me Access violation exception.

Is it like if I am having a texture created in my code in main thread then I cant create same texture with same image in another thread???

Check out the FlickrTestMultithreaded sample which does this.

-Gabor

@gabor_papp In that sample what given is to wait till Texture gets ready with the help of new thread. What I require is thread running in parallel to main thread which would just create new textures and when first slideshow ends those new textures should be displayed in next slideshow. The looping is continuous and I dont want main thread to wait till new textures get created. Hence implementing another thread to create textures for next slideshow will will run parallel to main thread.

I might not understand properly what you would like to do, but I think your main thread has to wait anyway if your second thread is not ready with the new textures. If your second thread produces the textures continuously at the same rate as the first thread displays them, the waiting should not be noticeable, especially for a slideshow. The sample shows you how to share a context that should prevent the access violation you are seeing.

I dont want to share the context. Just want to fetch the data in background when main thread is running. Can I do that without sharing context between two threads?

As far as I know you need the context to create textures for it in an other thread. Why don’t you want to do this?

You can load the images and pass the data to the main thread using some kind of thread synchronization, for example ConcurrentCircularBuffer, and create the textures there. You don’t need to share the context for this. And if you don’t create too many textures very quickly it could work without stuttering.

@gabor_papp

What I understood is you are telling me to use a data structure ConcurrentCircularBuffer to store data.
I have used vector of TextureRef to store created textures.
That is not the issue in the main thread.
But when I am following same procedure in another thread while main thread is running it is giving me Access violation exception.
It gives exception when it goes to Texture2D construtor for creating textures.

There are two things here.

The two threads have to be synchronized. It can happen that you are writing to the vector at the exact same moment as the another thread is reading from it. If the writing has not completely finished you are reading back incomplete data that can cause an access violation. ConcurrentCircularBuffer does the synchronization for you, or you can use a mutex. You can find some usage examples in the samples folder, like in the [Kaleidoscope] ([https://github.com/cinder/Cinder/tree/master/samples/Kaleidoscope) sample, which sends Surface's back to the main thread in the InstagramStream class.

Creating textures won’t work in an other thread if you don’t share the context.

1 Like

My another thread is just fetching the data from database and storing it in a vector. The main thread is not reading that vector. It is reading from another vector. Not a single resource is getting utilized by both the threads at same time.You have mentioned that CREATING A TEXTURE WONT WORK IN ANOTHER THREAD WITHOUT SHARING CONTEXT. If that is the case then might be this is the thing which is creating problem.

Hi,

I’ll try to explain by linking to the relevant lines in the FlickrMultithreaded sample:

  • A Texture is stored in GPU (device) memory. It requires an OpenGL context to create one.
  • OpenGL is inherently single threaded, so each thread needs to have its own context.
  • The context for the main thread is created for you by Cinder. For background threads, you have to create your own. See below.
  • Normally, contexts are independent of each other. However, Textures can be shared between contexts, so that they can be used by both contexts. In order to do so, you’ll have to create a shared context on the main thread and activate it from the background thread.
  • You can now load the image data and create a Texture from it on the background thread. Note, however, that creating the Texture takes a while because the data must be copied from system memory to device memory. The GPU driver is smart enough to schedule this copy for you, so that it can first finish rendering your current scene, as long as we do not draw (use) the Texture. That’s why we have to check if the Texture is ready by using a fence.
  • When the Texture has been copied to device memory, we can hand over the TextureRef to the main thread by using the ConcurrentCircularBuffer. Like @gabor_papp said, this is a thread-safe buffer that you can use to send data from one thread to another. The background thread writes to the buffer, the main thread reads from it. Note that we’re just telling the main thread that it can immediately use the Texture, we’re not copying the Texture itself.

  • Alternatively, you can load images on a background thread easily if you simply load them as a Surface. A Surface is stored in CPU (system) memory and does not require an OpenGL context. You could then send the SurfaceRef to the main thread and create the Texture there. Note, however, that you still have to use a fence in order to avoid a stutter if you try to draw it before it has been uploaded (copied to device memory).

  • For even smoother performance, consider using a Pbo to copy the Texture from system memory to device memory. This will allow the GPU to use direct memory access (DMA), which greatly reduces the time required to copy the Texture. See also this discussion.

// Create a Pbo buffer large enough to contain the largest image. 
int bytesPerPixel = 4; // RGBA
int maxBytes = maxWidth * maxHeight * bytesPerPixel;
auto pbo = gl::Pbo::create( GL_PIXEL_UNPACK_BUFFER, maxBytes, nullptr, GL_STATIC_DRAW );
// To use it, assign it to the Texture::Format.
auto fmt = gl::Texture::Format().intermediatePbo( pbo );

auto surface = loadImage( ... );
auto texture = gl::Texture::create( surface, fmt );

-Paul

5 Likes

Hi,

It looks like using a PBO is a win for creating textures from Surfaces. Under what circumstances would you not recommend using a PBO for this purpose? (Presumably for very small images? How small is very small?)

Thank you,
Bala.

Hi,

as long as you don’t use the Texture immediately after loading, a Pbo is always a good idea. If you do use the Texture immediately, the GPU still needs to wait for the data to be uploaded to device memory, in which case it does not really matter if you use a Pbo or not.

Smaller images benefit less from using a Pbo because the upload is pretty fast anyway.

-Paul

3 Likes

Thank you @gabor_papp and @paul.houx :slight_smile:

Will try using PBO approach for asynchronous Texture creation