1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/gl/GLUploadHelpers.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,557 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "GLUploadHelpers.h" 1.10 + 1.11 +#include "GLContext.h" 1.12 +#include "mozilla/gfx/2D.h" 1.13 +#include "mozilla/gfx/Tools.h" // For BytesPerPixel 1.14 +#include "nsRegion.h" 1.15 + 1.16 +namespace mozilla { 1.17 + 1.18 +using namespace gfx; 1.19 + 1.20 +namespace gl { 1.21 + 1.22 +/* These two techniques are suggested by "Bit Twiddling Hacks" 1.23 + */ 1.24 + 1.25 +/** 1.26 + * Returns true if |aNumber| is a power of two 1.27 + * 0 is incorreclty considered a power of two 1.28 + */ 1.29 +static bool 1.30 +IsPowerOfTwo(int aNumber) 1.31 +{ 1.32 + return (aNumber & (aNumber - 1)) == 0; 1.33 +} 1.34 + 1.35 +/** 1.36 + * Returns the first integer greater than |aNumber| which is a power of two 1.37 + * Undefined for |aNumber| < 0 1.38 + */ 1.39 +static int 1.40 +NextPowerOfTwo(int aNumber) 1.41 +{ 1.42 +#if defined(__arm__) 1.43 + return 1 << (32 - __builtin_clz(aNumber - 1)); 1.44 +#else 1.45 + --aNumber; 1.46 + aNumber |= aNumber >> 1; 1.47 + aNumber |= aNumber >> 2; 1.48 + aNumber |= aNumber >> 4; 1.49 + aNumber |= aNumber >> 8; 1.50 + aNumber |= aNumber >> 16; 1.51 + return ++aNumber; 1.52 +#endif 1.53 +} 1.54 + 1.55 +static unsigned int 1.56 +DataOffset(const nsIntPoint &aPoint, int32_t aStride, SurfaceFormat aFormat) 1.57 +{ 1.58 + unsigned int data = aPoint.y * aStride; 1.59 + data += aPoint.x * BytesPerPixel(aFormat); 1.60 + return data; 1.61 +} 1.62 + 1.63 +static GLint GetAddressAlignment(ptrdiff_t aAddress) 1.64 +{ 1.65 + if (!(aAddress & 0x7)) { 1.66 + return 8; 1.67 + } else if (!(aAddress & 0x3)) { 1.68 + return 4; 1.69 + } else if (!(aAddress & 0x1)) { 1.70 + return 2; 1.71 + } else { 1.72 + return 1; 1.73 + } 1.74 +} 1.75 + 1.76 +// Take texture data in a given buffer and copy it into a larger buffer, 1.77 +// padding out the edge pixels for filtering if necessary 1.78 +static void 1.79 +CopyAndPadTextureData(const GLvoid* srcBuffer, 1.80 + GLvoid* dstBuffer, 1.81 + GLsizei srcWidth, GLsizei srcHeight, 1.82 + GLsizei dstWidth, GLsizei dstHeight, 1.83 + GLsizei stride, GLint pixelsize) 1.84 +{ 1.85 + unsigned char *rowDest = static_cast<unsigned char*>(dstBuffer); 1.86 + const unsigned char *source = static_cast<const unsigned char*>(srcBuffer); 1.87 + 1.88 + for (GLsizei h = 0; h < srcHeight; ++h) { 1.89 + memcpy(rowDest, source, srcWidth * pixelsize); 1.90 + rowDest += dstWidth * pixelsize; 1.91 + source += stride; 1.92 + } 1.93 + 1.94 + GLsizei padHeight = srcHeight; 1.95 + 1.96 + // Pad out an extra row of pixels so that edge filtering doesn't use garbage data 1.97 + if (dstHeight > srcHeight) { 1.98 + memcpy(rowDest, source - stride, srcWidth * pixelsize); 1.99 + padHeight++; 1.100 + } 1.101 + 1.102 + // Pad out an extra column of pixels 1.103 + if (dstWidth > srcWidth) { 1.104 + rowDest = static_cast<unsigned char*>(dstBuffer) + srcWidth * pixelsize; 1.105 + for (GLsizei h = 0; h < padHeight; ++h) { 1.106 + memcpy(rowDest, rowDest - pixelsize, pixelsize); 1.107 + rowDest += dstWidth * pixelsize; 1.108 + } 1.109 + } 1.110 +} 1.111 + 1.112 +// In both of these cases (for the Adreno at least) it is impossible 1.113 +// to determine good or bad driver versions for POT texture uploads, 1.114 +// so blacklist them all. Newer drivers use a different rendering 1.115 +// string in the form "Adreno (TM) 200" and the drivers we've seen so 1.116 +// far work fine with NPOT textures, so don't blacklist those until we 1.117 +// have evidence of any problems with them. 1.118 +bool 1.119 +CanUploadSubTextures(GLContext* gl) 1.120 +{ 1.121 + if (!gl->WorkAroundDriverBugs()) 1.122 + return true; 1.123 + 1.124 + // There are certain GPUs that we don't want to use glTexSubImage2D on 1.125 + // because that function can be very slow and/or buggy 1.126 + if (gl->Renderer() == GLRenderer::Adreno200 || 1.127 + gl->Renderer() == GLRenderer::Adreno205) 1.128 + { 1.129 + return false; 1.130 + } 1.131 + 1.132 + // On PowerVR glTexSubImage does a readback, so it will be slower 1.133 + // than just doing a glTexImage2D() directly. i.e. 26ms vs 10ms 1.134 + if (gl->Renderer() == GLRenderer::SGX540 || 1.135 + gl->Renderer() == GLRenderer::SGX530) 1.136 + { 1.137 + return false; 1.138 + } 1.139 + 1.140 + return true; 1.141 +} 1.142 + 1.143 +static void 1.144 +TexSubImage2DWithUnpackSubimageGLES(GLContext* gl, 1.145 + GLenum target, GLint level, 1.146 + GLint xoffset, GLint yoffset, 1.147 + GLsizei width, GLsizei height, 1.148 + GLsizei stride, GLint pixelsize, 1.149 + GLenum format, GLenum type, 1.150 + const GLvoid* pixels) 1.151 +{ 1.152 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1.153 + std::min(GetAddressAlignment((ptrdiff_t)pixels), 1.154 + GetAddressAlignment((ptrdiff_t)stride))); 1.155 + // When using GL_UNPACK_ROW_LENGTH, we need to work around a Tegra 1.156 + // driver crash where the driver apparently tries to read 1.157 + // (stride - width * pixelsize) bytes past the end of the last input 1.158 + // row. We only upload the first height-1 rows using GL_UNPACK_ROW_LENGTH, 1.159 + // and then we upload the final row separately. See bug 697990. 1.160 + int rowLength = stride/pixelsize; 1.161 + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); 1.162 + gl->fTexSubImage2D(target, 1.163 + level, 1.164 + xoffset, 1.165 + yoffset, 1.166 + width, 1.167 + height-1, 1.168 + format, 1.169 + type, 1.170 + pixels); 1.171 + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); 1.172 + gl->fTexSubImage2D(target, 1.173 + level, 1.174 + xoffset, 1.175 + yoffset+height-1, 1.176 + width, 1.177 + 1, 1.178 + format, 1.179 + type, 1.180 + (const unsigned char *)pixels+(height-1)*stride); 1.181 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); 1.182 +} 1.183 + 1.184 +static void 1.185 +TexSubImage2DWithoutUnpackSubimage(GLContext* gl, 1.186 + GLenum target, GLint level, 1.187 + GLint xoffset, GLint yoffset, 1.188 + GLsizei width, GLsizei height, 1.189 + GLsizei stride, GLint pixelsize, 1.190 + GLenum format, GLenum type, 1.191 + const GLvoid* pixels) 1.192 +{ 1.193 + // Not using the whole row of texture data and GL_UNPACK_ROW_LENGTH 1.194 + // isn't supported. We make a copy of the texture data we're using, 1.195 + // such that we're using the whole row of data in the copy. This turns 1.196 + // out to be more efficient than uploading row-by-row; see bug 698197. 1.197 + unsigned char *newPixels = new unsigned char[width*height*pixelsize]; 1.198 + unsigned char *rowDest = newPixels; 1.199 + const unsigned char *rowSource = (const unsigned char *)pixels; 1.200 + for (int h = 0; h < height; h++) { 1.201 + memcpy(rowDest, rowSource, width*pixelsize); 1.202 + rowDest += width*pixelsize; 1.203 + rowSource += stride; 1.204 + } 1.205 + 1.206 + stride = width*pixelsize; 1.207 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1.208 + std::min(GetAddressAlignment((ptrdiff_t)newPixels), 1.209 + GetAddressAlignment((ptrdiff_t)stride))); 1.210 + gl->fTexSubImage2D(target, 1.211 + level, 1.212 + xoffset, 1.213 + yoffset, 1.214 + width, 1.215 + height, 1.216 + format, 1.217 + type, 1.218 + newPixels); 1.219 + delete [] newPixels; 1.220 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); 1.221 +} 1.222 + 1.223 +static void 1.224 +TexSubImage2DHelper(GLContext *gl, 1.225 + GLenum target, GLint level, 1.226 + GLint xoffset, GLint yoffset, 1.227 + GLsizei width, GLsizei height, GLsizei stride, 1.228 + GLint pixelsize, GLenum format, 1.229 + GLenum type, const GLvoid* pixels) 1.230 +{ 1.231 + if (gl->IsGLES()) { 1.232 + if (stride == width * pixelsize) { 1.233 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1.234 + std::min(GetAddressAlignment((ptrdiff_t)pixels), 1.235 + GetAddressAlignment((ptrdiff_t)stride))); 1.236 + gl->fTexSubImage2D(target, 1.237 + level, 1.238 + xoffset, 1.239 + yoffset, 1.240 + width, 1.241 + height, 1.242 + format, 1.243 + type, 1.244 + pixels); 1.245 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); 1.246 + } else if (gl->IsExtensionSupported(GLContext::EXT_unpack_subimage)) { 1.247 + TexSubImage2DWithUnpackSubimageGLES(gl, target, level, xoffset, yoffset, 1.248 + width, height, stride, 1.249 + pixelsize, format, type, pixels); 1.250 + 1.251 + } else { 1.252 + TexSubImage2DWithoutUnpackSubimage(gl, target, level, xoffset, yoffset, 1.253 + width, height, stride, 1.254 + pixelsize, format, type, pixels); 1.255 + } 1.256 + } else { 1.257 + // desktop GL (non-ES) path 1.258 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1.259 + std::min(GetAddressAlignment((ptrdiff_t)pixels), 1.260 + GetAddressAlignment((ptrdiff_t)stride))); 1.261 + int rowLength = stride/pixelsize; 1.262 + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); 1.263 + gl->fTexSubImage2D(target, 1.264 + level, 1.265 + xoffset, 1.266 + yoffset, 1.267 + width, 1.268 + height, 1.269 + format, 1.270 + type, 1.271 + pixels); 1.272 + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); 1.273 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); 1.274 + } 1.275 +} 1.276 + 1.277 +static void 1.278 +TexImage2DHelper(GLContext *gl, 1.279 + GLenum target, GLint level, GLint internalformat, 1.280 + GLsizei width, GLsizei height, GLsizei stride, 1.281 + GLint pixelsize, GLint border, GLenum format, 1.282 + GLenum type, const GLvoid *pixels) 1.283 +{ 1.284 + if (gl->IsGLES()) { 1.285 + 1.286 + NS_ASSERTION(format == (GLenum)internalformat, 1.287 + "format and internalformat not the same for glTexImage2D on GLES2"); 1.288 + 1.289 + if (!CanUploadNonPowerOfTwo(gl) 1.290 + && (stride != width * pixelsize 1.291 + || !IsPowerOfTwo(width) 1.292 + || !IsPowerOfTwo(height))) { 1.293 + 1.294 + // Pad out texture width and height to the next power of two 1.295 + // as we don't support/want non power of two texture uploads 1.296 + GLsizei paddedWidth = NextPowerOfTwo(width); 1.297 + GLsizei paddedHeight = NextPowerOfTwo(height); 1.298 + 1.299 + GLvoid* paddedPixels = new unsigned char[paddedWidth * paddedHeight * pixelsize]; 1.300 + 1.301 + // Pad out texture data to be in a POT sized buffer for uploading to 1.302 + // a POT sized texture 1.303 + CopyAndPadTextureData(pixels, paddedPixels, width, height, 1.304 + paddedWidth, paddedHeight, stride, pixelsize); 1.305 + 1.306 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1.307 + std::min(GetAddressAlignment((ptrdiff_t)paddedPixels), 1.308 + GetAddressAlignment((ptrdiff_t)paddedWidth * pixelsize))); 1.309 + gl->fTexImage2D(target, 1.310 + border, 1.311 + internalformat, 1.312 + paddedWidth, 1.313 + paddedHeight, 1.314 + border, 1.315 + format, 1.316 + type, 1.317 + paddedPixels); 1.318 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); 1.319 + 1.320 + delete[] static_cast<unsigned char*>(paddedPixels); 1.321 + return; 1.322 + } 1.323 + 1.324 + if (stride == width * pixelsize) { 1.325 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1.326 + std::min(GetAddressAlignment((ptrdiff_t)pixels), 1.327 + GetAddressAlignment((ptrdiff_t)stride))); 1.328 + gl->fTexImage2D(target, 1.329 + border, 1.330 + internalformat, 1.331 + width, 1.332 + height, 1.333 + border, 1.334 + format, 1.335 + type, 1.336 + pixels); 1.337 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); 1.338 + } else { 1.339 + // Use GLES-specific workarounds for GL_UNPACK_ROW_LENGTH; these are 1.340 + // implemented in TexSubImage2D. 1.341 + gl->fTexImage2D(target, 1.342 + border, 1.343 + internalformat, 1.344 + width, 1.345 + height, 1.346 + border, 1.347 + format, 1.348 + type, 1.349 + nullptr); 1.350 + TexSubImage2DHelper(gl, 1.351 + target, 1.352 + level, 1.353 + 0, 1.354 + 0, 1.355 + width, 1.356 + height, 1.357 + stride, 1.358 + pixelsize, 1.359 + format, 1.360 + type, 1.361 + pixels); 1.362 + } 1.363 + } else { 1.364 + // desktop GL (non-ES) path 1.365 + 1.366 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1.367 + std::min(GetAddressAlignment((ptrdiff_t)pixels), 1.368 + GetAddressAlignment((ptrdiff_t)stride))); 1.369 + int rowLength = stride/pixelsize; 1.370 + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); 1.371 + gl->fTexImage2D(target, 1.372 + level, 1.373 + internalformat, 1.374 + width, 1.375 + height, 1.376 + border, 1.377 + format, 1.378 + type, 1.379 + pixels); 1.380 + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); 1.381 + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); 1.382 + } 1.383 +} 1.384 + 1.385 +SurfaceFormat 1.386 +UploadImageDataToTexture(GLContext* gl, 1.387 + unsigned char* aData, 1.388 + int32_t aStride, 1.389 + SurfaceFormat aFormat, 1.390 + const nsIntRegion& aDstRegion, 1.391 + GLuint& aTexture, 1.392 + bool aOverwrite, 1.393 + bool aPixelBuffer, 1.394 + GLenum aTextureUnit, 1.395 + GLenum aTextureTarget) 1.396 +{ 1.397 + bool textureInited = aOverwrite ? false : true; 1.398 + gl->MakeCurrent(); 1.399 + gl->fActiveTexture(aTextureUnit); 1.400 + 1.401 + if (!aTexture) { 1.402 + gl->fGenTextures(1, &aTexture); 1.403 + gl->fBindTexture(aTextureTarget, aTexture); 1.404 + gl->fTexParameteri(aTextureTarget, 1.405 + LOCAL_GL_TEXTURE_MIN_FILTER, 1.406 + LOCAL_GL_LINEAR); 1.407 + gl->fTexParameteri(aTextureTarget, 1.408 + LOCAL_GL_TEXTURE_MAG_FILTER, 1.409 + LOCAL_GL_LINEAR); 1.410 + gl->fTexParameteri(aTextureTarget, 1.411 + LOCAL_GL_TEXTURE_WRAP_S, 1.412 + LOCAL_GL_CLAMP_TO_EDGE); 1.413 + gl->fTexParameteri(aTextureTarget, 1.414 + LOCAL_GL_TEXTURE_WRAP_T, 1.415 + LOCAL_GL_CLAMP_TO_EDGE); 1.416 + textureInited = false; 1.417 + } else { 1.418 + gl->fBindTexture(aTextureTarget, aTexture); 1.419 + } 1.420 + 1.421 + nsIntRegion paintRegion; 1.422 + if (!textureInited) { 1.423 + paintRegion = nsIntRegion(aDstRegion.GetBounds()); 1.424 + } else { 1.425 + paintRegion = aDstRegion; 1.426 + } 1.427 + 1.428 + GLenum format = 0; 1.429 + GLenum internalFormat = 0; 1.430 + GLenum type = 0; 1.431 + int32_t pixelSize = BytesPerPixel(aFormat); 1.432 + SurfaceFormat surfaceFormat = gfx::SurfaceFormat::UNKNOWN; 1.433 + 1.434 + MOZ_ASSERT(gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA || 1.435 + gl->GetPreferredARGB32Format() == LOCAL_GL_RGBA); 1.436 + switch (aFormat) { 1.437 + case SurfaceFormat::B8G8R8A8: 1.438 + if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { 1.439 + format = LOCAL_GL_BGRA; 1.440 + surfaceFormat = SurfaceFormat::R8G8B8A8; 1.441 + type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; 1.442 + } else { 1.443 + format = LOCAL_GL_RGBA; 1.444 + surfaceFormat = SurfaceFormat::B8G8R8A8; 1.445 + type = LOCAL_GL_UNSIGNED_BYTE; 1.446 + } 1.447 + internalFormat = LOCAL_GL_RGBA; 1.448 + break; 1.449 + case SurfaceFormat::B8G8R8X8: 1.450 + // Treat BGRX surfaces as BGRA except for the surface 1.451 + // format used. 1.452 + if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { 1.453 + format = LOCAL_GL_BGRA; 1.454 + surfaceFormat = SurfaceFormat::R8G8B8X8; 1.455 + type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; 1.456 + } else { 1.457 + format = LOCAL_GL_RGBA; 1.458 + surfaceFormat = SurfaceFormat::B8G8R8X8; 1.459 + type = LOCAL_GL_UNSIGNED_BYTE; 1.460 + } 1.461 + internalFormat = LOCAL_GL_RGBA; 1.462 + break; 1.463 + case SurfaceFormat::R5G6B5: 1.464 + internalFormat = format = LOCAL_GL_RGB; 1.465 + type = LOCAL_GL_UNSIGNED_SHORT_5_6_5; 1.466 + surfaceFormat = SurfaceFormat::R5G6B5; 1.467 + break; 1.468 + case SurfaceFormat::A8: 1.469 + internalFormat = format = LOCAL_GL_LUMINANCE; 1.470 + type = LOCAL_GL_UNSIGNED_BYTE; 1.471 + // We don't have a specific luminance shader 1.472 + surfaceFormat = SurfaceFormat::A8; 1.473 + break; 1.474 + default: 1.475 + NS_ASSERTION(false, "Unhandled image surface format!"); 1.476 + } 1.477 + 1.478 + nsIntRegionRectIterator iter(paintRegion); 1.479 + const nsIntRect *iterRect; 1.480 + 1.481 + // Top left point of the region's bounding rectangle. 1.482 + nsIntPoint topLeft = paintRegion.GetBounds().TopLeft(); 1.483 + 1.484 + while ((iterRect = iter.Next())) { 1.485 + // The inital data pointer is at the top left point of the region's 1.486 + // bounding rectangle. We need to find the offset of this rect 1.487 + // within the region and adjust the data pointer accordingly. 1.488 + unsigned char *rectData = 1.489 + aData + DataOffset(iterRect->TopLeft() - topLeft, aStride, aFormat); 1.490 + 1.491 + NS_ASSERTION(textureInited || (iterRect->x == 0 && iterRect->y == 0), 1.492 + "Must be uploading to the origin when we don't have an existing texture"); 1.493 + 1.494 + if (textureInited && CanUploadSubTextures(gl)) { 1.495 + TexSubImage2DHelper(gl, 1.496 + aTextureTarget, 1.497 + 0, 1.498 + iterRect->x, 1.499 + iterRect->y, 1.500 + iterRect->width, 1.501 + iterRect->height, 1.502 + aStride, 1.503 + pixelSize, 1.504 + format, 1.505 + type, 1.506 + rectData); 1.507 + } else { 1.508 + TexImage2DHelper(gl, 1.509 + aTextureTarget, 1.510 + 0, 1.511 + internalFormat, 1.512 + iterRect->width, 1.513 + iterRect->height, 1.514 + aStride, 1.515 + pixelSize, 1.516 + 0, 1.517 + format, 1.518 + type, 1.519 + rectData); 1.520 + } 1.521 + 1.522 + } 1.523 + 1.524 + return surfaceFormat; 1.525 +} 1.526 + 1.527 +SurfaceFormat 1.528 +UploadSurfaceToTexture(GLContext* gl, 1.529 + DataSourceSurface *aSurface, 1.530 + const nsIntRegion& aDstRegion, 1.531 + GLuint& aTexture, 1.532 + bool aOverwrite, 1.533 + const nsIntPoint& aSrcPoint, 1.534 + bool aPixelBuffer, 1.535 + GLenum aTextureUnit, 1.536 + GLenum aTextureTarget) 1.537 +{ 1.538 + unsigned char* data = aPixelBuffer ? nullptr : aSurface->GetData(); 1.539 + int32_t stride = aSurface->Stride(); 1.540 + SurfaceFormat format = aSurface->GetFormat(); 1.541 + data += DataOffset(aSrcPoint, stride, format); 1.542 + return UploadImageDataToTexture(gl, data, stride, format, 1.543 + aDstRegion, aTexture, aOverwrite, 1.544 + aPixelBuffer, aTextureUnit, 1.545 + aTextureTarget); 1.546 +} 1.547 + 1.548 +bool 1.549 +CanUploadNonPowerOfTwo(GLContext* gl) 1.550 +{ 1.551 + if (!gl->WorkAroundDriverBugs()) 1.552 + return true; 1.553 + 1.554 + // Some GPUs driver crash when uploading non power of two 565 textures. 1.555 + return gl->Renderer() != GLRenderer::Adreno200 && 1.556 + gl->Renderer() != GLRenderer::Adreno205; 1.557 +} 1.558 + 1.559 +} 1.560 +}