Commit 3b9b027c by Geoff Lang Committed by Commit Bot

Add basic tests of (unsafe) multithreaded contexts.

BUG=angleproject:2464 Change-Id: Ia0f0788a1bc4d2ecd883b058f15c629cac5fd166 Reviewed-on: https://chromium-review.googlesource.com/1036063 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 9f394f8c
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
'<(angle_path)/src/tests/gl_tests/MaxTextureSizeTest.cpp', '<(angle_path)/src/tests/gl_tests/MaxTextureSizeTest.cpp',
'<(angle_path)/src/tests/gl_tests/MipmapTest.cpp', '<(angle_path)/src/tests/gl_tests/MipmapTest.cpp',
'<(angle_path)/src/tests/gl_tests/MultisampleCompatibilityTest.cpp', '<(angle_path)/src/tests/gl_tests/MultisampleCompatibilityTest.cpp',
'<(angle_path)/src/tests/gl_tests/MultithreadingTest.cpp',
'<(angle_path)/src/tests/gl_tests/MultiviewDrawTest.cpp', '<(angle_path)/src/tests/gl_tests/MultiviewDrawTest.cpp',
'<(angle_path)/src/tests/gl_tests/media/pixel.inl', '<(angle_path)/src/tests/gl_tests/media/pixel.inl',
'<(angle_path)/src/tests/gl_tests/PackUnpackTest.cpp', '<(angle_path)/src/tests/gl_tests/PackUnpackTest.cpp',
......
//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// MulithreadingTest.cpp : Tests of multithreaded rendering
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include <mutex>
#include <thread>
namespace angle
{
class MultithreadingTest : public ANGLETest
{
protected:
MultithreadingTest()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
bool platformSupportsMultithreading() const { return false; }
};
// Test that it's possible to make one context current on different threads
TEST_P(MultithreadingTest, MakeCurrentSingleContext)
{
ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
std::mutex mutex;
EGLWindow *window = getEGLWindow();
EGLDisplay dpy = window->getDisplay();
EGLContext ctx = window->getContext();
EGLSurface surface = window->getSurface();
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_EGL_SUCCESS();
constexpr size_t kThreadCount = 16;
std::array<std::thread, kThreadCount> threads;
for (std::thread &thread : threads)
{
thread = std::thread([&]() {
std::lock_guard<decltype(mutex)> lock(mutex);
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
EXPECT_EGL_SUCCESS();
EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
EXPECT_EGL_SUCCESS();
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_EGL_SUCCESS();
});
}
for (std::thread &thread : threads)
{
thread.join();
}
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
EXPECT_EGL_SUCCESS();
}
// Test that it's possible to make one multiple contexts current on different threads simultaneously
TEST_P(MultithreadingTest, MakeCurrentMultiContext)
{
ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
std::mutex mutex;
EGLWindow *window = getEGLWindow();
EGLDisplay dpy = window->getDisplay();
EGLConfig config = window->getConfig();
constexpr size_t kThreadCount = 16;
constexpr size_t kIterationsPerThread = 16;
constexpr EGLint kPBufferSize = 256;
std::array<std::thread, kThreadCount> threads;
for (size_t thread = 0; thread < kThreadCount; thread++)
{
threads[thread] = std::thread([&]() {
EGLSurface pbuffer= EGL_NO_SURFACE;
EGLConfig ctx= EGL_NO_CONTEXT;
{
std::lock_guard<decltype(mutex)> lock(mutex);
// Initialize the pbuffer and context
EGLint pbufferAttributes[] = {
EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_NONE, EGL_NONE,
};
pbuffer = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
EXPECT_EGL_SUCCESS();
ctx = window->createContext(EGL_NO_CONTEXT);
EXPECT_NE(EGL_NO_CONTEXT, ctx);
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, pbuffer, pbuffer, ctx));
EXPECT_EGL_SUCCESS();
}
for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
{
std::lock_guard<decltype(mutex)> lock(mutex);
// Base the clear color on the thread and iteration indexes so every clear color is
// unique
const GLColor color(static_cast<GLubyte>(thread), static_cast<GLubyte>(iteration),
0, 255);
const angle::Vector4 floatColor = color.toNormalizedVector();
glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
EXPECT_GL_NO_ERROR();
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, color);
}
{
std::lock_guard<decltype(mutex)> lock(mutex);
// Clean up
EXPECT_EGL_TRUE(
eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EXPECT_EGL_SUCCESS();
eglDestroySurface(dpy, pbuffer);
eglDestroyContext(dpy, ctx);
}
});
}
for (std::thread &thread : threads)
{
thread.join();
}
}
// TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads
ANGLE_INSTANTIATE_TEST(MultithreadingTest,
ES2_OPENGL(),
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES());
} // namespace angle
...@@ -300,7 +300,7 @@ bool EGLWindow::initializeDisplayAndSurface(OSWindow *osWindow) ...@@ -300,7 +300,7 @@ bool EGLWindow::initializeDisplayAndSurface(OSWindow *osWindow)
return true; return true;
} }
bool EGLWindow::initializeContext() EGLContext EGLWindow::createContext(EGLContext share) const
{ {
const char *displayExtensions = eglQueryString(mDisplay, EGL_EXTENSIONS); const char *displayExtensions = eglQueryString(mDisplay, EGL_EXTENSIONS);
...@@ -309,39 +309,34 @@ bool EGLWindow::initializeContext() ...@@ -309,39 +309,34 @@ bool EGLWindow::initializeContext()
if (mClientMajorVersion > 2 && !(mEGLMajorVersion > 1 || mEGLMinorVersion >= 5) && if (mClientMajorVersion > 2 && !(mEGLMajorVersion > 1 || mEGLMinorVersion >= 5) &&
!hasKHRCreateContext) !hasKHRCreateContext)
{ {
destroyGL(); return EGL_NO_CONTEXT;
return false;
} }
bool hasWebGLCompatibility = bool hasWebGLCompatibility =
strstr(displayExtensions, "EGL_ANGLE_create_context_webgl_compatibility") != nullptr; strstr(displayExtensions, "EGL_ANGLE_create_context_webgl_compatibility") != nullptr;
if (mWebGLCompatibility && !hasWebGLCompatibility) if (mWebGLCompatibility && !hasWebGLCompatibility)
{ {
destroyGL(); return EGL_NO_CONTEXT;
return false;
} }
bool hasCreateContextExtensionsEnabled = bool hasCreateContextExtensionsEnabled =
strstr(displayExtensions, "EGL_ANGLE_create_context_extensions_enabled") != nullptr; strstr(displayExtensions, "EGL_ANGLE_create_context_extensions_enabled") != nullptr;
if (mExtensionsEnabled.valid() && !hasCreateContextExtensionsEnabled) if (mExtensionsEnabled.valid() && !hasCreateContextExtensionsEnabled)
{ {
destroyGL(); return EGL_NO_CONTEXT;
return false;
} }
bool hasRobustness = strstr(displayExtensions, "EGL_EXT_create_context_robustness") != nullptr; bool hasRobustness = strstr(displayExtensions, "EGL_EXT_create_context_robustness") != nullptr;
if (mRobustAccess && !hasRobustness) if (mRobustAccess && !hasRobustness)
{ {
destroyGL(); return EGL_NO_CONTEXT;
return false;
} }
bool hasBindGeneratesResource = bool hasBindGeneratesResource =
strstr(displayExtensions, "EGL_CHROMIUM_create_context_bind_generates_resource") != nullptr; strstr(displayExtensions, "EGL_CHROMIUM_create_context_bind_generates_resource") != nullptr;
if (!mBindGeneratesResource && !hasBindGeneratesResource) if (!mBindGeneratesResource && !hasBindGeneratesResource)
{ {
destroyGL(); return EGL_NO_CONTEXT;
return false;
} }
bool hasClientArraysExtension = bool hasClientArraysExtension =
...@@ -349,23 +344,20 @@ bool EGLWindow::initializeContext() ...@@ -349,23 +344,20 @@ bool EGLWindow::initializeContext()
if (!mClientArraysEnabled && !hasClientArraysExtension) if (!mClientArraysEnabled && !hasClientArraysExtension)
{ {
// Non-default state requested without the extension present // Non-default state requested without the extension present
destroyGL(); return EGL_NO_CONTEXT;
return false;
} }
bool hasProgramCacheControlExtension = bool hasProgramCacheControlExtension =
strstr(displayExtensions, "EGL_ANGLE_program_cache_control ") != nullptr; strstr(displayExtensions, "EGL_ANGLE_program_cache_control ") != nullptr;
if (mContextProgramCacheEnabled.valid() && !hasProgramCacheControlExtension) if (mContextProgramCacheEnabled.valid() && !hasProgramCacheControlExtension)
{ {
destroyGL(); return EGL_NO_CONTEXT;
return false;
} }
eglBindAPI(EGL_OPENGL_ES_API); eglBindAPI(EGL_OPENGL_ES_API);
if (eglGetError() != EGL_SUCCESS) if (eglGetError() != EGL_SUCCESS)
{ {
destroyGL(); return EGL_NO_CONTEXT;
return false;
} }
std::vector<EGLint> contextAttributes; std::vector<EGLint> contextAttributes;
...@@ -433,9 +425,20 @@ bool EGLWindow::initializeContext() ...@@ -433,9 +425,20 @@ bool EGLWindow::initializeContext()
} }
contextAttributes.push_back(EGL_NONE); contextAttributes.push_back(EGL_NONE);
mContext = eglCreateContext(mDisplay, mConfig, nullptr, &contextAttributes[0]); EGLContext context = eglCreateContext(mDisplay, mConfig, nullptr, &contextAttributes[0]);
if (eglGetError() != EGL_SUCCESS) if (eglGetError() != EGL_SUCCESS)
{ {
return EGL_NO_CONTEXT;
}
return context;
}
bool EGLWindow::initializeContext()
{
mContext = createContext(EGL_NO_CONTEXT);
if (mContext == EGL_NO_CONTEXT)
{
destroyGL(); destroyGL();
return false; return false;
} }
......
...@@ -118,6 +118,9 @@ class ANGLE_EXPORT EGLWindow : angle::NonCopyable ...@@ -118,6 +118,9 @@ class ANGLE_EXPORT EGLWindow : angle::NonCopyable
// Only initializes the Display and Surface. // Only initializes the Display and Surface.
bool initializeDisplayAndSurface(OSWindow *osWindow); bool initializeDisplayAndSurface(OSWindow *osWindow);
// Create an EGL context with this window's configuration
EGLContext createContext(EGLContext share) const;
// Only initializes the Context. // Only initializes the Context.
bool initializeContext(); bool initializeContext();
......
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