背景:
做opengl 跨线程渲染的时候,避免了不了使用Fence机制其使用也很简单,今天我们就从源码角度看下Fence到底做了啥
Fence
C++ GL_APICALL GLsync GL_APIENTRY glFenceSync(GLenum condition, GLbitfield flags) GL_APICALL void GL_APIENTRY glDeleteSync(GLsync sync) GL_APICALL GLenum GL_APIENTRY glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) |
今天就从这个3个方法来看下源码里具体的实现
glFenceSync
C++ GL_APICALL GLsync GL_APIENTRY glFenceSync(GLenum condition, GLbitfield flags)
{ TRACE("(GLenum condition = 0x%X, GLbitfield flags = %X)", condition, flags);
es2::Context *context = es2::getContext();
if(context) { return context->createFenceSync(condition, flags); }
return nullptr; } |
createFenceSync
C++ GLsync Context::createFenceSync(GLenum condition, GLbitfield flags) { GLuint handle = mResourceManager->createFenceSync(condition, flags);
return reinterpret_cast(static_cast(handle)); } GLuint ResourceManager::createFenceSync(GLenum condition, GLbitfield flags) { GLuint name = mFenceSyncNameSpace.allocate(); FenceSync *fenceSync = new FenceSync(name, condition, flags); fenceSync->addRef(); mFenceSyncNameSpace.insert(name, fenceSync); return name; } |
可以看的出来这个fenc指针最终存储在mResourceManager这个对象里边,而mResourceManager是可以通过共享context被另外一个线程context持有的,所以fence指针另外线程是可以访问到的
glClientWaitSync
一般AddFence后会在另外一个线程调用glClientWaitSync 等待完成,那我们就看下它是怎么实现的
C++ GL_APICALL GLenum GL_APIENTRY glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) { TRACE("(GLsync sync = %p, GLbitfield flags = %X, GLuint64 timeout = %llu)", sync, flags, timeout);
if((flags & ~(GL_SYNC_FLUSH_COMMANDS_BIT)) != 0) { error(GL_INVALID_VALUE); }
es2::Context *context = es2::getContext();
if(context) { es2::FenceSync *fenceSyncObject = context->getFenceSync(sync);
if(fenceSyncObject) { return fenceSyncObject->clientWait(flags, timeout); } else { return error(GL_INVALID_VALUE, GL_FALSE); } }
return GL_FALSE; } |
接下来看下fenceSyncObject->clientWait这个实现
C++ GLenum FenceSync::clientWait(GLbitfield flags, GLuint64 timeout) { // The current assumtion is that no matter where the fence is placed, it is // done by the time it is tested, which is similar to Context::flush(), since // we don't queue anything without processing it as fast as possible. return GL_ALREADY_SIGNALED; } |
我看到这个开源库是没实现这个方法的,这里比较遗憾
glDeleteSync
这个就很简单了
C++ GL_APICALL void GL_APIENTRY glDeleteSync(GLsync sync) { TRACE("(GLsync sync = %p)", sync);
es2::Context *context = es2::getContext();
if(context) { context->deleteFenceSync(sync); } } void Context::deleteFenceSync(GLsync fenceSync) { // The spec specifies the underlying Fence object is not deleted until all current // wait commands finish. However, since the name becomes invalid, we cannot query the fence, // and since our API is currently designed for being called from a single thread, we can delete // the fence immediately. mResourceManager->deleteFenceSync(static_cast(reinterpret_cast(fenceSync))); } void ResourceManager::deleteFenceSync(GLuint fenceSync) { FenceSync *fenceObject = mFenceSyncNameSpace.remove(fenceSync); if(fenceObject) { fenceObject->release(); } } void Object::release() { if(dereference() == 0) { delete this; } } |
当引用计数是0的时候,会清楚这个资源,也就是一旦调用Delete共享context线程,与原线程都不用使用了Fence了