I’ll try to explain by linking to the relevant lines in the FlickrMultithreaded sample:
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
Alternatively, you can load images on a background thread easily if you simply load them as 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 );