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