Support multisampled framebuffers with the GL backend.
Move validation of sample counts into the Renderbuffer implementations
because the exact supported sample counts are not always known.
BUG=angleoproject:886
Change-Id: I9c90d9d435e940b852343a29a6aa11d6cb1ad23b
Reviewed-on: https://chromium-review.googlesource.com/255513
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index cbdfe63..3daf5b5 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -33,6 +33,7 @@
// Support for being used as a framebuffer attachment or renderbuffer format
bool renderable;
+ // Set of supported sample counts, only guaranteed to be valid in ES3.
SupportedSampleSet sampleCounts;
// Get the maximum number of samples supported
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 689c602..a79cf2d 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -1548,7 +1548,6 @@
mCaps.maxFragmentInputComponents = std::min<GLuint>(mCaps.maxFragmentInputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4);
- GLuint maxSamples = 0;
mCaps.compressedTextureFormats.clear();
const TextureCapsMap &rendererFormats = mRenderer->getRendererTextureCaps();
@@ -1569,7 +1568,6 @@
{
formatCaps.sampleCounts.clear();
}
- maxSamples = std::max(maxSamples, formatCaps.getMaxSamples());
if (formatCaps.texturable && formatInfo.compressed)
{
@@ -1578,8 +1576,6 @@
mTextureCaps.insert(format, formatCaps);
}
-
- mCaps.maxSamples = maxSamples;
}
Data Context::getData() const
diff --git a/src/libANGLE/renderer/d3d/RenderbufferD3D.cpp b/src/libANGLE/renderer/d3d/RenderbufferD3D.cpp
index c7f3c4c..1a44161 100644
--- a/src/libANGLE/renderer/d3d/RenderbufferD3D.cpp
+++ b/src/libANGLE/renderer/d3d/RenderbufferD3D.cpp
@@ -41,6 +41,17 @@
creationFormat = GL_DEPTH24_STENCIL8_OES;
}
+ // ANGLE_framebuffer_multisample states GL_OUT_OF_MEMORY is generated on a failure to create
+ // the specified storage.
+ // Because ES 3.0 already knows the exact number of supported samples, it would already have been
+ // validated and generated GL_INVALID_VALUE.
+ const gl::TextureCaps &formatCaps = mRenderer->getRendererTextureCaps().get(creationFormat);
+ if (samples > formatCaps.getMaxSamples())
+ {
+ return gl::Error(GL_OUT_OF_MEMORY, "Renderbuffer format does not support %u samples, %u is the maximum.",
+ samples, formatCaps.getMaxSamples());
+ }
+
RenderTargetD3D *newRT = NULL;
gl::Error error = mRenderer->createRenderTarget(width, height, creationFormat, samples, &newRT);
if (error.isError())
diff --git a/src/libANGLE/renderer/gl/FunctionsGL.cpp b/src/libANGLE/renderer/gl/FunctionsGL.cpp
index a56dec1..cc20bda 100644
--- a/src/libANGLE/renderer/gl/FunctionsGL.cpp
+++ b/src/libANGLE/renderer/gl/FunctionsGL.cpp
@@ -7,18 +7,89 @@
// FunctionsGL.cpp: Implements the FuntionsGL class to contain loaded GL functions
#include "libANGLE/renderer/gl/FunctionsGL.h"
+#include "libANGLE/renderer/gl/renderergl_utils.h"
namespace rx
{
+static void GetGLVersion(PFNGLGETSTRINGPROC getStringFunction, GLuint *outMajorVersion, GLuint *outMinorVersion,
+ bool *outIsES)
+{
+ const std::string version = reinterpret_cast<const char*>(getStringFunction(GL_VERSION));
+ if (version.find("OpenGL ES") == std::string::npos)
+ {
+ // ES spec states that the GL_VERSION string will be in the following format:
+ // "OpenGL ES N.M vendor-specific information"
+ *outIsES = false;
+ *outMajorVersion = version[0] - '0';
+ *outMinorVersion = version[2] - '0';
+ }
+ else
+ {
+ // OpenGL spec states the GL_VERSION string will be in the following format:
+ // <version number><space><vendor-specific information>
+ // The version number is either of the form major number.minor number or major
+ // number.minor number.release number, where the numbers all have one or more
+ // digits
+ *outIsES = true;
+ *outMajorVersion = version[10] - '0';
+ *outMinorVersion = version[12] - '0';
+ }
+}
+
+static std::vector<std::string> GetNonIndexedExtensions(PFNGLGETSTRINGPROC getStringFunction)
+{
+ std::vector<std::string> result;
+
+ std::istringstream stream(reinterpret_cast<const char*>(getStringFunction(GL_EXTENSIONS)));
+ std::string extension;
+ while (std::getline(stream, extension, ' '))
+ {
+ result.push_back(extension);
+ }
+
+ return result;
+}
+
+static std::vector<std::string> GetIndexedExtensions(PFNGLGETINTEGERVPROC getIntegerFunction, PFNGLGETSTRINGIPROC getStringIFunction)
+{
+ std::vector<std::string> result;
+
+ GLint numExtensions;
+ getIntegerFunction(GL_NUM_EXTENSIONS, &numExtensions);
+
+ result.reserve(numExtensions);
+
+ for (GLint i = 0; i < numExtensions; i++)
+ {
+ result.push_back(reinterpret_cast<const char*>(getStringIFunction(GL_EXTENSIONS, i)));
+ }
+
+ return result;
+}
+
template <typename T>
static void AssignGLEntryPoint(void *function, T *outFunction)
{
*outFunction = reinterpret_cast<T>(function);
}
+template <typename T>
+static void AssignGLExtensionEntryPoint(const std::vector<std::string> &extensions, const std::string &extension, void *function, T *outFunction)
+{
+ if (std::find(extensions.begin(), extensions.end(), extension) != extensions.end())
+ {
+ *outFunction = reinterpret_cast<T>(function);
+ }
+}
+
FunctionsGL::FunctionsGL()
- : blendFunc(nullptr),
+ : majorVersion(0),
+ minorVersion(0),
+ openGLES(false),
+ extensions(),
+
+ blendFunc(nullptr),
clear(nullptr),
clearColor(nullptr),
clearDepth(nullptr),
@@ -694,8 +765,24 @@
{
}
-void FunctionsGL::initialize(GLuint majorVersion, GLuint minorVersion)
+void FunctionsGL::initialize()
{
+ // Grab the version number
+ AssignGLEntryPoint(loadProcAddress("glGetString"), &getString);
+ GetGLVersion(getString, &majorVersion, &minorVersion, &openGLES);
+
+ // Grab the GL extensions
+ if (majorVersion > 3 || majorVersion == 3 && minorVersion >= 0)
+ {
+ AssignGLEntryPoint(loadProcAddress("glGetIntegerv"), &getIntegerv);
+ AssignGLEntryPoint(loadProcAddress("glGetStringi"), &getStringi);
+ extensions = GetIndexedExtensions(getIntegerv, getStringi);
+ }
+ else
+ {
+ extensions = GetNonIndexedExtensions(getString);
+ }
+
// 1.0
if (majorVersion > 1 || majorVersion == 1 && minorVersion >= 0)
{
@@ -1024,6 +1111,9 @@
AssignGLEntryPoint(loadProcAddress("glVertexAttribI4uiv"), &vertexAttribI4uiv);
AssignGLEntryPoint(loadProcAddress("glVertexAttribI4usv"), &vertexAttribI4usv);
AssignGLEntryPoint(loadProcAddress("glVertexAttribIPointer"), &vertexAttribIPointer);
+
+ // Extensions
+ AssignGLExtensionEntryPoint(extensions, "GL_ARB_internalformat_query", loadProcAddress("glGetInternalformativ"), &getInternalformativ);
}
// 3.1
diff --git a/src/libANGLE/renderer/gl/FunctionsGL.h b/src/libANGLE/renderer/gl/FunctionsGL.h
index 6da9aa6..f479086 100644
--- a/src/libANGLE/renderer/gl/FunctionsGL.h
+++ b/src/libANGLE/renderer/gl/FunctionsGL.h
@@ -22,8 +22,17 @@
FunctionsGL();
virtual ~FunctionsGL();
- void initialize(GLuint majorVersion, GLuint minorVersion);
+ void initialize();
+ // Version information
+ GLuint majorVersion;
+ GLuint minorVersion;
+ bool openGLES;
+
+ // Extensions
+ std::vector<std::string> extensions;
+
+ // Entry Points
// 1.0
PFNGLBLENDFUNCPROC blendFunc;
PFNGLCLEARPROC clear;
diff --git a/src/libANGLE/renderer/gl/RenderbufferGL.cpp b/src/libANGLE/renderer/gl/RenderbufferGL.cpp
index b580ea8..823c0ea 100644
--- a/src/libANGLE/renderer/gl/RenderbufferGL.cpp
+++ b/src/libANGLE/renderer/gl/RenderbufferGL.cpp
@@ -9,17 +9,20 @@
#include "libANGLE/renderer/gl/RenderbufferGL.h"
#include "common/debug.h"
+#include "libANGLE/Caps.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/StateManagerGL.h"
+#include "libANGLE/renderer/gl/renderergl_utils.h"
namespace rx
{
-RenderbufferGL::RenderbufferGL(const FunctionsGL *functions, StateManagerGL *stateManager)
+RenderbufferGL::RenderbufferGL(const FunctionsGL *functions, StateManagerGL *stateManager, const gl::TextureCapsMap &textureCaps)
: RenderbufferImpl(),
mFunctions(functions),
mStateManager(stateManager),
+ mTextureCaps(textureCaps),
mRenderbufferID(0)
{
mFunctions->genRenderbuffers(1, &mRenderbufferID);
@@ -45,6 +48,25 @@
{
mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mRenderbufferID);
mFunctions->renderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat, width, height);
+
+ const gl::TextureCaps &formatCaps = mTextureCaps.get(internalformat);
+ if (samples > formatCaps.getMaxSamples())
+ {
+ // Before version 4.2, it is unknown if the specific internal format can support the requested number
+ // of samples. It is expected that GL_OUT_OF_MEMORY is returned if the renderbuffer cannot be created.
+ GLenum error = GL_NO_ERROR;
+ do
+ {
+ GLenum error = mFunctions->getError();
+ if (error == GL_OUT_OF_MEMORY)
+ {
+ return gl::Error(GL_OUT_OF_MEMORY);
+ }
+
+ ASSERT(error == GL_NO_ERROR);
+ } while (error != GL_NO_ERROR);
+ }
+
return gl::Error(GL_NO_ERROR);
}
diff --git a/src/libANGLE/renderer/gl/RenderbufferGL.h b/src/libANGLE/renderer/gl/RenderbufferGL.h
index 46d1571..876318a 100644
--- a/src/libANGLE/renderer/gl/RenderbufferGL.h
+++ b/src/libANGLE/renderer/gl/RenderbufferGL.h
@@ -11,6 +11,11 @@
#include "libANGLE/renderer/RenderbufferImpl.h"
+namespace gl
+{
+class TextureCapsMap;
+}
+
namespace rx
{
@@ -20,7 +25,7 @@
class RenderbufferGL : public RenderbufferImpl
{
public:
- RenderbufferGL(const FunctionsGL *functions, StateManagerGL *stateManager);
+ RenderbufferGL(const FunctionsGL *functions, StateManagerGL *stateManager, const gl::TextureCapsMap &textureCaps);
~RenderbufferGL() override;
virtual gl::Error setStorage(GLenum internalformat, size_t width, size_t height) override;
@@ -31,6 +36,7 @@
private:
const FunctionsGL *mFunctions;
StateManagerGL *mStateManager;
+ const gl::TextureCapsMap &mTextureCaps;
GLuint mRenderbufferID;
};
diff --git a/src/libANGLE/renderer/gl/RendererGL.cpp b/src/libANGLE/renderer/gl/RendererGL.cpp
index a8c5ef7..b4abac6 100644
--- a/src/libANGLE/renderer/gl/RendererGL.cpp
+++ b/src/libANGLE/renderer/gl/RendererGL.cpp
@@ -124,7 +124,7 @@
RenderbufferImpl *RendererGL::createRenderbuffer()
{
- return new RenderbufferGL(mFunctions, mStateManager);
+ return new RenderbufferGL(mFunctions, mStateManager, getRendererTextureCaps());
}
BufferImpl *RendererGL::createBuffer()
@@ -196,18 +196,13 @@
std::string nativeVendorString(reinterpret_cast<const char*>(mFunctions->getString(GL_VENDOR)));
std::string nativeRendererString(reinterpret_cast<const char*>(mFunctions->getString(GL_RENDERER)));
- GLuint major;
- GLuint minor;
- bool isES;
- nativegl::GetGLVersion(mFunctions->getString, &major, &minor, &isES);
-
std::ostringstream rendererString;
rendererString << nativeVendorString << " " << nativeRendererString << " OpenGL";
- if (isES)
+ if (mFunctions->openGLES)
{
rendererString << " ES";
}
- rendererString << " " << major << "." << minor;
+ rendererString << " " << mFunctions->majorVersion << "." << mFunctions->minorVersion;
return rendererString.str();
}
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index d7ba1ce..433aee2 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -21,62 +21,35 @@
namespace rx
{
-namespace nativegl
-{
-
-void GetGLVersion(PFNGLGETSTRINGPROC getStringFunction, GLuint *outMajorVersion, GLuint *outMinorVersion,
- bool *outIsES)
-{
- const std::string version = reinterpret_cast<const char*>(getStringFunction(GL_VERSION));
- if (version.find("OpenGL ES") == std::string::npos)
- {
- // ES spec states that the GL_VERSION string will be in the following format:
- // "OpenGL ES N.M vendor-specific information"
- *outIsES = false;
- *outMajorVersion = version[0] - '0';
- *outMinorVersion = version[2] - '0';
- }
- else
- {
- // OpenGL spec states the GL_VERSION string will be in the following format:
- // <version number><space><vendor-specific information>
- // The version number is either of the form major number.minor number or major
- // number.minor number.release number, where the numbers all have one or more
- // digits
- *outIsES = true;
- *outMajorVersion = version[10] - '0';
- *outMinorVersion = version[12] - '0';
- }
-}
-
-std::vector<std::string> GetGLExtensions(PFNGLGETSTRINGPROC getStringFunction)
-{
- std::vector<std::string> result;
-
- std::istringstream stream(reinterpret_cast<const char*>(getStringFunction(GL_EXTENSIONS)));
- std::string extension;
- while (std::getline(stream, extension, ' '))
- {
- result.push_back(extension);
- }
-
- return result;
-}
-
-}
-
namespace nativegl_gl
{
-static gl::TextureCaps GenerateTextureFormatCaps(const FunctionsGL *functions, GLenum internalFormat, GLuint majorVersion, GLuint minorVersion,
- const std::vector<std::string> &extensions)
+static gl::TextureCaps GenerateTextureFormatCaps(const FunctionsGL *functions, GLenum internalFormat)
{
gl::TextureCaps textureCaps;
const nativegl::InternalFormat &formatInfo = nativegl::GetInternalFormatInfo(internalFormat);
- textureCaps.texturable = formatInfo.textureSupport(majorVersion, minorVersion, extensions);
- textureCaps.renderable = formatInfo.renderSupport(majorVersion, minorVersion, extensions);
- textureCaps.filterable = formatInfo.filterSupport(majorVersion, minorVersion, extensions);
+ textureCaps.texturable = formatInfo.textureSupport(functions->majorVersion, functions->minorVersion, functions->extensions);
+ textureCaps.renderable = formatInfo.renderSupport(functions->majorVersion, functions->minorVersion, functions->extensions);
+ textureCaps.filterable = formatInfo.filterSupport(functions->majorVersion, functions->minorVersion, functions->extensions);
+
+ // glGetInternalformativ is not available until version 4.2 but may be available through the 3.0
+ // extension GL_ARB_internalformat_query
+ if (textureCaps.renderable && functions->getInternalformativ)
+ {
+ GLint numSamples = 0;
+ functions->getInternalformativ(GL_RENDERBUFFER, internalFormat, GL_NUM_SAMPLE_COUNTS, 1, &numSamples);
+
+ if (numSamples > 0)
+ {
+ std::vector<GLint> samples(numSamples);
+ functions->getInternalformativ(GL_RENDERBUFFER, internalFormat, GL_SAMPLES, samples.size(), &samples[0]);
+ for (size_t sampleIndex = 0; sampleIndex < samples.size(); sampleIndex++)
+ {
+ textureCaps.sampleCounts.insert(samples[sampleIndex]);
+ }
+ }
+ }
return textureCaps;
}
@@ -91,23 +64,13 @@
void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsMap *textureCapsMap,
gl::Extensions *extensions)
{
- GLuint majorVersion = 0;
- GLuint minorVersion = 0;
- bool isES = false;
- nativegl::GetGLVersion(functions->getString, &majorVersion, &minorVersion, &isES);
-
- std::vector<std::string> nativeExtensions = nativegl::GetGLExtensions(functions->getString);
-
// Texture format support checks
- GLuint maxSamples = 0;
const gl::FormatSet &allFormats = gl::GetAllSizedInternalFormats();
for (GLenum internalFormat : allFormats)
{
- gl::TextureCaps textureCaps = GenerateTextureFormatCaps(functions, internalFormat, majorVersion, minorVersion, nativeExtensions);
+ gl::TextureCaps textureCaps = GenerateTextureFormatCaps(functions, internalFormat);
textureCapsMap->insert(internalFormat, textureCaps);
- maxSamples = std::max(maxSamples, textureCaps.getMaxSamples());
-
if (gl::GetInternalFormatInfo(internalFormat).compressed)
{
caps->compressedTextureFormats.push_back(internalFormat);
@@ -184,13 +147,14 @@
caps->maxTransformFeedbackSeparateComponents = 4;
// Table 6.35, Framebuffer Dependent Values
- caps->maxSamples = 4;
+ caps->maxSamples = QuerySingleGLInt(functions, GL_MAX_SAMPLES);
// Extension support
extensions->setTextureExtensionSupport(*textureCapsMap);
extensions->textureNPOT = true;
extensions->textureStorage = true;
extensions->fboRenderMipmap = true;
+ extensions->framebufferMultisample = caps->maxSamples > 0;
}
}
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.h b/src/libANGLE/renderer/gl/renderergl_utils.h
index 0e028c9..3a6fa4d 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.h
+++ b/src/libANGLE/renderer/gl/renderergl_utils.h
@@ -25,14 +25,6 @@
{
class FunctionsGL;
-namespace nativegl
-{
-
-void GetGLVersion(PFNGLGETSTRINGPROC getStringFunction, GLuint *outMajorVersion, GLuint *outMinorVersion, bool *outIsES);
-std::vector<std::string> GetGLExtensions(PFNGLGETSTRINGPROC getStringFunction);
-
-}
-
namespace nativegl_gl
{
diff --git a/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp b/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp
index a7ff405..6180f35 100644
--- a/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp
+++ b/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp
@@ -58,8 +58,6 @@
DisplayWGL::DisplayWGL()
: DisplayGL(),
mOpenGLModule(nullptr),
- mGLVersionMajor(0),
- mGLVersionMinor(0),
mFunctionsWGL(nullptr),
mFunctionsGL(nullptr),
mWindowClass(0),
@@ -186,12 +184,6 @@
return egl::Error(EGL_NOT_INITIALIZED, "Failed to get glGetString pointer.");
}
- GLuint maxGLVersionMajor = 0;
- GLuint maxGLVersionMinor = 0;
- bool isES = false;
- nativegl::GetGLVersion(getString, &maxGLVersionMajor, &maxGLVersionMinor, &isES);
- ASSERT(!isES);
-
// Reinitialize the wgl functions to grab the extensions
mFunctionsWGL->intialize(mOpenGLModule, dummyDeviceContext);
@@ -283,9 +275,6 @@
if (!mWGLContext)
{
// Don't have control over GL versions
- mGLVersionMajor = maxGLVersionMajor;
- mGLVersionMinor = maxGLVersionMinor;
-
mWGLContext = mFunctionsWGL->createContext(mDeviceContext);
}
@@ -299,11 +288,8 @@
return egl::Error(EGL_NOT_INITIALIZED, "Failed to make the intermediate WGL context current.");
}
- nativegl::GetGLVersion(getString, &mGLVersionMajor, &mGLVersionMinor, &isES);
- ASSERT(!isES);
-
mFunctionsGL = new FunctionsGLWindows(mOpenGLModule, mFunctionsWGL->getProcAddress);
- mFunctionsGL->initialize(mGLVersionMajor, mGLVersionMinor);
+ mFunctionsGL->initialize();
return DisplayGL::initialize(display);
}
diff --git a/src/libANGLE/renderer/gl/wgl/DisplayWGL.h b/src/libANGLE/renderer/gl/wgl/DisplayWGL.h
index 75be07b..75de2a2 100644
--- a/src/libANGLE/renderer/gl/wgl/DisplayWGL.h
+++ b/src/libANGLE/renderer/gl/wgl/DisplayWGL.h
@@ -55,8 +55,6 @@
void generateCaps(egl::Caps *outCaps) const override;
HMODULE mOpenGLModule;
- GLuint mGLVersionMajor;
- GLuint mGLVersionMinor;
FunctionsWGL *mFunctionsWGL;
FunctionsGL *mFunctionsGL;
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index b1685c7..6aaf698 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -362,11 +362,15 @@
// ANGLE_framebuffer_multisample states GL_OUT_OF_MEMORY is generated on a failure to create
// the specified storage. This is different than ES 3.0 in which a sample number higher
// than the maximum sample number supported by this format generates a GL_INVALID_VALUE.
- const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
- if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
+ // The TextureCaps::getMaxSamples method is only guarenteed to be valid when the context is ES3.
+ if (context->getClientVersion() >= 3)
{
- context->recordError(Error(GL_OUT_OF_MEMORY));
- return false;
+ const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
+ if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
+ {
+ context->recordError(Error(GL_OUT_OF_MEMORY));
+ return false;
+ }
}
return ValidateRenderbufferStorageParametersBase(context, target, samples, internalformat, width, height);