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/layers/ImageDataSerializer.h"
14 #include "mozilla/RefPtr.h"
15 #include "nsLayoutUtils.h"
16 #include "WebGLBuffer.h"
17 #include "WebGLContext.h"
18 #include "WebGLFormats.h"
19 #include "WebGLTexelConversions.h"
20 #include "WebGLTexture.h"
24 bool webgl::PixelPackingState::AssertCurrentUnpack(gl::GLContext
& gl
,
25 const bool isWebgl2
) const {
26 auto actual
= PixelPackingState
{};
27 gl
.GetInt(LOCAL_GL_UNPACK_ALIGNMENT
, &actual
.alignmentInTypeElems
);
29 gl
.GetInt(LOCAL_GL_UNPACK_ROW_LENGTH
, &actual
.rowLength
);
30 gl
.GetInt(LOCAL_GL_UNPACK_IMAGE_HEIGHT
, &actual
.imageHeight
);
32 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_PIXELS
, &actual
.skipPixels
);
33 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_ROWS
, &actual
.skipRows
);
34 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_IMAGES
, &actual
.skipImages
);
36 if (*this == actual
) return true;
38 const auto ToStr
= [](const PixelPackingState
& x
) {
39 const auto text
= nsPrintfCString(
40 "%u,%u,%u;%u,%u,%u", x
.alignmentInTypeElems
, x
.rowLength
, x
.imageHeight
,
41 x
.skipPixels
, x
.skipRows
, x
.skipImages
);
42 return mozilla::ToString(text
);
45 const auto was
= ToStr(actual
);
46 const auto expected
= ToStr(*this);
47 gfxCriticalError() << "PixelUnpackStateGl was not current. Was " << was
48 << ". Expected << " << expected
<< ".";
52 void webgl::PixelPackingState::ApplyUnpack(gl::GLContext
& gl
,
54 const uvec3
& uploadSize
) const {
55 gl
.fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
56 AssertedCast
<GLsizei
>(alignmentInTypeElems
));
57 if (!isWebgl2
) return;
59 // Re-simplify. (ANGLE seems to have an issue with imageHeight ==
61 auto rowLengthOrZero
= rowLength
;
62 auto imageHeightOrZero
= imageHeight
;
63 if (rowLengthOrZero
== uploadSize
.x
) {
66 if (imageHeightOrZero
== uploadSize
.y
) {
67 imageHeightOrZero
= 0;
70 gl
.fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
,
71 AssertedCast
<GLsizei
>(rowLengthOrZero
));
72 gl
.fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT
,
73 AssertedCast
<GLsizei
>(imageHeightOrZero
));
75 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS
,
76 AssertedCast
<GLsizei
>(skipPixels
));
77 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS
, AssertedCast
<GLsizei
>(skipRows
));
78 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
,
79 AssertedCast
<GLsizei
>(skipImages
));
84 static bool IsPIValidForDOM(const webgl::PackingInfo
& pi
) {
85 // https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE
87 // Just check for invalid individual formats and types, not combinations.
91 case LOCAL_GL_LUMINANCE_ALPHA
:
92 case LOCAL_GL_LUMINANCE
:
95 case LOCAL_GL_RED_INTEGER
:
97 case LOCAL_GL_RG_INTEGER
:
98 case LOCAL_GL_RGB_INTEGER
:
99 case LOCAL_GL_RGBA_INTEGER
:
103 case LOCAL_GL_SRGB_ALPHA
:
104 // Allowed in WebGL1+EXT_srgb
112 case LOCAL_GL_UNSIGNED_BYTE
:
113 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
114 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
115 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
116 case LOCAL_GL_HALF_FLOAT
:
117 case LOCAL_GL_HALF_FLOAT_OES
:
119 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
129 static bool ValidatePIForDOM(const WebGLContext
* const webgl
,
130 const webgl::PackingInfo
& pi
) {
131 if (!IsPIValidForDOM(pi
)) {
132 webgl
->ErrorInvalidValue("Format or type is invalid for DOM sources.");
138 static WebGLTexelFormat
FormatForPackingInfo(const PackingInfo
& pi
) {
140 case LOCAL_GL_UNSIGNED_BYTE
:
143 case LOCAL_GL_LUMINANCE
:
144 case LOCAL_GL_RED_INTEGER
:
145 return WebGLTexelFormat::R8
;
148 return WebGLTexelFormat::A8
;
150 case LOCAL_GL_LUMINANCE_ALPHA
:
151 return WebGLTexelFormat::RA8
;
154 case LOCAL_GL_RGB_INTEGER
:
156 return WebGLTexelFormat::RGB8
;
159 case LOCAL_GL_RGBA_INTEGER
:
160 case LOCAL_GL_SRGB_ALPHA
:
161 return WebGLTexelFormat::RGBA8
;
164 case LOCAL_GL_RG_INTEGER
:
165 return WebGLTexelFormat::RG8
;
172 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
173 if (pi
.format
== LOCAL_GL_RGB
) return WebGLTexelFormat::RGB565
;
176 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
177 if (pi
.format
== LOCAL_GL_RGBA
) return WebGLTexelFormat::RGBA5551
;
180 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
181 if (pi
.format
== LOCAL_GL_RGBA
) return WebGLTexelFormat::RGBA4444
;
184 case LOCAL_GL_HALF_FLOAT
:
185 case LOCAL_GL_HALF_FLOAT_OES
:
188 case LOCAL_GL_LUMINANCE
:
189 return WebGLTexelFormat::R16F
;
192 return WebGLTexelFormat::A16F
;
193 case LOCAL_GL_LUMINANCE_ALPHA
:
194 return WebGLTexelFormat::RA16F
;
196 return WebGLTexelFormat::RG16F
;
198 return WebGLTexelFormat::RGB16F
;
200 return WebGLTexelFormat::RGBA16F
;
210 case LOCAL_GL_LUMINANCE
:
211 return WebGLTexelFormat::R32F
;
214 return WebGLTexelFormat::A32F
;
215 case LOCAL_GL_LUMINANCE_ALPHA
:
216 return WebGLTexelFormat::RA32F
;
218 return WebGLTexelFormat::RG32F
;
220 return WebGLTexelFormat::RGB32F
;
222 return WebGLTexelFormat::RGBA32F
;
229 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
230 if (pi
.format
== LOCAL_GL_RGB
) return WebGLTexelFormat::RGB11F11F10F
;
237 return WebGLTexelFormat::FormatNotSupportingAnyConversion
;
242 static uint32_t ZeroOn2D(const GLenum target
, const uint32_t val
) {
243 const bool is2d
= !IsTexTarget3D(target
);
248 static bool ValidateUnpackPixels(const WebGLContext
* webgl
,
249 const webgl::PackingInfo
& pi
,
250 const uint32_t availRows
,
251 const webgl::TexUnpackBlob
& blob
) {
252 const auto& unpackingRes
= blob
.mDesc
.ExplicitUnpacking(pi
, {});
253 if (!unpackingRes
.isOk()) {
254 webgl
->ErrorInvalidOperation("%s", unpackingRes
.inspectErr().c_str());
257 const auto& unpacking
= unpackingRes
.inspect();
259 if (availRows
< unpacking
.metrics
.totalRows
) {
260 webgl
->ErrorInvalidOperation(
261 "Desired upload requires more rows (%zu) than is"
263 unpacking
.metrics
.totalRows
, availRows
);
270 static bool ValidateUnpackBytes(const WebGLContext
* const webgl
,
271 const webgl::PackingInfo
& pi
,
272 const size_t availByteCount
,
273 const webgl::TexUnpackBlob
& blob
) {
274 const auto& unpackingRes
= blob
.mDesc
.ExplicitUnpacking(pi
, {});
275 if (!unpackingRes
.isOk()) {
276 webgl
->ErrorInvalidOperation("%s", unpackingRes
.inspectErr().c_str());
279 const auto& unpacking
= unpackingRes
.inspect();
281 if (availByteCount
< unpacking
.metrics
.totalBytesUsed
) {
282 webgl
->ErrorInvalidOperation(
283 "Desired upload requires more bytes (%zu) than are"
285 unpacking
.metrics
.totalBytesUsed
, availByteCount
);
294 // Check if the surface descriptor describes a memory which contains a single
296 static bool SDIsRGBBuffer(const layers::SurfaceDescriptor
& sd
) {
297 return sd
.type() == layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
&&
298 sd
.get_SurfaceDescriptorBuffer().desc().type() ==
299 layers::BufferDescriptor::TRGBDescriptor
;
303 std::unique_ptr
<TexUnpackBlob
> TexUnpackBlob::Create(
304 const TexUnpackBlobDesc
& desc
) {
305 return std::unique_ptr
<TexUnpackBlob
>{[&]() -> TexUnpackBlob
* {
306 if (!IsTarget3D(desc
.imageTarget
) && desc
.size
.z
!= 1) {
311 switch (desc
.unpacking
.alignmentInTypeElems
) {
323 // Shmem buffers need to be treated as if they were a DataSourceSurface.
324 // Otherwise, TexUnpackImage will try to blit the surface descriptor as
325 // if it can be mapped as a framebuffer, whereas the Shmem is still CPU
327 if (SDIsRGBBuffer(*desc
.sd
)) return new TexUnpackSurface(desc
);
328 return new TexUnpackImage(desc
);
331 return new TexUnpackSurface(desc
);
334 if (desc
.srcAlphaType
!= gfxAlphaType::NonPremult
) {
338 return new TexUnpackBytes(desc
);
342 static bool HasColorAndAlpha(const WebGLTexelFormat format
) {
344 case WebGLTexelFormat::RA8
:
345 case WebGLTexelFormat::RA16F
:
346 case WebGLTexelFormat::RA32F
:
347 case WebGLTexelFormat::RGBA8
:
348 case WebGLTexelFormat::RGBA5551
:
349 case WebGLTexelFormat::RGBA4444
:
350 case WebGLTexelFormat::RGBA16F
:
351 case WebGLTexelFormat::RGBA32F
:
352 case WebGLTexelFormat::BGRA8
:
359 bool TexUnpackBlob::ConvertIfNeeded(
360 const WebGLContext
* const webgl
, const uint32_t rowLength
,
361 const uint32_t rowCount
, WebGLTexelFormat srcFormat
,
362 const uint8_t* const srcBegin
, const ptrdiff_t srcStride
,
363 WebGLTexelFormat dstFormat
, const ptrdiff_t dstStride
,
364 const uint8_t** const out_begin
,
365 UniqueBuffer
* const out_anchoredBuffer
) const {
366 MOZ_ASSERT(srcFormat
!= WebGLTexelFormat::FormatNotSupportingAnyConversion
);
367 MOZ_ASSERT(dstFormat
!= WebGLTexelFormat::FormatNotSupportingAnyConversion
);
369 *out_begin
= srcBegin
;
371 const auto& unpacking
= mDesc
.unpacking
;
373 if (!rowLength
|| !rowCount
) return true;
375 const auto srcIsPremult
= (mDesc
.srcAlphaType
== gfxAlphaType::Premult
);
376 auto dstIsPremult
= unpacking
.premultiplyAlpha
;
377 const auto fnHasPremultMismatch
= [&]() {
378 if (mDesc
.srcAlphaType
== gfxAlphaType::Opaque
) return false;
380 if (!HasColorAndAlpha(srcFormat
)) return false;
382 return srcIsPremult
!= dstIsPremult
;
385 const auto srcOrigin
=
386 (unpacking
.flipY
? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft
);
387 auto dstOrigin
= gl::OriginPos::BottomLeft
;
389 if (!mDesc
.applyUnpackTransforms
) {
390 dstIsPremult
= srcIsPremult
;
391 dstOrigin
= srcOrigin
;
394 if (srcFormat
!= dstFormat
) {
395 webgl
->GeneratePerfWarning(
396 "Conversion requires pixel reformatting. (%u->%u)", uint32_t(srcFormat
),
397 uint32_t(dstFormat
));
398 } else if (fnHasPremultMismatch()) {
399 webgl
->GeneratePerfWarning(
400 "Conversion requires change in"
401 " alpha-premultiplication.");
402 } else if (srcOrigin
!= dstOrigin
) {
403 webgl
->GeneratePerfWarning("Conversion requires y-flip.");
404 } else if (srcStride
!= dstStride
) {
405 webgl
->GeneratePerfWarning("Conversion requires change in stride. (%u->%u)",
406 uint32_t(srcStride
), uint32_t(dstStride
));
413 const auto dstTotalBytes
= CheckedUint32(rowCount
) * dstStride
;
414 if (!dstTotalBytes
.isValid()) {
415 webgl
->ErrorOutOfMemory("Calculation failed.");
419 auto dstBuffer
= UniqueBuffer::Take(calloc(1u, dstTotalBytes
.value()));
420 if (!dstBuffer
.get()) {
421 webgl
->ErrorOutOfMemory("Failed to allocate dest buffer.");
424 const auto dstBegin
= static_cast<uint8_t*>(dstBuffer
.get());
430 if (!ConvertImage(rowLength
, rowCount
, srcBegin
, srcStride
, srcOrigin
,
431 srcFormat
, srcIsPremult
, dstBegin
, dstStride
, dstOrigin
,
432 dstFormat
, dstIsPremult
, &wasTrivial
)) {
433 webgl
->ErrorImplementationBug("ConvertImage failed.");
437 *out_begin
= dstBegin
;
438 *out_anchoredBuffer
= std::move(dstBuffer
);
442 static GLenum
DoTexOrSubImage(bool isSubImage
, gl::GLContext
* gl
,
443 TexImageTarget target
, GLint level
,
444 const DriverUnpackInfo
* dui
, GLint xOffset
,
445 GLint yOffset
, GLint zOffset
, GLsizei width
,
446 GLsizei height
, GLsizei depth
, const void* data
) {
448 return DoTexSubImage(gl
, target
, level
, xOffset
, yOffset
, zOffset
, width
,
449 height
, depth
, dui
->ToPacking(), data
);
451 return DoTexImage(gl
, target
, level
, dui
, width
, height
, depth
, data
);
455 //////////////////////////////////////////////////////////////////////////////////////////
458 bool TexUnpackBytes::Validate(const WebGLContext
* const webgl
,
459 const webgl::PackingInfo
& pi
) {
460 if (!HasData()) return true;
462 CheckedInt
<size_t> availBytes
= 0;
464 const auto& range
= mDesc
.cpuData
->Data();
465 availBytes
= range
.length();
466 } else if (mDesc
.pboOffset
) {
467 const auto& pboOffset
= *mDesc
.pboOffset
;
470 webgl
->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
471 if (!pbo
) return false; // Might be invalid e.g. due to in-use by TF.
472 availBytes
= pbo
->ByteLength();
473 availBytes
-= pboOffset
;
475 MOZ_ASSERT(false, "Must be one of the above");
477 if (!availBytes
.isValid()) {
478 webgl
->ErrorInvalidOperation("Offset is passed end of buffer.");
482 return ValidateUnpackBytes(webgl
, pi
, availBytes
.value(), *this);
485 bool TexUnpackBytes::TexOrSubImage(bool isSubImage
, bool needsRespec
,
486 WebGLTexture
* tex
, GLint level
,
487 const webgl::DriverUnpackInfo
* dui
,
488 GLint xOffset
, GLint yOffset
, GLint zOffset
,
489 const webgl::PackingInfo
& pi
,
490 GLenum
* const out_error
) const {
491 const auto& webgl
= tex
->mContext
;
492 const auto& target
= mDesc
.imageTarget
;
493 const auto& size
= mDesc
.size
;
494 const auto& webglUnpackState
= mDesc
.unpacking
;
496 const auto unpackingRes
= mDesc
.ExplicitUnpacking(pi
, {});
498 const auto format
= FormatForPackingInfo(pi
);
500 const uint8_t* uploadPtr
= nullptr;
502 const auto range
= mDesc
.cpuData
->Data();
503 uploadPtr
= range
.begin().get();
505 MOZ_ASSERT(!range
.length());
507 } else if (mDesc
.pboOffset
) {
508 uploadPtr
= reinterpret_cast<const uint8_t*>(*mDesc
.pboOffset
);
511 UniqueBuffer tempBuffer
;
514 if (mDesc
.pboOffset
|| !uploadPtr
) break;
516 if (!webglUnpackState
.flipY
&& !webglUnpackState
.premultiplyAlpha
) {
520 webgl
->GenerateWarning(
521 "Alpha-premult and y-flip are deprecated for"
522 " non-DOM-Element uploads.");
524 MOZ_RELEASE_ASSERT(unpackingRes
.isOk());
525 const auto& unpacking
= unpackingRes
.inspect();
526 const auto stride
= unpacking
.metrics
.bytesPerRowStride
;
528 if (!ConvertIfNeeded(webgl
, unpacking
.state
.rowLength
,
529 unpacking
.metrics
.totalRows
,
530 format
, uploadPtr
, AutoAssertCast(stride
),
531 format
, AutoAssertCast(stride
), &uploadPtr
, &tempBuffer
)) {
539 const auto& gl
= webgl
->gl
;
541 bool useParanoidHandling
= false;
542 if (mNeedsExactUpload
&& webgl
->mBoundPixelUnpackBuffer
) {
543 webgl
->GenerateWarning(
544 "Uploads from a buffer with a final row with a byte"
545 " count smaller than the row stride can incur extra"
548 if (gl
->WorkAroundDriverBugs()) {
549 useParanoidHandling
|= (gl
->Vendor() == gl::GLVendor::NVIDIA
);
553 if (!useParanoidHandling
) {
554 const ScopedLazyBind
bindPBO(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
555 webgl
->mBoundPixelUnpackBuffer
);
558 DoTexOrSubImage(isSubImage
, gl
, target
, level
, dui
, xOffset
, yOffset
,
559 zOffset
, size
.x
, size
.y
, size
.z
, uploadPtr
);
565 MOZ_ASSERT(webgl
->mBoundPixelUnpackBuffer
);
568 // Alloc first to catch OOMs.
569 AssertUintParamCorrect(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING
, 0);
571 DoTexOrSubImage(false, gl
, target
, level
, dui
, xOffset
, yOffset
,
572 zOffset
, size
.x
, size
.y
, size
.z
, nullptr);
573 if (*out_error
) return true;
575 if (!size
.x
|| !size
.y
|| !size
.z
) {
580 MOZ_RELEASE_ASSERT(unpackingRes
.isOk());
581 const auto& unpacking
= unpackingRes
.inspect();
583 const ScopedLazyBind
bindPBO(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
584 webgl
->mBoundPixelUnpackBuffer
);
588 // Make our sometimes-implicit values explicit. Also this keeps them constant
589 // when we ask for height=mHeight-1 and such.
590 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
,
591 AutoAssertCast(unpacking
.state
.rowLength
));
592 gl
->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT
,
593 AutoAssertCast(unpacking
.state
.imageHeight
));
597 DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
, yOffset
, zOffset
,
598 size
.x
, size
.y
, size
.z
- 1, uploadPtr
);
601 // Skip the images we uploaded.
602 const auto skipImages
= ZeroOn2D(target
, unpacking
.state
.skipImages
);
603 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, skipImages
+ size
.z
- 1);
607 DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
, yOffset
,
608 zOffset
+ size
.z
- 1, size
.x
, size
.y
- 1, 1, uploadPtr
);
613 const auto lastRowOffset
=
614 unpacking
.metrics
.totalBytesStrided
- unpacking
.metrics
.bytesPerRowStride
;
615 const auto lastRowPtr
= uploadPtr
+ lastRowOffset
;
617 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 1); // No stride padding.
618 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0); // No padding in general.
619 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, 0); // Don't skip images,
620 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS
,
622 // Keep skipping pixels though!
623 *out_error
= DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
,
624 yOffset
+ size
.y
- 1, zOffset
+ size
.z
- 1,
625 AutoAssertCast(size
.x
), 1, 1, lastRowPtr
);
627 // Caller will reset all our modified PixelStorei state.
632 ////////////////////////////////////////////////////////////////////////////////
633 ////////////////////////////////////////////////////////////////////////////////
636 TexUnpackImage::~TexUnpackImage() = default;
638 bool TexUnpackImage::Validate(const WebGLContext
* const webgl
,
639 const webgl::PackingInfo
& pi
) {
640 if (!ValidatePIForDOM(webgl
, pi
)) return false;
642 if (!mDesc
.structuredSrcSize
) {
643 gfxCriticalError() << "TexUnpackImage missing structuredSrcSize.";
646 const auto& elemSize
= *mDesc
.structuredSrcSize
;
647 if (mDesc
.dataSurf
) {
648 const auto& surfSize
= mDesc
.dataSurf
->GetSize();
649 const auto surfSize2
= ivec2::FromSize(surfSize
)->StaticCast
<uvec2
>();
650 if (uvec2
{elemSize
.x
, elemSize
.y
} != surfSize2
) {
652 << "TexUnpackImage mismatched structuredSrcSize for dataSurf.";
657 const auto fullRows
= elemSize
.y
;
658 return ValidateUnpackPixels(webgl
, pi
, fullRows
, *this);
661 Maybe
<std::string
> BlitPreventReason(const int32_t level
, const ivec3
& offset
,
662 const webgl::PackingInfo
& pi
,
663 const TexUnpackBlobDesc
& desc
) {
664 const auto& size
= desc
.size
;
665 const auto& unpacking
= desc
.unpacking
;
667 const auto ret
= [&]() -> const char* {
669 return "depth is not 1";
671 if (offset
.x
!= 0 || offset
.y
!= 0 || offset
.z
!= 0) {
672 return "x/y/zOffset is not 0";
675 if (unpacking
.skipPixels
|| unpacking
.skipRows
|| unpacking
.skipImages
) {
676 return "non-zero UNPACK_SKIP_* not yet supported";
679 const auto premultReason
= [&]() -> const char* {
680 if (desc
.srcAlphaType
== gfxAlphaType::Opaque
) return nullptr;
682 const bool srcIsPremult
= (desc
.srcAlphaType
== gfxAlphaType::Premult
);
683 const auto& dstIsPremult
= unpacking
.premultiplyAlpha
;
684 if (srcIsPremult
== dstIsPremult
) return nullptr;
687 return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
689 return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
692 if (premultReason
) return premultReason
;
694 if (pi
.format
!= LOCAL_GL_RGBA
) {
695 return "`format` is not RGBA";
698 if (pi
.type
!= LOCAL_GL_UNSIGNED_BYTE
) {
699 return "`type` is not UNSIGNED_BYTE";
704 return Some(std::string(ret
));
709 bool TexUnpackImage::TexOrSubImage(bool isSubImage
, bool needsRespec
,
710 WebGLTexture
* tex
, GLint level
,
711 const webgl::DriverUnpackInfo
* dui
,
712 GLint xOffset
, GLint yOffset
, GLint zOffset
,
713 const webgl::PackingInfo
& pi
,
714 GLenum
* const out_error
) const {
715 MOZ_ASSERT_IF(needsRespec
, !isSubImage
);
717 const auto& webgl
= tex
->mContext
;
718 const auto& target
= mDesc
.imageTarget
;
719 const auto& size
= mDesc
.size
;
720 const auto& sd
= *(mDesc
.sd
);
721 const auto& unpacking
= mDesc
.unpacking
;
723 const auto& gl
= webgl
->GL();
728 BlitPreventReason(level
, {xOffset
, yOffset
, zOffset
}, pi
, mDesc
);
730 webgl
->GeneratePerfWarning(
731 "Failed to hit GPU-copy fast-path."
732 " (%s) Falling back to CPU upload.",
741 DoTexOrSubImage(isSubImage
, gl
, target
, level
, dui
, xOffset
, yOffset
,
742 zOffset
, size
.x
, size
.y
, size
.z
, nullptr);
743 if (*out_error
) return true;
747 gl::ScopedFramebuffer
scopedFB(gl
);
748 gl::ScopedBindFramebuffer
bindFB(gl
, scopedFB
.FB());
751 gl::GLContext::LocalErrorScope
errorScope(*gl
);
753 gl
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
,
754 LOCAL_GL_COLOR_ATTACHMENT0
, target
,
755 tex
->mGLName
, level
);
757 const auto err
= errorScope
.GetError();
758 MOZ_ALWAYS_TRUE(!err
);
761 const GLenum status
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
762 MOZ_ALWAYS_TRUE(status
== LOCAL_GL_FRAMEBUFFER_COMPLETE
);
764 const auto dstOrigin
=
765 (unpacking
.flipY
? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft
);
766 if (!gl
->BlitHelper()->BlitSdToFramebuffer(sd
, {size
.x
, size
.y
},
768 gfxCriticalNote
<< "BlitSdToFramebuffer failed for type "
770 // Maybe the resource isn't valid anymore?
771 gl
->fClearColor(0.2, 0.0, 0.2, 1.0);
772 gl
->fClear(LOCAL_GL_COLOR_BUFFER_BIT
);
773 const auto& cur
= webgl
->mColorClearValue
;
774 gl
->fClearColor(cur
[0], cur
[1], cur
[2], cur
[3]);
775 webgl
->GenerateWarning(
776 "Fast Tex(Sub)Image upload failed without recourse, clearing to "
777 "[0.2, 0.0, 0.2, 1.0]. Please file a bug!");
784 ////////////////////////////////////////////////////////////////////////////////
785 ////////////////////////////////////////////////////////////////////////////////
788 TexUnpackSurface::~TexUnpackSurface() = default;
792 static bool GetFormatForSurf(const gfx::SourceSurface
* surf
,
793 WebGLTexelFormat
* const out_texelFormat
,
794 uint8_t* const out_bpp
) {
795 const auto surfFormat
= surf
->GetFormat();
796 switch (surfFormat
) {
797 case gfx::SurfaceFormat::B8G8R8A8
:
798 *out_texelFormat
= WebGLTexelFormat::BGRA8
;
802 case gfx::SurfaceFormat::B8G8R8X8
:
803 *out_texelFormat
= WebGLTexelFormat::BGRX8
;
807 case gfx::SurfaceFormat::R8G8B8A8
:
808 *out_texelFormat
= WebGLTexelFormat::RGBA8
;
812 case gfx::SurfaceFormat::R8G8B8X8
:
813 *out_texelFormat
= WebGLTexelFormat::RGBX8
;
817 case gfx::SurfaceFormat::R5G6B5_UINT16
:
818 *out_texelFormat
= WebGLTexelFormat::RGB565
;
822 case gfx::SurfaceFormat::A8
:
823 *out_texelFormat
= WebGLTexelFormat::A8
;
827 case gfx::SurfaceFormat::YUV
:
829 NS_ERROR("We don't handle uploads from YUV sources yet.");
830 // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically
831 // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB)
841 bool TexUnpackSurface::Validate(const WebGLContext
* const webgl
,
842 const webgl::PackingInfo
& pi
) {
843 if (!ValidatePIForDOM(webgl
, pi
)) return false;
845 if (!mDesc
.structuredSrcSize
) {
846 gfxCriticalError() << "TexUnpackSurface missing structuredSrcSize.";
849 const auto& elemSize
= *mDesc
.structuredSrcSize
;
850 if (mDesc
.dataSurf
) {
851 const auto& surfSize
= mDesc
.dataSurf
->GetSize();
852 const auto surfSize2
= ivec2::FromSize(surfSize
)->StaticCast
<uvec2
>();
853 if (uvec2
{elemSize
.x
, elemSize
.y
} != surfSize2
) {
855 << "TexUnpackSurface mismatched structuredSrcSize for dataSurf.";
860 const auto fullRows
= elemSize
.y
;
861 return ValidateUnpackPixels(webgl
, pi
, fullRows
, *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 RefPtr
<gfx::DataSourceSurface
> surf
;
875 // If we get here, we assume the SD describes an RGBA Shmem.
876 const auto& sd
= *(mDesc
.sd
);
877 MOZ_ASSERT(SDIsRGBBuffer(sd
));
878 const auto& sdb
= sd
.get_SurfaceDescriptorBuffer();
879 const auto& rgb
= sdb
.desc().get_RGBDescriptor();
880 const auto& data
= sdb
.data();
881 MOZ_ASSERT(data
.type() == layers::MemoryOrShmem::TShmem
);
882 const auto& shmem
= data
.get_Shmem();
883 surf
= gfx::Factory::CreateWrappingDataSourceSurface(
884 shmem
.get
<uint8_t>(), layers::ImageDataSerializer::GetRGBStride(rgb
),
885 rgb
.size(), rgb
.format());
887 gfxCriticalError() << "TexUnpackSurface failed to create wrapping "
888 "DataSourceSurface for Shmem.";
892 surf
= mDesc
.dataSurf
;
897 WebGLTexelFormat srcFormat
;
899 if (!GetFormatForSurf(surf
, &srcFormat
, &srcBPP
)) {
900 webgl
->ErrorImplementationBug(
901 "GetFormatForSurf failed for"
902 " WebGLTexelFormat::%u.",
903 uint32_t(surf
->GetFormat()));
907 gfx::DataSourceSurface::ScopedMap
map(surf
,
908 gfx::DataSourceSurface::MapType::READ
);
909 if (!map
.IsMapped()) {
910 webgl
->ErrorOutOfMemory("Failed to map source surface for upload.");
914 const auto& srcBegin
= map
.GetData();
915 const auto srcStride
= static_cast<size_t>(map
.GetStride());
919 const auto dstFormat
= FormatForPackingInfo(dstPI
);
920 const auto dstBpp
= BytesPerPixel(dstPI
);
921 const size_t dstUsedBytesPerRow
= dstBpp
* surf
->GetSize().width
;
922 auto dstStride
= dstUsedBytesPerRow
;
923 if (dstFormat
== srcFormat
) {
924 dstStride
= srcStride
; // Try to match.
929 auto dstUnpackingRes
= mDesc
.ExplicitUnpacking(dstPI
, Some(dstStride
));
930 if (dstUnpackingRes
.isOk()) {
931 const auto& dstUnpacking
= dstUnpackingRes
.inspect();
932 if (!webgl
->IsWebGL2() && dstUnpacking
.state
.rowLength
!= size
.x
) {
933 dstUnpackingRes
= Err("WebGL1 can't handle rowLength != size.x");
936 if (!dstUnpackingRes
.isOk()) {
937 dstStride
= dstUsedBytesPerRow
;
938 dstUnpackingRes
= mDesc
.ExplicitUnpacking(dstPI
, Some(dstStride
));
940 if (!dstUnpackingRes
.isOk()) {
941 gfxCriticalError() << dstUnpackingRes
.inspectErr();
942 webgl
->ErrorImplementationBug("ExplicitUnpacking failed: %s",
943 dstUnpackingRes
.inspectErr().c_str());
946 const auto& dstUnpacking
= dstUnpackingRes
.inspect();
947 MOZ_ASSERT(dstUnpacking
.metrics
.bytesPerRowStride
== dstStride
);
951 const uint8_t* dstBegin
= srcBegin
;
952 UniqueBuffer tempBuffer
;
954 if (!ConvertIfNeeded(webgl
, surf
->GetSize().width
, surf
->GetSize().height
,
955 srcFormat
, srcBegin
, AutoAssertCast(srcStride
),
956 dstFormat
, AutoAssertCast(dstUnpacking
.metrics
.bytesPerRowStride
), &dstBegin
,
964 const auto& gl
= webgl
->gl
;
965 if (!gl
->MakeCurrent()) {
966 *out_error
= LOCAL_GL_CONTEXT_LOST
;
970 dstUnpacking
.state
.ApplyUnpack(*gl
, webgl
->IsWebGL2(), size
);
973 DoTexOrSubImage(isSubImage
, gl
, mDesc
.imageTarget
, level
, dui
, xOffset
,
974 yOffset
, zOffset
, size
.x
, size
.y
, size
.z
, dstBegin
);
976 // Caller will reset all our modified PixelStorei state.
982 } // namespace mozilla