Commit 26e3736f by Corentin Wallez

GLX backend: fallback to lesser swap_control extensions

Ideally we want to use EXT_swap_control as the code currently does as it allows us to specify the swap interval of each drawable independently. However open source drivers do not expose this extension and instead expose the MESA and SGI versions that have a global swap interval. This commit changes the code to use the best swap_control extension it can, potentially changing the swap interval on each swap buffers on lesser extensions. BUG=angleproject:1206 Change-Id: I251c6edf400aeeae5359a4370020e74b8e6cae51
parent 24db8644
......@@ -26,6 +26,13 @@ static int IgnoreX11Errors(Display *, XErrorEvent *)
return 0;
}
SwapControlData::SwapControlData()
: targetSwapInterval(0),
maxSwapInterval(-1),
currentSwapInterval(-1)
{
}
class FunctionsGLGLX : public FunctionsGL
{
public:
......@@ -55,6 +62,10 @@ DisplayGLX::DisplayGLX()
mDummyPbuffer(0),
mUsesNewXDisplay(false),
mIsMesa(false),
mSwapControl(SwapControl::Absent),
mMinSwapInterval(0),
mMaxSwapInterval(0),
mCurrentSwapInterval(-1),
mEGLDisplay(nullptr)
{
}
......@@ -102,6 +113,40 @@ egl::Error DisplayGLX::initialize(egl::Display *display)
}
}
// Choose the swap_control extension to use, if any.
// The EXT version is better as it allows glXSwapInterval to be called per
// window, while we'll potentially need to change the swap interval on each
// swap buffers when using the SGI or MESA versions.
if (mGLX.hasExtension("GLX_EXT_swap_control"))
{
mSwapControl = SwapControl::EXT;
// In GLX_EXT_swap_control querying these is done on a GLXWindow so we just
// set default values.
mMinSwapInterval = 0;
mMaxSwapInterval = 4;
}
else if (mGLX.hasExtension("GLX_MESA_swap_control"))
{
// If we have the Mesa or SGI extension, assume that you can at least set
// a swap interval of 0 or 1.
mSwapControl = SwapControl::Mesa;
mMinSwapInterval = 0;
mMinSwapInterval = 1;
}
else if (mGLX.hasExtension("GLX_SGI_swap_control"))
{
mSwapControl = SwapControl::SGI;
mMinSwapInterval = 0;
mMinSwapInterval = 1;
}
else
{
mSwapControl = SwapControl::Absent;
mMinSwapInterval = 1;
mMinSwapInterval = 1;
}
// When glXMakeCurrent is called, the context and the surface must be
// compatible which in glX-speak means that their config have the same
// color buffer type, are both RGBA or ColorIndex, and their buffers have
......@@ -338,8 +383,6 @@ egl::ConfigSet DisplayGLX::generateConfigs() const
egl::ConfigSet configs;
configIdToGLXConfig.clear();
bool hasSwapControl = mGLX.hasExtension("GLX_EXT_swap_control");
int contextRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_RED_SIZE);
int contextGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_GREEN_SIZE);
int contextBlueSize = getGLXFBConfigAttrib(mContextConfig, GLX_BLUE_SIZE);
......@@ -474,17 +517,9 @@ egl::ConfigSet DisplayGLX::generateConfigs() const
(glxDrawable & GLX_PBUFFER_BIT ? EGL_PBUFFER_BIT : 0) |
(glxDrawable & GLX_PIXMAP_BIT ? EGL_PIXMAP_BIT : 0);
if (hasSwapControl)
{
// In GLX_EXT_swap_control querying these is done on a GLXWindow so we just set a default value.
config.minSwapInterval = 0;
config.maxSwapInterval = 4;
}
else
{
config.minSwapInterval = 1;
config.maxSwapInterval = 1;
}
config.minSwapInterval = mMinSwapInterval;
config.maxSwapInterval = mMaxSwapInterval;
// TODO(cwallez) wildly guessing these formats, another TODO says they should be removed anyway
config.renderTargetFormat = GL_RGBA8;
config.depthStencilFormat = GL_DEPTH24_STENCIL8;
......@@ -557,6 +592,42 @@ void DisplayGLX::syncXCommands() const
}
}
void DisplayGLX::setSwapInterval(glx::Drawable drawable, SwapControlData *data)
{
// TODO(cwallez) error checking?
if (mSwapControl == SwapControl::EXT)
{
// Prefer the EXT extension, it gives per-drawable swap intervals, which will
// minimize the number of driver calls.
if (data->maxSwapInterval < 0)
{
unsigned int maxSwapInterval = 0;
mGLX.queryDrawable(drawable, GLX_MAX_SWAP_INTERVAL_EXT, &maxSwapInterval);
data->maxSwapInterval = maxSwapInterval;
}
if (data->currentSwapInterval != data->targetSwapInterval)
{
const int realInterval = std::min(data->targetSwapInterval, data->maxSwapInterval);
mGLX.swapIntervalEXT(drawable, realInterval);
data->currentSwapInterval = data->targetSwapInterval;
}
}
else if (mCurrentSwapInterval != data->targetSwapInterval)
{
// With the Mesa or SGI extensions we can still do per-drawable swap control
// manually but it is more expensive in number of driver calls.
if (mSwapControl == SwapControl::Mesa)
{
mGLX.swapIntervalMESA(data->targetSwapInterval);
}
else if (mSwapControl == SwapControl::Mesa)
{
mGLX.swapIntervalSGI(data->targetSwapInterval);
}
mCurrentSwapInterval = data->targetSwapInterval;
}
}
const FunctionsGL *DisplayGLX::getFunctionsGL() const
{
return mFunctionsGL;
......
......@@ -20,6 +20,20 @@ namespace rx
class FunctionsGLX;
// State-tracking data for the swap control to allow DisplayGLX to remember per
// drawable information for swap control.
struct SwapControlData
{
SwapControlData();
// Set by the drawable
int targetSwapInterval;
// DisplayGLX-side state-tracking
int maxSwapInterval;
int currentSwapInterval;
};
class DisplayGLX : public DisplayGL
{
public:
......@@ -59,6 +73,12 @@ class DisplayGLX : public DisplayGL
// between the application's display and ANGLE's one.
void syncXCommands() const;
// Depending on the supported GLX extension, swap interval can be set
// globally or per drawable. This function will make sure the drawable's
// swap interval is the one required so that the subsequent swapBuffers
// acts as expected.
void setSwapInterval(glx::Drawable drawable, SwapControlData *data);
private:
const FunctionsGL *getFunctionsGL() const override;
......@@ -83,6 +103,18 @@ class DisplayGLX : public DisplayGL
bool mUsesNewXDisplay;
bool mIsMesa;
enum class SwapControl
{
Absent,
EXT,
Mesa,
SGI,
};
SwapControl mSwapControl;
int mMinSwapInterval;
int mMaxSwapInterval;
int mCurrentSwapInterval;
FunctionsGLX mGLX;
egl::Display *mEGLDisplay;
};
......
......@@ -55,7 +55,9 @@ struct FunctionsGLX::GLXFunctionTable
destroyPbufferPtr(nullptr),
queryDrawablePtr(nullptr),
createContextAttribsARBPtr(nullptr),
swapIntervalEXTPtr(nullptr)
swapIntervalEXTPtr(nullptr),
swapIntervalMESAPtr(nullptr),
swapIntervalSGIPtr(nullptr)
{
}
......@@ -89,6 +91,12 @@ struct FunctionsGLX::GLXFunctionTable
// GLX_EXT_swap_control
PFNGLXSWAPINTERVALEXTPROC swapIntervalEXTPtr;
// GLX_MESA_swap_control
PFNGLXSWAPINTERVALMESAPROC swapIntervalMESAPtr;
// GLX_SGI_swap_control
PFNGLXSWAPINTERVALSGIPROC swapIntervalSGIPtr;
};
FunctionsGLX::FunctionsGLX()
......@@ -213,17 +221,17 @@ bool FunctionsGLX::initialize(Display *xDisplay, int screen, std::string *errorS
{
GET_PROC_OR_ERROR(&mFnPtrs->createContextAttribsARBPtr, glXCreateContextAttribsARB);
}
else
{
mFnPtrs->createContextAttribsARBPtr = nullptr;
}
if (hasExtension("GLX_EXT_swap_control"))
{
GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalEXTPtr, glXSwapIntervalEXT);
}
else
if (hasExtension("GLX_MESA_swap_control"))
{
GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalMESAPtr, glXSwapIntervalMESA);
}
if (hasExtension("GLX_SGI_swap_control"))
{
mFnPtrs->swapIntervalEXTPtr = nullptr;
GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalSGIPtr, glXSwapIntervalSGI);
}
#undef GET_FNPTR_OR_ERROR
......@@ -360,4 +368,14 @@ void FunctionsGLX::swapIntervalEXT(glx::Drawable drawable, int intervals) const
mFnPtrs->swapIntervalEXTPtr(mXDisplay, drawable, intervals);
}
int FunctionsGLX::swapIntervalMESA(int intervals) const
{
return mFnPtrs->swapIntervalMESAPtr(intervals);
}
int FunctionsGLX::swapIntervalSGI(int intervals) const
{
return mFnPtrs->swapIntervalSGIPtr(intervals);
}
}
......@@ -67,6 +67,12 @@ class FunctionsGLX
// GLX_EXT_swap_control
void swapIntervalEXT(glx::Drawable drawable, int interval) const;
// GLX_MESA_swap_control
int swapIntervalMESA(int interval) const;
// GLX_SGI_swap_control
int swapIntervalSGI(int interval) const;
private:
// So as to isolate GLX from angle we do not include angleutils.h and cannot
// use angle::NonCopyable so we replicated it here instead.
......
......@@ -28,11 +28,10 @@ WindowSurfaceGLX::WindowSurfaceGLX(const FunctionsGLX &glx,
mWindow(0),
mDisplay(display),
mGLX(glx),
mGLXDisplay(*glxDisplay),
mGLXDisplay(glxDisplay),
mContext(context),
mFBConfig(fbConfig),
mGLXWindow(0),
mMaxSwapInterval(1)
mGLXWindow(0)
{
}
......@@ -48,7 +47,7 @@ WindowSurfaceGLX::~WindowSurfaceGLX()
XDestroyWindow(mDisplay, mWindow);
}
mGLXDisplay.syncXCommands();
mGLXDisplay->syncXCommands();
}
egl::Error WindowSurfaceGLX::initialize()
......@@ -95,18 +94,13 @@ egl::Error WindowSurfaceGLX::initialize()
0, visualInfo->depth, InputOutput, visual, attributeMask, &attributes);
mGLXWindow = mGLX.createWindow(mFBConfig, mWindow, nullptr);
if (mGLX.hasExtension("GLX_EXT_swap_control"))
{
mGLX.queryDrawable(mGLXWindow, GLX_MAX_SWAP_INTERVAL_EXT, &mMaxSwapInterval);
}
XMapWindow(mDisplay, mWindow);
XFlush(mDisplay);
XFree(visualInfo);
XFreeColormap(mDisplay, colormap);
mGLXDisplay.syncXCommands();
mGLXDisplay->syncXCommands();
return egl::Error(EGL_SUCCESS);
}
......@@ -140,6 +134,7 @@ egl::Error WindowSurfaceGLX::swap()
mGLX.waitX();
}
mGLXDisplay->setSwapInterval(mGLXWindow, &mSwapControl);
mGLX.swapBuffers(mGLXWindow);
return egl::Error(EGL_SUCCESS);
......@@ -171,12 +166,7 @@ egl::Error WindowSurfaceGLX::releaseTexImage(EGLint buffer)
void WindowSurfaceGLX::setSwapInterval(EGLint interval)
{
if (mGLX.hasExtension("GLX_EXT_swap_control"))
{
// TODO(cwallez) error checking?
const int realInterval = std::min(interval, static_cast<int>(mMaxSwapInterval));
mGLX.swapIntervalEXT(mGLXWindow, realInterval);
}
mSwapControl.targetSwapInterval = interval;
}
EGLint WindowSurfaceGLX::getWidth() const
......
......@@ -10,6 +10,7 @@
#define LIBANGLE_RENDERER_GL_GLX_WINDOWSURFACEGLX_H_
#include "libANGLE/renderer/gl/SurfaceGL.h"
#include "libANGLE/renderer/gl/glx/DisplayGLX.h"
#include "libANGLE/renderer/gl/glx/platform_glx.h"
namespace rx
......@@ -55,12 +56,13 @@ class WindowSurfaceGLX : public SurfaceGL
Display *mDisplay;
const FunctionsGLX &mGLX;
const DisplayGLX &mGLXDisplay;
DisplayGLX *mGLXDisplay;
glx::Context mContext;
glx::FBConfig mFBConfig;
glx::Window mGLXWindow;
unsigned int mMaxSwapInterval;
SwapControlData mSwapControl;
};
}
......
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