1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "TexUnpackBlob.h"
8 #include "GLBlitHelper.h"
10 #include "mozilla/dom/Element.h"
11 #include "mozilla/dom/HTMLCanvasElement.h"
12 #include "mozilla/gfx/Logging.h"
13 #include "mozilla/RefPtr.h"
14 #include "nsLayoutUtils.h"
15 #include "WebGLBuffer.h"
16 #include "WebGLContext.h"
17 #include "WebGLFormats.h"
18 #include "WebGLTexelConversions.h"
19 #include "WebGLTexture.h"
23 bool WebGLPixelStore::AssertCurrent(gl::GLContext
& gl
,
24 const bool isWebgl2
) const {
25 WebGLPixelStore actual
;
26 gl
.GetInt(LOCAL_GL_UNPACK_ALIGNMENT
, &actual
.mUnpackAlignment
);
28 gl
.GetInt(LOCAL_GL_UNPACK_ROW_LENGTH
, &actual
.mUnpackRowLength
);
29 gl
.GetInt(LOCAL_GL_UNPACK_IMAGE_HEIGHT
, &actual
.mUnpackImageHeight
);
31 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_PIXELS
, &actual
.mUnpackSkipPixels
);
32 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_ROWS
, &actual
.mUnpackSkipRows
);
33 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_IMAGES
, &actual
.mUnpackSkipImages
);
37 ok
&= (mUnpackAlignment
== actual
.mUnpackAlignment
);
38 ok
&= (mUnpackRowLength
== actual
.mUnpackRowLength
);
39 ok
&= (mUnpackImageHeight
== actual
.mUnpackImageHeight
);
40 ok
&= (mUnpackSkipPixels
== actual
.mUnpackSkipPixels
);
41 ok
&= (mUnpackSkipRows
== actual
.mUnpackSkipRows
);
42 ok
&= (mUnpackSkipImages
== actual
.mUnpackSkipImages
);
45 const auto fnToStr
= [](const WebGLPixelStore
& x
) {
46 const auto text
= nsPrintfCString("%u,%u,%u;%u,%u,%u", x
.mUnpackAlignment
,
47 x
.mUnpackRowLength
, x
.mUnpackImageHeight
,
48 x
.mUnpackSkipPixels
, x
.mUnpackSkipRows
,
50 return ToString(text
);
53 const auto was
= fnToStr(actual
);
54 const auto expected
= fnToStr(*this);
55 gfxCriticalError() << "WebGLPixelStore not current. Was " << was
56 << ". Expected << " << expected
<< ".";
60 void WebGLPixelStore::Apply(gl::GLContext
& gl
, const bool isWebgl2
,
61 const uvec3
& uploadSize
) const {
62 gl
.fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, mUnpackAlignment
);
63 if (!isWebgl2
) return;
65 // Re-simplify. (ANGLE seems to have an issue with imageHeight ==
67 auto rowLength
= mUnpackRowLength
;
68 auto imageHeight
= mUnpackImageHeight
;
69 if (rowLength
== uploadSize
.x
) {
72 if (imageHeight
== uploadSize
.y
) {
76 gl
.fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, rowLength
);
77 gl
.fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT
, imageHeight
);
79 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS
, mUnpackSkipPixels
);
80 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS
, mUnpackSkipRows
);
81 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, mUnpackSkipImages
);
86 static bool IsPIValidForDOM(const webgl::PackingInfo
& pi
) {
87 // https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE
89 // Just check for invalid individual formats and types, not combinations.
93 case LOCAL_GL_LUMINANCE_ALPHA
:
94 case LOCAL_GL_LUMINANCE
:
97 case LOCAL_GL_RED_INTEGER
:
99 case LOCAL_GL_RG_INTEGER
:
100 case LOCAL_GL_RGB_INTEGER
:
101 case LOCAL_GL_RGBA_INTEGER
:
105 case LOCAL_GL_SRGB_ALPHA
:
106 // Allowed in WebGL1+EXT_srgb
114 case LOCAL_GL_UNSIGNED_BYTE
:
115 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
116 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
117 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
118 case LOCAL_GL_HALF_FLOAT
:
119 case LOCAL_GL_HALF_FLOAT_OES
:
121 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
131 static bool ValidatePIForDOM(const WebGLContext
* const webgl
,
132 const webgl::PackingInfo
& pi
) {
133 if (!IsPIValidForDOM(pi
)) {
134 webgl
->ErrorInvalidValue("Format or type is invalid for DOM sources.");
140 static WebGLTexelFormat
FormatForPackingInfo(const PackingInfo
& pi
) {
142 case LOCAL_GL_UNSIGNED_BYTE
:
145 case LOCAL_GL_LUMINANCE
:
146 case LOCAL_GL_RED_INTEGER
:
147 return WebGLTexelFormat::R8
;
150 return WebGLTexelFormat::A8
;
152 case LOCAL_GL_LUMINANCE_ALPHA
:
153 return WebGLTexelFormat::RA8
;
156 case LOCAL_GL_RGB_INTEGER
:
158 return WebGLTexelFormat::RGB8
;
161 case LOCAL_GL_RGBA_INTEGER
:
162 case LOCAL_GL_SRGB_ALPHA
:
163 return WebGLTexelFormat::RGBA8
;
166 case LOCAL_GL_RG_INTEGER
:
167 return WebGLTexelFormat::RG8
;
174 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
175 if (pi
.format
== LOCAL_GL_RGB
) return WebGLTexelFormat::RGB565
;
178 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
179 if (pi
.format
== LOCAL_GL_RGBA
) return WebGLTexelFormat::RGBA5551
;
182 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
183 if (pi
.format
== LOCAL_GL_RGBA
) return WebGLTexelFormat::RGBA4444
;
186 case LOCAL_GL_HALF_FLOAT
:
187 case LOCAL_GL_HALF_FLOAT_OES
:
190 case LOCAL_GL_LUMINANCE
:
191 return WebGLTexelFormat::R16F
;
194 return WebGLTexelFormat::A16F
;
195 case LOCAL_GL_LUMINANCE_ALPHA
:
196 return WebGLTexelFormat::RA16F
;
198 return WebGLTexelFormat::RG16F
;
200 return WebGLTexelFormat::RGB16F
;
202 return WebGLTexelFormat::RGBA16F
;
212 case LOCAL_GL_LUMINANCE
:
213 return WebGLTexelFormat::R32F
;
216 return WebGLTexelFormat::A32F
;
217 case LOCAL_GL_LUMINANCE_ALPHA
:
218 return WebGLTexelFormat::RA32F
;
220 return WebGLTexelFormat::RG32F
;
222 return WebGLTexelFormat::RGB32F
;
224 return WebGLTexelFormat::RGBA32F
;
231 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
232 if (pi
.format
== LOCAL_GL_RGB
) return WebGLTexelFormat::RGB11F11F10F
;
239 return WebGLTexelFormat::FormatNotSupportingAnyConversion
;
244 static uint32_t ZeroOn2D(const GLenum target
, const uint32_t val
) {
245 const bool is2d
= !IsTexTarget3D(target
);
250 static bool ValidateUnpackPixels(const WebGLContext
* webgl
, uint32_t fullRows
,
252 webgl::TexUnpackBlob
* const blob
) {
253 const auto& size
= blob
->mDesc
.size
;
254 if (!size
.x
|| !size
.y
|| !size
.z
) return true;
256 const auto& unpacking
= blob
->mDesc
.unpacking
;
260 const auto usedPixelsPerRow
=
261 CheckedUint32(unpacking
.mUnpackSkipPixels
) + size
.x
;
262 if (!usedPixelsPerRow
.isValid() ||
263 usedPixelsPerRow
.value() > unpacking
.mUnpackRowLength
) {
264 webgl
->ErrorInvalidOperation(
265 "UNPACK_SKIP_PIXELS + width >"
266 " UNPACK_ROW_LENGTH.");
270 if (size
.y
> unpacking
.mUnpackImageHeight
) {
271 webgl
->ErrorInvalidOperation("height > UNPACK_IMAGE_HEIGHT.");
277 // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately.
279 CheckedUint32(unpacking
.mUnpackSkipImages
) * unpacking
.mUnpackImageHeight
;
280 skipFullRows
+= unpacking
.mUnpackSkipRows
;
282 // Full rows in the final image, excluding the tail.
283 MOZ_ASSERT(size
.y
>= 1);
284 MOZ_ASSERT(size
.z
>= 1);
285 auto usedFullRows
= CheckedUint32(size
.z
- 1) * unpacking
.mUnpackImageHeight
;
286 usedFullRows
+= size
.y
- 1;
288 const auto fullRowsNeeded
= skipFullRows
+ usedFullRows
;
289 if (!fullRowsNeeded
.isValid()) {
290 webgl
->ErrorOutOfMemory("Invalid calculation for required row count.");
294 if (fullRows
> fullRowsNeeded
.value()) {
295 blob
->mNeedsExactUpload
= false;
299 if (fullRows
== fullRowsNeeded
.value() &&
300 tailPixels
>= usedPixelsPerRow
.value()) {
301 MOZ_ASSERT(blob
->mNeedsExactUpload
);
305 webgl
->ErrorInvalidOperation(
306 "Desired upload requires more data than is"
307 " available: (%u rows plus %u pixels needed, %u rows"
308 " plus %u pixels available)",
309 fullRowsNeeded
.value(), usedPixelsPerRow
.value(), fullRows
, tailPixels
);
313 static bool ValidateUnpackBytes(const WebGLContext
* const webgl
,
314 const webgl::PackingInfo
& pi
,
315 size_t availByteCount
,
316 webgl::TexUnpackBlob
* const blob
) {
317 const auto& size
= blob
->mDesc
.size
;
318 if (!size
.x
|| !size
.y
|| !size
.z
) return true;
319 const auto& unpacking
= blob
->mDesc
.unpacking
;
321 const auto bytesPerPixel
= webgl::BytesPerPixel(pi
);
322 const auto bytesPerRow
=
323 CheckedUint32(unpacking
.mUnpackRowLength
) * bytesPerPixel
;
324 const auto rowStride
=
325 RoundUpToMultipleOf(bytesPerRow
, unpacking
.mUnpackAlignment
);
327 const auto fullRows
= availByteCount
/ rowStride
;
328 if (!fullRows
.isValid()) {
329 webgl
->ErrorOutOfMemory("Unacceptable upload size calculated.");
333 const auto bodyBytes
= fullRows
.value() * rowStride
.value();
334 const auto tailPixels
= (availByteCount
- bodyBytes
) / bytesPerPixel
;
336 return ValidateUnpackPixels(webgl
, fullRows
.value(), tailPixels
, blob
);
342 std::unique_ptr
<TexUnpackBlob
> TexUnpackBlob::Create(
343 const TexUnpackBlobDesc
& desc
) {
344 return std::unique_ptr
<TexUnpackBlob
>{[&]() -> TexUnpackBlob
* {
345 if (!IsTarget3D(desc
.imageTarget
) && desc
.size
.z
!= 1) {
350 switch (desc
.unpacking
.mUnpackAlignment
) {
362 return new TexUnpackImage(desc
);
365 return new TexUnpackSurface(desc
);
368 if (desc
.srcAlphaType
!= gfxAlphaType::NonPremult
) {
372 return new TexUnpackBytes(desc
);
376 static bool HasColorAndAlpha(const WebGLTexelFormat format
) {
378 case WebGLTexelFormat::RA8
:
379 case WebGLTexelFormat::RA16F
:
380 case WebGLTexelFormat::RA32F
:
381 case WebGLTexelFormat::RGBA8
:
382 case WebGLTexelFormat::RGBA5551
:
383 case WebGLTexelFormat::RGBA4444
:
384 case WebGLTexelFormat::RGBA16F
:
385 case WebGLTexelFormat::RGBA32F
:
386 case WebGLTexelFormat::BGRA8
:
393 bool TexUnpackBlob::ConvertIfNeeded(
394 const WebGLContext
* const webgl
, const uint32_t rowLength
,
395 const uint32_t rowCount
, WebGLTexelFormat srcFormat
,
396 const uint8_t* const srcBegin
, const ptrdiff_t srcStride
,
397 WebGLTexelFormat dstFormat
, const ptrdiff_t dstStride
,
398 const uint8_t** const out_begin
,
399 UniqueBuffer
* const out_anchoredBuffer
) const {
400 MOZ_ASSERT(srcFormat
!= WebGLTexelFormat::FormatNotSupportingAnyConversion
);
401 MOZ_ASSERT(dstFormat
!= WebGLTexelFormat::FormatNotSupportingAnyConversion
);
403 *out_begin
= srcBegin
;
405 const auto& unpacking
= mDesc
.unpacking
;
407 if (!rowLength
|| !rowCount
) return true;
409 const auto srcIsPremult
= (mDesc
.srcAlphaType
== gfxAlphaType::Premult
);
410 const auto& dstIsPremult
= unpacking
.mPremultiplyAlpha
;
411 const auto fnHasPremultMismatch
= [&]() {
412 if (mDesc
.srcAlphaType
== gfxAlphaType::Opaque
) return false;
414 if (!HasColorAndAlpha(srcFormat
)) return false;
416 return srcIsPremult
!= dstIsPremult
;
419 const auto srcOrigin
=
420 (unpacking
.mFlipY
? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft
);
421 const auto dstOrigin
= gl::OriginPos::BottomLeft
;
423 if (srcFormat
!= dstFormat
) {
424 webgl
->GeneratePerfWarning(
425 "Conversion requires pixel reformatting. (%u->%u)", uint32_t(srcFormat
),
426 uint32_t(dstFormat
));
427 } else if (fnHasPremultMismatch()) {
428 webgl
->GeneratePerfWarning(
429 "Conversion requires change in"
430 " alpha-premultiplication.");
431 } else if (srcOrigin
!= dstOrigin
) {
432 webgl
->GeneratePerfWarning("Conversion requires y-flip.");
433 } else if (srcStride
!= dstStride
) {
434 webgl
->GeneratePerfWarning("Conversion requires change in stride. (%u->%u)",
435 uint32_t(srcStride
), uint32_t(dstStride
));
442 const auto dstTotalBytes
= CheckedUint32(rowCount
) * dstStride
;
443 if (!dstTotalBytes
.isValid()) {
444 webgl
->ErrorOutOfMemory("Calculation failed.");
448 UniqueBuffer dstBuffer
= calloc(1u, (size_t)dstTotalBytes
.value());
449 if (!dstBuffer
.get()) {
450 webgl
->ErrorOutOfMemory("Failed to allocate dest buffer.");
453 const auto dstBegin
= static_cast<uint8_t*>(dstBuffer
.get());
459 if (!ConvertImage(rowLength
, rowCount
, srcBegin
, srcStride
, srcOrigin
,
460 srcFormat
, srcIsPremult
, dstBegin
, dstStride
, dstOrigin
,
461 dstFormat
, dstIsPremult
, &wasTrivial
)) {
462 webgl
->ErrorImplementationBug("ConvertImage failed.");
466 *out_begin
= dstBegin
;
467 *out_anchoredBuffer
= std::move(dstBuffer
);
471 static GLenum
DoTexOrSubImage(bool isSubImage
, gl::GLContext
* gl
,
472 TexImageTarget target
, GLint level
,
473 const DriverUnpackInfo
* dui
, GLint xOffset
,
474 GLint yOffset
, GLint zOffset
, GLsizei width
,
475 GLsizei height
, GLsizei depth
, const void* data
) {
477 return DoTexSubImage(gl
, target
, level
, xOffset
, yOffset
, zOffset
, width
,
478 height
, depth
, dui
->ToPacking(), data
);
480 return DoTexImage(gl
, target
, level
, dui
, width
, height
, depth
, data
);
484 //////////////////////////////////////////////////////////////////////////////////////////
487 bool TexUnpackBytes::Validate(const WebGLContext
* const webgl
,
488 const webgl::PackingInfo
& pi
) {
489 if (!HasData()) return true;
491 CheckedInt
<size_t> availBytes
= 0;
493 const auto& range
= mDesc
.cpuData
->Data();
494 availBytes
= range
.length();
495 } else if (mDesc
.pboOffset
) {
496 const auto& pboOffset
= *mDesc
.pboOffset
;
499 webgl
->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
500 if (!pbo
) return false; // Might be invalid e.g. due to in-use by TF.
501 availBytes
= pbo
->ByteLength();
502 availBytes
-= pboOffset
;
504 MOZ_ASSERT(false, "Must be one of the above");
506 if (!availBytes
.isValid()) {
507 webgl
->ErrorInvalidOperation("Offset is passed end of buffer.");
511 return ValidateUnpackBytes(webgl
, pi
, availBytes
.value(), this);
514 bool TexUnpackBytes::TexOrSubImage(bool isSubImage
, bool needsRespec
,
515 WebGLTexture
* tex
, GLint level
,
516 const webgl::DriverUnpackInfo
* dui
,
517 GLint xOffset
, GLint yOffset
, GLint zOffset
,
518 const webgl::PackingInfo
& pi
,
519 GLenum
* const out_error
) const {
520 const auto& webgl
= tex
->mContext
;
521 const auto& target
= mDesc
.imageTarget
;
522 const auto& size
= mDesc
.size
;
523 const auto& unpacking
= mDesc
.unpacking
;
525 const auto format
= FormatForPackingInfo(pi
);
526 const auto bytesPerPixel
= webgl::BytesPerPixel(pi
);
528 const uint8_t* uploadPtr
= nullptr;
530 const auto range
= mDesc
.cpuData
->Data();
531 uploadPtr
= range
.begin().get();
533 MOZ_ASSERT(!range
.length());
537 UniqueBuffer tempBuffer
;
540 if (mDesc
.pboOffset
|| !uploadPtr
) break;
542 if (!unpacking
.mFlipY
&& !unpacking
.mPremultiplyAlpha
) {
546 webgl
->GenerateWarning(
547 "Alpha-premult and y-flip are deprecated for"
548 " non-DOM-Element uploads.");
550 const uint32_t rowLength
= size
.x
;
551 const uint32_t rowCount
= size
.y
* size
.z
;
552 const auto stride
= RoundUpToMultipleOf(rowLength
* bytesPerPixel
,
553 unpacking
.mUnpackAlignment
);
554 const auto srcPtr
= uploadPtr
;
555 if (!ConvertIfNeeded(webgl
, rowLength
, rowCount
, format
, srcPtr
, stride
,
556 format
, stride
, &uploadPtr
, &tempBuffer
)) {
563 const auto& gl
= webgl
->gl
;
565 bool useParanoidHandling
= false;
566 if (mNeedsExactUpload
&& webgl
->mBoundPixelUnpackBuffer
) {
567 webgl
->GenerateWarning(
568 "Uploads from a buffer with a final row with a byte"
569 " count smaller than the row stride can incur extra"
572 if (gl
->WorkAroundDriverBugs()) {
573 useParanoidHandling
|= (gl
->Vendor() == gl::GLVendor::NVIDIA
);
577 if (!useParanoidHandling
) {
578 if (webgl
->mBoundPixelUnpackBuffer
) {
579 gl
->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER
,
580 webgl
->mBoundPixelUnpackBuffer
->mGLName
);
584 DoTexOrSubImage(isSubImage
, gl
, target
, level
, dui
, xOffset
, yOffset
,
585 zOffset
, size
.x
, size
.y
, size
.z
, uploadPtr
);
587 if (webgl
->mBoundPixelUnpackBuffer
) {
588 gl
->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER
, 0);
595 MOZ_ASSERT(webgl
->mBoundPixelUnpackBuffer
);
598 // Alloc first to catch OOMs.
599 AssertUintParamCorrect(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
, 0);
601 DoTexOrSubImage(false, gl
, target
, level
, dui
, xOffset
, yOffset
,
602 zOffset
, size
.x
, size
.y
, size
.z
, nullptr);
603 if (*out_error
) return true;
606 const ScopedLazyBind
bindPBO(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
607 webgl
->mBoundPixelUnpackBuffer
);
611 // Make our sometimes-implicit values explicit. Also this keeps them constant
612 // when we ask for height=mHeight-1 and such.
613 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, unpacking
.mUnpackRowLength
);
614 gl
->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT
, unpacking
.mUnpackImageHeight
);
618 DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
, yOffset
, zOffset
,
619 size
.x
, size
.y
, size
.z
- 1, uploadPtr
);
622 // Skip the images we uploaded.
623 const auto skipImages
= ZeroOn2D(target
, unpacking
.mUnpackSkipImages
);
624 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, skipImages
+ size
.z
- 1);
628 DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
, yOffset
,
629 zOffset
+ size
.z
- 1, size
.x
, size
.y
- 1, 1, uploadPtr
);
632 const auto totalSkipRows
=
633 CheckedUint32(skipImages
) * unpacking
.mUnpackImageHeight
+
634 unpacking
.mUnpackSkipRows
;
635 const auto totalFullRows
=
636 CheckedUint32(size
.z
- 1) * unpacking
.mUnpackImageHeight
+ size
.y
- 1;
637 const auto tailOffsetRows
= totalSkipRows
+ totalFullRows
;
639 const auto bytesPerRow
=
640 CheckedUint32(unpacking
.mUnpackRowLength
) * bytesPerPixel
;
641 const auto rowStride
=
642 RoundUpToMultipleOf(bytesPerRow
, unpacking
.mUnpackAlignment
);
643 if (!rowStride
.isValid()) {
644 MOZ_CRASH("Should be checked earlier.");
646 const auto tailOffsetBytes
= tailOffsetRows
* rowStride
;
648 uploadPtr
+= tailOffsetBytes
.value();
652 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 1); // No stride padding.
653 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0); // No padding in general.
654 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, 0); // Don't skip images,
655 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS
,
657 // Keep skipping pixels though!
659 *out_error
= DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
,
660 yOffset
+ size
.y
- 1, zOffset
+ size
.z
- 1,
661 size
.x
, 1, 1, uploadPtr
);
663 // Caller will reset all our modified PixelStorei state.
668 ////////////////////////////////////////////////////////////////////////////////
669 ////////////////////////////////////////////////////////////////////////////////
672 TexUnpackImage::~TexUnpackImage() = default;
674 bool TexUnpackImage::Validate(const WebGLContext
* const webgl
,
675 const webgl::PackingInfo
& pi
) {
676 if (!ValidatePIForDOM(webgl
, pi
)) return false;
678 const auto fullRows
= mDesc
.imageSize
.y
;
679 return ValidateUnpackPixels(webgl
, fullRows
, 0, this);
682 Maybe
<std::string
> BlitPreventReason(const int32_t level
, const ivec3
& offset
,
683 const webgl::PackingInfo
& pi
,
684 const TexUnpackBlobDesc
& desc
) {
685 const auto& size
= desc
.size
;
686 const auto& unpacking
= desc
.unpacking
;
688 const auto ret
= [&]() -> const char* {
690 return "depth is not 1";
692 if (offset
.x
!= 0 || offset
.y
!= 0 || offset
.z
!= 0) {
693 return "x/y/zOffset is not 0";
696 if (unpacking
.mUnpackSkipPixels
|| unpacking
.mUnpackSkipRows
||
697 unpacking
.mUnpackSkipImages
) {
698 return "non-zero UNPACK_SKIP_* not yet supported";
701 const auto premultReason
= [&]() -> const char* {
702 if (desc
.srcAlphaType
== gfxAlphaType::Opaque
) return nullptr;
704 const bool srcIsPremult
= (desc
.srcAlphaType
== gfxAlphaType::Premult
);
705 const auto& dstIsPremult
= unpacking
.mPremultiplyAlpha
;
706 if (srcIsPremult
== dstIsPremult
) return nullptr;
709 return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
711 return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
714 if (premultReason
) return premultReason
;
716 if (pi
.format
!= LOCAL_GL_RGBA
) {
717 return "`format` is not RGBA";
720 if (pi
.type
!= LOCAL_GL_UNSIGNED_BYTE
) {
721 return "`type` is not UNSIGNED_BYTE";
726 return Some(std::string(ret
));
731 bool TexUnpackImage::TexOrSubImage(bool isSubImage
, bool needsRespec
,
732 WebGLTexture
* tex
, GLint level
,
733 const webgl::DriverUnpackInfo
* dui
,
734 GLint xOffset
, GLint yOffset
, GLint zOffset
,
735 const webgl::PackingInfo
& pi
,
736 GLenum
* const out_error
) const {
737 MOZ_ASSERT_IF(needsRespec
, !isSubImage
);
739 const auto& webgl
= tex
->mContext
;
740 const auto& target
= mDesc
.imageTarget
;
741 const auto& size
= mDesc
.size
;
742 const auto& sd
= *(mDesc
.sd
);
743 const auto& unpacking
= mDesc
.unpacking
;
745 const auto& gl
= webgl
->GL();
750 BlitPreventReason(level
, {xOffset
, yOffset
, zOffset
}, pi
, mDesc
);
752 webgl
->GeneratePerfWarning(
753 "Failed to hit GPU-copy fast-path."
754 " (%s) Falling back to CPU upload.",
763 DoTexOrSubImage(isSubImage
, gl
, target
, level
, dui
, xOffset
, yOffset
,
764 zOffset
, size
.x
, size
.y
, size
.z
, nullptr);
765 if (*out_error
) return true;
769 gl::ScopedFramebuffer
scopedFB(gl
);
770 gl::ScopedBindFramebuffer
bindFB(gl
, scopedFB
.FB());
773 gl::GLContext::LocalErrorScope
errorScope(*gl
);
775 gl
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
,
776 LOCAL_GL_COLOR_ATTACHMENT0
, target
,
777 tex
->mGLName
, level
);
779 const auto err
= errorScope
.GetError();
780 MOZ_ALWAYS_TRUE(!err
);
783 const GLenum status
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
784 MOZ_ALWAYS_TRUE(status
== LOCAL_GL_FRAMEBUFFER_COMPLETE
);
786 const auto dstOrigin
=
787 (unpacking
.mFlipY
? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft
);
788 if (!gl
->BlitHelper()->BlitSdToFramebuffer(sd
, {size
.x
, size
.y
},
790 webgl
->ErrorImplementationBug("BlitSdToFramebuffer failed for type %i.",
799 ////////////////////////////////////////////////////////////////////////////////
800 ////////////////////////////////////////////////////////////////////////////////
803 TexUnpackSurface::~TexUnpackSurface() = default;
807 static bool GetFormatForSurf(const gfx::SourceSurface
* surf
,
808 WebGLTexelFormat
* const out_texelFormat
,
809 uint8_t* const out_bpp
) {
810 const auto surfFormat
= surf
->GetFormat();
811 switch (surfFormat
) {
812 case gfx::SurfaceFormat::B8G8R8A8
:
813 *out_texelFormat
= WebGLTexelFormat::BGRA8
;
817 case gfx::SurfaceFormat::B8G8R8X8
:
818 *out_texelFormat
= WebGLTexelFormat::BGRX8
;
822 case gfx::SurfaceFormat::R8G8B8A8
:
823 *out_texelFormat
= WebGLTexelFormat::RGBA8
;
827 case gfx::SurfaceFormat::R8G8B8X8
:
828 *out_texelFormat
= WebGLTexelFormat::RGBX8
;
832 case gfx::SurfaceFormat::R5G6B5_UINT16
:
833 *out_texelFormat
= WebGLTexelFormat::RGB565
;
837 case gfx::SurfaceFormat::A8
:
838 *out_texelFormat
= WebGLTexelFormat::A8
;
842 case gfx::SurfaceFormat::YUV
:
844 NS_ERROR("We don't handle uploads from YUV sources yet.");
845 // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically
846 // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB)
856 bool TexUnpackSurface::Validate(const WebGLContext
* const webgl
,
857 const webgl::PackingInfo
& pi
) {
858 if (!ValidatePIForDOM(webgl
, pi
)) return false;
860 const auto fullRows
= mDesc
.dataSurf
->GetSize().height
;
861 return ValidateUnpackPixels(webgl
, fullRows
, 0, this);
864 bool TexUnpackSurface::TexOrSubImage(bool isSubImage
, bool needsRespec
,
865 WebGLTexture
* tex
, GLint level
,
866 const webgl::DriverUnpackInfo
* dui
,
867 GLint xOffset
, GLint yOffset
,
869 const webgl::PackingInfo
& dstPI
,
870 GLenum
* const out_error
) const {
871 const auto& webgl
= tex
->mContext
;
872 const auto& size
= mDesc
.size
;
873 auto& surf
= *(mDesc
.dataSurf
);
877 const auto rowLength
= surf
.GetSize().width
;
878 const auto rowCount
= surf
.GetSize().height
;
880 const auto& dstBPP
= webgl::BytesPerPixel(dstPI
);
881 const auto dstFormat
= FormatForPackingInfo(dstPI
);
885 WebGLTexelFormat srcFormat
;
887 if (!GetFormatForSurf(&surf
, &srcFormat
, &srcBPP
)) {
888 webgl
->ErrorImplementationBug(
889 "GetFormatForSurf failed for"
890 " WebGLTexelFormat::%u.",
891 uint32_t(surf
.GetFormat()));
895 gfx::DataSourceSurface::ScopedMap
map(&surf
,
896 gfx::DataSourceSurface::MapType::READ
);
897 if (!map
.IsMapped()) {
898 webgl
->ErrorOutOfMemory("Failed to map source surface for upload.");
902 const auto& srcBegin
= map
.GetData();
903 const auto& srcStride
= map
.GetStride();
907 const auto srcRowLengthBytes
= rowLength
* srcBPP
;
909 const uint8_t maxGLAlignment
= 8;
910 uint8_t srcAlignment
= 1;
911 for (; srcAlignment
<= maxGLAlignment
; srcAlignment
*= 2) {
912 const auto strideGuess
=
913 RoundUpToMultipleOf(srcRowLengthBytes
, srcAlignment
);
914 if (strideGuess
== srcStride
) break;
916 const uint32_t dstAlignment
=
917 (srcAlignment
> maxGLAlignment
) ? 1 : srcAlignment
;
919 const auto dstRowLengthBytes
= rowLength
* dstBPP
;
920 const auto dstStride
= RoundUpToMultipleOf(dstRowLengthBytes
, dstAlignment
);
924 const uint8_t* dstBegin
= srcBegin
;
925 UniqueBuffer tempBuffer
;
926 if (!ConvertIfNeeded(webgl
, rowLength
, rowCount
, srcFormat
, srcBegin
,
927 srcStride
, dstFormat
, dstStride
, &dstBegin
,
934 const auto& gl
= webgl
->gl
;
935 if (!gl
->MakeCurrent()) {
936 *out_error
= LOCAL_GL_CONTEXT_LOST
;
940 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, dstAlignment
);
941 if (webgl
->IsWebGL2()) {
942 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, rowLength
);
946 DoTexOrSubImage(isSubImage
, gl
, mDesc
.imageTarget
, level
, dui
, xOffset
,
947 yOffset
, zOffset
, size
.x
, size
.y
, size
.z
, dstBegin
);
949 // Caller will reset all our modified PixelStorei state.
955 } // namespace mozilla