Commit 24ddc7a8 by Geoff Lang Committed by Commit Bot

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: 's avatarYuly Novikov <ynovikov@chromium.org>
parent 5bdf8bd1
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;
};
......
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