Bug 1164027 - Do not call glTexImage2D if likely to fail on Adreno 3xx devices; r?jrmuizel,snorp
There is a bug in the graphics driver for Adreno 3xx GPUs on android
versions up to 4.4 which segfaults if a texture allocation fails due to
virtual memory exhaustion.
On affected devices, guess whether an allocation would succeed and avoid
calling glTexImage2D where we believe it would fail. As the texture will
not be allocated this will likely cause problems such as black
rectangles appearing on the screen, but that is preferable to crashing.
MozReview-Commit-ID: 7xVI22pAdwb
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -6,16 +6,20 @@
#include "GLContext.h"
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <vector>
+#ifdef MOZ_WIDGET_ANDROID
+#include <fcntl.h>
+#include <sys/mman.h>
+#endif
#include "GLBlitHelper.h"
#include "GLReadTexImageHelper.h"
#include "GLScreenBuffer.h"
#include "gfxCrashReporterUtils.h"
#include "gfxEnv.h"
#include "gfxUtils.h"
@@ -27,29 +31,34 @@
#include "prlink.h"
#include "ScopedGLHelpers.h"
#include "SharedSurfaceGL.h"
#include "GfxTexturesReporter.h"
#include "TextureGarbageBin.h"
#include "gfx2DGlue.h"
#include "gfxPrefs.h"
#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/gfx/Logging.h"
#include "OGLShaderProgram.h" // for ShaderProgramType
#include "mozilla/DebugOnly.h"
#ifdef XP_MACOSX
#include <CoreServices/CoreServices.h>
#endif
#if defined(MOZ_WIDGET_COCOA)
#include "nsCocoaFeatures.h"
#endif
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
namespace mozilla {
namespace gl {
using namespace mozilla::gfx;
using namespace mozilla::layers;
#ifdef MOZ_GL_DEBUG
unsigned GLContext::sCurrentGLContextTLS = -1;
@@ -456,16 +465,17 @@ GLContext::GLContext(CreateContextFlags
mLockedSurface(nullptr),
mMaxTextureSize(0),
mMaxCubeMapTextureSize(0),
mMaxTextureImageSize(0),
mMaxRenderbufferSize(0),
mMaxSamples(0),
mNeedsTextureSizeChecks(false),
mNeedsFlushBeforeDeleteFB(false),
+ mTextureAllocCrashesOnMapFailure(false),
mWorkAroundDriverBugs(true),
mHeavyGLCallsSinceLastFlush(false)
{
mMaxViewportDims[0] = 0;
mMaxViewportDims[1] = 0;
mOwningThreadId = PlatformThread::CurrentId();
}
@@ -800,17 +810,19 @@ GLContext::InitWithPrefixImpl(const char
// The order of these strings must match up with the order of the enum
// defined in GLContext.h for renderer IDs.
const char* rendererMatchStrings[size_t(GLRenderer::Other)] = {
"Adreno 200",
"Adreno 205",
"Adreno (TM) 200",
"Adreno (TM) 205",
+ "Adreno (TM) 305",
"Adreno (TM) 320",
+ "Adreno (TM) 330",
"Adreno (TM) 420",
"Mali-400 MP",
"PowerVR SGX 530",
"PowerVR SGX 540",
"NVIDIA Tegra",
"Android Emulator",
"Gallium 0.4 on llvmpipe",
"Intel HD Graphics 3000 OpenGL Engine",
@@ -1043,16 +1055,27 @@ GLContext::InitWithPrefixImpl(const char
}
#endif
if (mWorkAroundDriverBugs &&
Renderer() == GLRenderer::AdrenoTM420) {
// see bug 1194923. Calling glFlush before glDeleteFramebuffers
// prevents occasional driver crash.
mNeedsFlushBeforeDeleteFB = true;
}
+#ifdef MOZ_WIDGET_ANDROID
+ if (mWorkAroundDriverBugs &&
+ (Renderer() == GLRenderer::AdrenoTM305 ||
+ Renderer() == GLRenderer::AdrenoTM320 ||
+ Renderer() == GLRenderer::AdrenoTM330) &&
+ AndroidBridge::Bridge()->GetAPIVersion() < 21) {
+ // Bug 1164027. Driver crashes when functions such as
+ // glTexImage2D fail due to virtual memory exhaustion.
+ mTextureAllocCrashesOnMapFailure = true;
+ }
+#endif
mMaxTextureImageSize = mMaxTextureSize;
if (IsSupported(GLFeature::framebuffer_multisample)) {
fGetIntegerv(LOCAL_GL_MAX_SAMPLES, (GLint*)&mMaxSamples);
}
////////////////////////////////////////////////////////////////////////////
@@ -2845,16 +2868,68 @@ GLContext::fDeleteFramebuffers(GLsizei n
if (n == 1 && *names == 0) {
// Deleting framebuffer 0 causes hangs on the DROID. See bug 623228.
} else {
raw_fDeleteFramebuffers(n, names);
}
TRACKING_CONTEXT(DeletedFramebuffers(this, n, names));
}
+#ifdef MOZ_WIDGET_ANDROID
+/**
+ * Conservatively estimate whether there is enough available
+ * contiguous virtual address space to map a newly allocated texture.
+ */
+static bool
+WillTextureMapSucceed(GLsizei width, GLsizei height, GLenum format, GLenum type)
+{
+ bool willSucceed = false;
+ // Some drivers leave large gaps between textures, so require
+ // there to be double the actual size of the texture available.
+ size_t size = width * height * GetBytesPerTexel(format, type) * 2;
+
+ int fd = open("/dev/zero", O_RDONLY);
+
+ void *p = mmap(nullptr, size, PROT_NONE, MAP_SHARED, fd, 0);
+ if (p != MAP_FAILED) {
+ willSucceed = true;
+ munmap(p, size);
+ }
+
+ close(fd);
+
+ return willSucceed;
+}
+#endif // MOZ_WIDGET_ANDROID
+
+void
+GLContext::fTexImage2D(GLenum target, GLint level, GLint internalformat,
+ GLsizei width, GLsizei height, GLint border,
+ GLenum format, GLenum type, const GLvoid* pixels) {
+ if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
+ // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
+ // See bug 737182 and the comment in IsTextureSizeSafeToPassToDriver.
+ level = -1;
+ width = -1;
+ height = -1;
+ border = -1;
+ }
+#if MOZ_WIDGET_ANDROID
+ if (mTextureAllocCrashesOnMapFailure) {
+ // We have no way of knowing whether this texture already has
+ // storage allocated for it, and therefore whether this check
+ // is necessary. We must therefore assume it does not and
+ // always perform the check.
+ if (!WillTextureMapSucceed(width, height, internalformat, type)) {
+ return;
+ }
+ }
+#endif
+ raw_fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+}
GLuint
GLContext::GetDrawFB()
{
if (mScreen)
return mScreen->GetDrawFB();
GLuint ret = 0;
@@ -2963,11 +3038,55 @@ CreateTextureForOffscreen(GLContext* aGL
MOZ_ASSERT(unpackType == LOCAL_GL_UNSIGNED_BYTE);
internalFormat = LOCAL_GL_BGRA_EXT;
unpackFormat = LOCAL_GL_BGRA_EXT;
}
return CreateTexture(aGL, internalFormat, unpackFormat, unpackType, aSize);
}
+uint32_t
+GetBytesPerTexel(GLenum format, GLenum type)
+{
+ // If there is no defined format or type, we're not taking up any memory
+ if (!format || !type) {
+ return 0;
+ }
+
+ if (format == LOCAL_GL_DEPTH_COMPONENT) {
+ if (type == LOCAL_GL_UNSIGNED_SHORT)
+ return 2;
+ else if (type == LOCAL_GL_UNSIGNED_INT)
+ return 4;
+ } else if (format == LOCAL_GL_DEPTH_STENCIL) {
+ if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
+ return 4;
+ }
+
+ if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
+ uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
+ switch (format) {
+ case LOCAL_GL_ALPHA:
+ case LOCAL_GL_LUMINANCE:
+ return 1 * multiplier;
+ case LOCAL_GL_LUMINANCE_ALPHA:
+ return 2 * multiplier;
+ case LOCAL_GL_RGB:
+ return 3 * multiplier;
+ case LOCAL_GL_RGBA:
+ return 4 * multiplier;
+ default:
+ break;
+ }
+ } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
+ type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
+ type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
+ {
+ return 2;
+ }
+
+ gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
+ MOZ_CRASH();
+ return 0;
+}
} /* namespace gl */
} /* namespace mozilla */
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -165,17 +165,19 @@ enum class GLVendor {
Other
};
enum class GLRenderer {
Adreno200,
Adreno205,
AdrenoTM200,
AdrenoTM205,
+ AdrenoTM305,
AdrenoTM320,
+ AdrenoTM330,
AdrenoTM420,
Mali400MP,
SGX530,
SGX540,
Tegra,
AndroidEmulator,
GalliumLlvmpipe,
IntelHD3000,
@@ -1616,27 +1618,19 @@ private:
ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
BEFORE_GL_CALL;
mSymbols.fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
AFTER_GL_CALL;
mHeavyGLCallsSinceLastFlush = true;
}
public:
- void fTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) {
- if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
- // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
- // See bug 737182 and the comment in IsTextureSizeSafeToPassToDriver.
- level = -1;
- width = -1;
- height = -1;
- border = -1;
- }
- raw_fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
- }
+ void fTexImage2D(GLenum target, GLint level, GLint internalformat,
+ GLsizei width, GLsizei height, GLint border,
+ GLenum format, GLenum type, const GLvoid* pixels);
void fTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) {
ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
BEFORE_GL_CALL;
mSymbols.fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
AFTER_GL_CALL;
mHeavyGLCallsSinceLastFlush = true;
}
@@ -3526,16 +3520,17 @@ protected:
GLint mMaxTextureSize;
GLint mMaxCubeMapTextureSize;
GLint mMaxTextureImageSize;
GLint mMaxRenderbufferSize;
GLint mMaxViewportDims[2];
GLsizei mMaxSamples;
bool mNeedsTextureSizeChecks;
bool mNeedsFlushBeforeDeleteFB;
+ bool mTextureAllocCrashesOnMapFailure;
bool mWorkAroundDriverBugs;
bool IsTextureSizeSafeToPassToDriver(GLenum target, GLsizei width, GLsizei height) const {
if (mNeedsTextureSizeChecks) {
// some drivers incorrectly handle some large texture sizes that are below the
// max texture size that they report. So we check ourselves against our own values
// (mMax[CubeMap]TextureSize).
// see bug 737182 for Mac Intel 2D textures
@@ -3694,12 +3689,18 @@ GLuint CreateTextureForOffscreen(GLConte
* GL_TEXTURE_MIN_FILTER = GL_LINEAR
* GL_TEXTURE_MAG_FILTER = GL_LINEAR
* GL_TEXTURE_WRAP_S = GL_CLAMP_TO_EDGE
* GL_TEXTURE_WRAP_T = GL_CLAMP_TO_EDGE
*/
GLuint CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
GLenum aType, const gfx::IntSize& aSize, bool linear = true);
+/**
+ * Helper function that calculates the number of bytes required per
+ * texel for a texture from its format and type.
+ */
+uint32_t GetBytesPerTexel(GLenum format, GLenum type);
+
} /* namespace gl */
} /* namespace mozilla */
#endif /* GLCONTEXT_H_ */
--- a/gfx/gl/GLUploadHelpers.cpp
+++ b/gfx/gl/GLUploadHelpers.cpp
@@ -376,61 +376,16 @@ TexImage2DHelper(GLContext* gl,
format,
type,
pixels);
gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
}
}
-static uint32_t
-GetBytesPerTexel(GLenum format, GLenum type)
-{
- // If there is no defined format or type, we're not taking up any memory
- if (!format || !type) {
- return 0;
- }
-
- if (format == LOCAL_GL_DEPTH_COMPONENT) {
- if (type == LOCAL_GL_UNSIGNED_SHORT)
- return 2;
- else if (type == LOCAL_GL_UNSIGNED_INT)
- return 4;
- } else if (format == LOCAL_GL_DEPTH_STENCIL) {
- if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
- return 4;
- }
-
- if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
- uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
- switch (format) {
- case LOCAL_GL_ALPHA:
- case LOCAL_GL_LUMINANCE:
- return 1 * multiplier;
- case LOCAL_GL_LUMINANCE_ALPHA:
- return 2 * multiplier;
- case LOCAL_GL_RGB:
- return 3 * multiplier;
- case LOCAL_GL_RGBA:
- return 4 * multiplier;
- default:
- break;
- }
- } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
- type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
- type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
- {
- return 2;
- }
-
- gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
- MOZ_CRASH();
- return 0;
-}
-
SurfaceFormat
UploadImageDataToTexture(GLContext* gl,
unsigned char* aData,
int32_t aStride,
SurfaceFormat aFormat,
const nsIntRegion& aDstRegion,
GLuint& aTexture,
size_t* aOutUploadSize,