Commit 4eb2f6dc by Jonah Ryan-Davis Committed by Commit Bot

Add basic multithreading support to GLX

The first step for true multithreading support is allowing multiple threads to use ANGLE as long as only one thread has a current context at a time. Added a test to MultithreadingTests to reproduce this behavior and implemented it in GLX. Bug: angleproject:4724 Bug: angleproject:4725 Bug: chromium:1087084 Change-Id: I908d3f02f34a681f1c9d0919dd17aee1a489173c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2241616 Commit-Queue: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 6d781655
...@@ -257,6 +257,8 @@ egl::Error DisplayGLX::initialize(egl::Display *display) ...@@ -257,6 +257,8 @@ egl::Error DisplayGLX::initialize(egl::Display *display)
} }
ASSERT(mContext); ASSERT(mContext);
mCurrentContexts[std::this_thread::get_id()] = mContext;
// FunctionsGL and DisplayGL need to make a few GL calls, for example to // FunctionsGL and DisplayGL need to make a few GL calls, for example to
// query the version of the context so we need to make the context current. // query the version of the context so we need to make the context current.
// glXMakeCurrent requires a GLXDrawable so we create a temporary Pbuffer // glXMakeCurrent requires a GLXDrawable so we create a temporary Pbuffer
...@@ -352,6 +354,8 @@ void DisplayGLX::terminate() ...@@ -352,6 +354,8 @@ void DisplayGLX::terminate()
} }
mWorkerPbufferPool.clear(); mWorkerPbufferPool.clear();
mCurrentContexts.clear();
if (mContext) if (mContext)
{ {
mGLX.destroyContext(mContext); mGLX.destroyContext(mContext);
...@@ -378,15 +382,25 @@ egl::Error DisplayGLX::makeCurrent(egl::Surface *drawSurface, ...@@ -378,15 +382,25 @@ egl::Error DisplayGLX::makeCurrent(egl::Surface *drawSurface,
egl::Surface *readSurface, egl::Surface *readSurface,
gl::Context *context) gl::Context *context)
{ {
glx::Drawable drawable = glx::Drawable newDrawable =
(drawSurface ? GetImplAs<SurfaceGLX>(drawSurface)->getDrawable() : mDummyPbuffer); (drawSurface ? GetImplAs<SurfaceGLX>(drawSurface)->getDrawable() : mDummyPbuffer);
if (drawable != mCurrentDrawable) glx::Context newContext = mContext;
// If the thread calling makeCurrent does not have the correct context current (either mContext
// or 0), we need to set it current.
if (!context)
{
newDrawable = 0;
newContext = 0;
}
if (newDrawable != mCurrentDrawable ||
newContext != mCurrentContexts[std::this_thread::get_id()])
{ {
if (mGLX.makeCurrent(drawable, mContext) != True) if (mGLX.makeCurrent(newDrawable, newContext) != True)
{ {
return egl::EglContextLost() << "Failed to make the GLX context current"; return egl::EglContextLost() << "Failed to make the GLX context current";
} }
mCurrentDrawable = drawable; mCurrentContexts[std::this_thread::get_id()] = newContext;
mCurrentDrawable = newDrawable;
} }
return DisplayGL::makeCurrent(drawSurface, readSurface, context); return DisplayGL::makeCurrent(drawSurface, readSurface, context);
...@@ -739,11 +753,6 @@ egl::ConfigSet DisplayGLX::generateConfigs() ...@@ -739,11 +753,6 @@ egl::ConfigSet DisplayGLX::generateConfigs()
bool DisplayGLX::testDeviceLost() bool DisplayGLX::testDeviceLost()
{ {
if (mHasARBCreateContextRobustness)
{
return mRenderer->getResetStatus() != gl::GraphicsResetStatus::NoError;
}
return false; return false;
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define LIBANGLE_RENDERER_GL_GLX_DISPLAYGLX_H_ #define LIBANGLE_RENDERER_GL_GLX_DISPLAYGLX_H_
#include <string> #include <string>
#include <thread>
#include <vector> #include <vector>
#include "common/Optional.h" #include "common/Optional.h"
...@@ -122,6 +123,7 @@ class DisplayGLX : public DisplayGL ...@@ -122,6 +123,7 @@ class DisplayGLX : public DisplayGL
XVisualInfo *mVisuals; XVisualInfo *mVisuals;
glx::Context mContext; glx::Context mContext;
glx::Context mSharedContext; glx::Context mSharedContext;
std::unordered_map<std::thread::id, glx::Context> mCurrentContexts;
// A pbuffer the context is current on during ANGLE initialization // A pbuffer the context is current on during ANGLE initialization
glx::Pbuffer mDummyPbuffer; glx::Pbuffer mDummyPbuffer;
......
...@@ -556,11 +556,6 @@ egl::ConfigSet DisplayWGL::generateConfigs() ...@@ -556,11 +556,6 @@ egl::ConfigSet DisplayWGL::generateConfigs()
bool DisplayWGL::testDeviceLost() bool DisplayWGL::testDeviceLost()
{ {
if (mHasRobustness)
{
return mRenderer->getResetStatus() != gl::GraphicsResetStatus::NoError;
}
return false; return false;
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "test_utils/gl_raii.h" #include "test_utils/gl_raii.h"
#include "util/EGLWindow.h" #include "util/EGLWindow.h"
#include <atomic>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
...@@ -201,6 +202,55 @@ TEST_P(MultithreadingTest, MultiContextDraw) ...@@ -201,6 +202,55 @@ TEST_P(MultithreadingTest, MultiContextDraw)
runMultithreadedGLTest(testBody, 4); runMultithreadedGLTest(testBody, 4);
} }
TEST_P(MultithreadingTest, MultiCreateContext)
{
// Supported by WGL and GLX (https://anglebug.com/4725)
ANGLE_SKIP_TEST_IF(!IsWindows() && !IsLinux());
EGLWindow *window = getEGLWindow();
EGLDisplay dpy = window->getDisplay();
EGLContext ctx = window->getContext();
EGLSurface surface = window->getSurface();
// Un-makeCurrent the test window's context
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_EGL_SUCCESS();
constexpr size_t kThreadCount = 16;
std::atomic<uint32_t> barrier(0);
std::vector<std::thread> threads(kThreadCount);
std::vector<EGLContext> contexts(kThreadCount);
for (size_t threadIdx = 0; threadIdx < kThreadCount; threadIdx++)
{
threads[threadIdx] = std::thread([&, threadIdx]() {
contexts[threadIdx] = EGL_NO_CONTEXT;
{
contexts[threadIdx] = window->createContext(EGL_NO_CONTEXT);
EXPECT_NE(EGL_NO_CONTEXT, contexts[threadIdx]);
barrier++;
}
while (barrier < kThreadCount)
{
}
{
EXPECT_TRUE(eglDestroyContext(dpy, contexts[threadIdx]));
}
});
}
for (std::thread &thread : threads)
{
thread.join();
}
// Re-make current the test window's context for teardown.
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
EXPECT_EGL_SUCCESS();
}
// TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads // TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads
ANGLE_INSTANTIATE_TEST(MultithreadingTest, ANGLE_INSTANTIATE_TEST(MultithreadingTest,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment