glTexStorage and multiple GL contexts

We are using a separate thread and a shared context to upload textures, the are allocated using glTexStorage and uploaded using pixel buffer objects.

The scheme was as follows:

thread A (main context)
glGenTextures()
glTexStorage2D()

thread B (shared context)
… setup and fill PBO …
glTexSubimage2D()

This scheme worked fine on the Quadro card I was developing on, but texture corruption started happening on Geforce cards. There was no errors or warnings reported in the GL debug log, the miplevels just seem to be corrupted with random data.

I finally figured out that calling glTexStorage2D() on thread A was the issue, moving it to thread B would solve the issue:

thread A (main context)
glGenTextures()

thread B (shared context)
glTexStorage2D()
… setup and fill PBO …
glTexSubimage2D()

This sounds like a bug to me, that there is no error reported or that the texture gets corrupted. I cannot find any clear detail online about how the shared context and GL calls should work together, but this seem to work now.

If you are interested, I have a apitrace file that reproduces corruption on a Geforce card and none on a Quadro.

How are you synchronizing the data to guarantee coherent access ? Seems like you are playing Russian Roulette as your pseudo-code does not show any synchronization between between the 2 threads nor the main thread or where-ever the texture is being used.

There are synchronization happening, it is done using
GLsync s = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)
on thread B and then waiting for a
GLenum wait_result = glClientWaitSync(s,0,0);
bool passed = !(wait_result == GL_TIMEOUT_EXPIRED || wait_result == GL_WAIT_FAILED);
to return passed on thread A before using in a draw.

But, in the first example you gave:

thread A (main context)
glGenTextures()
glTexStorage2D()

thread B (shared context)
… setup and fill PBO …
glTexSubimage2D()

How is thread B guaranteeing that the texture storage allocation is complete before trying to modify it ?

This probably explains why it works whenever thread B does the allocation as the commands in the stream will be executed in order.

Whenever any command is added to the stream in one thread, it must be guaranteed to have been completed before using the result on any other thread, just like you are doing after uploading the texture data in thread B.

Gotcha, thanks that makes a whole lot of sense. I will have a go at adding another fence to make sure the commands on the resource context are not being run until the surface is allocated.