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"
9 #include "mozilla/gfx/2D.h"
10 #include "mozilla/gfx/Tools.h" // For BytesPerPixel
19 /* These two techniques are suggested by "Bit Twiddling Hacks"
23 * Returns true if |aNumber| is a power of two
24 * 0 is incorreclty considered a power of two
27 IsPowerOfTwo(int aNumber
)
29 return (aNumber
& (aNumber
- 1)) == 0;
33 * Returns the first integer greater than |aNumber| which is a power of two
34 * Undefined for |aNumber| < 0
37 NextPowerOfTwo(int aNumber
)
40 return 1 << (32 - __builtin_clz(aNumber
- 1));
43 aNumber
|= aNumber
>> 1;
44 aNumber
|= aNumber
>> 2;
45 aNumber
|= aNumber
>> 4;
46 aNumber
|= aNumber
>> 8;
47 aNumber
|= aNumber
>> 16;
53 DataOffset(const nsIntPoint
&aPoint
, int32_t aStride
, SurfaceFormat aFormat
)
55 unsigned int data
= aPoint
.y
* aStride
;
56 data
+= aPoint
.x
* BytesPerPixel(aFormat
);
60 static GLint
GetAddressAlignment(ptrdiff_t aAddress
)
62 if (!(aAddress
& 0x7)) {
64 } else if (!(aAddress
& 0x3)) {
66 } else if (!(aAddress
& 0x1)) {
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
76 CopyAndPadTextureData(const GLvoid
* srcBuffer
,
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
;
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
);
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.
116 CanUploadSubTextures(GLContext
* gl
)
118 if (!gl
->WorkAroundDriverBugs())
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
)
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
)
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
,
168 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0);
169 gl
->fTexSubImage2D(target
,
177 (const unsigned char *)pixels
+(height
-1)*stride
);
178 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
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
;
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
,
217 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
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
)
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
,
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
);
249 TexSubImage2DWithoutUnpackSubimage(gl
, target
, level
, xoffset
, yoffset
,
250 width
, height
, stride
,
251 pixelsize
, format
, type
, pixels
);
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
,
269 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0);
270 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
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
)
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
,
315 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
317 delete[] static_cast<unsigned char*>(paddedPixels
);
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
,
334 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
336 // Use GLES-specific workarounds for GL_UNPACK_ROW_LENGTH; these are
337 // implemented in TexSubImage2D.
338 gl
->fTexImage2D(target
,
347 TexSubImage2DHelper(gl
,
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
,
377 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0);
378 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
383 UploadImageDataToTexture(GLContext
* gl
,
384 unsigned char* aData
,
386 SurfaceFormat aFormat
,
387 const nsIntRegion
& aDstRegion
,
392 GLenum aTextureTarget
)
394 bool textureInited
= aOverwrite
? false : true;
396 gl
->fActiveTexture(aTextureUnit
);
399 gl
->fGenTextures(1, &aTexture
);
400 gl
->fBindTexture(aTextureTarget
, aTexture
);
401 gl
->fTexParameteri(aTextureTarget
,
402 LOCAL_GL_TEXTURE_MIN_FILTER
,
404 gl
->fTexParameteri(aTextureTarget
,
405 LOCAL_GL_TEXTURE_MAG_FILTER
,
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;
415 gl
->fBindTexture(aTextureTarget
, aTexture
);
418 nsIntRegion paintRegion
;
419 if (!textureInited
) {
420 paintRegion
= nsIntRegion(aDstRegion
.GetBounds());
422 paintRegion
= aDstRegion
;
426 GLenum internalFormat
= 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
);
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
;
440 format
= LOCAL_GL_RGBA
;
441 surfaceFormat
= SurfaceFormat::B8G8R8A8
;
442 type
= LOCAL_GL_UNSIGNED_BYTE
;
444 internalFormat
= LOCAL_GL_RGBA
;
446 case SurfaceFormat::B8G8R8X8
:
447 // Treat BGRX surfaces as BGRA except for the surface
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
;
454 format
= LOCAL_GL_RGBA
;
455 surfaceFormat
= SurfaceFormat::B8G8R8X8
;
456 type
= LOCAL_GL_UNSIGNED_BYTE
;
458 internalFormat
= LOCAL_GL_RGBA
;
460 case SurfaceFormat::R5G6B5
:
461 internalFormat
= format
= LOCAL_GL_RGB
;
462 type
= LOCAL_GL_UNSIGNED_SHORT_5_6_5
;
463 surfaceFormat
= SurfaceFormat::R5G6B5
;
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
;
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
,
521 return surfaceFormat
;
525 UploadSurfaceToTexture(GLContext
* gl
,
526 DataSourceSurface
*aSurface
,
527 const nsIntRegion
& aDstRegion
,
530 const nsIntPoint
& aSrcPoint
,
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
,
546 CanUploadNonPowerOfTwo(GLContext
* gl
)
548 if (!gl
->WorkAroundDriverBugs())
551 // Some GPUs driver crash when uploading non power of two 565 textures.
552 return gl
->Renderer() != GLRenderer::Adreno200
&&
553 gl
->Renderer() != GLRenderer::Adreno205
;