WGL: Support unvirtualized contexts and unsafe multithreading.

When using unvirtualized contexts, DisplayWGL still creates a Renderer for
managing any internal GL resources such as emulated back buffers for
DXGISwapChainWindowSurfaceWGL or D3DTextureSurfaceWGL but also creates a new
Renderer for each GL context.  All created contexts share resources.

BUG=angleproject:2464

Change-Id: I945502514079368e062beef70bed49c61ed44403
Reviewed-on: https://chromium-review.googlesource.com/1097459
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
This commit is contained in:
Geoff Lang 2018-06-11 14:56:34 -04:00 коммит произвёл Commit Bot
Родитель 5bdf8bd1cf
Коммит 24ddc7a894
15 изменённых файлов: 278 добавлений и 42 удалений

Просмотреть файл

@ -0,0 +1,80 @@
Name
ANGLE_platform_angle_context_virtualization
Name Strings
EGL_ANGLE_platform_angle_context_virtualization
Contributors
Geoff Lang, Google
Contacts
Geoff Lang, Google (geofflang 'at' chromium 'dot' org)
Status
Draft
Version
Version 1, 2018-06-11
Number
EGL Extension XXX
Extension Type
EGL client extension
Dependencies
Requires ANGLE_platform_angle.
Overview
This extension allows the client to request a Display that internally
virtualizes contexts. Requesting virtualized contexts may impact
performance or ability to render from multiple threads simultaneously.
New Types
None
New Procedures and Functions
None
New Tokens
Accepted as an attribute name in the <attrib_list> argument of
eglGetPlatformDisplayEXT:
EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE 0x3481
Additions to the EGL Specification
None.
New Behavior
To request a display that internally utilizes context virtualization,
use the attribute EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE.
EGL_TRUE enables context virtualization and EGL false disables it.
If it is set to EGL_DONT_CARE, the default setting depends on the
implementation. Any value other than these will result in an error.
The default value for EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE
is EGL_DONT_CARE. Display creation may fail if hardware limitations
prohibit the requested setting.
Issues
None
Revision History
Version 1, 2018-06-11 (Geoff Lang)
- Initial draft

Просмотреть файл

@ -82,6 +82,11 @@
#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450
#endif /* EGL_ANGLE_platform_angle_vulkan */
#ifndef EGL_ANGLE_platform_angle_context_virtualization
#define EGL_ANGLE_platform_angle_context_virtualization 1
#define EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE 0x3481
#endif /* EGL_ANGLE_platform_angle_context_virtualization */
#ifndef EGL_ANGLE_x11_visual
#define EGL_ANGLE_x11_visual
#define EGL_X11_VISUAL_ID_ANGLE 0x33A3

Просмотреть файл

@ -1389,13 +1389,14 @@ ClientExtensions::ClientExtensions()
platformANGLED3D(false),
platformANGLEOpenGL(false),
platformANGLEVulkan(false),
platformANGLEContextVirtualization(false),
deviceCreation(false),
deviceCreationD3D11(false),
x11Visual(false),
experimentalPresentPath(false),
clientGetAllProcAddresses(false),
explicitContext(false),
debug(false)
debug(false),
explicitContext(false)
{
}
@ -1406,22 +1407,23 @@ std::vector<std::string> ClientExtensions::getStrings() const
std::vector<std::string> extensionStrings;
// clang-format off
// | Extension name | Supported flag | Output vector |
InsertExtensionString("EGL_EXT_client_extensions", clientExtensions, &extensionStrings);
InsertExtensionString("EGL_EXT_platform_base", platformBase, &extensionStrings);
InsertExtensionString("EGL_EXT_platform_device", platformDevice, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle", platformANGLE, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_d3d", platformANGLED3D, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_opengl", platformANGLEOpenGL, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_null", platformANGLENULL, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_vulkan", platformANGLEVulkan, &extensionStrings);
InsertExtensionString("EGL_ANGLE_device_creation", deviceCreation, &extensionStrings);
InsertExtensionString("EGL_ANGLE_device_creation_d3d11", deviceCreationD3D11, &extensionStrings);
InsertExtensionString("EGL_ANGLE_x11_visual", x11Visual, &extensionStrings);
InsertExtensionString("EGL_ANGLE_experimental_present_path", experimentalPresentPath, &extensionStrings);
InsertExtensionString("EGL_KHR_client_get_all_proc_addresses", clientGetAllProcAddresses, &extensionStrings);
InsertExtensionString("EGL_ANGLE_explicit_context", explicitContext, &extensionStrings);
InsertExtensionString("EGL_KHR_debug", debug, &extensionStrings);
// | Extension name | Supported flag | Output vector |
InsertExtensionString("EGL_EXT_client_extensions", clientExtensions, &extensionStrings);
InsertExtensionString("EGL_EXT_platform_base", platformBase, &extensionStrings);
InsertExtensionString("EGL_EXT_platform_device", platformDevice, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle", platformANGLE, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_d3d", platformANGLED3D, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_opengl", platformANGLEOpenGL, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_null", platformANGLENULL, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_vulkan", platformANGLEVulkan, &extensionStrings);
InsertExtensionString("EGL_ANGLE_platform_angle_context_virtualization", platformANGLEContextVirtualization, &extensionStrings);
InsertExtensionString("EGL_ANGLE_device_creation", deviceCreation, &extensionStrings);
InsertExtensionString("EGL_ANGLE_device_creation_d3d11", deviceCreationD3D11, &extensionStrings);
InsertExtensionString("EGL_ANGLE_x11_visual", x11Visual, &extensionStrings);
InsertExtensionString("EGL_ANGLE_experimental_present_path", experimentalPresentPath, &extensionStrings);
InsertExtensionString("EGL_KHR_client_get_all_proc_addresses", clientGetAllProcAddresses, &extensionStrings);
InsertExtensionString("EGL_KHR_debug", debug, &extensionStrings);
InsertExtensionString("EGL_ANGLE_explicit_context", explicitContext, &extensionStrings);
// clang-format on
return extensionStrings;

Просмотреть файл

@ -813,6 +813,9 @@ struct ClientExtensions
// EGL_ANGLE_platform_angle_vulkan
bool platformANGLEVulkan;
// EGL_ANGLE_platform_angle_context_virtualization
bool platformANGLEContextVirtualization;
// EGL_ANGLE_device_creation
bool deviceCreation;
@ -828,11 +831,11 @@ struct ClientExtensions
// EGL_KHR_client_get_all_proc_addresses
bool clientGetAllProcAddresses;
// EGL_ANGLE_explicit_context
bool explicitContext;
// EGL_KHR_debug
bool debug;
// EGL_ANGLE_explicit_context
bool explicitContext;
};
} // namespace egl

Просмотреть файл

@ -1027,6 +1027,9 @@ static ClientExtensions GenerateClientExtensions()
#if defined(ANGLE_ENABLE_OPENGL)
extensions.platformANGLEOpenGL = true;
// Selecting context virtualization is currently only supported in the OpenGL backend.
extensions.platformANGLEContextVirtualization = true;
#endif
#if defined(ANGLE_ENABLE_NULL)

Просмотреть файл

@ -24,6 +24,7 @@
#include "libANGLE/renderer/gl/wgl/RendererWGL.h"
#include "libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h"
#include "libANGLE/renderer/gl/wgl/wgl_utils.h"
#include "libANGLE/renderer/renderer_utils.h"
#include "platform/Platform.h"
@ -34,6 +35,9 @@
namespace rx
{
// Use context virtualization by default because Chrome uses it.
static constexpr bool kDefaultWGLVirtualizedContexts = true;
class FunctionsGLWindows : public FunctionsGL
{
public:
@ -65,6 +69,7 @@ class FunctionsGLWindows : public FunctionsGL
DisplayWGL::DisplayWGL(const egl::DisplayState &state)
: DisplayGL(state),
mRenderer(nullptr),
mVirtualizedContexts(kDefaultWGLVirtualizedContexts),
mCurrentData(),
mOpenGLModule(nullptr),
mFunctionsWGL(nullptr),
@ -103,6 +108,9 @@ egl::Error DisplayWGL::initializeImpl(egl::Display *display)
{
mDisplayAttributes = display->getAttributeMap();
mVirtualizedContexts =
ShouldUseVirtualizedContexts(mDisplayAttributes, kDefaultWGLVirtualizedContexts);
mOpenGLModule = LoadLibraryA("opengl32.dll");
if (!mOpenGLModule)
{
@ -250,7 +258,7 @@ egl::Error DisplayWGL::initializeImpl(egl::Display *display)
<< "Failed to set the pixel format on the intermediate OpenGL window.";
}
ANGLE_TRY(createRenderer(&mRenderer));
ANGLE_TRY(createRenderer(nullptr, true, &mRenderer));
const FunctionsGL *functionsGL = mRenderer->getFunctions();
mHasRobustness = functionsGL->getGraphicsResetStatus != nullptr;
@ -439,7 +447,23 @@ SurfaceImpl *DisplayWGL::createPixmapSurface(const egl::SurfaceState &state,
ContextImpl *DisplayWGL::createContext(const gl::ContextState &state)
{
return new ContextWGL(state, mRenderer);
std::shared_ptr<RendererWGL> renderer;
if (mVirtualizedContexts)
{
renderer = mRenderer;
}
else
{
// Create a new renderer that shares with the Display-level one.
egl::Error error = createRenderer(mRenderer->getContext(), false, &renderer);
if (error.isError())
{
ERR() << "Failed to create a shared renderer: " << error.getMessage();
return nullptr;
}
}
return new ContextWGL(state, renderer);
}
DeviceImpl *DisplayWGL::createDevice()
@ -527,8 +551,11 @@ egl::ConfigSet DisplayWGL::generateConfigs()
bool DisplayWGL::testDeviceLost()
{
if (mHasRobustness)
// Can only call Renderer::getResetStatus if we know that it's context is current. This is only
// guarenteed when we're using virtualized contexts.
if (mVirtualizedContexts && mHasRobustness)
{
ASSERT(mCurrentData.at(std::this_thread::get_id()).glrc == mRenderer->getContext());
return mRenderer->getResetStatus() != GL_NO_ERROR;
}
@ -665,20 +692,45 @@ egl::Error DisplayWGL::makeCurrent(egl::Surface *drawSurface,
{
CurrentNativeContext &currentContext = mCurrentData[std::this_thread::get_id()];
HDC newDC = currentContext.dc;
HDC newDC = nullptr;
if (drawSurface)
{
SurfaceWGL *drawSurfaceWGL = GetImplAs<SurfaceWGL>(drawSurface);
newDC = drawSurfaceWGL->getDC();
}
HGLRC newContext = currentContext.glrc;
HGLRC newContext = nullptr;
if (context)
{
ContextWGL *contextWGL = GetImplAs<ContextWGL>(context);
newContext = contextWGL->getContext();
}
// The context should never change when context virtualization is being used, even when a null
// context is being bound.
if (mVirtualizedContexts)
{
ASSERT(newContext == nullptr || currentContext.glrc == nullptr ||
newContext == currentContext.glrc);
newContext = mRenderer->getContext();
// When contexts are virtualized, only single-threaded rendering is supported. This allows
// us to leave the previous surface bound if a null surface is being bound and emulate the
// surfaceless functionality because we know that this surface can not be made current on
// another thread.
if (newDC == nullptr)
{
newDC = currentContext.dc;
}
}
// WGL sometimes fails to make current a null DC even though it should support surfaceless make
// current calls, bind root device context for the intermediate window in this case.
if (newDC == nullptr && newContext != nullptr)
{
newDC = mDeviceContext;
}
if (newDC != currentContext.dc || newContext != currentContext.glrc)
{
if (!mFunctionsWGL->makeCurrent(newDC, newContext))
@ -749,9 +801,19 @@ gl::Version DisplayWGL::getMaxSupportedESVersion() const
void DisplayWGL::destroyNativeContext(HGLRC context)
{
mFunctionsWGL->deleteContext(context);
// If this context is current, remove it from the tracking of current contexts to make sure we
// don't try to make it current again.
CurrentNativeContext &currentContext = mCurrentData[std::this_thread::get_id()];
if (currentContext.glrc == context)
{
currentContext.dc = nullptr;
currentContext.glrc = nullptr;
}
}
HGLRC DisplayWGL::initializeContextAttribs(const egl::AttributeMap &eglAttributes) const
HGLRC DisplayWGL::initializeContextAttribs(HGLRC shareContext,
const egl::AttributeMap &eglAttributes) const
{
EGLint requestedDisplayType = static_cast<EGLint>(
eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE));
@ -770,7 +832,7 @@ HGLRC DisplayWGL::initializeContextAttribs(const egl::AttributeMap &eglAttribute
{
profileMask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
}
return createContextAttribs(requestedVersion, profileMask);
return createContextAttribs(shareContext, requestedVersion, profileMask);
}
// Try all the GL version in order as a workaround for Mesa context creation where the driver
@ -787,7 +849,7 @@ HGLRC DisplayWGL::initializeContextAttribs(const egl::AttributeMap &eglAttribute
profileFlag |= WGL_CONTEXT_ES_PROFILE_BIT_EXT;
}
HGLRC context = createContextAttribs(info.version, profileFlag);
HGLRC context = createContextAttribs(shareContext, info.version, profileFlag);
if (context != nullptr)
{
return context;
@ -797,7 +859,9 @@ HGLRC DisplayWGL::initializeContextAttribs(const egl::AttributeMap &eglAttribute
return nullptr;
}
HGLRC DisplayWGL::createContextAttribs(const gl::Version &version, int profileMask) const
HGLRC DisplayWGL::createContextAttribs(HGLRC shareContext,
const gl::Version &version,
int profileMask) const
{
std::vector<int> attribs;
@ -824,15 +888,17 @@ HGLRC DisplayWGL::createContextAttribs(const gl::Version &version, int profileMa
attribs.push_back(0);
attribs.push_back(0);
return mFunctionsWGL->createContextAttribsARB(mDeviceContext, nullptr, &attribs[0]);
return mFunctionsWGL->createContextAttribsARB(mDeviceContext, shareContext, &attribs[0]);
}
egl::Error DisplayWGL::createRenderer(std::shared_ptr<RendererWGL> *outRenderer)
egl::Error DisplayWGL::createRenderer(HGLRC shareContext,
bool makeNewContextCurrent,
std::shared_ptr<RendererWGL> *outRenderer)
{
HGLRC context = nullptr;
if (mFunctionsWGL->createContextAttribsARB)
{
context = initializeContextAttribs(mDisplayAttributes);
context = initializeContextAttribs(shareContext, mDisplayAttributes);
}
// If wglCreateContextAttribsARB is unavailable or failed, try the standard wglCreateContext
@ -840,6 +906,14 @@ egl::Error DisplayWGL::createRenderer(std::shared_ptr<RendererWGL> *outRenderer)
{
// Don't have control over GL versions
context = mFunctionsWGL->createContext(mDeviceContext);
if (context && shareContext)
{
if (!mFunctionsWGL->shareLists(shareContext, context))
{
return egl::EglNotInitialized()
<< "Failed to share lists to newly created context.";
}
}
}
if (!context)
@ -852,9 +926,6 @@ egl::Error DisplayWGL::createRenderer(std::shared_ptr<RendererWGL> *outRenderer)
{
return egl::EglNotInitialized() << "Failed to make the intermediate WGL context current.";
}
CurrentNativeContext &currentContext = mCurrentData[std::this_thread::get_id()];
currentContext.dc = mDeviceContext;
currentContext.glrc = context;
std::unique_ptr<FunctionsGL> functionsGL(
new FunctionsGLWindows(mOpenGLModule, mFunctionsWGL->getProcAddress));
@ -862,6 +933,21 @@ egl::Error DisplayWGL::createRenderer(std::shared_ptr<RendererWGL> *outRenderer)
outRenderer->reset(new RendererWGL(std::move(functionsGL), mDisplayAttributes, this, context));
CurrentNativeContext &currentContext = mCurrentData[std::this_thread::get_id()];
if (makeNewContextCurrent)
{
currentContext.dc = mDeviceContext;
currentContext.glrc = context;
}
else
{
// Reset the current context back to the previous state
if (!mFunctionsWGL->makeCurrent(currentContext.dc, currentContext.glrc))
{
return egl::EglNotInitialized() << "Failed reset the current context.";
}
}
return egl::NoError();
}
}

Просмотреть файл

@ -87,12 +87,18 @@ class DisplayWGL : public DisplayGL
egl::Error makeCurrentSurfaceless(gl::Context *context) override;
HGLRC initializeContextAttribs(const egl::AttributeMap &eglAttributes) const;
HGLRC createContextAttribs(const gl::Version &version, int profileMask) const;
HGLRC initializeContextAttribs(HGLRC shareContext,
const egl::AttributeMap &eglAttributes) const;
HGLRC createContextAttribs(HGLRC shareContext,
const gl::Version &version,
int profileMask) const;
egl::Error createRenderer(std::shared_ptr<RendererWGL> *outRenderer);
egl::Error createRenderer(HGLRC shareContext,
bool makeNewContextCurrent,
std::shared_ptr<RendererWGL> *outRenderer);
std::shared_ptr<RendererWGL> mRenderer;
bool mVirtualizedContexts;
struct CurrentNativeContext
{

Просмотреть файл

@ -442,6 +442,20 @@ bool ShouldUseDebugLayers(const egl::AttributeMap &attribs)
#endif // defined(ANGLE_ENABLE_ASSERTS)
}
bool ShouldUseVirtualizedContexts(const egl::AttributeMap &attribs, bool defaultValue)
{
EGLAttrib virtualizedContextRequest =
attribs.get(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE, EGL_DONT_CARE);
if (defaultValue)
{
return (virtualizedContextRequest != EGL_FALSE);
}
else
{
return (virtualizedContextRequest == EGL_TRUE);
}
}
void CopyImageCHROMIUM(const uint8_t *sourceData,
size_t sourceRowPitch,
size_t sourcePixelBytes,

Просмотреть файл

@ -209,6 +209,7 @@ struct LoadImageFunctionInfo
using LoadFunctionMap = LoadImageFunctionInfo (*)(GLenum);
bool ShouldUseDebugLayers(const egl::AttributeMap &attribs);
bool ShouldUseVirtualizedContexts(const egl::AttributeMap &attribs, bool defaultValue);
void CopyImageCHROMIUM(const uint8_t *sourceData,
size_t sourceRowPitch,

Просмотреть файл

@ -409,6 +409,27 @@ Error ValidateGetPlatformDisplayCommon(EGLenum platform,
}
break;
case EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE:
if (!clientExtensions.platformANGLEContextVirtualization)
{
return EglBadAttribute() << "EGL_ANGLE_platform_angle_context_"
"virtualization extension not active";
}
switch (value)
{
case EGL_DONT_CARE:
case EGL_FALSE:
case EGL_TRUE:
break;
default:
return EglBadAttribute() << "Invalid value for "
"EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_"
"ANGLE attrib";
}
break;
default:
break;
}

Просмотреть файл

@ -26,9 +26,10 @@ class MultithreadingTest : public ANGLETest
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setContextVirtualization(false);
}
bool platformSupportsMultithreading() const { return false; }
bool platformSupportsMultithreading() const { return IsOpenGL() && IsWindows(); }
};
// Test that it's possible to make one context current on different threads
@ -92,7 +93,7 @@ TEST_P(MultithreadingTest, MakeCurrentMultiContext)
std::array<std::thread, kThreadCount> threads;
for (size_t thread = 0; thread < kThreadCount; thread++)
{
threads[thread] = std::thread([&]() {
threads[thread] = std::thread([&, thread]() {
EGLSurface pbuffer= EGL_NO_SURFACE;
EGLConfig ctx= EGL_NO_CONTEXT;
@ -102,7 +103,6 @@ TEST_P(MultithreadingTest, MakeCurrentMultiContext)
// 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);

Просмотреть файл

@ -897,6 +897,11 @@ void ANGLETestBase::setContextProgramCacheEnabled(bool enabled)
mEGLWindow->setContextProgramCacheEnabled(enabled);
}
void ANGLETestBase::setContextVirtualization(bool enabled)
{
mEGLWindow->setContextVirtualization(enabled);
}
void ANGLETestBase::setDeferContextInit(bool enabled)
{
mDeferContextInit = enabled;

Просмотреть файл

@ -341,6 +341,7 @@ class ANGLETestBase
void setClientArraysEnabled(bool enabled);
void setRobustResourceInit(bool enabled);
void setContextProgramCacheEnabled(bool enabled);
void setContextVirtualization(bool enabled);
// Some EGL extension tests would like to defer the Context init until the test body.
void setDeferContextInit(bool enabled);

Просмотреть файл

@ -127,6 +127,7 @@ EGLWindow::EGLWindow(EGLint glesMajorVersion,
mSamples(-1),
mDebugLayersEnabled(),
mContextProgramCacheEnabled(),
mContextVirtualization(),
mPlatformMethods(nullptr)
{
}
@ -205,6 +206,12 @@ bool EGLWindow::initializeDisplayAndSurface(OSWindow *osWindow)
displayAttributes.push_back(mDebugLayersEnabled.value() ? EGL_TRUE : EGL_FALSE);
}
if (mContextVirtualization.valid())
{
displayAttributes.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
displayAttributes.push_back(mContextVirtualization.value() ? EGL_TRUE : EGL_FALSE);
}
if (mPlatformMethods)
{
static_assert(sizeof(EGLAttrib) == sizeof(mPlatformMethods), "Unexpected pointer size");

Просмотреть файл

@ -89,6 +89,7 @@ class ANGLE_EXPORT EGLWindow : angle::NonCopyable
mPlatformMethods = platformMethods;
}
void setContextProgramCacheEnabled(bool enabled) { mContextProgramCacheEnabled = enabled; }
void setContextVirtualization(bool enabled) { mContextVirtualization = enabled; }
static EGLBoolean FindEGLConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *config);
@ -162,6 +163,7 @@ class ANGLE_EXPORT EGLWindow : angle::NonCopyable
EGLint mSamples;
Optional<bool> mDebugLayersEnabled;
Optional<bool> mContextProgramCacheEnabled;
Optional<bool> mContextVirtualization;
angle::PlatformMethods *mPlatformMethods;
};