Rendering to pbuffers in different threads

#1

My use-case is fairly straight forward. I need to create a collection of images, each image and the data that goes into creating it is entirely independent of the other images. I am using SkiaSharp to connect through to EGL and OpenGL.

I make sure to create the EGL context in an independent worker thread, and from that thread I create a pbuffer that I use to generate bitmaps. It all works fine, but when I monitor the throughput I can see that the GPU is being utilitized at most 40%.

Ideally I’d like to parallelize the generation of the images so that the throughput is optimized and the GPU utilization is increased.

In each of two threads I do the following:

  1. Generate an EGLDisplay (eglGetDisplay, eglInitialize)
  2. Choose a configuration (eglChooseConfig)
  3. Create an off-screen Rendering area (eglCreatePbufferSurface)
  4. Create a rendering context (eglCreateContext)
  5. Make the EGLContext current (eglMakeCurrent)

I then create a GRContext in Skia in the hosting thread so that SkiaSharp can reference the EGLContext.

When I run the app using two threads, each will generate valid contexts and surfaces, but when I have both threads issuing instructions OpenGL commands I get memory violations.

Am I correct in understanding that as long as in each thread an independent context is generated, an independent surface is created, and the context is made current and no state is shared with another thread doing the same thing, that the OpenGL calls will not affect each other?

I’ve hit a bit of a dead end with this. Any insights would be greatly appreciated.

Cheers,
Tom

#2

Ok, so each is generated on a different CPU core and rendered by a completely different GPU? :wink: … I think you see where I’m going with this.

Ok. Where?

Generally, yes. But keep in mind that these are all funneling down and beating on the same GPU (assuming a 1 GPU system). So you have to have the GPU memory required to support the sum total GPU memory needs across all of the tasks. Also, your rendering perf is going to be split across these tasks.

Parallelizing can help in some circumstances where you are CPU limited, but it doesn’t gain you a free GPU. Also, you end up with the overhead of context swapping when you have multiple GL threads trying to make use of the same GPU at the same time.

Generally what’s recommended (for single GPU) is to offload CPU-heavy processing to other CPU threads, and have a single CPU thread that is responsible for issuing all of the rendering work to the GPU.

#3

Thanks for the explanation. The issue is that the GPU never gets higher than approaching 40% of its utilitization capacity when I generate the bitmaps in one thread. If I try two threads as I’ve described above I get memory violations. The contexts don’t share any state, so I’m scratching my head about what is happening.

Above is the code that I am using. I call CreateGRContext in a background thread.

Is there anything apparent which I’m doing incorrectly? I don’t know much about the actual implementation of how the buffers are managed. Is there something I’m doing wrong with the config attributes?

Any clues greatly appreciated!
Tom

#4

I’ve just seen that the code isn’t very readable. I’ll recapture it in smaller chunks.

#5

You know, you can just post the source code inside a pair of of ``` tags (each on their own line), and the forum software will format it for you. For example:

    // Bind the framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

    // Set the texture unit the shader should read from
    glUniform1i(samplerUniform, 0);

From your code above, it appears that:

  1. You are trying to render through an OpenGL ES -to- Direct3D conversion shim (ANGLE) on Windows, and
  2. You may be requesting that ANGLE not even make use of the GPU but instead make use of a CPU software rasterizer (EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE; see ANGLE_platform_angle_d3d).

With either of these in-play, you probably shouldn’t expect to max out the performance of your GPU.

Also, do you have VSync disabled? I don’t see a call to eglSwapInterval

Yes, you mentioned that. What you didn’t answer was my question about where. I would check with the ANGLE docs and forums to make sure that what you’re trying to do is even supported by ANGLE. If so, run your app through a memory debugger to get some insights as to where things might be going awry.

#6

Hi Dark_Photon,

Thanks for helping me to understand all of this better. I don’t know much about Angle (I got the settings on line from a Window app that works) but I can confirm with the debugger that the GetPlatformDisplayEXT call is successful using the defulatDisplayAttributes, so the EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE is not passed in as part of the configuration request.

I also made sure the VSync was disabled, by adding these lines:

if (Egl.eglSwapInterval(eglDisplay, 0) == Egl.EGL_FALSE)
{
    var errorCode = Egl.eglGetError();
    throw new Exception("Failed to get Control swapping in EGL display");
}

With the debugger I can verify that the setting of the interval was successful.

I have a log which lists the calls to the openGL which I’ll put in the next message. It shows that both surfaces appear to generate the same way.

The memory violation happens on the first call to Skia’s DrawPath (sorry I don’t have the breakdown of what that maps to in OpenGL). That is the very first thing that either thread does: both just generate a random sequence of polygons.

I know that this may not be of any help (and may be a shortcoming of Skia???).

Thanks again for your help. I know a fair few people have been trying to do the same thing.

Tom

#7

Log:

Seeding GR context on thread:7.
*** libEGL for QueueWithSeed:35 is : 140734929436672 ###
*** getting proc for glGetString ###
*** getting proc for glGetIntegerv ###
*** getting proc for glGetStringi ###
*** getting proc for eglQueryString ###
*** getting proc for eglGetCurrentDisplay ###
*** getting proc for glActiveTexture ###
*** getting proc for glAttachShader ###
*** getting proc for glBindAttribLocation ###
*** getting proc for glBindBuffer ###
*** getting proc for glBindTexture ###
*** getting proc for glBindVertexArrayOES ###
*** getting proc for glDeleteVertexArraysOES ###
*** getting proc for glGenVertexArraysOES ###
*** getting proc for glBlendColor ###
*** getting proc for glBlendEquation ###
*** getting proc for glBlendFunc ###
*** getting proc for glBufferData ###
*** getting proc for glBufferSubData ###
*** getting proc for glClear ###
*** getting proc for glClearColor ###
*** getting proc for glClearStencil ###
*** getting proc for glColorMask ###
*** getting proc for glCompileShader ###
*** getting proc for glCompressedTexImage2D ###
*** getting proc for glCompressedTexSubImage2D ###
*** getting proc for glCopyTexSubImage2D ###
*** getting proc for glCreateProgram ###
*** getting proc for glCreateShader ###
*** getting proc for glCullFace ###
*** getting proc for glDeleteBuffers ###
*** getting proc for glDeleteProgram ###
*** getting proc for glDeleteShader ###
*** getting proc for glDeleteTextures ###
*** getting proc for glDepthMask ###
*** getting proc for glDisable ###
*** getting proc for glDisableVertexAttribArray ###
*** getting proc for glDrawArrays ###
*** getting proc for glDrawElements ###
*** getting proc for glEnable ###
*** getting proc for glEnableVertexAttribArray ###
*** getting proc for glFinish ###
*** getting proc for glFlush ###
*** getting proc for glFrontFace ###
*** getting proc for glGenBuffers ###
*** getting proc for glGenerateMipmap ###
*** getting proc for glGenTextures ###
*** getting proc for glGetBufferParameteriv ###
*** getting proc for glGetError ###
*** getting proc for glGetIntegerv ###
*** getting proc for glGetProgramInfoLog ###
*** getting proc for glGetProgramiv ###
*** getting proc for glGetShaderInfoLog ###
*** getting proc for glGetShaderPrecisionFormat ###
*** getting proc for glGetShaderiv ###
*** getting proc for glGetString ###
*** getting proc for glGetStringi ###
*** getting proc for glGetUniformLocation ###
*** getting proc for glIsTexture ###
*** getting proc for glLineWidth ###
*** getting proc for glLinkProgram ###
*** getting proc for glPixelStorei ###
*** getting proc for glReadPixels ###
*** getting proc for glScissor ###
*** getting proc for glShaderSource ###
*** getting proc for glStencilFunc ###
*** getting proc for glStencilFuncSeparate ###
*** getting proc for glStencilMask ###
*** getting proc for glStencilMaskSeparate ###
*** getting proc for glStencilOp ###
*** getting proc for glStencilOpSeparate ###
*** getting proc for glTexImage2D ###
*** getting proc for glTexParameteri ###
*** getting proc for glTexParameteriv ###
*** getting proc for glTexSubImage2D ###
*** getting proc for glTexStorage2DEXT ###
*** getting proc for glDiscardFramebufferEXT ###
*** getting proc for glUniform1f ###
*** getting proc for glUniform1i ###
*** getting proc for glUniform1fv ###
*** getting proc for glUniform1iv ###
*** getting proc for glUniform2f ###
*** getting proc for glUniform2i ###
*** getting proc for glUniform2fv ###
*** getting proc for glUniform2iv ###
*** getting proc for glUniform3f ###
*** getting proc for glUniform3i ###
*** getting proc for glUniform3fv ###
*** getting proc for glUniform3iv ###
*** getting proc for glUniform4f ###
*** getting proc for glUniform4i ###
*** getting proc for glUniform4fv ###
*** getting proc for glUniform4iv ###
*** getting proc for glUniformMatrix2fv ###
*** getting proc for glUniformMatrix3fv ###
*** getting proc for glUniformMatrix4fv ###
*** getting proc for glUseProgram ###
*** getting proc for glVertexAttrib1f ###
*** getting proc for glVertexAttrib2fv ###
*** getting proc for glVertexAttrib3fv ###
*** getting proc for glVertexAttrib4fv ###
*** getting proc for glVertexAttribPointer ###
*** getting proc for glViewport ###
*** getting proc for glBindFramebuffer ###
*** getting proc for glBindRenderbuffer ###
*** getting proc for glCheckFramebufferStatus ###
*** getting proc for glDeleteFramebuffers ###
*** getting proc for glDeleteRenderbuffers ###
*** getting proc for glFramebufferRenderbuffer ###
*** getting proc for glFramebufferTexture2D ###
*** getting proc for glRenderbufferStorageMultisampleANGLE ###
*** getting proc for glBlitFramebufferANGLE ###
*** getting proc for glGenFramebuffers ###
*** getting proc for glGenRenderbuffers ###
*** getting proc for glGetFramebufferAttachmentParameteriv ###
*** getting proc for glGetRenderbufferParameteriv ###
*** getting proc for glRenderbufferStorage ###
*** getting proc for glMapBufferOES ###
*** getting proc for glUnmapBufferOES ###
*** getting proc for glMapBufferRangeEXT ###
*** getting proc for glFlushMappedBufferRangeEXT ###
*** getting proc for glInsertEventMarkerEXT ###
*** getting proc for glPushGroupMarkerEXT ###
*** getting proc for glPopGroupMarkerEXT ###
*** getting proc for glInvalidateFramebuffer ###
*** getting proc for glInvalidateSubFramebuffer ###
*** getting proc for glInvalidateBufferData ###
*** getting proc for glInvalidateBufferSubData ###
*** getting proc for glInvalidateTexImage ###
*** getting proc for glInvalidateTexSubImage ###
*** getting proc for glDebugMessageControlKHR ###
*** getting proc for glDebugMessageInsertKHR ###
*** getting proc for glDebugMessageCallbackKHR ###
*** getting proc for glGetDebugMessageLogKHR ###
*** getting proc for glPushDebugGroupKHR ###
*** getting proc for glPopDebugGroupKHR ###
*** getting proc for glObjectLabelKHR ###
*** getting proc for glBindUniformLocationCHROMIUM ###
*** getting proc for eglCreateImageKHR ###
*** getting proc for eglDestroyImageKHR ###

Seeding GR context on thread:8.
*** libEGL for QueueWithSeed:23 is : 140734929436672 ###
*** getting proc for glGetString ###
*** getting proc for glGetIntegerv ###
*** getting proc for glGetStringi ###
*** getting proc for eglQueryString ###
*** getting proc for eglGetCurrentDisplay ###
*** getting proc for glActiveTexture ###
*** getting proc for glAttachShader ###
*** getting proc for glBindAttribLocation ###
*** getting proc for glBindBuffer ###
*** getting proc for glBindTexture ###
*** getting proc for glBindVertexArrayOES ###
*** getting proc for glDeleteVertexArraysOES ###
*** getting proc for glGenVertexArraysOES ###
*** getting proc for glBlendColor ###
*** getting proc for glBlendEquation ###
*** getting proc for glBlendFunc ###
*** getting proc for glBufferData ###
*** getting proc for glBufferSubData ###
*** getting proc for glClear ###
*** getting proc for glClearColor ###
*** getting proc for glClearStencil ###
*** getting proc for glColorMask ###
*** getting proc for glCompileShader ###
*** getting proc for glCompressedTexImage2D ###
*** getting proc for glCompressedTexSubImage2D ###
*** getting proc for glCopyTexSubImage2D ###
*** getting proc for glCreateProgram ###
*** getting proc for glCreateShader ###
*** getting proc for glCullFace ###
*** getting proc for glDeleteBuffers ###
*** getting proc for glDeleteProgram ###
*** getting proc for glDeleteShader ###
*** getting proc for glDeleteTextures ###
*** getting proc for glDepthMask ###
*** getting proc for glDisable ###
*** getting proc for glDisableVertexAttribArray ###
*** getting proc for glDrawArrays ###
*** getting proc for glDrawElements ###
*** getting proc for glEnable ###
*** getting proc for glEnableVertexAttribArray ###
*** getting proc for glFinish ###
*** getting proc for glFlush ###
*** getting proc for glFrontFace ###
*** getting proc for glGenBuffers ###
*** getting proc for glGenerateMipmap ###
*** getting proc for glGenTextures ###
*** getting proc for glGetBufferParameteriv ###
*** getting proc for glGetError ###
*** getting proc for glGetIntegerv ###
*** getting proc for glGetProgramInfoLog ###
*** getting proc for glGetProgramiv ###
*** getting proc for glGetShaderInfoLog ###
*** getting proc for glGetShaderPrecisionFormat ###
*** getting proc for glGetShaderiv ###
*** getting proc for glGetString ###
*** getting proc for glGetStringi ###
*** getting proc for glGetUniformLocation ###
*** getting proc for glIsTexture ###
*** getting proc for glLineWidth ###
*** getting proc for glLinkProgram ###
*** getting proc for glPixelStorei ###
*** getting proc for glReadPixels ###
*** getting proc for glScissor ###
*** getting proc for glShaderSource ###
*** getting proc for glStencilFunc ###
*** getting proc for glStencilFuncSeparate ###
*** getting proc for glStencilMask ###
*** getting proc for glStencilMaskSeparate ###
*** getting proc for glStencilOp ###
*** getting proc for glStencilOpSeparate ###
*** getting proc for glTexImage2D ###
*** getting proc for glTexParameteri ###
*** getting proc for glTexParameteriv ###
*** getting proc for glTexSubImage2D ###
*** getting proc for glTexStorage2DEXT ###
*** getting proc for glDiscardFramebufferEXT ###
*** getting proc for glUniform1f ###
*** getting proc for glUniform1i ###
*** getting proc for glUniform1fv ###
*** getting proc for glUniform1iv ###
*** getting proc for glUniform2f ###
*** getting proc for glUniform2i ###
*** getting proc for glUniform2fv ###
*** getting proc for glUniform2iv ###
*** getting proc for glUniform3f ###
*** getting proc for glUniform3i ###
*** getting proc for glUniform3fv ###
*** getting proc for glUniform3iv ###
*** getting proc for glUniform4f ###
*** getting proc for glUniform4i ###
*** getting proc for glUniform4fv ###
*** getting proc for glUniform4iv ###
*** getting proc for glUniformMatrix2fv ###
*** getting proc for glUniformMatrix3fv ###
*** getting proc for glUniformMatrix4fv ###
*** getting proc for glUseProgram ###
*** getting proc for glVertexAttrib1f ###
*** getting proc for glVertexAttrib2fv ###
*** getting proc for glVertexAttrib3fv ###
*** getting proc for glVertexAttrib4fv ###
*** getting proc for glVertexAttribPointer ###
*** getting proc for glViewport ###
*** getting proc for glBindFramebuffer ###
*** getting proc for glBindRenderbuffer ###
*** getting proc for glCheckFramebufferStatus ###
*** getting proc for glDeleteFramebuffers ###
*** getting proc for glDeleteRenderbuffers ###
*** getting proc for glFramebufferRenderbuffer ###
*** getting proc for glFramebufferTexture2D ###
*** getting proc for glRenderbufferStorageMultisampleANGLE ###
*** getting proc for glBlitFramebufferANGLE ###
*** getting proc for glGenFramebuffers ###
*** getting proc for glGenRenderbuffers ###
*** getting proc for glGetFramebufferAttachmentParameteriv ###
*** getting proc for glGetRenderbufferParameteriv ###
*** getting proc for glRenderbufferStorage ###
*** getting proc for glMapBufferOES ###
*** getting proc for glUnmapBufferOES ###
*** getting proc for glMapBufferRangeEXT ###
*** getting proc for glFlushMappedBufferRangeEXT ###
*** getting proc for glInsertEventMarkerEXT ###
*** getting proc for glPushGroupMarkerEXT ###
*** getting proc for glPopGroupMarkerEXT ###
*** getting proc for glInvalidateFramebuffer ###
*** getting proc for glInvalidateSubFramebuffer ###
*** getting proc for glInvalidateBufferData ###
*** getting proc for glInvalidateBufferSubData ###
*** getting proc for glInvalidateTexImage ###
*** getting proc for glInvalidateTexSubImage ###
*** getting proc for glDebugMessageControlKHR ###
*** getting proc for glDebugMessageInsertKHR ###
*** getting proc for glDebugMessageCallbackKHR ###
*** getting proc for glGetDebugMessageLogKHR ###
*** getting proc for glPushDebugGroupKHR ###
*** getting proc for glPopDebugGroupKHR ###
*** getting proc for glObjectLabelKHR ###
*** getting proc for glBindUniformLocationCHROMIUM ###
*** getting proc for eglCreateImageKHR ###
*** getting proc for eglDestroyImageKHR ###


		$exception	{System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at SkiaSharp.SkiaApi.sk_canvas_draw_path(IntPtr t, IntPtr path, IntPtr paint)
   at SkiaSharp.SKCanvas.DrawPath(SKPath path, SKPaint paint)
   at TestMultithreadedEGL.Queue.RenderingQueue.ProcessDrawTileOfPolygons()
   at TestMultithreadedEGL.Queue.RenderingQueue.ProcessTask(QueuedEvent queuedEvent)
   at TestMultithreadedEGL.Queue.RenderingQueue.ProcessTaskLoop()
   at System.Threading.Tasks.Task.Execute()
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)}	System.AccessViolationException
#8

Given where the crash is occurring, you’re probably more likely to get useful tips by posting about your problem in a SkiSharp forum. It’s not really sounding like this is an OpenGL ES issue.

Here are a few threads that describe symptoms similar to yours. The causes may not be the same, but they could give you some ideas:

#9

Hi Dark_Photon,

Thanks so much for your help. I don’t hold onto the SKCanvas object, so that is not the problem. Also, the SkiaSharp forum is where I started with all of this, months ago. The developer of SkiaSharp is also actively querying the Skia team (see https://groups.google.com/forum/#!topic/skia-discuss/dj8UJT_tqcU) without hearing anything back. It is good to know that the setup of the EGL matches how you think it should go, so it must be something in the way that SkiaSharp / Skia calls into the library.

I will keep querying the forum, and in the worst case, use CPU rendering instead.

Cheers,
Tom