Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "GLUploadHelpers.h" |
michael@0 | 7 | |
michael@0 | 8 | #include "GLContext.h" |
michael@0 | 9 | #include "mozilla/gfx/2D.h" |
michael@0 | 10 | #include "mozilla/gfx/Tools.h" // For BytesPerPixel |
michael@0 | 11 | #include "nsRegion.h" |
michael@0 | 12 | |
michael@0 | 13 | namespace mozilla { |
michael@0 | 14 | |
michael@0 | 15 | using namespace gfx; |
michael@0 | 16 | |
michael@0 | 17 | namespace gl { |
michael@0 | 18 | |
michael@0 | 19 | /* These two techniques are suggested by "Bit Twiddling Hacks" |
michael@0 | 20 | */ |
michael@0 | 21 | |
michael@0 | 22 | /** |
michael@0 | 23 | * Returns true if |aNumber| is a power of two |
michael@0 | 24 | * 0 is incorreclty considered a power of two |
michael@0 | 25 | */ |
michael@0 | 26 | static bool |
michael@0 | 27 | IsPowerOfTwo(int aNumber) |
michael@0 | 28 | { |
michael@0 | 29 | return (aNumber & (aNumber - 1)) == 0; |
michael@0 | 30 | } |
michael@0 | 31 | |
michael@0 | 32 | /** |
michael@0 | 33 | * Returns the first integer greater than |aNumber| which is a power of two |
michael@0 | 34 | * Undefined for |aNumber| < 0 |
michael@0 | 35 | */ |
michael@0 | 36 | static int |
michael@0 | 37 | NextPowerOfTwo(int aNumber) |
michael@0 | 38 | { |
michael@0 | 39 | #if defined(__arm__) |
michael@0 | 40 | return 1 << (32 - __builtin_clz(aNumber - 1)); |
michael@0 | 41 | #else |
michael@0 | 42 | --aNumber; |
michael@0 | 43 | aNumber |= aNumber >> 1; |
michael@0 | 44 | aNumber |= aNumber >> 2; |
michael@0 | 45 | aNumber |= aNumber >> 4; |
michael@0 | 46 | aNumber |= aNumber >> 8; |
michael@0 | 47 | aNumber |= aNumber >> 16; |
michael@0 | 48 | return ++aNumber; |
michael@0 | 49 | #endif |
michael@0 | 50 | } |
michael@0 | 51 | |
michael@0 | 52 | static unsigned int |
michael@0 | 53 | DataOffset(const nsIntPoint &aPoint, int32_t aStride, SurfaceFormat aFormat) |
michael@0 | 54 | { |
michael@0 | 55 | unsigned int data = aPoint.y * aStride; |
michael@0 | 56 | data += aPoint.x * BytesPerPixel(aFormat); |
michael@0 | 57 | return data; |
michael@0 | 58 | } |
michael@0 | 59 | |
michael@0 | 60 | static GLint GetAddressAlignment(ptrdiff_t aAddress) |
michael@0 | 61 | { |
michael@0 | 62 | if (!(aAddress & 0x7)) { |
michael@0 | 63 | return 8; |
michael@0 | 64 | } else if (!(aAddress & 0x3)) { |
michael@0 | 65 | return 4; |
michael@0 | 66 | } else if (!(aAddress & 0x1)) { |
michael@0 | 67 | return 2; |
michael@0 | 68 | } else { |
michael@0 | 69 | return 1; |
michael@0 | 70 | } |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | // Take texture data in a given buffer and copy it into a larger buffer, |
michael@0 | 74 | // padding out the edge pixels for filtering if necessary |
michael@0 | 75 | static void |
michael@0 | 76 | CopyAndPadTextureData(const GLvoid* srcBuffer, |
michael@0 | 77 | GLvoid* dstBuffer, |
michael@0 | 78 | GLsizei srcWidth, GLsizei srcHeight, |
michael@0 | 79 | GLsizei dstWidth, GLsizei dstHeight, |
michael@0 | 80 | GLsizei stride, GLint pixelsize) |
michael@0 | 81 | { |
michael@0 | 82 | unsigned char *rowDest = static_cast<unsigned char*>(dstBuffer); |
michael@0 | 83 | const unsigned char *source = static_cast<const unsigned char*>(srcBuffer); |
michael@0 | 84 | |
michael@0 | 85 | for (GLsizei h = 0; h < srcHeight; ++h) { |
michael@0 | 86 | memcpy(rowDest, source, srcWidth * pixelsize); |
michael@0 | 87 | rowDest += dstWidth * pixelsize; |
michael@0 | 88 | source += stride; |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | GLsizei padHeight = srcHeight; |
michael@0 | 92 | |
michael@0 | 93 | // Pad out an extra row of pixels so that edge filtering doesn't use garbage data |
michael@0 | 94 | if (dstHeight > srcHeight) { |
michael@0 | 95 | memcpy(rowDest, source - stride, srcWidth * pixelsize); |
michael@0 | 96 | padHeight++; |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | // Pad out an extra column of pixels |
michael@0 | 100 | if (dstWidth > srcWidth) { |
michael@0 | 101 | rowDest = static_cast<unsigned char*>(dstBuffer) + srcWidth * pixelsize; |
michael@0 | 102 | for (GLsizei h = 0; h < padHeight; ++h) { |
michael@0 | 103 | memcpy(rowDest, rowDest - pixelsize, pixelsize); |
michael@0 | 104 | rowDest += dstWidth * pixelsize; |
michael@0 | 105 | } |
michael@0 | 106 | } |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | // In both of these cases (for the Adreno at least) it is impossible |
michael@0 | 110 | // to determine good or bad driver versions for POT texture uploads, |
michael@0 | 111 | // so blacklist them all. Newer drivers use a different rendering |
michael@0 | 112 | // string in the form "Adreno (TM) 200" and the drivers we've seen so |
michael@0 | 113 | // far work fine with NPOT textures, so don't blacklist those until we |
michael@0 | 114 | // have evidence of any problems with them. |
michael@0 | 115 | bool |
michael@0 | 116 | CanUploadSubTextures(GLContext* gl) |
michael@0 | 117 | { |
michael@0 | 118 | if (!gl->WorkAroundDriverBugs()) |
michael@0 | 119 | return true; |
michael@0 | 120 | |
michael@0 | 121 | // There are certain GPUs that we don't want to use glTexSubImage2D on |
michael@0 | 122 | // because that function can be very slow and/or buggy |
michael@0 | 123 | if (gl->Renderer() == GLRenderer::Adreno200 || |
michael@0 | 124 | gl->Renderer() == GLRenderer::Adreno205) |
michael@0 | 125 | { |
michael@0 | 126 | return false; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | // On PowerVR glTexSubImage does a readback, so it will be slower |
michael@0 | 130 | // than just doing a glTexImage2D() directly. i.e. 26ms vs 10ms |
michael@0 | 131 | if (gl->Renderer() == GLRenderer::SGX540 || |
michael@0 | 132 | gl->Renderer() == GLRenderer::SGX530) |
michael@0 | 133 | { |
michael@0 | 134 | return false; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | return true; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | static void |
michael@0 | 141 | TexSubImage2DWithUnpackSubimageGLES(GLContext* gl, |
michael@0 | 142 | GLenum target, GLint level, |
michael@0 | 143 | GLint xoffset, GLint yoffset, |
michael@0 | 144 | GLsizei width, GLsizei height, |
michael@0 | 145 | GLsizei stride, GLint pixelsize, |
michael@0 | 146 | GLenum format, GLenum type, |
michael@0 | 147 | const GLvoid* pixels) |
michael@0 | 148 | { |
michael@0 | 149 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, |
michael@0 | 150 | std::min(GetAddressAlignment((ptrdiff_t)pixels), |
michael@0 | 151 | GetAddressAlignment((ptrdiff_t)stride))); |
michael@0 | 152 | // When using GL_UNPACK_ROW_LENGTH, we need to work around a Tegra |
michael@0 | 153 | // driver crash where the driver apparently tries to read |
michael@0 | 154 | // (stride - width * pixelsize) bytes past the end of the last input |
michael@0 | 155 | // row. We only upload the first height-1 rows using GL_UNPACK_ROW_LENGTH, |
michael@0 | 156 | // and then we upload the final row separately. See bug 697990. |
michael@0 | 157 | int rowLength = stride/pixelsize; |
michael@0 | 158 | gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); |
michael@0 | 159 | gl->fTexSubImage2D(target, |
michael@0 | 160 | level, |
michael@0 | 161 | xoffset, |
michael@0 | 162 | yoffset, |
michael@0 | 163 | width, |
michael@0 | 164 | height-1, |
michael@0 | 165 | format, |
michael@0 | 166 | type, |
michael@0 | 167 | pixels); |
michael@0 | 168 | gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); |
michael@0 | 169 | gl->fTexSubImage2D(target, |
michael@0 | 170 | level, |
michael@0 | 171 | xoffset, |
michael@0 | 172 | yoffset+height-1, |
michael@0 | 173 | width, |
michael@0 | 174 | 1, |
michael@0 | 175 | format, |
michael@0 | 176 | type, |
michael@0 | 177 | (const unsigned char *)pixels+(height-1)*stride); |
michael@0 | 178 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | static void |
michael@0 | 182 | TexSubImage2DWithoutUnpackSubimage(GLContext* gl, |
michael@0 | 183 | GLenum target, GLint level, |
michael@0 | 184 | GLint xoffset, GLint yoffset, |
michael@0 | 185 | GLsizei width, GLsizei height, |
michael@0 | 186 | GLsizei stride, GLint pixelsize, |
michael@0 | 187 | GLenum format, GLenum type, |
michael@0 | 188 | const GLvoid* pixels) |
michael@0 | 189 | { |
michael@0 | 190 | // Not using the whole row of texture data and GL_UNPACK_ROW_LENGTH |
michael@0 | 191 | // isn't supported. We make a copy of the texture data we're using, |
michael@0 | 192 | // such that we're using the whole row of data in the copy. This turns |
michael@0 | 193 | // out to be more efficient than uploading row-by-row; see bug 698197. |
michael@0 | 194 | unsigned char *newPixels = new unsigned char[width*height*pixelsize]; |
michael@0 | 195 | unsigned char *rowDest = newPixels; |
michael@0 | 196 | const unsigned char *rowSource = (const unsigned char *)pixels; |
michael@0 | 197 | for (int h = 0; h < height; h++) { |
michael@0 | 198 | memcpy(rowDest, rowSource, width*pixelsize); |
michael@0 | 199 | rowDest += width*pixelsize; |
michael@0 | 200 | rowSource += stride; |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | stride = width*pixelsize; |
michael@0 | 204 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, |
michael@0 | 205 | std::min(GetAddressAlignment((ptrdiff_t)newPixels), |
michael@0 | 206 | GetAddressAlignment((ptrdiff_t)stride))); |
michael@0 | 207 | gl->fTexSubImage2D(target, |
michael@0 | 208 | level, |
michael@0 | 209 | xoffset, |
michael@0 | 210 | yoffset, |
michael@0 | 211 | width, |
michael@0 | 212 | height, |
michael@0 | 213 | format, |
michael@0 | 214 | type, |
michael@0 | 215 | newPixels); |
michael@0 | 216 | delete [] newPixels; |
michael@0 | 217 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | static void |
michael@0 | 221 | TexSubImage2DHelper(GLContext *gl, |
michael@0 | 222 | GLenum target, GLint level, |
michael@0 | 223 | GLint xoffset, GLint yoffset, |
michael@0 | 224 | GLsizei width, GLsizei height, GLsizei stride, |
michael@0 | 225 | GLint pixelsize, GLenum format, |
michael@0 | 226 | GLenum type, const GLvoid* pixels) |
michael@0 | 227 | { |
michael@0 | 228 | if (gl->IsGLES()) { |
michael@0 | 229 | if (stride == width * pixelsize) { |
michael@0 | 230 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, |
michael@0 | 231 | std::min(GetAddressAlignment((ptrdiff_t)pixels), |
michael@0 | 232 | GetAddressAlignment((ptrdiff_t)stride))); |
michael@0 | 233 | gl->fTexSubImage2D(target, |
michael@0 | 234 | level, |
michael@0 | 235 | xoffset, |
michael@0 | 236 | yoffset, |
michael@0 | 237 | width, |
michael@0 | 238 | height, |
michael@0 | 239 | format, |
michael@0 | 240 | type, |
michael@0 | 241 | pixels); |
michael@0 | 242 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); |
michael@0 | 243 | } else if (gl->IsExtensionSupported(GLContext::EXT_unpack_subimage)) { |
michael@0 | 244 | TexSubImage2DWithUnpackSubimageGLES(gl, target, level, xoffset, yoffset, |
michael@0 | 245 | width, height, stride, |
michael@0 | 246 | pixelsize, format, type, pixels); |
michael@0 | 247 | |
michael@0 | 248 | } else { |
michael@0 | 249 | TexSubImage2DWithoutUnpackSubimage(gl, target, level, xoffset, yoffset, |
michael@0 | 250 | width, height, stride, |
michael@0 | 251 | pixelsize, format, type, pixels); |
michael@0 | 252 | } |
michael@0 | 253 | } else { |
michael@0 | 254 | // desktop GL (non-ES) path |
michael@0 | 255 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, |
michael@0 | 256 | std::min(GetAddressAlignment((ptrdiff_t)pixels), |
michael@0 | 257 | GetAddressAlignment((ptrdiff_t)stride))); |
michael@0 | 258 | int rowLength = stride/pixelsize; |
michael@0 | 259 | gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); |
michael@0 | 260 | gl->fTexSubImage2D(target, |
michael@0 | 261 | level, |
michael@0 | 262 | xoffset, |
michael@0 | 263 | yoffset, |
michael@0 | 264 | width, |
michael@0 | 265 | height, |
michael@0 | 266 | format, |
michael@0 | 267 | type, |
michael@0 | 268 | pixels); |
michael@0 | 269 | gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); |
michael@0 | 270 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); |
michael@0 | 271 | } |
michael@0 | 272 | } |
michael@0 | 273 | |
michael@0 | 274 | static void |
michael@0 | 275 | TexImage2DHelper(GLContext *gl, |
michael@0 | 276 | GLenum target, GLint level, GLint internalformat, |
michael@0 | 277 | GLsizei width, GLsizei height, GLsizei stride, |
michael@0 | 278 | GLint pixelsize, GLint border, GLenum format, |
michael@0 | 279 | GLenum type, const GLvoid *pixels) |
michael@0 | 280 | { |
michael@0 | 281 | if (gl->IsGLES()) { |
michael@0 | 282 | |
michael@0 | 283 | NS_ASSERTION(format == (GLenum)internalformat, |
michael@0 | 284 | "format and internalformat not the same for glTexImage2D on GLES2"); |
michael@0 | 285 | |
michael@0 | 286 | if (!CanUploadNonPowerOfTwo(gl) |
michael@0 | 287 | && (stride != width * pixelsize |
michael@0 | 288 | || !IsPowerOfTwo(width) |
michael@0 | 289 | || !IsPowerOfTwo(height))) { |
michael@0 | 290 | |
michael@0 | 291 | // Pad out texture width and height to the next power of two |
michael@0 | 292 | // as we don't support/want non power of two texture uploads |
michael@0 | 293 | GLsizei paddedWidth = NextPowerOfTwo(width); |
michael@0 | 294 | GLsizei paddedHeight = NextPowerOfTwo(height); |
michael@0 | 295 | |
michael@0 | 296 | GLvoid* paddedPixels = new unsigned char[paddedWidth * paddedHeight * pixelsize]; |
michael@0 | 297 | |
michael@0 | 298 | // Pad out texture data to be in a POT sized buffer for uploading to |
michael@0 | 299 | // a POT sized texture |
michael@0 | 300 | CopyAndPadTextureData(pixels, paddedPixels, width, height, |
michael@0 | 301 | paddedWidth, paddedHeight, stride, pixelsize); |
michael@0 | 302 | |
michael@0 | 303 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, |
michael@0 | 304 | std::min(GetAddressAlignment((ptrdiff_t)paddedPixels), |
michael@0 | 305 | GetAddressAlignment((ptrdiff_t)paddedWidth * pixelsize))); |
michael@0 | 306 | gl->fTexImage2D(target, |
michael@0 | 307 | border, |
michael@0 | 308 | internalformat, |
michael@0 | 309 | paddedWidth, |
michael@0 | 310 | paddedHeight, |
michael@0 | 311 | border, |
michael@0 | 312 | format, |
michael@0 | 313 | type, |
michael@0 | 314 | paddedPixels); |
michael@0 | 315 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); |
michael@0 | 316 | |
michael@0 | 317 | delete[] static_cast<unsigned char*>(paddedPixels); |
michael@0 | 318 | return; |
michael@0 | 319 | } |
michael@0 | 320 | |
michael@0 | 321 | if (stride == width * pixelsize) { |
michael@0 | 322 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, |
michael@0 | 323 | std::min(GetAddressAlignment((ptrdiff_t)pixels), |
michael@0 | 324 | GetAddressAlignment((ptrdiff_t)stride))); |
michael@0 | 325 | gl->fTexImage2D(target, |
michael@0 | 326 | border, |
michael@0 | 327 | internalformat, |
michael@0 | 328 | width, |
michael@0 | 329 | height, |
michael@0 | 330 | border, |
michael@0 | 331 | format, |
michael@0 | 332 | type, |
michael@0 | 333 | pixels); |
michael@0 | 334 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); |
michael@0 | 335 | } else { |
michael@0 | 336 | // Use GLES-specific workarounds for GL_UNPACK_ROW_LENGTH; these are |
michael@0 | 337 | // implemented in TexSubImage2D. |
michael@0 | 338 | gl->fTexImage2D(target, |
michael@0 | 339 | border, |
michael@0 | 340 | internalformat, |
michael@0 | 341 | width, |
michael@0 | 342 | height, |
michael@0 | 343 | border, |
michael@0 | 344 | format, |
michael@0 | 345 | type, |
michael@0 | 346 | nullptr); |
michael@0 | 347 | TexSubImage2DHelper(gl, |
michael@0 | 348 | target, |
michael@0 | 349 | level, |
michael@0 | 350 | 0, |
michael@0 | 351 | 0, |
michael@0 | 352 | width, |
michael@0 | 353 | height, |
michael@0 | 354 | stride, |
michael@0 | 355 | pixelsize, |
michael@0 | 356 | format, |
michael@0 | 357 | type, |
michael@0 | 358 | pixels); |
michael@0 | 359 | } |
michael@0 | 360 | } else { |
michael@0 | 361 | // desktop GL (non-ES) path |
michael@0 | 362 | |
michael@0 | 363 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, |
michael@0 | 364 | std::min(GetAddressAlignment((ptrdiff_t)pixels), |
michael@0 | 365 | GetAddressAlignment((ptrdiff_t)stride))); |
michael@0 | 366 | int rowLength = stride/pixelsize; |
michael@0 | 367 | gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); |
michael@0 | 368 | gl->fTexImage2D(target, |
michael@0 | 369 | level, |
michael@0 | 370 | internalformat, |
michael@0 | 371 | width, |
michael@0 | 372 | height, |
michael@0 | 373 | border, |
michael@0 | 374 | format, |
michael@0 | 375 | type, |
michael@0 | 376 | pixels); |
michael@0 | 377 | gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); |
michael@0 | 378 | gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); |
michael@0 | 379 | } |
michael@0 | 380 | } |
michael@0 | 381 | |
michael@0 | 382 | SurfaceFormat |
michael@0 | 383 | UploadImageDataToTexture(GLContext* gl, |
michael@0 | 384 | unsigned char* aData, |
michael@0 | 385 | int32_t aStride, |
michael@0 | 386 | SurfaceFormat aFormat, |
michael@0 | 387 | const nsIntRegion& aDstRegion, |
michael@0 | 388 | GLuint& aTexture, |
michael@0 | 389 | bool aOverwrite, |
michael@0 | 390 | bool aPixelBuffer, |
michael@0 | 391 | GLenum aTextureUnit, |
michael@0 | 392 | GLenum aTextureTarget) |
michael@0 | 393 | { |
michael@0 | 394 | bool textureInited = aOverwrite ? false : true; |
michael@0 | 395 | gl->MakeCurrent(); |
michael@0 | 396 | gl->fActiveTexture(aTextureUnit); |
michael@0 | 397 | |
michael@0 | 398 | if (!aTexture) { |
michael@0 | 399 | gl->fGenTextures(1, &aTexture); |
michael@0 | 400 | gl->fBindTexture(aTextureTarget, aTexture); |
michael@0 | 401 | gl->fTexParameteri(aTextureTarget, |
michael@0 | 402 | LOCAL_GL_TEXTURE_MIN_FILTER, |
michael@0 | 403 | LOCAL_GL_LINEAR); |
michael@0 | 404 | gl->fTexParameteri(aTextureTarget, |
michael@0 | 405 | LOCAL_GL_TEXTURE_MAG_FILTER, |
michael@0 | 406 | LOCAL_GL_LINEAR); |
michael@0 | 407 | gl->fTexParameteri(aTextureTarget, |
michael@0 | 408 | LOCAL_GL_TEXTURE_WRAP_S, |
michael@0 | 409 | LOCAL_GL_CLAMP_TO_EDGE); |
michael@0 | 410 | gl->fTexParameteri(aTextureTarget, |
michael@0 | 411 | LOCAL_GL_TEXTURE_WRAP_T, |
michael@0 | 412 | LOCAL_GL_CLAMP_TO_EDGE); |
michael@0 | 413 | textureInited = false; |
michael@0 | 414 | } else { |
michael@0 | 415 | gl->fBindTexture(aTextureTarget, aTexture); |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | nsIntRegion paintRegion; |
michael@0 | 419 | if (!textureInited) { |
michael@0 | 420 | paintRegion = nsIntRegion(aDstRegion.GetBounds()); |
michael@0 | 421 | } else { |
michael@0 | 422 | paintRegion = aDstRegion; |
michael@0 | 423 | } |
michael@0 | 424 | |
michael@0 | 425 | GLenum format = 0; |
michael@0 | 426 | GLenum internalFormat = 0; |
michael@0 | 427 | GLenum type = 0; |
michael@0 | 428 | int32_t pixelSize = BytesPerPixel(aFormat); |
michael@0 | 429 | SurfaceFormat surfaceFormat = gfx::SurfaceFormat::UNKNOWN; |
michael@0 | 430 | |
michael@0 | 431 | MOZ_ASSERT(gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA || |
michael@0 | 432 | gl->GetPreferredARGB32Format() == LOCAL_GL_RGBA); |
michael@0 | 433 | switch (aFormat) { |
michael@0 | 434 | case SurfaceFormat::B8G8R8A8: |
michael@0 | 435 | if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { |
michael@0 | 436 | format = LOCAL_GL_BGRA; |
michael@0 | 437 | surfaceFormat = SurfaceFormat::R8G8B8A8; |
michael@0 | 438 | type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; |
michael@0 | 439 | } else { |
michael@0 | 440 | format = LOCAL_GL_RGBA; |
michael@0 | 441 | surfaceFormat = SurfaceFormat::B8G8R8A8; |
michael@0 | 442 | type = LOCAL_GL_UNSIGNED_BYTE; |
michael@0 | 443 | } |
michael@0 | 444 | internalFormat = LOCAL_GL_RGBA; |
michael@0 | 445 | break; |
michael@0 | 446 | case SurfaceFormat::B8G8R8X8: |
michael@0 | 447 | // Treat BGRX surfaces as BGRA except for the surface |
michael@0 | 448 | // format used. |
michael@0 | 449 | if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { |
michael@0 | 450 | format = LOCAL_GL_BGRA; |
michael@0 | 451 | surfaceFormat = SurfaceFormat::R8G8B8X8; |
michael@0 | 452 | type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; |
michael@0 | 453 | } else { |
michael@0 | 454 | format = LOCAL_GL_RGBA; |
michael@0 | 455 | surfaceFormat = SurfaceFormat::B8G8R8X8; |
michael@0 | 456 | type = LOCAL_GL_UNSIGNED_BYTE; |
michael@0 | 457 | } |
michael@0 | 458 | internalFormat = LOCAL_GL_RGBA; |
michael@0 | 459 | break; |
michael@0 | 460 | case SurfaceFormat::R5G6B5: |
michael@0 | 461 | internalFormat = format = LOCAL_GL_RGB; |
michael@0 | 462 | type = LOCAL_GL_UNSIGNED_SHORT_5_6_5; |
michael@0 | 463 | surfaceFormat = SurfaceFormat::R5G6B5; |
michael@0 | 464 | break; |
michael@0 | 465 | case SurfaceFormat::A8: |
michael@0 | 466 | internalFormat = format = LOCAL_GL_LUMINANCE; |
michael@0 | 467 | type = LOCAL_GL_UNSIGNED_BYTE; |
michael@0 | 468 | // We don't have a specific luminance shader |
michael@0 | 469 | surfaceFormat = SurfaceFormat::A8; |
michael@0 | 470 | break; |
michael@0 | 471 | default: |
michael@0 | 472 | NS_ASSERTION(false, "Unhandled image surface format!"); |
michael@0 | 473 | } |
michael@0 | 474 | |
michael@0 | 475 | nsIntRegionRectIterator iter(paintRegion); |
michael@0 | 476 | const nsIntRect *iterRect; |
michael@0 | 477 | |
michael@0 | 478 | // Top left point of the region's bounding rectangle. |
michael@0 | 479 | nsIntPoint topLeft = paintRegion.GetBounds().TopLeft(); |
michael@0 | 480 | |
michael@0 | 481 | while ((iterRect = iter.Next())) { |
michael@0 | 482 | // The inital data pointer is at the top left point of the region's |
michael@0 | 483 | // bounding rectangle. We need to find the offset of this rect |
michael@0 | 484 | // within the region and adjust the data pointer accordingly. |
michael@0 | 485 | unsigned char *rectData = |
michael@0 | 486 | aData + DataOffset(iterRect->TopLeft() - topLeft, aStride, aFormat); |
michael@0 | 487 | |
michael@0 | 488 | NS_ASSERTION(textureInited || (iterRect->x == 0 && iterRect->y == 0), |
michael@0 | 489 | "Must be uploading to the origin when we don't have an existing texture"); |
michael@0 | 490 | |
michael@0 | 491 | if (textureInited && CanUploadSubTextures(gl)) { |
michael@0 | 492 | TexSubImage2DHelper(gl, |
michael@0 | 493 | aTextureTarget, |
michael@0 | 494 | 0, |
michael@0 | 495 | iterRect->x, |
michael@0 | 496 | iterRect->y, |
michael@0 | 497 | iterRect->width, |
michael@0 | 498 | iterRect->height, |
michael@0 | 499 | aStride, |
michael@0 | 500 | pixelSize, |
michael@0 | 501 | format, |
michael@0 | 502 | type, |
michael@0 | 503 | rectData); |
michael@0 | 504 | } else { |
michael@0 | 505 | TexImage2DHelper(gl, |
michael@0 | 506 | aTextureTarget, |
michael@0 | 507 | 0, |
michael@0 | 508 | internalFormat, |
michael@0 | 509 | iterRect->width, |
michael@0 | 510 | iterRect->height, |
michael@0 | 511 | aStride, |
michael@0 | 512 | pixelSize, |
michael@0 | 513 | 0, |
michael@0 | 514 | format, |
michael@0 | 515 | type, |
michael@0 | 516 | rectData); |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | } |
michael@0 | 520 | |
michael@0 | 521 | return surfaceFormat; |
michael@0 | 522 | } |
michael@0 | 523 | |
michael@0 | 524 | SurfaceFormat |
michael@0 | 525 | UploadSurfaceToTexture(GLContext* gl, |
michael@0 | 526 | DataSourceSurface *aSurface, |
michael@0 | 527 | const nsIntRegion& aDstRegion, |
michael@0 | 528 | GLuint& aTexture, |
michael@0 | 529 | bool aOverwrite, |
michael@0 | 530 | const nsIntPoint& aSrcPoint, |
michael@0 | 531 | bool aPixelBuffer, |
michael@0 | 532 | GLenum aTextureUnit, |
michael@0 | 533 | GLenum aTextureTarget) |
michael@0 | 534 | { |
michael@0 | 535 | unsigned char* data = aPixelBuffer ? nullptr : aSurface->GetData(); |
michael@0 | 536 | int32_t stride = aSurface->Stride(); |
michael@0 | 537 | SurfaceFormat format = aSurface->GetFormat(); |
michael@0 | 538 | data += DataOffset(aSrcPoint, stride, format); |
michael@0 | 539 | return UploadImageDataToTexture(gl, data, stride, format, |
michael@0 | 540 | aDstRegion, aTexture, aOverwrite, |
michael@0 | 541 | aPixelBuffer, aTextureUnit, |
michael@0 | 542 | aTextureTarget); |
michael@0 | 543 | } |
michael@0 | 544 | |
michael@0 | 545 | bool |
michael@0 | 546 | CanUploadNonPowerOfTwo(GLContext* gl) |
michael@0 | 547 | { |
michael@0 | 548 | if (!gl->WorkAroundDriverBugs()) |
michael@0 | 549 | return true; |
michael@0 | 550 | |
michael@0 | 551 | // Some GPUs driver crash when uploading non power of two 565 textures. |
michael@0 | 552 | return gl->Renderer() != GLRenderer::Adreno200 && |
michael@0 | 553 | gl->Renderer() != GLRenderer::Adreno205; |
michael@0 | 554 | } |
michael@0 | 555 | |
michael@0 | 556 | } |
michael@0 | 557 | } |