facebook/igl

Crash In VulkanContext::processDeferredTasks()

vinsentli opened this issue · 4 comments

I encountered two crashes in VulkanContext::processDeferredTasks().
First, IGL_ASSERT(handle.bufferIndex_ < kMaxCommandBuffers) failed.
handle.bufferIndex_ looks like an outlier.

image

The outlier comes from VulkanContext::deferredTasks_.
The handle of the front task is abnormal, likes below.Perhaps it is caused by multithreading?

image

Second: Processing some DeferredTask cause an exception when bufferID and submitID are zero.
image

it occcurs if destroy resources(igl::Texture, igl::Buffer...) in non-render thread.

IGL is not designed to be multi threaded at the moment, when handling resources you have to be extra careful on the calling threads (make sure render and non render threads are properly synchronized)

@vinsentli Trying to call any of IGL functions from different threads is a clear API misuse. If you want to use IGL in a multi-threaded environment, your app has to make sure all IGL calls are happening only on one single thread.

I'm closing this issue.

@corporateshark @pixelperfect3
I call most of the IGL APIs in the render thread currently, except call igl::Buffer::upload() in non-render thread only on metal & vulkan backend.
It can be complex and not very elegant to control the release of IGL resources only in render thread when they are held by std::shared_ptr.
I believe that igl::ITexture and igl::IBuffer are just wrappers that can be released in any thread, but the underlying opengl::textureID and VulkanTexture should only be released in the rendering thread. From what I can see, this is how it is currently designed and implemented in the OpenGL backend.

//This is the implemented in the OpenGL backend

void IContext::deleteTextures(const std::vector<GLuint>& textures) {
  if (isDestructionAllowed() && !textures.empty()) {
    if (shouldQueueAPI()) {
      //If the conditions are not met, instead of releasing immediately, add them to the deletion queue.
      deletionQueues_.queueDeleteTextures(textures);
    } else {
      GLCALL(DeleteTextures)(static_cast<GLsizei>(textures.size()), textures.data());
      APILOG("glDeleteTextures(%u, %p)\n", textures.size(), textures.data());
      GLCHECK_ERRORS();
    }
  }
}

void IContext::SynchronizedDeletionQueues::queueDeleteTextures(
    const std::vector<GLuint>& textures) {
  //Here, the presence of a mutex indicates that it supports being called from any thread.
  const std::lock_guard<std::mutex> guard(deletionQueueMutex_);
  texturesQueue_.insert(std::end(texturesQueue_), std::begin(textures), std::end(textures));
}

Currently, my app is running normally.