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/TextureHost.h"
15 #include "mozilla/layers/VideoBridgeParent.h"
16 #include "mozilla/RefPtr.h"
17 #include "nsLayoutUtils.h"
18 #include "WebGLBuffer.h"
19 #include "WebGLContext.h"
20 #include "WebGLFormats.h"
21 #include "WebGLTexelConversions.h"
22 #include "WebGLTexture.h"
26 bool webgl::PixelPackingState::AssertCurrentUnpack(gl::GLContext
& gl
,
27 const bool isWebgl2
) const {
28 auto actual
= PixelPackingState
{};
29 gl
.GetInt(LOCAL_GL_UNPACK_ALIGNMENT
, &actual
.alignmentInTypeElems
);
31 gl
.GetInt(LOCAL_GL_UNPACK_ROW_LENGTH
, &actual
.rowLength
);
32 gl
.GetInt(LOCAL_GL_UNPACK_IMAGE_HEIGHT
, &actual
.imageHeight
);
34 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_PIXELS
, &actual
.skipPixels
);
35 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_ROWS
, &actual
.skipRows
);
36 gl
.GetInt(LOCAL_GL_UNPACK_SKIP_IMAGES
, &actual
.skipImages
);
38 if (*this == actual
) return true;
40 const auto ToStr
= [](const PixelPackingState
& x
) {
41 const auto text
= nsPrintfCString(
42 "%u,%u,%u;%u,%u,%u", x
.alignmentInTypeElems
, x
.rowLength
, x
.imageHeight
,
43 x
.skipPixels
, x
.skipRows
, x
.skipImages
);
44 return mozilla::ToString(text
);
47 const auto was
= ToStr(actual
);
48 const auto expected
= ToStr(*this);
49 gfxCriticalError() << "PixelUnpackStateGl was not current. Was " << was
50 << ". Expected << " << expected
<< ".";
54 void webgl::PixelPackingState::ApplyUnpack(gl::GLContext
& gl
,
56 const uvec3
& uploadSize
) const {
57 gl
.fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
,
58 AssertedCast
<GLsizei
>(alignmentInTypeElems
));
59 if (!isWebgl2
) return;
61 // Re-simplify. (ANGLE seems to have an issue with imageHeight ==
63 auto rowLengthOrZero
= rowLength
;
64 auto imageHeightOrZero
= imageHeight
;
65 if (rowLengthOrZero
== uploadSize
.x
) {
68 if (imageHeightOrZero
== uploadSize
.y
) {
69 imageHeightOrZero
= 0;
72 gl
.fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
,
73 AssertedCast
<GLsizei
>(rowLengthOrZero
));
74 gl
.fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT
,
75 AssertedCast
<GLsizei
>(imageHeightOrZero
));
77 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS
,
78 AssertedCast
<GLsizei
>(skipPixels
));
79 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS
, AssertedCast
<GLsizei
>(skipRows
));
80 gl
.fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
,
81 AssertedCast
<GLsizei
>(skipImages
));
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
,
251 const webgl::PackingInfo
& pi
,
252 const uint32_t availRows
,
253 const webgl::TexUnpackBlob
& blob
) {
254 const auto& unpackingRes
= blob
.mDesc
.ExplicitUnpacking(pi
, {});
255 if (!unpackingRes
.isOk()) {
256 webgl
->ErrorInvalidOperation("%s", unpackingRes
.inspectErr().c_str());
259 const auto& unpacking
= unpackingRes
.inspect();
261 if (availRows
< unpacking
.metrics
.totalRows
) {
262 webgl
->ErrorInvalidOperation(
263 "Desired upload requires more rows (%zu) than is"
265 unpacking
.metrics
.totalRows
, availRows
);
272 static bool ValidateUnpackBytes(const WebGLContext
* const webgl
,
273 const webgl::PackingInfo
& pi
,
274 const size_t availByteCount
,
275 const webgl::TexUnpackBlob
& blob
) {
276 const auto& unpackingRes
= blob
.mDesc
.ExplicitUnpacking(pi
, {});
277 if (!unpackingRes
.isOk()) {
278 webgl
->ErrorInvalidOperation("%s", unpackingRes
.inspectErr().c_str());
281 const auto& unpacking
= unpackingRes
.inspect();
283 if (availByteCount
< unpacking
.metrics
.totalBytesUsed
) {
284 webgl
->ErrorInvalidOperation(
285 "Desired upload requires more bytes (%zu) than are"
287 unpacking
.metrics
.totalBytesUsed
, availByteCount
);
296 // Check if the surface descriptor describes a memory which contains a single
298 static bool SDIsRGBBuffer(const layers::SurfaceDescriptor
& sd
) {
299 return sd
.type() == layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
&&
300 sd
.get_SurfaceDescriptorBuffer().desc().type() ==
301 layers::BufferDescriptor::TRGBDescriptor
;
304 // Check if the surface descriptor describes a GPUVideo texture for which we
305 // only have an opaque source/handle from SurfaceDescriptorRemoteDecoder to
306 // derive the actual texture from.
307 static bool SDIsNullRemoteDecoder(const layers::SurfaceDescriptor
& sd
) {
308 return sd
.type() == layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo
&&
309 sd
.get_SurfaceDescriptorGPUVideo()
310 .get_SurfaceDescriptorRemoteDecoder()
312 .type() == layers::RemoteDecoderVideoSubDescriptor::Tnull_t
;
316 std::unique_ptr
<TexUnpackBlob
> TexUnpackBlob::Create(
317 const TexUnpackBlobDesc
& desc
) {
318 return std::unique_ptr
<TexUnpackBlob
>{[&]() -> TexUnpackBlob
* {
319 if (!IsTarget3D(desc
.imageTarget
) && desc
.size
.z
!= 1) {
324 switch (desc
.unpacking
.alignmentInTypeElems
) {
336 // Shmem buffers need to be treated as if they were a DataSourceSurface.
337 // Otherwise, TexUnpackImage will try to blit the surface descriptor as
338 // if it can be mapped as a framebuffer, whereas the Shmem is still CPU
340 if (SDIsRGBBuffer(*desc
.sd
) || SDIsNullRemoteDecoder(*desc
.sd
)) {
341 return new TexUnpackSurface(desc
);
343 return new TexUnpackImage(desc
);
346 return new TexUnpackSurface(desc
);
349 if (desc
.srcAlphaType
!= gfxAlphaType::NonPremult
) {
353 return new TexUnpackBytes(desc
);
357 static bool HasColorAndAlpha(const WebGLTexelFormat format
) {
359 case WebGLTexelFormat::RA8
:
360 case WebGLTexelFormat::RA16F
:
361 case WebGLTexelFormat::RA32F
:
362 case WebGLTexelFormat::RGBA8
:
363 case WebGLTexelFormat::RGBA5551
:
364 case WebGLTexelFormat::RGBA4444
:
365 case WebGLTexelFormat::RGBA16F
:
366 case WebGLTexelFormat::RGBA32F
:
367 case WebGLTexelFormat::BGRA8
:
374 bool TexUnpackBlob::ConvertIfNeeded(
375 const WebGLContext
* const webgl
, const uint32_t rowLength
,
376 const uint32_t rowCount
, WebGLTexelFormat srcFormat
,
377 const uint8_t* const srcBegin
, const ptrdiff_t srcStride
,
378 WebGLTexelFormat dstFormat
, const ptrdiff_t dstStride
,
379 const uint8_t** const out_begin
,
380 UniqueBuffer
* const out_anchoredBuffer
) const {
381 MOZ_ASSERT(srcFormat
!= WebGLTexelFormat::FormatNotSupportingAnyConversion
);
382 MOZ_ASSERT(dstFormat
!= WebGLTexelFormat::FormatNotSupportingAnyConversion
);
384 *out_begin
= srcBegin
;
386 const auto& unpacking
= mDesc
.unpacking
;
388 if (!rowLength
|| !rowCount
) return true;
390 const auto srcIsPremult
= (mDesc
.srcAlphaType
== gfxAlphaType::Premult
);
391 auto dstIsPremult
= unpacking
.premultiplyAlpha
;
392 const auto fnHasPremultMismatch
= [&]() {
393 if (mDesc
.srcAlphaType
== gfxAlphaType::Opaque
) return false;
395 if (!HasColorAndAlpha(srcFormat
)) return false;
397 return srcIsPremult
!= dstIsPremult
;
400 const auto srcOrigin
=
401 (unpacking
.flipY
? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft
);
402 auto dstOrigin
= gl::OriginPos::BottomLeft
;
404 if (!mDesc
.applyUnpackTransforms
) {
405 dstIsPremult
= srcIsPremult
;
406 dstOrigin
= srcOrigin
;
409 if (srcFormat
!= dstFormat
) {
410 webgl
->GeneratePerfWarning(
411 "Conversion requires pixel reformatting. (%u->%u)", uint32_t(srcFormat
),
412 uint32_t(dstFormat
));
413 } else if (fnHasPremultMismatch()) {
414 webgl
->GeneratePerfWarning(
415 "Conversion requires change in"
416 " alpha-premultiplication.");
417 } else if (srcOrigin
!= dstOrigin
) {
418 webgl
->GeneratePerfWarning("Conversion requires y-flip.");
419 } else if (srcStride
!= dstStride
) {
420 webgl
->GeneratePerfWarning("Conversion requires change in stride. (%u->%u)",
421 uint32_t(srcStride
), uint32_t(dstStride
));
428 const auto dstTotalBytes
= CheckedUint32(rowCount
) * dstStride
;
429 if (!dstTotalBytes
.isValid()) {
430 webgl
->ErrorOutOfMemory("Calculation failed.");
434 auto dstBuffer
= UniqueBuffer::Take(calloc(1u, dstTotalBytes
.value()));
435 if (!dstBuffer
.get()) {
436 webgl
->ErrorOutOfMemory("Failed to allocate dest buffer.");
439 const auto dstBegin
= static_cast<uint8_t*>(dstBuffer
.get());
445 if (!ConvertImage(rowLength
, rowCount
, srcBegin
, srcStride
, srcOrigin
,
446 srcFormat
, srcIsPremult
, dstBegin
, dstStride
, dstOrigin
,
447 dstFormat
, dstIsPremult
, &wasTrivial
)) {
448 webgl
->ErrorImplementationBug("ConvertImage failed.");
452 *out_begin
= dstBegin
;
453 *out_anchoredBuffer
= std::move(dstBuffer
);
457 static GLenum
DoTexOrSubImage(bool isSubImage
, gl::GLContext
* gl
,
458 TexImageTarget target
, GLint level
,
459 const DriverUnpackInfo
* dui
, GLint xOffset
,
460 GLint yOffset
, GLint zOffset
, GLsizei width
,
461 GLsizei height
, GLsizei depth
, const void* data
) {
463 return DoTexSubImage(gl
, target
, level
, xOffset
, yOffset
, zOffset
, width
,
464 height
, depth
, dui
->ToPacking(), data
);
466 return DoTexImage(gl
, target
, level
, dui
, width
, height
, depth
, data
);
470 //////////////////////////////////////////////////////////////////////////////////////////
473 bool TexUnpackBytes::Validate(const WebGLContext
* const webgl
,
474 const webgl::PackingInfo
& pi
) {
475 if (!HasData()) return true;
477 CheckedInt
<size_t> availBytes
= 0;
479 availBytes
= mDesc
.cpuData
->size();
480 } else if (mDesc
.pboOffset
) {
481 const auto& pboOffset
= *mDesc
.pboOffset
;
484 webgl
->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
485 if (!pbo
) return false; // Might be invalid e.g. due to in-use by TF.
486 availBytes
= pbo
->ByteLength();
487 availBytes
-= pboOffset
;
489 MOZ_ASSERT(false, "Must be one of the above");
491 if (!availBytes
.isValid()) {
492 webgl
->ErrorInvalidOperation("Offset is passed end of buffer.");
496 return ValidateUnpackBytes(webgl
, pi
, availBytes
.value(), *this);
499 bool TexUnpackBytes::TexOrSubImage(bool isSubImage
, bool needsRespec
,
500 WebGLTexture
* tex
, GLint level
,
501 const webgl::DriverUnpackInfo
* dui
,
502 GLint xOffset
, GLint yOffset
, GLint zOffset
,
503 const webgl::PackingInfo
& pi
,
504 GLenum
* const out_error
) const {
505 const auto& webgl
= tex
->mContext
;
506 const auto& target
= mDesc
.imageTarget
;
507 const auto& size
= mDesc
.size
;
508 const auto& webglUnpackState
= mDesc
.unpacking
;
510 const auto unpackingRes
= mDesc
.ExplicitUnpacking(pi
, {});
512 const auto format
= FormatForPackingInfo(pi
);
514 const uint8_t* uploadPtr
= nullptr;
516 uploadPtr
= mDesc
.cpuData
->data();
517 } else if (mDesc
.pboOffset
) {
518 uploadPtr
= reinterpret_cast<const uint8_t*>(*mDesc
.pboOffset
);
521 UniqueBuffer tempBuffer
;
524 if (mDesc
.pboOffset
|| !uploadPtr
) break;
526 if (!webglUnpackState
.flipY
&& !webglUnpackState
.premultiplyAlpha
) {
530 webgl
->GenerateWarning(
531 "Alpha-premult and y-flip are deprecated for"
532 " non-DOM-Element uploads.");
534 MOZ_RELEASE_ASSERT(unpackingRes
.isOk());
535 const auto& unpacking
= unpackingRes
.inspect();
536 const auto stride
= unpacking
.metrics
.bytesPerRowStride
;
538 if (!ConvertIfNeeded(webgl
, unpacking
.state
.rowLength
,
539 unpacking
.metrics
.totalRows
,
540 format
, uploadPtr
, AutoAssertCast(stride
),
541 format
, AutoAssertCast(stride
), &uploadPtr
, &tempBuffer
)) {
549 const auto& gl
= webgl
->gl
;
551 bool useParanoidHandling
= false;
552 if (mNeedsExactUpload
&& webgl
->mBoundPixelUnpackBuffer
) {
553 webgl
->GenerateWarning(
554 "Uploads from a buffer with a final row with a byte"
555 " count smaller than the row stride can incur extra"
558 if (gl
->WorkAroundDriverBugs()) {
559 useParanoidHandling
|= (gl
->Vendor() == gl::GLVendor::NVIDIA
);
563 if (!useParanoidHandling
) {
564 const ScopedLazyBind
bindPBO(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
565 webgl
->mBoundPixelUnpackBuffer
);
568 DoTexOrSubImage(isSubImage
, gl
, target
, level
, dui
, xOffset
, yOffset
,
569 zOffset
, size
.x
, size
.y
, size
.z
, uploadPtr
);
575 MOZ_ASSERT(webgl
->mBoundPixelUnpackBuffer
);
578 // Alloc first to catch OOMs.
579 AssertUintParamCorrect(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING
, 0);
581 DoTexOrSubImage(false, gl
, target
, level
, dui
, xOffset
, yOffset
,
582 zOffset
, size
.x
, size
.y
, size
.z
, nullptr);
583 if (*out_error
) return true;
585 if (!size
.x
|| !size
.y
|| !size
.z
) {
590 MOZ_RELEASE_ASSERT(unpackingRes
.isOk());
591 const auto& unpacking
= unpackingRes
.inspect();
593 const ScopedLazyBind
bindPBO(gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
594 webgl
->mBoundPixelUnpackBuffer
);
598 // Make our sometimes-implicit values explicit. Also this keeps them constant
599 // when we ask for height=mHeight-1 and such.
600 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
,
601 AutoAssertCast(unpacking
.state
.rowLength
));
602 gl
->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT
,
603 AutoAssertCast(unpacking
.state
.imageHeight
));
607 DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
, yOffset
, zOffset
,
608 size
.x
, size
.y
, size
.z
- 1, uploadPtr
);
611 // Skip the images we uploaded.
612 const auto skipImages
= ZeroOn2D(target
, unpacking
.state
.skipImages
);
613 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, skipImages
+ size
.z
- 1);
617 DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
, yOffset
,
618 zOffset
+ size
.z
- 1, size
.x
, size
.y
- 1, 1, uploadPtr
);
623 const auto lastRowOffset
=
624 unpacking
.metrics
.totalBytesStrided
- unpacking
.metrics
.bytesPerRowStride
;
625 const auto lastRowPtr
= uploadPtr
+ lastRowOffset
;
627 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 1); // No stride padding.
628 gl
->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH
, 0); // No padding in general.
629 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES
, 0); // Don't skip images,
630 gl
->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS
,
632 // Keep skipping pixels though!
633 *out_error
= DoTexOrSubImage(true, gl
, target
, level
, dui
, xOffset
,
634 yOffset
+ size
.y
- 1, zOffset
+ size
.z
- 1,
635 AutoAssertCast(size
.x
), 1, 1, lastRowPtr
);
637 // Caller will reset all our modified PixelStorei state.
642 ////////////////////////////////////////////////////////////////////////////////
643 ////////////////////////////////////////////////////////////////////////////////
646 TexUnpackImage::~TexUnpackImage() = default;
648 bool TexUnpackImage::Validate(const WebGLContext
* const webgl
,
649 const webgl::PackingInfo
& pi
) {
650 if (!ValidatePIForDOM(webgl
, pi
)) return false;
652 if (!mDesc
.structuredSrcSize
) {
653 gfxCriticalError() << "TexUnpackImage missing structuredSrcSize.";
656 const auto& elemSize
= *mDesc
.structuredSrcSize
;
657 if (mDesc
.dataSurf
) {
658 const auto& surfSize
= mDesc
.dataSurf
->GetSize();
659 const auto surfSize2
= ivec2::FromSize(surfSize
)->StaticCast
<uvec2
>();
660 if (uvec2
{elemSize
.x
, elemSize
.y
} != surfSize2
) {
662 << "TexUnpackImage mismatched structuredSrcSize for dataSurf.";
667 const auto fullRows
= elemSize
.y
;
668 return ValidateUnpackPixels(webgl
, pi
, fullRows
, *this);
671 Maybe
<std::string
> BlitPreventReason(const int32_t level
, const ivec3
& offset
,
672 const GLenum internalFormat
,
673 const webgl::PackingInfo
& pi
,
674 const TexUnpackBlobDesc
& desc
,
675 const bool isRgb8Renderable
) {
676 const auto& size
= desc
.size
;
677 const auto& unpacking
= desc
.unpacking
;
679 const auto ret
= [&]() -> const char* {
681 return "depth is not 1";
683 if (offset
.x
!= 0 || offset
.y
!= 0 || offset
.z
!= 0) {
684 return "x/y/zOffset is not 0";
687 if (unpacking
.skipPixels
|| unpacking
.skipRows
|| unpacking
.skipImages
) {
688 return "non-zero UNPACK_SKIP_* not yet supported";
691 const auto premultReason
= [&]() -> const char* {
692 if (desc
.srcAlphaType
== gfxAlphaType::Opaque
) return nullptr;
694 const bool srcIsPremult
= (desc
.srcAlphaType
== gfxAlphaType::Premult
);
695 const auto& dstIsPremult
= unpacking
.premultiplyAlpha
;
696 if (srcIsPremult
== dstIsPremult
) return nullptr;
699 return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
701 return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
704 if (premultReason
) return premultReason
;
706 const auto formatReason
= [&]() -> const char* {
707 if (pi
.type
!= LOCAL_GL_UNSIGNED_BYTE
) {
708 return "`type` must be `UNSIGNED_BYTE`";
711 switch (internalFormat
) {
718 if (isRgb8Renderable
) {
723 if (isRgb8Renderable
) {
724 return "effective format must be RGB8 or RGBA8";
726 return "effective format must be RGBA8";
729 if (formatReason
) return formatReason
;
734 return Some(std::string(ret
));
739 bool TexUnpackImage::TexOrSubImage(bool isSubImage
, bool needsRespec
,
740 WebGLTexture
* tex
, GLint level
,
741 const webgl::DriverUnpackInfo
* dui
,
742 GLint xOffset
, GLint yOffset
, GLint zOffset
,
743 const webgl::PackingInfo
& pi
,
744 GLenum
* const out_error
) const {
745 MOZ_ASSERT_IF(needsRespec
, !isSubImage
);
747 const auto& webgl
= tex
->mContext
;
748 const auto& target
= mDesc
.imageTarget
;
749 const auto& size
= mDesc
.size
;
750 const auto& sd
= *(mDesc
.sd
);
751 const auto& unpacking
= mDesc
.unpacking
;
753 const auto& gl
= webgl
->GL();
758 BlitPreventReason(level
, {xOffset
, yOffset
, zOffset
}, dui
->internalFormat
,
759 pi
, mDesc
, webgl
->mIsRgb8Renderable
);
761 webgl
->GeneratePerfWarning(
762 "Failed to hit GPU-copy fast-path."
763 " (%s) Falling back to CPU upload.",
772 DoTexOrSubImage(isSubImage
, gl
, target
, level
, dui
, xOffset
, yOffset
,
773 zOffset
, size
.x
, size
.y
, size
.z
, nullptr);
774 if (*out_error
) return true;
778 gl::ScopedFramebuffer
scopedFB(gl
);
779 gl::ScopedBindFramebuffer
bindFB(gl
, scopedFB
.FB());
782 gl::GLContext::LocalErrorScope
errorScope(*gl
);
784 gl
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
,
785 LOCAL_GL_COLOR_ATTACHMENT0
, target
,
786 tex
->mGLName
, level
);
788 const auto err
= errorScope
.GetError();
789 MOZ_ALWAYS_TRUE(!err
);
792 const GLenum status
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
793 MOZ_ALWAYS_TRUE(status
== LOCAL_GL_FRAMEBUFFER_COMPLETE
);
795 const auto dstOrigin
=
796 (unpacking
.flipY
? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft
);
797 if (!gl
->BlitHelper()->BlitSdToFramebuffer(sd
, {size
.x
, size
.y
},
799 gfxCriticalNote
<< "BlitSdToFramebuffer failed for type "
801 // Maybe the resource isn't valid anymore?
802 gl
->fClearColor(0.2, 0.0, 0.2, 1.0);
803 gl
->fClear(LOCAL_GL_COLOR_BUFFER_BIT
);
804 const auto& cur
= webgl
->mColorClearValue
;
805 gl
->fClearColor(cur
[0], cur
[1], cur
[2], cur
[3]);
806 webgl
->GenerateWarning(
807 "Fast Tex(Sub)Image upload failed without recourse, clearing to "
808 "[0.2, 0.0, 0.2, 1.0]. Please file a bug!");
815 ////////////////////////////////////////////////////////////////////////////////
816 ////////////////////////////////////////////////////////////////////////////////
819 TexUnpackSurface::~TexUnpackSurface() = default;
823 static bool GetFormatForSurf(const gfx::SourceSurface
* surf
,
824 WebGLTexelFormat
* const out_texelFormat
,
825 uint8_t* const out_bpp
) {
826 const auto surfFormat
= surf
->GetFormat();
827 switch (surfFormat
) {
828 case gfx::SurfaceFormat::B8G8R8A8
:
829 *out_texelFormat
= WebGLTexelFormat::BGRA8
;
833 case gfx::SurfaceFormat::B8G8R8X8
:
834 *out_texelFormat
= WebGLTexelFormat::BGRX8
;
838 case gfx::SurfaceFormat::R8G8B8A8
:
839 *out_texelFormat
= WebGLTexelFormat::RGBA8
;
843 case gfx::SurfaceFormat::R8G8B8X8
:
844 *out_texelFormat
= WebGLTexelFormat::RGBX8
;
848 case gfx::SurfaceFormat::R5G6B5_UINT16
:
849 *out_texelFormat
= WebGLTexelFormat::RGB565
;
853 case gfx::SurfaceFormat::A8
:
854 *out_texelFormat
= WebGLTexelFormat::A8
;
858 case gfx::SurfaceFormat::YUV
:
860 NS_ERROR("We don't handle uploads from YUV sources yet.");
861 // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically
862 // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB)
872 bool TexUnpackSurface::Validate(const WebGLContext
* const webgl
,
873 const webgl::PackingInfo
& pi
) {
874 if (!ValidatePIForDOM(webgl
, pi
)) return false;
876 if (!mDesc
.structuredSrcSize
) {
877 gfxCriticalError() << "TexUnpackSurface missing structuredSrcSize.";
880 const auto& elemSize
= *mDesc
.structuredSrcSize
;
881 if (mDesc
.dataSurf
) {
882 const auto& surfSize
= mDesc
.dataSurf
->GetSize();
883 const auto surfSize2
= ivec2::FromSize(surfSize
)->StaticCast
<uvec2
>();
884 if (uvec2
{elemSize
.x
, elemSize
.y
} != surfSize2
) {
886 << "TexUnpackSurface mismatched structuredSrcSize for dataSurf.";
891 const auto fullRows
= elemSize
.y
;
892 return ValidateUnpackPixels(webgl
, pi
, fullRows
, *this);
895 bool TexUnpackSurface::TexOrSubImage(bool isSubImage
, bool needsRespec
,
896 WebGLTexture
* tex
, GLint level
,
897 const webgl::DriverUnpackInfo
* dui
,
898 GLint xOffset
, GLint yOffset
,
900 const webgl::PackingInfo
& dstPI
,
901 GLenum
* const out_error
) const {
902 const auto& webgl
= tex
->mContext
;
903 const auto& size
= mDesc
.size
;
904 RefPtr
<gfx::DataSourceSurface
> surf
;
906 // If we get here, we assume the SD describes an RGBA Shmem.
907 const auto& sd
= *(mDesc
.sd
);
908 if (SDIsRGBBuffer(sd
)) {
909 const auto& sdb
= sd
.get_SurfaceDescriptorBuffer();
910 const auto& rgb
= sdb
.desc().get_RGBDescriptor();
911 const auto& data
= sdb
.data();
912 MOZ_ASSERT(data
.type() == layers::MemoryOrShmem::TShmem
);
913 const auto& shmem
= data
.get_Shmem();
914 surf
= gfx::Factory::CreateWrappingDataSourceSurface(
915 shmem
.get
<uint8_t>(), layers::ImageDataSerializer::GetRGBStride(rgb
),
916 rgb
.size(), rgb
.format());
917 } else if (SDIsNullRemoteDecoder(sd
)) {
918 const auto& sdrd
= sd
.get_SurfaceDescriptorGPUVideo()
919 .get_SurfaceDescriptorRemoteDecoder();
920 RefPtr
<layers::VideoBridgeParent
> parent
=
921 layers::VideoBridgeParent::GetSingleton(sdrd
.source());
923 gfxCriticalNote
<< "TexUnpackSurface failed to get VideoBridgeParent";
926 RefPtr
<layers::TextureHost
> texture
=
927 parent
->LookupTexture(webgl
->GetContentId(), sdrd
.handle());
929 gfxCriticalNote
<< "TexUnpackSurface failed to get TextureHost";
932 surf
= texture
->GetAsSurface();
934 MOZ_ASSERT_UNREACHABLE("Unexpected surface descriptor!");
937 gfxCriticalError() << "TexUnpackSurface failed to create wrapping "
938 "DataSourceSurface for Shmem.";
942 surf
= mDesc
.dataSurf
;
947 WebGLTexelFormat srcFormat
;
949 if (!GetFormatForSurf(surf
, &srcFormat
, &srcBPP
)) {
950 webgl
->ErrorImplementationBug(
951 "GetFormatForSurf failed for"
952 " WebGLTexelFormat::%u.",
953 uint32_t(surf
->GetFormat()));
957 gfx::DataSourceSurface::ScopedMap
map(surf
,
958 gfx::DataSourceSurface::MapType::READ
);
959 if (!map
.IsMapped()) {
960 webgl
->ErrorOutOfMemory("Failed to map source surface for upload.");
964 const auto& srcBegin
= map
.GetData();
965 const auto srcStride
= static_cast<size_t>(map
.GetStride());
969 const auto dstFormat
= FormatForPackingInfo(dstPI
);
970 const auto dstBpp
= BytesPerPixel(dstPI
);
971 const size_t dstUsedBytesPerRow
= dstBpp
* surf
->GetSize().width
;
972 auto dstStride
= dstUsedBytesPerRow
;
973 if (dstFormat
== srcFormat
) {
974 dstStride
= srcStride
; // Try to match.
979 auto dstUnpackingRes
= mDesc
.ExplicitUnpacking(dstPI
, Some(dstStride
));
980 if (dstUnpackingRes
.isOk()) {
981 const auto& dstUnpacking
= dstUnpackingRes
.inspect();
982 if (!webgl
->IsWebGL2() && dstUnpacking
.state
.rowLength
!= size
.x
) {
983 dstUnpackingRes
= Err("WebGL1 can't handle rowLength != size.x");
986 if (!dstUnpackingRes
.isOk()) {
987 dstStride
= dstUsedBytesPerRow
;
988 dstUnpackingRes
= mDesc
.ExplicitUnpacking(dstPI
, Some(dstStride
));
990 if (!dstUnpackingRes
.isOk()) {
991 gfxCriticalError() << dstUnpackingRes
.inspectErr();
992 webgl
->ErrorImplementationBug("ExplicitUnpacking failed: %s",
993 dstUnpackingRes
.inspectErr().c_str());
996 const auto& dstUnpacking
= dstUnpackingRes
.inspect();
997 MOZ_ASSERT(dstUnpacking
.metrics
.bytesPerRowStride
== dstStride
);
1001 const uint8_t* dstBegin
= srcBegin
;
1002 UniqueBuffer tempBuffer
;
1004 if (!ConvertIfNeeded(webgl
, surf
->GetSize().width
, surf
->GetSize().height
,
1005 srcFormat
, srcBegin
, AutoAssertCast(srcStride
),
1006 dstFormat
, AutoAssertCast(dstUnpacking
.metrics
.bytesPerRowStride
), &dstBegin
,
1014 const auto& gl
= webgl
->gl
;
1015 if (!gl
->MakeCurrent()) {
1016 *out_error
= LOCAL_GL_CONTEXT_LOST
;
1020 dstUnpacking
.state
.ApplyUnpack(*gl
, webgl
->IsWebGL2(), size
);
1023 DoTexOrSubImage(isSubImage
, gl
, mDesc
.imageTarget
, level
, dui
, xOffset
,
1024 yOffset
, zOffset
, size
.x
, size
.y
, size
.z
, dstBegin
);
1026 // Caller will reset all our modified PixelStorei state.
1031 } // namespace webgl
1032 } // namespace mozilla