1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 4; -*- */
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"
11 #include "mozilla/gfx/Tools.h" // For BytesPerPixel
13 #include "GfxTexturesReporter.h"
14 #include "mozilla/gfx/Logging.h"
22 static unsigned int DataOffset(const IntPoint
& aPoint
, int32_t aStride
,
23 SurfaceFormat aFormat
) {
24 unsigned int data
= aPoint
.y
* aStride
;
25 data
+= aPoint
.x
* BytesPerPixel(aFormat
);
29 static bool CheckUploadBounds(const IntSize
& aDst
, const IntSize
& aSrc
,
30 const IntPoint
& aOffset
) {
31 if (aOffset
.x
< 0 || aOffset
.y
< 0 || aOffset
.x
>= aSrc
.width
||
32 aOffset
.y
>= aSrc
.height
) {
33 MOZ_ASSERT_UNREACHABLE("Offset outside source bounds");
36 if (aDst
.width
> (aSrc
.width
- aOffset
.x
) ||
37 aDst
.height
> (aSrc
.height
- aOffset
.y
)) {
38 MOZ_ASSERT_UNREACHABLE("Source has insufficient data");
44 static GLint
GetAddressAlignment(ptrdiff_t aAddress
) {
45 if (!(aAddress
& 0x7)) {
47 } else if (!(aAddress
& 0x3)) {
49 } else if (!(aAddress
& 0x1)) {
56 // Take texture data in a given buffer and copy it into a larger buffer,
57 // padding out the edge pixels for filtering if necessary
58 static void CopyAndPadTextureData(const GLvoid
* srcBuffer
, GLvoid
* dstBuffer
,
59 GLsizei srcWidth
, GLsizei srcHeight
,
60 GLsizei dstWidth
, GLsizei dstHeight
,
61 GLsizei stride
, GLint pixelsize
) {
62 unsigned char* rowDest
= static_cast<unsigned char*>(dstBuffer
);
63 const unsigned char* source
= static_cast<const unsigned char*>(srcBuffer
);
65 for (GLsizei h
= 0; h
< srcHeight
; ++h
) {
66 memcpy(rowDest
, source
, srcWidth
* pixelsize
);
67 rowDest
+= dstWidth
* pixelsize
;
71 GLsizei padHeight
= srcHeight
;
73 // Pad out an extra row of pixels so that edge filtering doesn't use garbage
75 if (dstHeight
> srcHeight
) {
76 memcpy(rowDest
, source
- stride
, srcWidth
* pixelsize
);
80 // Pad out an extra column of pixels
81 if (dstWidth
> srcWidth
) {
82 rowDest
= static_cast<unsigned char*>(dstBuffer
) + srcWidth
* pixelsize
;
83 for (GLsizei h
= 0; h
< padHeight
; ++h
) {
84 memcpy(rowDest
, rowDest
- pixelsize
, pixelsize
);
85 rowDest
+= dstWidth
* pixelsize
;
90 // In both of these cases (for the Adreno at least) it is impossible
91 // to determine good or bad driver versions for POT texture uploads,
92 // so blacklist them all. Newer drivers use a different rendering
93 // string in the form "Adreno (TM) 200" and the drivers we've seen so
94 // far work fine with NPOT textures, so don't blacklist those until we
95 // have evidence of any problems with them.
96 bool ShouldUploadSubTextures(GLContext
* gl
) {
97 if (!gl
->WorkAroundDriverBugs()) return true;
99 // There are certain GPUs that we don't want to use glTexSubImage2D on
100 // because that function can be very slow and/or buggy
101 if (gl
->Renderer() == GLRenderer::Adreno200
||
102 gl
->Renderer() == GLRenderer::Adreno205
) {
106 // On PowerVR glTexSubImage does a readback, so it will be slower
107 // than just doing a glTexImage2D() directly. i.e. 26ms vs 10ms
108 if (gl
->Renderer() == GLRenderer::SGX540
||
109 gl
->Renderer() == GLRenderer::SGX530
) {
116 static void TexSubImage2DWithUnpackSubimageGLES(
117 GLContext
* gl
, GLenum target
, GLint level
, GLint xoffset
, GLint yoffset
,
118 GLsizei width
, GLsizei height
, GLsizei stride
, GLint pixelsize
,
119 GLenum format
, GLenum type
, const GLvoid
* pixels
) {
120 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
121 std::min(GetAddressAlignment((ptrdiff_t)pixels
),
122 GetAddressAlignment((ptrdiff_t)stride
)));
123 // When using GL_UNPACK_ROW_LENGTH, we need to work around a Tegra
124 // driver crash where the driver apparently tries to read
125 // (stride - width * pixelsize) bytes past the end of the last input
126 // row. We only upload the first height-1 rows using GL_UNPACK_ROW_LENGTH,
127 // and then we upload the final row separately. See bug 697990.
128 int rowLength
= stride
/ pixelsize
;
129 if (gl
->HasPBOState()) {
130 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, rowLength
);
131 gl
->fTexSubImage2D(target
, level
, xoffset
, yoffset
, width
, height
, format
,
133 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0);
135 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, rowLength
);
136 gl
->fTexSubImage2D(target
, level
, xoffset
, yoffset
, width
, height
- 1,
137 format
, type
, pixels
);
138 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0);
139 gl
->fTexSubImage2D(target
, level
, xoffset
, yoffset
+ height
- 1, width
, 1,
141 (const unsigned char*)pixels
+ (height
- 1) * stride
);
143 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
146 static void TexSubImage2DWithoutUnpackSubimage(
147 GLContext
* gl
, GLenum target
, GLint level
, GLint xoffset
, GLint yoffset
,
148 GLsizei width
, GLsizei height
, GLsizei stride
, GLint pixelsize
,
149 GLenum format
, GLenum type
, const GLvoid
* pixels
) {
150 // Not using the whole row of texture data and GL_UNPACK_ROW_LENGTH
151 // isn't supported. We make a copy of the texture data we're using,
152 // such that we're using the whole row of data in the copy. This turns
153 // out to be more efficient than uploading row-by-row; see bug 698197.
155 // Width and height are never more than 16384. At 16Ki*16Ki, 4bpp is 1GiB, but
156 // if we allow 8bpp (or higher) here, that's 2GiB, which would overflow on
158 MOZ_ASSERT(width
<= 16384);
159 MOZ_ASSERT(height
<= 16384);
160 MOZ_ASSERT(pixelsize
< 8);
162 const auto size
= CheckedInt
<size_t>(width
) * height
* pixelsize
;
163 if (!size
.isValid()) {
164 // This should never happen, but we use a defensive check.
165 MOZ_ASSERT_UNREACHABLE("Unacceptable size calculated.!");
169 unsigned char* newPixels
= new (fallible
) unsigned char[size
.value()];
172 unsigned char* rowDest
= newPixels
;
173 const unsigned char* rowSource
= (const unsigned char*)pixels
;
174 for (int h
= 0; h
< height
; h
++) {
175 memcpy(rowDest
, rowSource
, width
* pixelsize
);
176 rowDest
+= width
* pixelsize
;
180 stride
= width
* pixelsize
;
181 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
182 std::min(GetAddressAlignment((ptrdiff_t)newPixels
),
183 GetAddressAlignment((ptrdiff_t)stride
)));
184 gl
->fTexSubImage2D(target
, level
, xoffset
, yoffset
, width
, height
, format
,
187 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
190 // If we did not have sufficient memory for the required
191 // temporary buffer, then fall back to uploading row-by-row.
192 const unsigned char* rowSource
= (const unsigned char*)pixels
;
194 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
195 std::min(GetAddressAlignment((ptrdiff_t)pixels
),
196 GetAddressAlignment((ptrdiff_t)stride
)));
198 for (int i
= 0; i
< height
; i
++) {
199 gl
->fTexSubImage2D(target
, level
, xoffset
, yoffset
+ i
, width
, 1, format
,
204 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
207 static void TexSubImage2DHelper(GLContext
* gl
, GLenum target
, GLint level
,
208 GLint xoffset
, GLint yoffset
, GLsizei width
,
209 GLsizei height
, GLsizei stride
, GLint pixelsize
,
210 GLenum format
, GLenum type
,
211 const GLvoid
* pixels
) {
213 if (stride
== width
* pixelsize
) {
214 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
215 std::min(GetAddressAlignment((ptrdiff_t)pixels
),
216 GetAddressAlignment((ptrdiff_t)stride
)));
217 gl
->fTexSubImage2D(target
, level
, xoffset
, yoffset
, width
, height
, format
,
219 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
220 } else if (gl
->IsExtensionSupported(GLContext::EXT_unpack_subimage
) ||
222 TexSubImage2DWithUnpackSubimageGLES(gl
, target
, level
, xoffset
, yoffset
,
223 width
, height
, stride
, pixelsize
,
224 format
, type
, pixels
);
227 TexSubImage2DWithoutUnpackSubimage(gl
, target
, level
, xoffset
, yoffset
,
228 width
, height
, stride
, pixelsize
,
229 format
, type
, pixels
);
232 // desktop GL (non-ES) path
233 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
234 std::min(GetAddressAlignment((ptrdiff_t)pixels
),
235 GetAddressAlignment((ptrdiff_t)stride
)));
236 int rowLength
= stride
/ pixelsize
;
237 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, rowLength
);
238 gl
->fTexSubImage2D(target
, level
, xoffset
, yoffset
, width
, height
, format
,
240 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0);
241 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
245 static void TexImage2DHelper(GLContext
* gl
, GLenum target
, GLint level
,
246 GLint internalformat
, GLsizei width
,
247 GLsizei height
, GLsizei stride
, GLint pixelsize
,
248 GLint border
, GLenum format
, GLenum type
,
249 const GLvoid
* pixels
) {
252 format
== (GLenum
)internalformat
,
253 "format and internalformat not the same for glTexImage2D on GLES2");
255 MOZ_ASSERT(width
>= 0 && height
>= 0);
256 if (!CanUploadNonPowerOfTwo(gl
) &&
257 (stride
!= width
* pixelsize
|| !IsPowerOfTwo((uint32_t)width
) ||
258 !IsPowerOfTwo((uint32_t)height
))) {
259 // Pad out texture width and height to the next power of two
260 // as we don't support/want non power of two texture uploads
261 GLsizei paddedWidth
= RoundUpPow2((uint32_t)width
);
262 GLsizei paddedHeight
= RoundUpPow2((uint32_t)height
);
264 // Width and height are never more than 16384. At 16Ki*16Ki, 4bpp is 1GiB,
265 // but if we allow 8bpp (or higher) here, that's 2GiB, which would
266 // overflow on 32-bit.
267 MOZ_ASSERT(width
<= 16384);
268 MOZ_ASSERT(height
<= 16384);
269 MOZ_ASSERT(pixelsize
< 8);
272 CheckedInt
<size_t>(paddedWidth
) * paddedHeight
* pixelsize
;
273 if (!size
.isValid()) {
274 // This should never happen, but we use a defensive check.
275 MOZ_ASSERT_UNREACHABLE("Unacceptable size calculated.!");
279 GLvoid
* paddedPixels
= new unsigned char[size
.value()];
281 // Pad out texture data to be in a POT sized buffer for uploading to
282 // a POT sized texture
283 CopyAndPadTextureData(pixels
, paddedPixels
, width
, height
, paddedWidth
,
284 paddedHeight
, stride
, pixelsize
);
287 LOCAL_GL_UNPACK_ALIGNMENT
,
288 std::min(GetAddressAlignment((ptrdiff_t)paddedPixels
),
289 GetAddressAlignment((ptrdiff_t)paddedWidth
* pixelsize
)));
290 gl
->fTexImage2D(target
, border
, internalformat
, paddedWidth
, paddedHeight
,
291 border
, format
, type
, paddedPixels
);
292 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
294 delete[] static_cast<unsigned char*>(paddedPixels
);
298 if (stride
== width
* pixelsize
) {
299 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
300 std::min(GetAddressAlignment((ptrdiff_t)pixels
),
301 GetAddressAlignment((ptrdiff_t)stride
)));
302 gl
->fTexImage2D(target
, border
, internalformat
, width
, height
, border
,
303 format
, type
, pixels
);
304 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
306 // Use GLES-specific workarounds for GL_UNPACK_ROW_LENGTH; these are
307 // implemented in TexSubImage2D.
308 gl
->fTexImage2D(target
, border
, internalformat
, width
, height
, border
,
309 format
, type
, nullptr);
310 TexSubImage2DHelper(gl
, target
, level
, 0, 0, width
, height
, stride
,
311 pixelsize
, format
, type
, pixels
);
314 // desktop GL (non-ES) path
316 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
317 std::min(GetAddressAlignment((ptrdiff_t)pixels
),
318 GetAddressAlignment((ptrdiff_t)stride
)));
319 int rowLength
= stride
/ pixelsize
;
320 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, rowLength
);
321 gl
->fTexImage2D(target
, level
, internalformat
, width
, height
, border
,
322 format
, type
, pixels
);
323 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0);
324 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4);
328 SurfaceFormat
UploadImageDataToTexture(
329 GLContext
* gl
, unsigned char* aData
, const gfx::IntSize
& aDataSize
,
330 const IntPoint
& aDstOffset
, int32_t aStride
, SurfaceFormat aFormat
,
331 const nsIntRegion
& aDstRegion
, GLuint aTexture
, const gfx::IntSize
& aSize
,
332 size_t* aOutUploadSize
, bool aNeedInit
, GLenum aTextureUnit
,
333 GLenum aTextureTarget
) {
335 gl
->fActiveTexture(aTextureUnit
);
336 gl
->fBindTexture(aTextureTarget
, aTexture
);
339 GLenum internalFormat
= 0;
341 int32_t pixelSize
= BytesPerPixel(aFormat
);
342 SurfaceFormat surfaceFormat
= gfx::SurfaceFormat::UNKNOWN
;
344 MOZ_ASSERT(gl
->GetPreferredARGB32Format() == LOCAL_GL_BGRA
||
345 gl
->GetPreferredARGB32Format() == LOCAL_GL_RGBA
);
348 case SurfaceFormat::B8G8R8A8
:
349 if (gl
->GetPreferredARGB32Format() == LOCAL_GL_BGRA
) {
350 format
= LOCAL_GL_BGRA
;
351 surfaceFormat
= SurfaceFormat::R8G8B8A8
;
352 type
= LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
;
354 format
= LOCAL_GL_RGBA
;
355 surfaceFormat
= SurfaceFormat::B8G8R8A8
;
356 type
= LOCAL_GL_UNSIGNED_BYTE
;
358 internalFormat
= LOCAL_GL_RGBA
;
360 case SurfaceFormat::B8G8R8X8
:
361 // Treat BGRX surfaces as BGRA except for the surface
363 if (gl
->GetPreferredARGB32Format() == LOCAL_GL_BGRA
) {
364 format
= LOCAL_GL_BGRA
;
365 surfaceFormat
= SurfaceFormat::R8G8B8X8
;
366 type
= LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
;
368 format
= LOCAL_GL_RGBA
;
369 surfaceFormat
= SurfaceFormat::B8G8R8X8
;
370 type
= LOCAL_GL_UNSIGNED_BYTE
;
372 internalFormat
= LOCAL_GL_RGBA
;
374 case SurfaceFormat::R8G8B8A8
:
375 if (gl
->GetPreferredARGB32Format() == LOCAL_GL_BGRA
) {
376 // Upload our RGBA as BGRA, but store that the uploaded format is
377 // BGRA. (sample from R to get B)
378 format
= LOCAL_GL_BGRA
;
379 type
= LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
;
380 surfaceFormat
= SurfaceFormat::B8G8R8A8
;
382 format
= LOCAL_GL_RGBA
;
383 type
= LOCAL_GL_UNSIGNED_BYTE
;
384 surfaceFormat
= SurfaceFormat::R8G8B8A8
;
386 internalFormat
= LOCAL_GL_RGBA
;
388 case SurfaceFormat::R8G8B8X8
:
389 // Treat RGBX surfaces as RGBA except for the surface
391 if (gl
->GetPreferredARGB32Format() == LOCAL_GL_BGRA
) {
392 format
= LOCAL_GL_BGRA
;
393 type
= LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
;
394 surfaceFormat
= SurfaceFormat::B8G8R8X8
;
396 format
= LOCAL_GL_RGBA
;
397 type
= LOCAL_GL_UNSIGNED_BYTE
;
398 surfaceFormat
= SurfaceFormat::R8G8B8X8
;
400 internalFormat
= LOCAL_GL_RGBA
;
402 case SurfaceFormat::R5G6B5_UINT16
:
403 internalFormat
= format
= LOCAL_GL_RGB
;
404 type
= LOCAL_GL_UNSIGNED_SHORT_5_6_5
;
405 surfaceFormat
= SurfaceFormat::R5G6B5_UINT16
;
407 case SurfaceFormat::A8
:
409 format
= LOCAL_GL_LUMINANCE
;
410 internalFormat
= LOCAL_GL_LUMINANCE
;
412 format
= LOCAL_GL_RED
;
413 internalFormat
= LOCAL_GL_R8
;
415 type
= LOCAL_GL_UNSIGNED_BYTE
;
416 // We don't have a specific luminance shader
417 surfaceFormat
= SurfaceFormat::A8
;
419 case SurfaceFormat::A16
:
421 format
= LOCAL_GL_LUMINANCE
;
422 internalFormat
= LOCAL_GL_LUMINANCE16
;
424 format
= LOCAL_GL_RED
;
425 internalFormat
= LOCAL_GL_R16
;
427 type
= LOCAL_GL_UNSIGNED_SHORT
;
428 // We don't have a specific luminance shader
429 surfaceFormat
= SurfaceFormat::A8
;
433 MOZ_ASSERT_UNREACHABLE("Unhandled image surface format!");
436 if (aOutUploadSize
) {
440 if (surfaceFormat
== gfx::SurfaceFormat::UNKNOWN
) {
441 return gfx::SurfaceFormat::UNKNOWN
;
444 // We can only skip SubTextures if aOffset = 0 because we need the whole
446 if (aNeedInit
|| (!ShouldUploadSubTextures(gl
) && aDstOffset
== IntPoint())) {
447 if (!CheckUploadBounds(aSize
, aDataSize
, IntPoint())) {
448 return SurfaceFormat::UNKNOWN
;
450 // If the texture needs initialized, or we are unable to
451 // upload sub textures, then initialize and upload the entire
453 TexImage2DHelper(gl
, aTextureTarget
, 0, internalFormat
, aSize
.width
,
454 aSize
.height
, aStride
, pixelSize
, 0, format
, type
, aData
);
456 if (aOutUploadSize
&& aNeedInit
) {
457 uint32_t texelSize
= GetBytesPerTexel(internalFormat
, type
);
458 size_t numTexels
= size_t(aSize
.width
) * size_t(aSize
.height
);
459 *aOutUploadSize
+= texelSize
* numTexels
;
462 // Upload each rect in the region to the texture
463 for (auto iter
= aDstRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
464 IntRect rect
= iter
.Get();
465 if (!CheckUploadBounds(rect
.Size(), aDataSize
, rect
.TopLeft())) {
466 return SurfaceFormat::UNKNOWN
;
469 const unsigned char* rectData
=
470 aData
+ DataOffset(rect
.TopLeft(), aStride
, aFormat
);
473 TexSubImage2DHelper(gl
, aTextureTarget
, 0, rect
.X(), rect
.Y(),
474 rect
.Width(), rect
.Height(), aStride
, pixelSize
,
475 format
, type
, rectData
);
479 return surfaceFormat
;
482 SurfaceFormat
UploadSurfaceToTexture(GLContext
* gl
, DataSourceSurface
* aSurface
,
483 const nsIntRegion
& aDstRegion
,
484 GLuint aTexture
, const gfx::IntSize
& aSize
,
485 size_t* aOutUploadSize
, bool aNeedInit
,
486 const gfx::IntPoint
& aSrcOffset
,
487 const gfx::IntPoint
& aDstOffset
,
489 GLenum aTextureTarget
) {
490 DataSourceSurface::ScopedMap
map(aSurface
, DataSourceSurface::READ
);
491 int32_t stride
= map
.GetStride();
492 SurfaceFormat format
= aSurface
->GetFormat();
493 gfx::IntSize size
= aSurface
->GetSize();
495 // only fail if we'll need the entire surface for initialization
496 if (aNeedInit
&& !CheckUploadBounds(aSize
, size
, aSrcOffset
)) {
497 return SurfaceFormat::UNKNOWN
;
500 unsigned char* data
= map
.GetData() + DataOffset(aSrcOffset
, stride
, format
);
501 size
.width
-= aSrcOffset
.x
;
502 size
.height
-= aSrcOffset
.y
;
504 return UploadImageDataToTexture(gl
, data
, size
, aDstOffset
, stride
, format
,
505 aDstRegion
, aTexture
, aSize
, aOutUploadSize
,
506 aNeedInit
, aTextureUnit
, aTextureTarget
);
509 bool CanUploadNonPowerOfTwo(GLContext
* gl
) {
510 if (!gl
->WorkAroundDriverBugs()) return true;
512 // Some GPUs driver crash when uploading non power of two 565 textures.
513 return gl
->Renderer() != GLRenderer::Adreno200
&&
514 gl
->Renderer() != GLRenderer::Adreno205
;
518 } // namespace mozilla