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/layers/SharedSurfacesParent.h"
15 #include "mozilla/layers/TextureHost.h"
16 #include "mozilla/layers/VideoBridgeParent.h"
17 #include "mozilla/RefPtr.h"
18 #include "nsLayoutUtils.h"
19 #include "WebGLBuffer.h"
20 #include "WebGLContext.h"
21 #include "WebGLFormats.h"
22 #include "WebGLTexelConversions.h"
23 #include "WebGLTexture.h"
27 bool webgl::PixelPackingState::AssertCurrentUnpack(gl::GLContext
& gl
,
28 const bool isWebgl2
) const {
29 if (!kIsDebug
) return true;
31 auto actual
= PixelPackingState
{};
32 gl
.GetInt(LOCAL_GL_UNPACK_ALIGNMENT
, &actual
.alignmentInTypeElems
);
34 gl
.GetInt(LOCAL_GL_UNPACK_ROW_LENGTH
, &actual
.rowLength
);
35 gl
.GetInt(LOCAL_GL_UNPACK_IMAGE_HEIGHT
, &actual
.imageHeight
);
37 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_PIXELS
, &actual
.skipPixels
);
38 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_ROWS
, &actual
.skipRows
);
39 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_IMAGES
, &actual
.skipImages
);
41 if (*this == actual
) return true;
43 const auto ToStr
= [](const PixelPackingState
& x
) {
44 const auto text
= nsPrintfCString(
45 "%u,%u,%u;%u,%u,%u", x
.alignmentInTypeElems
, x
.rowLength
, x
.imageHeight
,
46 x
.skipPixels
, x
.skipRows
, x
.skipImages
);
47 return mozilla::ToString(text
);
50 const auto was
= ToStr(actual
);
51 const auto expected
= ToStr(*this);
52 gfxCriticalError() << "PixelUnpackStateGl was not current. Was " << was
53 << ". Expected << " << expected
<< ".";
57 void webgl::PixelPackingState::ApplyUnpack(gl::GLContext
& gl
,
59 const uvec3
& uploadSize
) const {
60 gl
.fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
61 AssertedCast
<GLsizei
>(alignmentInTypeElems
));
62 if (!isWebgl2
) return;
64 // Re-simplify. (ANGLE seems to have an issue with imageHeight ==
66 auto rowLengthOrZero
= rowLength
;
67 auto imageHeightOrZero
= imageHeight
;
68 if (rowLengthOrZero
== uploadSize
.x
) {
71 if (imageHeightOrZero
== uploadSize
.y
) {
72 imageHeightOrZero
= 0;
75 gl
.fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
,
76 AssertedCast
<GLsizei
>(rowLengthOrZero
));
77 gl
.fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT
,
78 AssertedCast
<GLsizei
>(imageHeightOrZero
));
80 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS
,
81 AssertedCast
<GLsizei
>(skipPixels
));
82 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS
, AssertedCast
<GLsizei
>(skipRows
));
83 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
,
84 AssertedCast
<GLsizei
>(skipImages
));
89 static bool IsPIValidForDOM(const webgl::PackingInfo
& pi
) {
90 // https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE
92 // Just check for invalid individual formats and types, not combinations.
96 case LOCAL_GL_LUMINANCE_ALPHA
:
97 case LOCAL_GL_LUMINANCE
:
100 case LOCAL_GL_RED_INTEGER
:
102 case LOCAL_GL_RG_INTEGER
:
103 case LOCAL_GL_RGB_INTEGER
:
104 case LOCAL_GL_RGBA_INTEGER
:
108 case LOCAL_GL_SRGB_ALPHA
:
109 // Allowed in WebGL1+EXT_srgb
117 case LOCAL_GL_UNSIGNED_BYTE
:
118 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
119 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
120 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
121 case LOCAL_GL_HALF_FLOAT
:
122 case LOCAL_GL_HALF_FLOAT_OES
:
124 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
134 static bool ValidatePIForDOM(const WebGLContext
* const webgl
,
135 const webgl::PackingInfo
& pi
) {
136 if (!IsPIValidForDOM(pi
)) {
137 webgl
->ErrorInvalidValue("Format or type is invalid for DOM sources.");
143 static WebGLTexelFormat
FormatForPackingInfo(const PackingInfo
& pi
) {
145 case LOCAL_GL_UNSIGNED_BYTE
:
148 case LOCAL_GL_LUMINANCE
:
149 case LOCAL_GL_RED_INTEGER
:
150 return WebGLTexelFormat::R8
;
153 return WebGLTexelFormat::A8
;
155 case LOCAL_GL_LUMINANCE_ALPHA
:
156 return WebGLTexelFormat::RA8
;
159 case LOCAL_GL_RGB_INTEGER
:
161 return WebGLTexelFormat::RGB8
;
164 case LOCAL_GL_RGBA_INTEGER
:
165 case LOCAL_GL_SRGB_ALPHA
:
166 return WebGLTexelFormat::RGBA8
;
169 case LOCAL_GL_RG_INTEGER
:
170 return WebGLTexelFormat::RG8
;
177 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
178 if (pi
.format
== LOCAL_GL_RGB
) return WebGLTexelFormat::RGB565
;
181 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
182 if (pi
.format
== LOCAL_GL_RGBA
) return WebGLTexelFormat::RGBA5551
;
185 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
186 if (pi
.format
== LOCAL_GL_RGBA
) return WebGLTexelFormat::RGBA4444
;
189 case LOCAL_GL_HALF_FLOAT
:
190 case LOCAL_GL_HALF_FLOAT_OES
:
193 case LOCAL_GL_LUMINANCE
:
194 return WebGLTexelFormat::R16F
;
197 return WebGLTexelFormat::A16F
;
198 case LOCAL_GL_LUMINANCE_ALPHA
:
199 return WebGLTexelFormat::RA16F
;
201 return WebGLTexelFormat::RG16F
;
203 return WebGLTexelFormat::RGB16F
;
205 return WebGLTexelFormat::RGBA16F
;
215 case LOCAL_GL_LUMINANCE
:
216 return WebGLTexelFormat::R32F
;
219 return WebGLTexelFormat::A32F
;
220 case LOCAL_GL_LUMINANCE_ALPHA
:
221 return WebGLTexelFormat::RA32F
;
223 return WebGLTexelFormat::RG32F
;
225 return WebGLTexelFormat::RGB32F
;
227 return WebGLTexelFormat::RGBA32F
;
234 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV
:
235 if (pi
.format
== LOCAL_GL_RGB
) return WebGLTexelFormat::RGB11F11F10F
;
242 return WebGLTexelFormat::FormatNotSupportingAnyConversion
;
247 static uint32_t ZeroOn2D(const GLenum target
, const uint32_t val
) {
248 const bool is2d
= !IsTexTarget3D(target
);
253 static bool ValidateUnpackPixels(const WebGLContext
* webgl
,
254 const webgl::PackingInfo
& pi
,
255 const uint32_t availRows
,
256 const webgl::TexUnpackBlob
& blob
) {
257 const auto& unpackingRes
= blob
.mDesc
.ExplicitUnpacking(pi
, {});
258 if (!unpackingRes
.isOk()) {
259 webgl
->ErrorInvalidOperation("%s", unpackingRes
.inspectErr().c_str());
262 const auto& unpacking
= unpackingRes
.inspect();
264 if (availRows
< unpacking
.metrics
.totalRows
) {
265 webgl
->ErrorInvalidOperation(
266 "Desired upload requires more rows (%zu) than is"
268 unpacking
.metrics
.totalRows
, availRows
);
275 static bool ValidateUnpackBytes(const WebGLContext
* const webgl
,
276 const webgl::PackingInfo
& pi
,
277 const size_t availByteCount
,
278 const webgl::TexUnpackBlob
& blob
) {
279 const auto& unpackingRes
= blob
.mDesc
.ExplicitUnpacking(pi
, {});
280 if (!unpackingRes
.isOk()) {
281 webgl
->ErrorInvalidOperation("%s", unpackingRes
.inspectErr().c_str());
284 const auto& unpacking
= unpackingRes
.inspect();
286 if (availByteCount
< unpacking
.metrics
.totalBytesUsed
) {
287 webgl
->ErrorInvalidOperation(
288 "Desired upload requires more bytes (%zu) than are"
290 unpacking
.metrics
.totalBytesUsed
, availByteCount
);
299 // Check if the surface descriptor describes a memory which contains a single
301 static bool SDIsRGBBuffer(const layers::SurfaceDescriptor
& sd
) {
302 return sd
.type() == layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
&&
303 sd
.get_SurfaceDescriptorBuffer().desc().type() ==
304 layers::BufferDescriptor::TRGBDescriptor
;
307 // Check if the surface descriptor describes a GPUVideo texture for which we
308 // only have an opaque source/handle from SurfaceDescriptorRemoteDecoder to
309 // derive the actual texture from.
310 static bool SDIsNullRemoteDecoder(const layers::SurfaceDescriptor
& sd
) {
311 return sd
.type() == layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo
&&
312 sd
.get_SurfaceDescriptorGPUVideo()
313 .get_SurfaceDescriptorRemoteDecoder()
315 .type() == layers::RemoteDecoderVideoSubDescriptor::Tnull_t
;
318 // Check if the surface descriptor describes an ExternalImage surface for which
319 // we only have an opaque source/handle to derive the actual surface from.
320 static bool SDIsExternalImage(const layers::SurfaceDescriptor
& sd
) {
322 layers::SurfaceDescriptor::TSurfaceDescriptorExternalImage
&&
323 sd
.get_SurfaceDescriptorExternalImage().source() ==
324 wr::ExternalImageSource::SharedSurfaces
;
328 std::unique_ptr
<TexUnpackBlob
> TexUnpackBlob::Create(
329 const TexUnpackBlobDesc
& desc
) {
330 return std::unique_ptr
<TexUnpackBlob
>{[&]() -> TexUnpackBlob
* {
331 if (!IsTarget3D(desc
.imageTarget
) && desc
.size
.z
!= 1) {
336 switch (desc
.unpacking
.alignmentInTypeElems
) {
348 // Shmem buffers need to be treated as if they were a DataSourceSurface.
349 // Otherwise, TexUnpackImage will try to blit the surface descriptor as
350 // if it can be mapped as a framebuffer, whereas the Shmem is still CPU
352 if (SDIsRGBBuffer(*desc
.sd
) || SDIsNullRemoteDecoder(*desc
.sd
) ||
353 SDIsExternalImage(*desc
.sd
)) {
354 return new TexUnpackSurface(desc
);
356 return new TexUnpackImage(desc
);
359 return new TexUnpackSurface(desc
);
362 if (desc
.srcAlphaType
!= gfxAlphaType::NonPremult
) {
366 return new TexUnpackBytes(desc
);
370 static bool HasColorAndAlpha(const WebGLTexelFormat format
) {
372 case WebGLTexelFormat::RA8
:
373 case WebGLTexelFormat::RA16F
:
374 case WebGLTexelFormat::RA32F
:
375 case WebGLTexelFormat::RGBA8
:
376 case WebGLTexelFormat::RGBA5551
:
377 case WebGLTexelFormat::RGBA4444
:
378 case WebGLTexelFormat::RGBA16F
:
379 case WebGLTexelFormat::RGBA32F
:
380 case WebGLTexelFormat::BGRA8
:
387 bool TexUnpackBlob::ConvertIfNeeded(
388 const WebGLContext
* const webgl
, const uint32_t rowLength
,
389 const uint32_t rowCount
, WebGLTexelFormat srcFormat
,
390 const uint8_t* const srcBegin
, const ptrdiff_t srcStride
,
391 WebGLTexelFormat dstFormat
, const ptrdiff_t dstStride
,
392 const uint8_t** const out_begin
,
393 UniqueBuffer
* const out_anchoredBuffer
) const {
394 MOZ_ASSERT(srcFormat
!= WebGLTexelFormat::FormatNotSupportingAnyConversion
);
395 MOZ_ASSERT(dstFormat
!= WebGLTexelFormat::FormatNotSupportingAnyConversion
);
397 *out_begin
= srcBegin
;
399 const auto& unpacking
= mDesc
.unpacking
;
401 if (!rowLength
|| !rowCount
) return true;
403 const auto srcIsPremult
= (mDesc
.srcAlphaType
== gfxAlphaType::Premult
);
404 auto dstIsPremult
= unpacking
.premultiplyAlpha
;
405 const auto fnHasPremultMismatch
= [&]() {
406 if (mDesc
.srcAlphaType
== gfxAlphaType::Opaque
) return false;
408 if (!HasColorAndAlpha(srcFormat
)) return false;
410 return srcIsPremult
!= dstIsPremult
;
413 const auto srcOrigin
=
414 (unpacking
.flipY
? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft
);
415 auto dstOrigin
= gl::OriginPos::BottomLeft
;
417 if (!mDesc
.applyUnpackTransforms
) {
418 dstIsPremult
= srcIsPremult
;
419 dstOrigin
= srcOrigin
;
422 // TODO (Bug 754256): Figure out the source colorSpace.
423 dom::PredefinedColorSpace srcColorSpace
= dom::PredefinedColorSpace::Srgb
;
424 dom::PredefinedColorSpace dstColorSpace
=
425 webgl
->mUnpackColorSpace
? *webgl
->mUnpackColorSpace
426 : dom::PredefinedColorSpace::Srgb
;
428 if (srcFormat
!= dstFormat
) {
429 webgl
->GeneratePerfWarning(
430 "Conversion requires pixel reformatting. (%u->%u)", uint32_t(srcFormat
),
431 uint32_t(dstFormat
));
432 } else if (fnHasPremultMismatch()) {
433 webgl
->GeneratePerfWarning(
434 "Conversion requires change in"
435 " alpha-premultiplication.");
436 } else if (srcOrigin
!= dstOrigin
) {
437 webgl
->GeneratePerfWarning("Conversion requires y-flip.");
438 } else if (srcStride
!= dstStride
) {
439 webgl
->GeneratePerfWarning("Conversion requires change in stride. (%u->%u)",
440 uint32_t(srcStride
), uint32_t(dstStride
));
441 } else if (srcColorSpace
!= dstColorSpace
) {
442 webgl
->GeneratePerfWarning(
443 "Conversion requires colorSpace conversion. (%u->%u)",
444 uint32_t(srcColorSpace
), uint32_t(dstColorSpace
));
451 const auto dstTotalBytes
= CheckedUint32(rowCount
) * dstStride
;
452 if (!dstTotalBytes
.isValid()) {
453 webgl
->ErrorOutOfMemory("Calculation failed.");
457 auto dstBuffer
= UniqueBuffer::Take(calloc(1u, dstTotalBytes
.value()));
458 if (!dstBuffer
.get()) {
459 webgl
->ErrorOutOfMemory("Failed to allocate dest buffer.");
462 const auto dstBegin
= static_cast<uint8_t*>(dstBuffer
.get());
468 if (!ConvertImage(rowLength
, rowCount
, srcBegin
, srcStride
, srcOrigin
,
469 srcFormat
, srcIsPremult
, dstBegin
, dstStride
, dstOrigin
,
470 dstFormat
, dstIsPremult
, srcColorSpace
, dstColorSpace
,
472 webgl
->ErrorImplementationBug("ConvertImage failed.");
476 *out_begin
= dstBegin
;
477 *out_anchoredBuffer
= std::move(dstBuffer
);
481 static GLenum
DoTexOrSubImage(bool isSubImage
, gl::GLContext
* gl
,
482 TexImageTarget target
, GLint level
,
483 const DriverUnpackInfo
* dui
, GLint xOffset
,
484 GLint yOffset
, GLint zOffset
, GLsizei width
,
485 GLsizei height
, GLsizei depth
, const void* data
) {
487 return DoTexSubImage(gl
, target
, level
, xOffset
, yOffset
, zOffset
, width
,
488 height
, depth
, dui
->ToPacking(), data
);
490 return DoTexImage(gl
, target
, level
, dui
, width
, height
, depth
, data
);
494 //////////////////////////////////////////////////////////////////////////////////////////
497 bool TexUnpackBytes::Validate(const WebGLContext
* const webgl
,
498 const webgl::PackingInfo
& pi
) {
499 if (!HasData()) return true;
501 CheckedInt
<size_t> availBytes
= 0;
503 availBytes
= mDesc
.cpuData
->size();
504 } else if (mDesc
.pboOffset
) {
505 const auto& pboOffset
= *mDesc
.pboOffset
;
508 webgl
->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
509 if (!pbo
) return false; // Might be invalid e.g. due to in-use by TF.
510 availBytes
= pbo
->ByteLength();
511 availBytes
-= pboOffset
;
513 MOZ_ASSERT(false, "Must be one of the above");
515 if (!availBytes
.isValid()) {
516 webgl
->ErrorInvalidOperation("Offset is passed end of buffer.");
520 return ValidateUnpackBytes(webgl
, pi
, availBytes
.value(), *this);
523 bool TexUnpackBytes::TexOrSubImage(bool isSubImage
, bool needsRespec
,
524 WebGLTexture
* tex
, GLint level
,
525 const webgl::DriverUnpackInfo
* dui
,
526 GLint xOffset
, GLint yOffset
, GLint zOffset
,
527 const webgl::PackingInfo
& pi
,
528 GLenum
* const out_error
) const {
529 const auto& webgl
= tex
->mContext
;
530 const auto& target
= mDesc
.imageTarget
;
531 const auto& size
= mDesc
.size
;
532 const auto& webglUnpackState
= mDesc
.unpacking
;
534 const auto unpackingRes
= mDesc
.ExplicitUnpacking(pi
, {});
536 const auto format
= FormatForPackingInfo(pi
);
538 const uint8_t* uploadPtr
= nullptr;
540 uploadPtr
= mDesc
.cpuData
->data();
541 } else if (mDesc
.pboOffset
) {
542 uploadPtr
= reinterpret_cast<const uint8_t*>(*mDesc
.pboOffset
);
545 UniqueBuffer tempBuffer
;
548 if (mDesc
.pboOffset
|| !uploadPtr
) break;
550 if (!webglUnpackState
.flipY
&& !webglUnpackState
.premultiplyAlpha
) {
554 webgl
->GenerateWarning(
555 "Alpha-premult and y-flip are deprecated for"
556 " non-DOM-Element uploads.");
558 MOZ_RELEASE_ASSERT(unpackingRes
.isOk());
559 const auto& unpacking
= unpackingRes
.inspect();
560 const auto stride
= unpacking
.metrics
.bytesPerRowStride
;
562 if (!ConvertIfNeeded(webgl
, unpacking
.state
.rowLength
,
563 unpacking
.metrics
.totalRows
,
564 format
, uploadPtr
, AutoAssertCast(stride
),
565 format
, AutoAssertCast(stride
), &uploadPtr
, &tempBuffer
)) {
573 const auto& gl
= webgl
->gl
;
575 bool useParanoidHandling
= false;
576 if (mNeedsExactUpload
&& webgl
->mBoundPixelUnpackBuffer
) {
577 webgl
->GenerateWarning(
578 "Uploads from a buffer with a final row with a byte"
579 " count smaller than the row stride can incur extra"
582 if (gl
->WorkAroundDriverBugs()) {
583 useParanoidHandling
|= (gl
->Vendor() == gl::GLVendor::NVIDIA
);
587 if (!useParanoidHandling
) {
588 const ScopedLazyBind
bindPBO(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
589 webgl
->mBoundPixelUnpackBuffer
);
592 DoTexOrSubImage(isSubImage
, gl
, target
, level
, dui
, xOffset
, yOffset
,
593 zOffset
, size
.x
, size
.y
, size
.z
, uploadPtr
);
599 MOZ_ASSERT(webgl
->mBoundPixelUnpackBuffer
);
602 // Alloc first to catch OOMs.
603 AssertUintParamCorrect(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING
, 0);
605 DoTexOrSubImage(false, gl
, target
, level
, dui
, xOffset
, yOffset
,
606 zOffset
, size
.x
, size
.y
, size
.z
, nullptr);
607 if (*out_error
) return true;
609 if (!size
.x
|| !size
.y
|| !size
.z
) {
614 MOZ_RELEASE_ASSERT(unpackingRes
.isOk());
615 const auto& unpacking
= unpackingRes
.inspect();
617 const ScopedLazyBind
bindPBO(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
618 webgl
->mBoundPixelUnpackBuffer
);
622 // Make our sometimes-implicit values explicit. Also this keeps them constant
623 // when we ask for height=mHeight-1 and such.
624 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
,
625 AutoAssertCast(unpacking
.state
.rowLength
));
626 gl
->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT
,
627 AutoAssertCast(unpacking
.state
.imageHeight
));
631 DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
, yOffset
, zOffset
,
632 size
.x
, size
.y
, size
.z
- 1, uploadPtr
);
635 // Skip the images we uploaded.
636 const auto skipImages
= ZeroOn2D(target
, unpacking
.state
.skipImages
);
637 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, skipImages
+ size
.z
- 1);
641 DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
, yOffset
,
642 zOffset
+ size
.z
- 1, size
.x
, size
.y
- 1, 1, uploadPtr
);
647 const auto lastRowOffset
=
648 unpacking
.metrics
.totalBytesStrided
- unpacking
.metrics
.bytesPerRowStride
;
649 const auto lastRowPtr
= uploadPtr
+ lastRowOffset
;
651 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 1); // No stride padding.
652 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0); // No padding in general.
653 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, 0); // Don't skip images,
654 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS
,
656 // Keep skipping pixels though!
657 *out_error
= DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
,
658 yOffset
+ size
.y
- 1, zOffset
+ size
.z
- 1,
659 AutoAssertCast(size
.x
), 1, 1, lastRowPtr
);
661 // Caller will reset all our modified PixelStorei state.
666 ////////////////////////////////////////////////////////////////////////////////
667 ////////////////////////////////////////////////////////////////////////////////
670 TexUnpackImage::~TexUnpackImage() = default;
672 bool TexUnpackImage::Validate(const WebGLContext
* const webgl
,
673 const webgl::PackingInfo
& pi
) {
674 if (!ValidatePIForDOM(webgl
, pi
)) return false;
676 if (!mDesc
.structuredSrcSize
) {
677 gfxCriticalError() << "TexUnpackImage missing structuredSrcSize.";
680 const auto& elemSize
= *mDesc
.structuredSrcSize
;
681 if (mDesc
.dataSurf
) {
682 const auto& surfSize
= mDesc
.dataSurf
->GetSize();
683 const auto surfSize2
= ivec2::FromSize(surfSize
)->StaticCast
<uvec2
>();
684 if (uvec2
{elemSize
.x
, elemSize
.y
} != surfSize2
) {
686 << "TexUnpackImage mismatched structuredSrcSize for dataSurf.";
691 const auto fullRows
= elemSize
.y
;
692 return ValidateUnpackPixels(webgl
, pi
, fullRows
, *this);
695 Maybe
<std::string
> BlitPreventReason(
696 const int32_t level
, const ivec3
& offset
, const GLenum internalFormat
,
697 const webgl::PackingInfo
& pi
, const TexUnpackBlobDesc
& desc
,
698 const OptionalRenderableFormatBits optionalRenderableFormatBits
,
699 bool sameColorSpace
) {
700 const auto& size
= desc
.size
;
701 const auto& unpacking
= desc
.unpacking
;
703 const auto ret
= [&]() -> const char* {
705 return "depth is not 1";
707 if (offset
.x
!= 0 || offset
.y
!= 0 || offset
.z
!= 0) {
708 return "x/y/zOffset is not 0";
711 if (unpacking
.skipPixels
|| unpacking
.skipRows
|| unpacking
.skipImages
) {
712 return "non-zero UNPACK_SKIP_* not yet supported";
715 const auto premultReason
= [&]() -> const char* {
716 if (desc
.srcAlphaType
== gfxAlphaType::Opaque
) return nullptr;
718 const bool srcIsPremult
= (desc
.srcAlphaType
== gfxAlphaType::Premult
);
719 const auto& dstIsPremult
= unpacking
.premultiplyAlpha
;
720 if (srcIsPremult
== dstIsPremult
) return nullptr;
723 return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
725 return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
728 if (premultReason
) return premultReason
;
730 if (!sameColorSpace
) {
731 return "not same colorSpace";
734 const auto formatReason
= [&]() -> const char* {
735 if (pi
.type
!= LOCAL_GL_UNSIGNED_BYTE
) {
736 return "`unpackType` must be `UNSIGNED_BYTE`";
741 return nullptr; // All internalFormats for unpackFormat=RGBA are
748 return "`unpackFormat` must be `RGBA` or maybe `RGB`";
754 OptionalRenderableFormatBits bits
;
755 const char* errorMsg
;
758 switch (internalFormat
) {
759 case LOCAL_GL_RGB565
:
764 OptionalRenderableFormatBits::RGB8
,
765 "Unavailable, as blitting internalFormats RGB or RGB8 requires "
766 "that RGB8 must be a renderable format.",
772 OptionalRenderableFormatBits::SRGB8
,
773 "Unavailable, as blitting internalFormats SRGB or SRGB8 requires "
774 "that SRGB8 must be a renderable format.",
778 // texSubImage, so internalFormat is unknown, and could be anything!
780 OptionalRenderableFormatBits::RGB8
|
781 OptionalRenderableFormatBits::SRGB8
,
782 "Unavailable, as blitting texSubImage with unpackFormat=RGB "
783 "requires that RGB8 and SRGB8 must be renderable formats.",
788 << "Unexpected internalFormat for unpackFormat=RGB: 0x"
789 << gfx::hexa(internalFormat
);
790 return "Unexpected internalFormat for unpackFormat=RGB";
793 const auto availableBits
= optionalRenderableFormatBits
;
794 if ((required
.bits
| availableBits
) != availableBits
) {
795 return required
.errorMsg
;
802 if (formatReason
) return formatReason
;
807 return Some(std::string(ret
));
812 bool TexUnpackImage::TexOrSubImage(bool isSubImage
, bool needsRespec
,
813 WebGLTexture
* tex
, GLint level
,
814 const webgl::DriverUnpackInfo
* dui
,
815 GLint xOffset
, GLint yOffset
, GLint zOffset
,
816 const webgl::PackingInfo
& pi
,
817 GLenum
* const out_error
) const {
818 MOZ_ASSERT_IF(needsRespec
, !isSubImage
);
820 const auto& webgl
= tex
->mContext
;
821 const auto& target
= mDesc
.imageTarget
;
822 const auto& size
= mDesc
.size
;
823 const auto& sd
= *(mDesc
.sd
);
824 const auto& unpacking
= mDesc
.unpacking
;
826 const auto& gl
= webgl
->GL();
830 // TODO (Bug 754256): Figure out the source colorSpace.
831 dom::PredefinedColorSpace srcColorSpace
= dom::PredefinedColorSpace::Srgb
;
832 dom::PredefinedColorSpace dstColorSpace
=
833 webgl
->mUnpackColorSpace
? *webgl
->mUnpackColorSpace
834 : dom::PredefinedColorSpace::Srgb
;
835 bool sameColorSpace
= (srcColorSpace
== dstColorSpace
);
837 const auto reason
= BlitPreventReason(
838 level
, {xOffset
, yOffset
, zOffset
}, dui
->internalFormat
, pi
, mDesc
,
839 webgl
->mOptionalRenderableFormatBits
, sameColorSpace
);
841 webgl
->GeneratePerfWarning(
842 "Failed to hit GPU-copy fast-path."
843 " (%s) Falling back to CPU upload.",
852 DoTexOrSubImage(isSubImage
, gl
, target
, level
, dui
, xOffset
, yOffset
,
853 zOffset
, size
.x
, size
.y
, size
.z
, nullptr);
854 if (*out_error
) return true;
858 gl::ScopedFramebuffer
scopedFB(gl
);
859 gl::ScopedBindFramebuffer
bindFB(gl
, scopedFB
.FB());
862 gl::GLContext::LocalErrorScope
errorScope(*gl
);
864 gl
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
,
865 LOCAL_GL_COLOR_ATTACHMENT0
, target
,
866 tex
->mGLName
, level
);
868 const auto err
= errorScope
.GetError();
869 MOZ_ALWAYS_TRUE(!err
);
872 const GLenum status
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
873 MOZ_ALWAYS_TRUE(status
== LOCAL_GL_FRAMEBUFFER_COMPLETE
);
875 const auto dstOrigin
=
876 (unpacking
.flipY
? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft
);
877 if (!gl
->BlitHelper()->BlitSdToFramebuffer(sd
, {size
.x
, size
.y
},
879 gfxCriticalNote
<< "BlitSdToFramebuffer failed for type "
881 // Maybe the resource isn't valid anymore?
882 gl
->fClearColor(0.2, 0.0, 0.2, 1.0);
883 gl
->fClear(LOCAL_GL_COLOR_BUFFER_BIT
);
884 const auto& cur
= webgl
->mColorClearValue
;
885 gl
->fClearColor(cur
[0], cur
[1], cur
[2], cur
[3]);
886 webgl
->GenerateWarning(
887 "Fast Tex(Sub)Image upload failed without recourse, clearing to "
888 "[0.2, 0.0, 0.2, 1.0]. Please file a bug!");
895 ////////////////////////////////////////////////////////////////////////////////
896 ////////////////////////////////////////////////////////////////////////////////
899 TexUnpackSurface::~TexUnpackSurface() = default;
903 static bool GetFormatForSurf(const gfx::SourceSurface
* surf
,
904 WebGLTexelFormat
* const out_texelFormat
,
905 uint8_t* const out_bpp
) {
906 const auto surfFormat
= surf
->GetFormat();
907 switch (surfFormat
) {
908 case gfx::SurfaceFormat::B8G8R8A8
:
909 *out_texelFormat
= WebGLTexelFormat::BGRA8
;
913 case gfx::SurfaceFormat::B8G8R8X8
:
914 *out_texelFormat
= WebGLTexelFormat::BGRX8
;
918 case gfx::SurfaceFormat::R8G8B8A8
:
919 *out_texelFormat
= WebGLTexelFormat::RGBA8
;
923 case gfx::SurfaceFormat::R8G8B8X8
:
924 *out_texelFormat
= WebGLTexelFormat::RGBX8
;
928 case gfx::SurfaceFormat::R5G6B5_UINT16
:
929 *out_texelFormat
= WebGLTexelFormat::RGB565
;
933 case gfx::SurfaceFormat::A8
:
934 *out_texelFormat
= WebGLTexelFormat::A8
;
938 case gfx::SurfaceFormat::YUV420
:
940 NS_ERROR("We don't handle uploads from YUV sources yet.");
941 // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically
942 // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB)
952 bool TexUnpackSurface::Validate(const WebGLContext
* const webgl
,
953 const webgl::PackingInfo
& pi
) {
954 if (!ValidatePIForDOM(webgl
, pi
)) return false;
956 if (!mDesc
.structuredSrcSize
) {
957 gfxCriticalError() << "TexUnpackSurface missing structuredSrcSize.";
960 const auto& elemSize
= *mDesc
.structuredSrcSize
;
961 if (mDesc
.dataSurf
) {
962 const auto& surfSize
= mDesc
.dataSurf
->GetSize();
963 const auto surfSize2
= ivec2::FromSize(surfSize
)->StaticCast
<uvec2
>();
964 if (uvec2
{elemSize
.x
, elemSize
.y
} != surfSize2
) {
966 << "TexUnpackSurface mismatched structuredSrcSize for dataSurf.";
971 const auto fullRows
= elemSize
.y
;
972 return ValidateUnpackPixels(webgl
, pi
, fullRows
, *this);
975 bool TexUnpackSurface::TexOrSubImage(bool isSubImage
, bool needsRespec
,
976 WebGLTexture
* tex
, GLint level
,
977 const webgl::DriverUnpackInfo
* dui
,
978 GLint xOffset
, GLint yOffset
,
980 const webgl::PackingInfo
& dstPI
,
981 GLenum
* const out_error
) const {
982 const auto& webgl
= tex
->mContext
;
983 const auto& size
= mDesc
.size
;
984 RefPtr
<gfx::DataSourceSurface
> surf
;
986 // If we get here, we assume the SD describes an RGBA Shmem.
987 const auto& sd
= *(mDesc
.sd
);
988 if (SDIsRGBBuffer(sd
)) {
989 const auto& sdb
= sd
.get_SurfaceDescriptorBuffer();
990 const auto& rgb
= sdb
.desc().get_RGBDescriptor();
991 const auto& data
= sdb
.data();
992 MOZ_ASSERT(data
.type() == layers::MemoryOrShmem::TShmem
);
993 const auto& shmem
= data
.get_Shmem();
994 surf
= gfx::Factory::CreateWrappingDataSourceSurface(
995 shmem
.get
<uint8_t>(), layers::ImageDataSerializer::GetRGBStride(rgb
),
996 rgb
.size(), rgb
.format());
997 } else if (SDIsNullRemoteDecoder(sd
)) {
998 const auto& sdrd
= sd
.get_SurfaceDescriptorGPUVideo()
999 .get_SurfaceDescriptorRemoteDecoder();
1000 RefPtr
<layers::VideoBridgeParent
> parent
=
1001 layers::VideoBridgeParent::GetSingleton(sdrd
.source());
1003 gfxCriticalNote
<< "TexUnpackSurface failed to get VideoBridgeParent";
1006 RefPtr
<layers::TextureHost
> texture
=
1007 parent
->LookupTexture(webgl
->GetContentId(), sdrd
.handle());
1009 gfxCriticalNote
<< "TexUnpackSurface failed to get TextureHost";
1012 surf
= texture
->GetAsSurface();
1013 } else if (SDIsExternalImage(sd
)) {
1014 const auto& sdei
= sd
.get_SurfaceDescriptorExternalImage();
1015 if (auto* sharedSurfacesHolder
= webgl
->GetSharedSurfacesHolder()) {
1016 surf
= sharedSurfacesHolder
->Get(sdei
.id());
1019 // Most likely the content process crashed before it was able to finish
1020 // sharing the surface with the compositor process.
1021 gfxCriticalNote
<< "TexUnpackSurface failed to get ExternalImage";
1025 MOZ_ASSERT_UNREACHABLE("Unexpected surface descriptor!");
1028 gfxCriticalError() << "TexUnpackSurface failed to create wrapping "
1029 "DataSourceSurface for Shmem.";
1033 surf
= mDesc
.dataSurf
;
1038 WebGLTexelFormat srcFormat
;
1040 if (!GetFormatForSurf(surf
, &srcFormat
, &srcBPP
)) {
1041 webgl
->ErrorImplementationBug(
1042 "GetFormatForSurf failed for"
1043 " WebGLTexelFormat::%u.",
1044 uint32_t(surf
->GetFormat()));
1048 gfx::DataSourceSurface::ScopedMap
map(surf
,
1049 gfx::DataSourceSurface::MapType::READ
);
1050 if (!map
.IsMapped()) {
1051 webgl
->ErrorOutOfMemory("Failed to map source surface for upload.");
1055 const auto& srcBegin
= map
.GetData();
1056 const auto srcStride
= static_cast<size_t>(map
.GetStride());
1060 const auto dstFormat
= FormatForPackingInfo(dstPI
);
1061 const auto dstBpp
= BytesPerPixel(dstPI
);
1062 const size_t dstUsedBytesPerRow
= dstBpp
* surf
->GetSize().width
;
1063 auto dstStride
= dstUsedBytesPerRow
;
1064 if (dstFormat
== srcFormat
) {
1065 dstStride
= srcStride
; // Try to match.
1070 auto dstUnpackingRes
= mDesc
.ExplicitUnpacking(dstPI
, Some(dstStride
));
1071 if (dstUnpackingRes
.isOk()) {
1072 const auto& dstUnpacking
= dstUnpackingRes
.inspect();
1073 if (!webgl
->IsWebGL2() && dstUnpacking
.state
.rowLength
!= size
.x
) {
1074 dstUnpackingRes
= Err("WebGL1 can't handle rowLength != size.x");
1077 if (!dstUnpackingRes
.isOk()) {
1078 dstStride
= dstUsedBytesPerRow
;
1079 dstUnpackingRes
= mDesc
.ExplicitUnpacking(dstPI
, Some(dstStride
));
1081 if (!dstUnpackingRes
.isOk()) {
1082 gfxCriticalError() << dstUnpackingRes
.inspectErr();
1083 webgl
->ErrorImplementationBug("ExplicitUnpacking failed: %s",
1084 dstUnpackingRes
.inspectErr().c_str());
1087 const auto& dstUnpacking
= dstUnpackingRes
.inspect();
1088 MOZ_ASSERT(dstUnpacking
.metrics
.bytesPerRowStride
== dstStride
);
1092 const uint8_t* dstBegin
= srcBegin
;
1093 UniqueBuffer tempBuffer
;
1095 if (!ConvertIfNeeded(webgl
, surf
->GetSize().width
, surf
->GetSize().height
,
1096 srcFormat
, srcBegin
, AutoAssertCast(srcStride
),
1097 dstFormat
, AutoAssertCast(dstUnpacking
.metrics
.bytesPerRowStride
), &dstBegin
,
1105 const auto& gl
= webgl
->gl
;
1106 if (!gl
->MakeCurrent()) {
1107 *out_error
= LOCAL_GL_CONTEXT_LOST
;
1111 dstUnpacking
.state
.ApplyUnpack(*gl
, webgl
->IsWebGL2(), size
);
1114 DoTexOrSubImage(isSubImage
, gl
, mDesc
.imageTarget
, level
, dui
, xOffset
,
1115 yOffset
, zOffset
, size
.x
, size
.y
, size
.z
, dstBegin
);
1117 // Caller will reset all our modified PixelStorei state.
1122 } // namespace webgl
1123 } // namespace mozilla