2018-11-30 22:52:05 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2015-08-04 00:34:46 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2023-09-21 01:40:24 +03:00
|
|
|
#include "WebGLTextureUpload.h"
|
2015-08-04 00:34:46 +03:00
|
|
|
#include "WebGLTexture.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
2020-01-09 01:19:14 +03:00
|
|
|
#include <limits>
|
2016-05-09 01:33:00 +03:00
|
|
|
|
2015-08-04 00:34:46 +03:00
|
|
|
#include "CanvasUtils.h"
|
2020-07-22 01:57:01 +03:00
|
|
|
#include "ClientWebGLContext.h"
|
2015-08-04 00:34:46 +03:00
|
|
|
#include "GLBlitHelper.h"
|
|
|
|
#include "GLContext.h"
|
2019-04-02 20:00:41 +03:00
|
|
|
#include "mozilla/Casting.h"
|
2016-05-09 01:33:00 +03:00
|
|
|
#include "mozilla/gfx/2D.h"
|
2020-11-23 19:21:38 +03:00
|
|
|
#include "mozilla/gfx/Logging.h"
|
2018-11-07 01:31:34 +03:00
|
|
|
#include "mozilla/dom/HTMLCanvasElement.h"
|
2015-08-04 00:34:46 +03:00
|
|
|
#include "mozilla/dom/HTMLVideoElement.h"
|
2016-12-21 01:59:51 +03:00
|
|
|
#include "mozilla/dom/ImageBitmap.h"
|
2015-08-04 00:34:46 +03:00
|
|
|
#include "mozilla/dom/ImageData.h"
|
2022-01-27 18:38:02 +03:00
|
|
|
#include "mozilla/dom/OffscreenCanvas.h"
|
2015-08-04 00:34:46 +03:00
|
|
|
#include "mozilla/MathAlgorithms.h"
|
2020-12-07 17:25:59 +03:00
|
|
|
#include "mozilla/ScopeExit.h"
|
2019-07-26 04:10:23 +03:00
|
|
|
#include "mozilla/StaticPrefs_webgl.h"
|
2016-08-23 07:09:32 +03:00
|
|
|
#include "mozilla/Unused.h"
|
2020-10-01 00:06:47 +03:00
|
|
|
#include "nsLayoutUtils.h"
|
2015-08-04 00:34:46 +03:00
|
|
|
#include "ScopedGLHelpers.h"
|
2015-11-25 07:15:29 +03:00
|
|
|
#include "TexUnpackBlob.h"
|
2016-09-10 07:02:54 +03:00
|
|
|
#include "WebGLBuffer.h"
|
2015-08-04 00:34:46 +03:00
|
|
|
#include "WebGLContext.h"
|
|
|
|
#include "WebGLContextUtils.h"
|
|
|
|
#include "WebGLFramebuffer.h"
|
|
|
|
#include "WebGLTexelConversions.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
2020-07-22 01:57:01 +03:00
|
|
|
namespace webgl {
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2023-09-21 01:40:24 +03:00
|
|
|
// The canvas spec says that drawImage should draw the first frame of
|
|
|
|
// animated images. The webgl spec doesn't mention the issue, so we do the
|
|
|
|
// same as drawImage.
|
|
|
|
static constexpr uint32_t kDefaultSurfaceFromElementFlags =
|
|
|
|
nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
|
|
|
|
nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR |
|
|
|
|
nsLayoutUtils::SFE_EXACT_SIZE_SURFACE |
|
|
|
|
nsLayoutUtils::SFE_ALLOW_NON_PREMULT;
|
|
|
|
|
2022-02-12 05:20:42 +03:00
|
|
|
Maybe<TexUnpackBlobDesc> FromImageBitmap(const GLenum target, Maybe<uvec3> size,
|
2020-07-22 01:57:01 +03:00
|
|
|
const dom::ImageBitmap& imageBitmap,
|
|
|
|
ErrorResult* const out_rv) {
|
2018-11-07 01:31:34 +03:00
|
|
|
if (imageBitmap.IsWriteOnly()) {
|
2020-07-22 01:57:01 +03:00
|
|
|
out_rv->Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
|
|
return {};
|
2018-11-07 01:31:34 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-07-22 01:57:01 +03:00
|
|
|
const auto cloneData = imageBitmap.ToCloneData();
|
2018-03-14 19:37:34 +03:00
|
|
|
if (!cloneData) {
|
2020-07-22 01:57:01 +03:00
|
|
|
return {};
|
2018-03-14 19:37:34 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-12-21 01:59:51 +03:00
|
|
|
const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface;
|
2024-02-28 21:08:57 +03:00
|
|
|
if (NS_WARN_IF(!surf)) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-01-14 04:23:06 +03:00
|
|
|
const auto imageSize = *uvec2::FromSize(surf->GetSize());
|
2022-02-12 05:20:42 +03:00
|
|
|
if (!size) {
|
|
|
|
size.emplace(imageSize.x, imageSize.y, 1);
|
2016-12-21 01:59:51 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-12-21 01:59:51 +03:00
|
|
|
// WhatWG "HTML Living Standard" (30 October 2015):
|
|
|
|
// "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
|
|
|
|
// non-premultiplied alpha values."
|
2021-01-14 04:23:06 +03:00
|
|
|
return Some(TexUnpackBlobDesc{target,
|
2022-02-12 05:20:42 +03:00
|
|
|
size.value(),
|
2021-01-14 04:23:06 +03:00
|
|
|
cloneData->mAlphaType,
|
|
|
|
{},
|
|
|
|
{},
|
2022-02-12 05:20:42 +03:00
|
|
|
Some(imageSize),
|
2021-01-14 04:23:06 +03:00
|
|
|
nullptr,
|
|
|
|
{},
|
2021-09-02 03:29:28 +03:00
|
|
|
surf,
|
|
|
|
{},
|
|
|
|
false});
|
2016-12-21 01:59:51 +03:00
|
|
|
}
|
|
|
|
|
2021-01-14 04:23:06 +03:00
|
|
|
static layers::SurfaceDescriptor Flatten(const layers::SurfaceDescriptor& sd) {
|
|
|
|
const auto sdType = sd.type();
|
|
|
|
if (sdType != layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo) {
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
const auto& sdv = sd.get_SurfaceDescriptorGPUVideo();
|
|
|
|
const auto& sdvType = sdv.type();
|
|
|
|
if (sdvType !=
|
|
|
|
layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder) {
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto& sdrd = sdv.get_SurfaceDescriptorRemoteDecoder();
|
|
|
|
const auto& subdesc = sdrd.subdesc();
|
|
|
|
const auto& subdescType = subdesc.type();
|
|
|
|
switch (subdescType) {
|
|
|
|
case layers::RemoteDecoderVideoSubDescriptor::T__None:
|
|
|
|
case layers::RemoteDecoderVideoSubDescriptor::Tnull_t:
|
|
|
|
return sd;
|
|
|
|
|
|
|
|
case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10:
|
|
|
|
return subdesc.get_SurfaceDescriptorD3D10();
|
|
|
|
case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr:
|
|
|
|
return subdesc.get_SurfaceDescriptorDXGIYCbCr();
|
|
|
|
case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf:
|
|
|
|
return subdesc.get_SurfaceDescriptorDMABuf();
|
|
|
|
case layers::RemoteDecoderVideoSubDescriptor::
|
|
|
|
TSurfaceDescriptorMacIOSurface:
|
|
|
|
return subdesc.get_SurfaceDescriptorMacIOSurface();
|
Bug 1771011 - part2 : wrap media engine's dcomp handle, and use it on our gfx pipeline in the GPU process.r=jolin,sotaro
In this patch, we ask the media engine to return a handle for shareable
dcomp surface, which will then be packaged into a new texture data type
and being shared with the GPU process via the video bridge.
DcompSurfaceImage is the image which contains the handle texture data,
which doesn't support being accessed in the content process. When the
compositor uploads the image to the GPU process, the corresponding
texture host will be created.
The render texture host will be created by that texture host, and it
will be used in DCLayerTree. In DCLayerTree, we create a new type of
surface for our dcomp handle. DCSurfaceHandle will ask the render
texture host to reconstruct the surface by the handle shared from the
remote process (the handle is actually duplicated to the parent process
first due to the sandbox policy, and then be duplicated to the GPU
process later)
DCSurfaceHandle will attach that surface to its visual in order to
display the video frame directly. In the whole process, it's not
possible for Gecko to access any decoded video data which is protected by the
media engine itself.
Depends on D149941
Differential Revision: https://phabricator.services.mozilla.com/D151019
2022-08-14 02:48:07 +03:00
|
|
|
case layers::RemoteDecoderVideoSubDescriptor::
|
|
|
|
TSurfaceDescriptorDcompSurface:
|
|
|
|
return subdesc.get_SurfaceDescriptorDcompSurface();
|
2021-01-14 04:23:06 +03:00
|
|
|
}
|
|
|
|
MOZ_CRASH("unreachable");
|
2016-06-22 20:28:11 +03:00
|
|
|
}
|
|
|
|
|
2022-01-27 18:38:02 +03:00
|
|
|
Maybe<webgl::TexUnpackBlobDesc> FromOffscreenCanvas(
|
2022-02-12 05:20:42 +03:00
|
|
|
const ClientWebGLContext& webgl, const GLenum target, Maybe<uvec3> size,
|
2022-01-27 18:38:02 +03:00
|
|
|
const dom::OffscreenCanvas& canvas, ErrorResult* const out_error) {
|
|
|
|
if (canvas.IsWriteOnly()) {
|
|
|
|
webgl.EnqueueWarning(
|
|
|
|
"OffscreenCanvas is write-only, thus cannot be uploaded.");
|
|
|
|
out_error->ThrowSecurityError(
|
|
|
|
"OffscreenCanvas is write-only, thus cannot be uploaded.");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto sfer = nsLayoutUtils::SurfaceFromOffscreenCanvas(
|
2023-09-21 01:40:24 +03:00
|
|
|
const_cast<dom::OffscreenCanvas*>(&canvas),
|
|
|
|
kDefaultSurfaceFromElementFlags);
|
|
|
|
return FromSurfaceFromElementResult(webgl, target, size, sfer, out_error);
|
|
|
|
}
|
2022-01-27 18:38:02 +03:00
|
|
|
|
2023-09-21 01:40:24 +03:00
|
|
|
Maybe<webgl::TexUnpackBlobDesc> FromVideoFrame(
|
|
|
|
const ClientWebGLContext& webgl, const GLenum target, Maybe<uvec3> size,
|
|
|
|
const dom::VideoFrame& videoFrame, ErrorResult* const out_error) {
|
|
|
|
auto sfer = nsLayoutUtils::SurfaceFromVideoFrame(
|
|
|
|
const_cast<dom::VideoFrame*>(&videoFrame),
|
|
|
|
kDefaultSurfaceFromElementFlags);
|
|
|
|
return FromSurfaceFromElementResult(webgl, target, size, sfer, out_error);
|
2022-01-27 18:38:02 +03:00
|
|
|
}
|
|
|
|
|
2020-07-22 01:57:01 +03:00
|
|
|
Maybe<webgl::TexUnpackBlobDesc> FromDomElem(const ClientWebGLContext& webgl,
|
2022-02-12 05:20:42 +03:00
|
|
|
const GLenum target,
|
|
|
|
Maybe<uvec3> size,
|
2020-07-22 01:57:01 +03:00
|
|
|
const dom::Element& elem,
|
|
|
|
ErrorResult* const out_error) {
|
2018-11-07 01:31:34 +03:00
|
|
|
if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
|
2020-07-22 01:57:01 +03:00
|
|
|
const dom::HTMLCanvasElement* srcCanvas =
|
2018-11-07 01:31:34 +03:00
|
|
|
static_cast<const dom::HTMLCanvasElement*>(&elem);
|
2020-07-22 01:57:01 +03:00
|
|
|
if (srcCanvas->IsWriteOnly()) {
|
2018-11-07 01:31:34 +03:00
|
|
|
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
|
2020-07-22 01:57:01 +03:00
|
|
|
return {};
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-21 01:40:24 +03:00
|
|
|
uint32_t flags = kDefaultSurfaceFromElementFlags;
|
2020-07-22 01:57:01 +03:00
|
|
|
const auto& unpacking = webgl.State().mPixelUnpackState;
|
2022-02-12 05:20:42 +03:00
|
|
|
if (unpacking.colorspaceConversion == LOCAL_GL_NONE) {
|
2016-07-12 08:51:19 +03:00
|
|
|
flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
|
2020-07-22 01:57:01 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
RefPtr<gfx::DrawTarget> idealDrawTarget = nullptr; // Don't care for now.
|
2016-08-10 03:16:09 +03:00
|
|
|
auto sfer = nsLayoutUtils::SurfaceFromElement(
|
|
|
|
const_cast<dom::Element*>(&elem), flags, idealDrawTarget);
|
2023-09-21 01:40:24 +03:00
|
|
|
return FromSurfaceFromElementResult(webgl, target, size, sfer, out_error);
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2023-09-21 01:40:24 +03:00
|
|
|
Maybe<webgl::TexUnpackBlobDesc> FromSurfaceFromElementResult(
|
|
|
|
const ClientWebGLContext& webgl, const GLenum target, Maybe<uvec3> size,
|
|
|
|
SurfaceFromElementResult& sfer, ErrorResult* const out_error) {
|
2021-01-14 04:23:06 +03:00
|
|
|
uvec2 elemSize;
|
2020-07-22 01:57:01 +03:00
|
|
|
|
2021-01-14 04:23:06 +03:00
|
|
|
const auto& layersImage = sfer.mLayersImage;
|
|
|
|
Maybe<layers::SurfaceDescriptor> sd;
|
|
|
|
if (layersImage) {
|
|
|
|
elemSize = *uvec2::FromSize(layersImage->GetSize());
|
|
|
|
|
|
|
|
sd = layersImage->GetDesc();
|
|
|
|
if (sd) {
|
|
|
|
sd = Some(Flatten(*sd));
|
|
|
|
}
|
2021-01-15 22:18:38 +03:00
|
|
|
if (!sd) {
|
|
|
|
NS_WARNING("No SurfaceDescriptor for layers::Image!");
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
RefPtr<gfx::DataSourceSurface> dataSurf;
|
2021-01-14 04:23:06 +03:00
|
|
|
if (!sd && sfer.GetSourceSurface()) {
|
2016-07-12 08:51:19 +03:00
|
|
|
const auto surf = sfer.GetSourceSurface();
|
2021-01-14 04:23:06 +03:00
|
|
|
elemSize = *uvec2::FromSize(surf->GetSize());
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
// WARNING: OSX can lose our MakeCurrent here.
|
|
|
|
dataSurf = surf->GetDataSurface();
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//////
|
|
|
|
|
2022-02-12 05:20:42 +03:00
|
|
|
if (!size) {
|
|
|
|
size.emplace(elemSize.x, elemSize.y, 1);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
////
|
|
|
|
|
2021-01-15 22:18:38 +03:00
|
|
|
if (!sd && !dataSurf) {
|
2021-01-14 04:23:06 +03:00
|
|
|
webgl.EnqueueWarning("Resource has no data (yet?). Uploading zeros.");
|
2022-02-12 05:20:42 +03:00
|
|
|
if (!size) {
|
|
|
|
size.emplace(0, 0, 1);
|
|
|
|
}
|
|
|
|
return Some(
|
|
|
|
TexUnpackBlobDesc{target, size.value(), gfxAlphaType::NonPremult});
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//////
|
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
// While it's counter-intuitive, the shape of the SFEResult API means that we
|
|
|
|
// should try to pull out a surface first, and then, if we do pull out a
|
|
|
|
// surface, check CORS/write-only/etc..
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-06-22 20:28:11 +03:00
|
|
|
if (!sfer.mCORSUsed) {
|
|
|
|
auto& srcPrincipal = sfer.mPrincipal;
|
2022-01-19 02:45:17 +03:00
|
|
|
nsIPrincipal* dstPrincipal = webgl.PrincipalOrNull();
|
|
|
|
if (!dstPrincipal || !dstPrincipal->Subsumes(srcPrincipal)) {
|
2020-07-22 01:57:01 +03:00
|
|
|
webgl.EnqueueWarning("Cross-origin elements require CORS.");
|
2015-11-25 07:15:29 +03:00
|
|
|
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
|
2020-07-22 01:57:01 +03:00
|
|
|
return {};
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-22 20:28:11 +03:00
|
|
|
if (sfer.mIsWriteOnly) {
|
|
|
|
// mIsWriteOnly defaults to true, and so will be true even if SFE merely
|
|
|
|
// failed. Thus we must test mIsWriteOnly after successfully retrieving an
|
|
|
|
// Image or SourceSurface.
|
2020-07-22 01:57:01 +03:00
|
|
|
webgl.EnqueueWarning("Element is write-only, thus cannot be uploaded.");
|
2015-11-25 07:15:29 +03:00
|
|
|
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
|
2020-07-22 01:57:01 +03:00
|
|
|
return {};
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//////
|
2016-06-22 20:28:11 +03:00
|
|
|
// Ok, we're good!
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2021-01-14 04:23:06 +03:00
|
|
|
return Some(TexUnpackBlobDesc{target,
|
2022-02-12 05:20:42 +03:00
|
|
|
size.value(),
|
2021-01-14 04:23:06 +03:00
|
|
|
sfer.mAlphaType,
|
|
|
|
{},
|
|
|
|
{},
|
2022-02-12 05:20:42 +03:00
|
|
|
Some(elemSize),
|
2021-01-14 04:23:06 +03:00
|
|
|
layersImage,
|
|
|
|
sd,
|
|
|
|
dataSurf});
|
2016-10-21 04:03:40 +03:00
|
|
|
}
|
|
|
|
|
2020-07-22 01:57:01 +03:00
|
|
|
} // namespace webgl
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-07-27 07:46:33 +03:00
|
|
|
static bool ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture,
|
2020-01-09 01:19:16 +03:00
|
|
|
TexImageTarget target, uint32_t level,
|
2018-10-17 07:18:15 +03:00
|
|
|
webgl::ImageInfo** const out_imageInfo) {
|
2015-11-25 07:15:29 +03:00
|
|
|
// Check level
|
|
|
|
if (level >= WebGLTexture::kMaxLevelCount) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidValue("`level` is too large.");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2018-10-17 07:18:15 +03:00
|
|
|
auto& imageInfo = texture->ImageInfoAt(target, level);
|
2015-11-25 07:15:29 +03:00
|
|
|
*out_imageInfo = &imageInfo;
|
|
|
|
return true;
|
2015-11-24 06:27:13 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
// For *TexImage*
|
2018-07-27 07:46:33 +03:00
|
|
|
bool WebGLTexture::ValidateTexImageSpecification(
|
2020-01-09 01:19:16 +03:00
|
|
|
TexImageTarget target, uint32_t level, const uvec3& size,
|
|
|
|
webgl::ImageInfo** const out_imageInfo) {
|
2018-07-27 07:46:33 +03:00
|
|
|
if (mImmutable) {
|
|
|
|
mContext->ErrorInvalidOperation("Specified texture is immutable.");
|
2015-12-04 02:38:06 +03:00
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
|
|
|
|
// Do this early to validate `level`.
|
|
|
|
webgl::ImageInfo* imageInfo;
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!ValidateTexImage(mContext, this, target, level, &imageInfo))
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && size.x != size.y) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidValue("Cube map images must be square.");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
/* GLES 3.0.4, p133-134:
|
|
|
|
* GL_MAX_TEXTURE_SIZE is *not* the max allowed texture size. Rather, it is
|
|
|
|
* the max (width/height) size guaranteed not to generate an INVALID_VALUE for
|
|
|
|
* too-large dimensions. Sizes larger than GL_MAX_TEXTURE_SIZE *may or may
|
|
|
|
* not* result in an INVALID_VALUE, or possibly GL_OOM.
|
2018-11-30 13:46:48 +03:00
|
|
|
*
|
2015-11-25 07:15:29 +03:00
|
|
|
* However, we have needed to set our maximums lower in the past to prevent
|
2020-01-09 01:19:16 +03:00
|
|
|
* resource corruption. Therefore we have limits.maxTex2dSize, which is
|
|
|
|
* neither necessarily lower nor higher than MAX_TEXTURE_SIZE.
|
2018-11-30 13:46:48 +03:00
|
|
|
*
|
2020-01-09 01:19:16 +03:00
|
|
|
* Note that limits.maxTex2dSize must be >= than the advertized
|
2017-07-27 10:48:58 +03:00
|
|
|
* MAX_TEXTURE_SIZE. For simplicity, we advertize MAX_TEXTURE_SIZE as
|
2020-01-09 01:19:16 +03:00
|
|
|
* limits.maxTex2dSize.
|
2018-11-30 13:46:48 +03:00
|
|
|
*/
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
uint32_t maxWidthHeight = 0;
|
|
|
|
uint32_t maxDepth = 0;
|
2018-04-02 22:31:00 +03:00
|
|
|
uint32_t maxLevel = 0;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto& limits = mContext->Limits();
|
2015-11-25 07:15:29 +03:00
|
|
|
MOZ_ASSERT(level <= 31);
|
|
|
|
switch (target.get()) {
|
|
|
|
case LOCAL_GL_TEXTURE_2D:
|
2020-01-09 01:19:16 +03:00
|
|
|
maxWidthHeight = limits.maxTex2dSize >> level;
|
2015-11-25 07:15:29 +03:00
|
|
|
maxDepth = 1;
|
2020-01-09 01:19:16 +03:00
|
|
|
maxLevel = CeilingLog2(limits.maxTex2dSize);
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LOCAL_GL_TEXTURE_3D:
|
2020-01-09 01:19:16 +03:00
|
|
|
maxWidthHeight = limits.maxTex3dSize >> level;
|
2015-11-25 07:15:29 +03:00
|
|
|
maxDepth = maxWidthHeight;
|
2020-01-09 01:19:16 +03:00
|
|
|
maxLevel = CeilingLog2(limits.maxTex3dSize);
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LOCAL_GL_TEXTURE_2D_ARRAY:
|
2020-01-09 01:19:16 +03:00
|
|
|
maxWidthHeight = limits.maxTex2dSize >> level;
|
2015-11-25 07:15:29 +03:00
|
|
|
// "The maximum number of layers for two-dimensional array textures
|
|
|
|
// (depth) must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels."
|
2020-01-09 01:19:16 +03:00
|
|
|
maxDepth = limits.maxTexArrayLayers;
|
|
|
|
maxLevel = CeilingLog2(limits.maxTex2dSize);
|
2018-11-30 13:46:48 +03:00
|
|
|
break;
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
default: // cube maps
|
|
|
|
MOZ_ASSERT(IsCubeMap());
|
2020-01-09 01:19:16 +03:00
|
|
|
maxWidthHeight = limits.maxTexCubeSize >> level;
|
2015-11-25 07:15:29 +03:00
|
|
|
maxDepth = 1;
|
2020-01-09 01:19:16 +03:00
|
|
|
maxLevel = CeilingLog2(limits.maxTexCubeSize);
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2016-12-15 06:03:25 +03:00
|
|
|
if (level > maxLevel) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidValue("Requested level is not supported for target.");
|
2016-12-15 06:03:25 +03:00
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (size.x > maxWidthHeight || size.y > maxWidthHeight || size.z > maxDepth) {
|
2017-07-27 10:48:58 +03:00
|
|
|
mContext->ErrorInvalidValue("Requested size at this level is unsupported.");
|
|
|
|
return false;
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2016-12-15 06:03:25 +03:00
|
|
|
{
|
2018-07-27 07:46:33 +03:00
|
|
|
/* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
|
|
|
|
* "If level is greater than zero, and either width or
|
|
|
|
* height is not a power-of-two, the error INVALID_VALUE is
|
|
|
|
* generated."
|
2018-11-30 13:46:48 +03:00
|
|
|
*
|
2018-07-27 07:46:33 +03:00
|
|
|
* This restriction does not apply to GL ES Version 3.0+.
|
2018-11-30 13:46:48 +03:00
|
|
|
*/
|
2018-07-27 07:46:33 +03:00
|
|
|
bool requirePOT = (!mContext->IsWebGL2() && level != 0);
|
2016-12-15 06:03:25 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (requirePOT) {
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!IsPowerOfTwo(size.x) || !IsPowerOfTwo(size.y)) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidValue(
|
|
|
|
"For level > 0, width and height must be"
|
|
|
|
" powers of two.");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
*out_imageInfo = imageInfo;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For *TexSubImage*
|
2018-07-27 07:46:33 +03:00
|
|
|
bool WebGLTexture::ValidateTexImageSelection(
|
2020-01-09 01:19:16 +03:00
|
|
|
TexImageTarget target, uint32_t level, const uvec3& offset,
|
|
|
|
const uvec3& size, webgl::ImageInfo** const out_imageInfo) {
|
2015-11-25 07:15:29 +03:00
|
|
|
webgl::ImageInfo* imageInfo;
|
|
|
|
if (!ValidateTexImage(mContext, this, target, level, &imageInfo))
|
|
|
|
return false;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (!imageInfo->IsDefined()) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"The specified TexImage has not yet been"
|
|
|
|
" specified.");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto totalX = CheckedUint32(offset.x) + size.x;
|
|
|
|
const auto totalY = CheckedUint32(offset.y) + size.y;
|
|
|
|
const auto totalZ = CheckedUint32(offset.z) + size.z;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (!totalX.isValid() || totalX.value() > imageInfo->mWidth ||
|
|
|
|
!totalY.isValid() || totalY.value() > imageInfo->mHeight ||
|
|
|
|
!totalZ.isValid() || totalZ.value() > imageInfo->mDepth) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidValue(
|
|
|
|
"Offset+size must be <= the size of the existing"
|
|
|
|
" specified image.");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2015-11-24 06:27:13 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
*out_imageInfo = imageInfo;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
static bool ValidateCompressedTexUnpack(WebGLContext* webgl, const uvec3& size,
|
2015-11-25 07:15:29 +03:00
|
|
|
const webgl::FormatInfo* format,
|
|
|
|
size_t dataSize) {
|
|
|
|
auto compression = format->compression;
|
|
|
|
|
|
|
|
auto bytesPerBlock = compression->bytesPerBlock;
|
|
|
|
auto blockWidth = compression->blockWidth;
|
|
|
|
auto blockHeight = compression->blockHeight;
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
auto widthInBlocks = CheckedUint32(size.x) / blockWidth;
|
|
|
|
auto heightInBlocks = CheckedUint32(size.y) / blockHeight;
|
|
|
|
if (size.x % blockWidth) widthInBlocks += 1;
|
|
|
|
if (size.y % blockHeight) heightInBlocks += 1;
|
2015-11-25 07:15:29 +03:00
|
|
|
|
|
|
|
const CheckedUint32 blocksPerImage = widthInBlocks * heightInBlocks;
|
|
|
|
const CheckedUint32 bytesPerImage = bytesPerBlock * blocksPerImage;
|
2020-01-09 01:19:16 +03:00
|
|
|
const CheckedUint32 bytesNeeded = bytesPerImage * size.z;
|
2015-11-25 07:15:29 +03:00
|
|
|
|
|
|
|
if (!bytesNeeded.isValid()) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorOutOfMemory("Overflow while computing the needed buffer size.");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dataSize != bytesNeeded.value()) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidValue(
|
|
|
|
"Provided buffer's size must match expected size."
|
2017-07-26 23:03:57 +03:00
|
|
|
" (needs %u, has %zu)",
|
2018-07-27 07:46:33 +03:00
|
|
|
bytesNeeded.value(), dataSize);
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool DoChannelsMatchForCopyTexImage(const webgl::FormatInfo* srcFormat,
|
|
|
|
const webgl::FormatInfo* dstFormat) {
|
|
|
|
// GLES 3.0.4 p140 Table 3.16 "Valid CopyTexImage source
|
|
|
|
// framebuffer/destination texture base internal format combinations."
|
|
|
|
|
|
|
|
switch (srcFormat->unsizedFormat) {
|
|
|
|
case webgl::UnsizedFormat::RGBA:
|
|
|
|
switch (dstFormat->unsizedFormat) {
|
|
|
|
case webgl::UnsizedFormat::A:
|
|
|
|
case webgl::UnsizedFormat::L:
|
|
|
|
case webgl::UnsizedFormat::LA:
|
|
|
|
case webgl::UnsizedFormat::R:
|
|
|
|
case webgl::UnsizedFormat::RG:
|
|
|
|
case webgl::UnsizedFormat::RGB:
|
|
|
|
case webgl::UnsizedFormat::RGBA:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
case webgl::UnsizedFormat::RGB:
|
|
|
|
switch (dstFormat->unsizedFormat) {
|
|
|
|
case webgl::UnsizedFormat::L:
|
|
|
|
case webgl::UnsizedFormat::R:
|
|
|
|
case webgl::UnsizedFormat::RG:
|
|
|
|
case webgl::UnsizedFormat::RGB:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
case webgl::UnsizedFormat::RG:
|
|
|
|
switch (dstFormat->unsizedFormat) {
|
|
|
|
case webgl::UnsizedFormat::L:
|
|
|
|
case webgl::UnsizedFormat::R:
|
|
|
|
case webgl::UnsizedFormat::RG:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
case webgl::UnsizedFormat::R:
|
|
|
|
switch (dstFormat->unsizedFormat) {
|
|
|
|
case webgl::UnsizedFormat::L:
|
|
|
|
case webgl::UnsizedFormat::R:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-27 07:46:33 +03:00
|
|
|
static bool EnsureImageDataInitializedForUpload(
|
2020-01-09 01:19:16 +03:00
|
|
|
WebGLTexture* tex, TexImageTarget target, uint32_t level,
|
2020-03-31 04:00:50 +03:00
|
|
|
const uvec3& offset, const uvec3& size, webgl::ImageInfo* imageInfo,
|
|
|
|
bool* const out_expectsInit = nullptr) {
|
|
|
|
if (out_expectsInit) {
|
|
|
|
*out_expectsInit = false;
|
|
|
|
}
|
2019-07-31 02:32:29 +03:00
|
|
|
if (!imageInfo->mUninitializedSlices) return true;
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (size.x == imageInfo->mWidth && size.y == imageInfo->mHeight) {
|
2020-03-31 04:00:50 +03:00
|
|
|
bool expectsInit = false;
|
|
|
|
auto& isSliceUninit = *imageInfo->mUninitializedSlices;
|
2020-01-09 01:19:16 +03:00
|
|
|
for (const auto z : IntegerRange(offset.z, offset.z + size.z)) {
|
2020-03-31 04:00:50 +03:00
|
|
|
if (!isSliceUninit[z]) continue;
|
|
|
|
expectsInit = true;
|
|
|
|
isSliceUninit[z] = false;
|
|
|
|
}
|
|
|
|
if (out_expectsInit) {
|
|
|
|
*out_expectsInit = expectsInit;
|
2019-07-26 09:43:02 +03:00
|
|
|
}
|
2020-03-31 04:00:50 +03:00
|
|
|
|
|
|
|
if (!expectsInit) return true;
|
|
|
|
|
2019-07-31 02:32:29 +03:00
|
|
|
bool hasUninitialized = false;
|
|
|
|
for (const auto z : IntegerRange(imageInfo->mDepth)) {
|
2020-03-31 04:00:50 +03:00
|
|
|
hasUninitialized |= isSliceUninit[z];
|
2019-07-31 02:32:29 +03:00
|
|
|
}
|
|
|
|
if (!hasUninitialized) {
|
2020-03-27 17:10:45 +03:00
|
|
|
imageInfo->mUninitializedSlices = Nothing();
|
2019-07-31 02:32:29 +03:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
WebGLContext* webgl = tex->mContext;
|
|
|
|
webgl->GenerateWarning(
|
|
|
|
"Texture has not been initialized prior to a"
|
|
|
|
" partial upload, forcing the browser to clear it."
|
|
|
|
" This may be slow.");
|
|
|
|
if (!tex->EnsureImageDataInitialized(target, level)) {
|
|
|
|
MOZ_ASSERT(false, "Unexpected failure to init image data.");
|
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
return true;
|
2015-11-24 06:27:13 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Actual calls
|
|
|
|
|
|
|
|
static inline GLenum DoTexStorage(gl::GLContext* gl, TexTarget target,
|
|
|
|
GLsizei levels, GLenum sizedFormat,
|
|
|
|
GLsizei width, GLsizei height,
|
|
|
|
GLsizei depth) {
|
|
|
|
gl::GLContext::LocalErrorScope errorScope(*gl);
|
|
|
|
|
|
|
|
switch (target.get()) {
|
|
|
|
case LOCAL_GL_TEXTURE_2D:
|
|
|
|
case LOCAL_GL_TEXTURE_CUBE_MAP:
|
|
|
|
MOZ_ASSERT(depth == 1);
|
|
|
|
gl->fTexStorage2D(target.get(), levels, sizedFormat, width, height);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LOCAL_GL_TEXTURE_3D:
|
|
|
|
case LOCAL_GL_TEXTURE_2D_ARRAY:
|
|
|
|
gl->fTexStorage3D(target.get(), levels, sizedFormat, width, height,
|
|
|
|
depth);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2016-06-06 18:17:23 +03:00
|
|
|
MOZ_CRASH("GFX: bad target");
|
2015-11-24 06:27:13 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
return errorScope.GetError();
|
|
|
|
}
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
bool IsTarget3D(TexImageTarget target) {
|
2015-11-25 07:15:29 +03:00
|
|
|
switch (target.get()) {
|
|
|
|
case LOCAL_GL_TEXTURE_2D:
|
|
|
|
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
|
|
|
|
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
|
|
|
|
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
|
|
|
|
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
|
|
|
|
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
|
|
|
|
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
|
|
|
|
return false;
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
case LOCAL_GL_TEXTURE_3D:
|
|
|
|
case LOCAL_GL_TEXTURE_2D_ARRAY:
|
|
|
|
return true;
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
default:
|
2016-06-06 18:17:23 +03:00
|
|
|
MOZ_CRASH("GFX: bad target");
|
2015-11-24 08:55:59 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
GLenum DoTexImage(gl::GLContext* gl, TexImageTarget target, GLint level,
|
|
|
|
const webgl::DriverUnpackInfo* dui, GLsizei width,
|
|
|
|
GLsizei height, GLsizei depth, const void* data) {
|
|
|
|
const GLint border = 0;
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl::GLContext::LocalErrorScope errorScope(*gl);
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
if (IsTarget3D(target)) {
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fTexImage3D(target.get(), level, dui->internalFormat, width, height,
|
|
|
|
depth, border, dui->unpackFormat, dui->unpackType, data);
|
2015-11-24 08:55:59 +03:00
|
|
|
} else {
|
2015-11-25 07:15:29 +03:00
|
|
|
MOZ_ASSERT(depth == 1);
|
|
|
|
gl->fTexImage2D(target.get(), level, dui->internalFormat, width, height,
|
|
|
|
border, dui->unpackFormat, dui->unpackType, data);
|
|
|
|
}
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
return errorScope.GetError();
|
|
|
|
}
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
GLenum DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level,
|
|
|
|
GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width,
|
|
|
|
GLsizei height, GLsizei depth,
|
2016-06-22 20:28:11 +03:00
|
|
|
const webgl::PackingInfo& pi, const void* data) {
|
2015-11-25 07:15:29 +03:00
|
|
|
gl::GLContext::LocalErrorScope errorScope(*gl);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
if (IsTarget3D(target)) {
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, width,
|
|
|
|
height, depth, pi.format, pi.type, data);
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(zOffset == 0);
|
|
|
|
MOZ_ASSERT(depth == 1);
|
|
|
|
gl->fTexSubImage2D(target.get(), level, xOffset, yOffset, width, height,
|
|
|
|
pi.format, pi.type, data);
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
return errorScope.GetError();
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline GLenum DoCompressedTexImage(gl::GLContext* gl,
|
|
|
|
TexImageTarget target, GLint level,
|
|
|
|
GLenum internalFormat, GLsizei width,
|
|
|
|
GLsizei height, GLsizei depth,
|
|
|
|
GLsizei dataSize, const void* data) {
|
2016-06-22 20:28:11 +03:00
|
|
|
const GLint border = 0;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl::GLContext::LocalErrorScope errorScope(*gl);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
if (IsTarget3D(target)) {
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fCompressedTexImage3D(target.get(), level, internalFormat, width,
|
|
|
|
height, depth, border, dataSize, data);
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(depth == 1);
|
|
|
|
gl->fCompressedTexImage2D(target.get(), level, internalFormat, width,
|
|
|
|
height, border, dataSize, data);
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
return errorScope.GetError();
|
|
|
|
}
|
|
|
|
|
|
|
|
GLenum DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target,
|
|
|
|
GLint level, GLint xOffset, GLint yOffset,
|
|
|
|
GLint zOffset, GLsizei width, GLsizei height,
|
|
|
|
GLsizei depth, GLenum sizedUnpackFormat,
|
|
|
|
GLsizei dataSize, const void* data) {
|
|
|
|
gl::GLContext::LocalErrorScope errorScope(*gl);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
if (IsTarget3D(target)) {
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fCompressedTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset,
|
|
|
|
width, height, depth, sizedUnpackFormat,
|
|
|
|
dataSize, data);
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(zOffset == 0);
|
|
|
|
MOZ_ASSERT(depth == 1);
|
|
|
|
gl->fCompressedTexSubImage2D(target.get(), level, xOffset, yOffset, width,
|
|
|
|
height, sizedUnpackFormat, dataSize, data);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
return errorScope.GetError();
|
|
|
|
}
|
|
|
|
|
2022-10-05 01:38:13 +03:00
|
|
|
static inline GLenum DoCopyTexSubImage(gl::GLContext* gl, TexImageTarget target,
|
|
|
|
GLint level, GLint xOffset,
|
|
|
|
GLint yOffset, GLint zOffset, GLint x,
|
|
|
|
GLint y, GLsizei width, GLsizei height) {
|
|
|
|
gl::GLContext::LocalErrorScope errorScope(*gl);
|
|
|
|
|
2016-07-12 08:51:19 +03:00
|
|
|
if (IsTarget3D(target)) {
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fCopyTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, x, y,
|
2018-07-27 07:46:33 +03:00
|
|
|
width, height);
|
2018-11-30 13:46:48 +03:00
|
|
|
} else {
|
2015-11-25 07:15:29 +03:00
|
|
|
MOZ_ASSERT(zOffset == 0);
|
|
|
|
gl->fCopyTexSubImage2D(target.get(), level, xOffset, yOffset, x, y, width,
|
2018-11-30 13:46:48 +03:00
|
|
|
height);
|
|
|
|
}
|
2022-10-05 01:38:13 +03:00
|
|
|
|
|
|
|
return errorScope.GetError();
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Actual (mostly generic) function implementations
|
|
|
|
|
2018-07-27 07:46:33 +03:00
|
|
|
static bool ValidateCompressedTexImageRestrictions(
|
2020-01-09 01:19:16 +03:00
|
|
|
const WebGLContext* webgl, TexImageTarget target, uint32_t level,
|
|
|
|
const webgl::FormatInfo* format, const uvec3& size) {
|
2021-08-07 10:44:20 +03:00
|
|
|
const auto fnIsDimValid_S3TC = [&](const char* const name, uint32_t levelSize,
|
|
|
|
uint32_t blockSize) {
|
|
|
|
const auto impliedBaseSize = levelSize << level;
|
|
|
|
if (impliedBaseSize % blockSize == 0) return true;
|
|
|
|
webgl->ErrorInvalidOperation(
|
|
|
|
"%u is never a valid %s for level %u, because it implies a base mip %s "
|
|
|
|
"of %u."
|
|
|
|
" %s requires that base mip levels have a %s multiple of %u.",
|
|
|
|
levelSize, name, level, name, impliedBaseSize, format->name, name,
|
|
|
|
blockSize);
|
|
|
|
return false;
|
2015-11-25 07:15:29 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
switch (format->compression->family) {
|
2016-11-23 05:59:35 +03:00
|
|
|
case webgl::CompressionFamily::ASTC:
|
|
|
|
if (target == LOCAL_GL_TEXTURE_3D &&
|
|
|
|
!webgl->gl->IsExtensionSupported(
|
|
|
|
gl::GLContext::KHR_texture_compression_astc_hdr)) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidOperation("TEXTURE_3D requires ASTC's hdr profile.");
|
2016-11-23 05:59:35 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
case webgl::CompressionFamily::PVRTC:
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!IsPowerOfTwo(size.x) || !IsPowerOfTwo(size.y)) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidValue("%s requires power-of-two width and height.",
|
|
|
|
format->name);
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
|
|
|
|
2021-08-07 10:44:20 +03:00
|
|
|
case webgl::CompressionFamily::BPTC:
|
|
|
|
case webgl::CompressionFamily::RGTC:
|
2015-11-25 07:15:29 +03:00
|
|
|
case webgl::CompressionFamily::S3TC:
|
2021-08-07 10:44:20 +03:00
|
|
|
if (!fnIsDimValid_S3TC("width", size.x,
|
|
|
|
format->compression->blockWidth) ||
|
|
|
|
!fnIsDimValid_S3TC("height", size.y,
|
|
|
|
format->compression->blockHeight)) {
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
// Default: There are no restrictions on CompressedTexImage.
|
2021-08-07 10:44:20 +03:00
|
|
|
case webgl::CompressionFamily::ES3:
|
|
|
|
case webgl::CompressionFamily::ETC1:
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
2015-11-24 08:55:59 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-03 16:27:12 +03:00
|
|
|
static bool ValidateFormatAndSize(const WebGLContext* webgl,
|
|
|
|
TexImageTarget target,
|
|
|
|
const webgl::FormatInfo* format,
|
|
|
|
const uvec3& size) {
|
|
|
|
// Check if texture size will likely be rejected by the driver and give a more
|
|
|
|
// meaningful error message.
|
|
|
|
auto baseImageSize = CheckedInt<uint64_t>(format->estimatedBytesPerPixel) *
|
|
|
|
(uint32_t)size.x * (uint32_t)size.y * (uint32_t)size.z;
|
|
|
|
if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
|
|
|
|
baseImageSize *= 6;
|
|
|
|
}
|
|
|
|
if (!baseImageSize.isValid() ||
|
|
|
|
baseImageSize.value() >
|
|
|
|
(uint64_t)StaticPrefs::webgl_max_size_per_texture_mib() *
|
|
|
|
(1024 * 1024)) {
|
|
|
|
webgl->ErrorOutOfMemory(
|
|
|
|
"Texture size too large; base image mebibytes > "
|
|
|
|
"webgl.max-size-per-texture-mib");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
// GLES 3.0.4 p127:
|
|
|
|
// "Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL
|
|
|
|
// are supported by texture image specification commands only if `target` is
|
|
|
|
// TEXTURE_2D, TEXTURE_2D_ARRAY, or TEXTURE_CUBE_MAP. Using these formats in
|
|
|
|
// conjunction with any other `target` will result in an INVALID_OPERATION
|
|
|
|
// error."
|
2018-09-28 02:37:40 +03:00
|
|
|
const bool ok = [&]() {
|
|
|
|
if (bool(format->d) & (target == LOCAL_GL_TEXTURE_3D)) return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-09-28 02:37:40 +03:00
|
|
|
if (format->compression) {
|
|
|
|
switch (format->compression->family) {
|
|
|
|
case webgl::CompressionFamily::ES3:
|
2015-11-25 07:15:29 +03:00
|
|
|
case webgl::CompressionFamily::S3TC:
|
2018-09-28 02:37:40 +03:00
|
|
|
if (target == LOCAL_GL_TEXTURE_3D) return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
break;
|
|
|
|
|
2018-09-28 02:37:40 +03:00
|
|
|
case webgl::CompressionFamily::ETC1:
|
2015-11-25 07:15:29 +03:00
|
|
|
case webgl::CompressionFamily::PVRTC:
|
2018-09-28 02:37:40 +03:00
|
|
|
case webgl::CompressionFamily::RGTC:
|
|
|
|
if (target == LOCAL_GL_TEXTURE_3D ||
|
|
|
|
target == LOCAL_GL_TEXTURE_2D_ARRAY) {
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2018-09-28 02:37:40 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
2015-11-24 08:55:59 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
return true;
|
2018-07-27 07:46:33 +03:00
|
|
|
}();
|
2015-11-25 07:15:29 +03:00
|
|
|
if (!ok) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidOperation("Format %s cannot be used with target %s.",
|
|
|
|
format->name, GetEnumName(target.get()));
|
2018-04-02 22:31:00 +03:00
|
|
|
return false;
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2017-06-22 21:32:00 +03:00
|
|
|
return true;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2017-06-22 21:32:00 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
void WebGLTexture::TexStorage(TexTarget target, uint32_t levels,
|
|
|
|
GLenum sizedFormat, const uvec3& size) {
|
2015-11-25 07:15:29 +03:00
|
|
|
// Check levels
|
|
|
|
if (levels < 1) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidValue("`levels` must be >= 1.");
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!size.x || !size.y || !size.z) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidValue("Dimensions must be non-zero.");
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
const TexImageTarget testTarget =
|
|
|
|
IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X : target.get();
|
2018-10-17 07:18:15 +03:00
|
|
|
webgl::ImageInfo* baseImageInfo;
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!ValidateTexImageSpecification(testTarget, 0, size, &baseImageInfo)) {
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-03-27 04:39:46 +03:00
|
|
|
MOZ_ALWAYS_TRUE(baseImageInfo);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
auto dstUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedFormat);
|
|
|
|
if (!dstUsage) {
|
2018-10-17 22:13:14 +03:00
|
|
|
mContext->ErrorInvalidEnumInfo("internalformat", sizedFormat);
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
auto dstFormat = dstUsage->format;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2022-10-03 16:27:12 +03:00
|
|
|
if (!ValidateFormatAndSize(mContext, testTarget, dstFormat, size)) return;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (dstFormat->compression) {
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!ValidateCompressedTexImageRestrictions(mContext, testTarget, 0,
|
|
|
|
dstFormat, size)) {
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-04-02 22:31:00 +03:00
|
|
|
const bool levelsOk = [&]() {
|
|
|
|
// Right-shift is only defined for bits-1, which is too large anyways.
|
|
|
|
const auto lastLevel = uint32_t(levels - 1);
|
|
|
|
if (lastLevel > 31) return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto lastLevelWidth = uint32_t(size.x) >> lastLevel;
|
|
|
|
const auto lastLevelHeight = uint32_t(size.y) >> lastLevel;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-04-02 22:31:00 +03:00
|
|
|
// If these are all zero, then some earlier level was the final 1x1(x1)
|
|
|
|
// level.
|
2018-05-16 03:00:31 +03:00
|
|
|
bool ok = lastLevelWidth || lastLevelHeight;
|
2018-04-02 22:31:00 +03:00
|
|
|
if (target == LOCAL_GL_TEXTURE_3D) {
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto lastLevelDepth = uint32_t(size.z) >> lastLevel;
|
2018-05-16 03:00:31 +03:00
|
|
|
ok |= bool(lastLevelDepth);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2018-04-02 22:31:00 +03:00
|
|
|
return ok;
|
2018-11-30 13:46:48 +03:00
|
|
|
}();
|
2018-04-02 22:31:00 +03:00
|
|
|
if (!levelsOk) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"Too many levels requested for the given"
|
2015-11-25 07:15:29 +03:00
|
|
|
" dimensions. (levels: %u, width: %u, height: %u,"
|
|
|
|
" depth: %u)",
|
2020-01-09 01:19:16 +03:00
|
|
|
levels, size.x, size.y, size.z);
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Do the thing!
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
GLenum error = DoTexStorage(mContext->gl, target.get(), levels, sizedFormat,
|
2020-01-09 01:19:16 +03:00
|
|
|
size.x, size.y, size.z);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2017-06-22 21:32:00 +03:00
|
|
|
mContext->OnDataAllocCall();
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (error == LOCAL_GL_OUT_OF_MEMORY) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorOutOfMemory("Ran out of memory during texture allocation.");
|
2019-06-14 06:26:18 +03:00
|
|
|
Truncate();
|
2015-08-04 00:34:46 +03:00
|
|
|
return;
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
|
|
|
if (error) {
|
2019-02-20 07:43:32 +03:00
|
|
|
mContext->GenerateError(error, "Unexpected error from driver.");
|
|
|
|
const nsPrintfCString call(
|
|
|
|
"DoTexStorage(0x%04x, %i, 0x%04x, %i,%i,%i) -> 0x%04x", target.get(),
|
2020-01-09 01:19:16 +03:00
|
|
|
levels, sizedFormat, size.x, size.y, size.z, error);
|
2019-02-20 07:43:32 +03:00
|
|
|
gfxCriticalError() << "Unexpected error from driver: "
|
|
|
|
<< call.BeginReading();
|
2015-11-25 07:15:29 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
// Update our specification data.
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
auto uninitializedSlices = Some(std::vector<bool>(size.z, true));
|
|
|
|
const webgl::ImageInfo newInfo{dstUsage, size.x, size.y, size.z,
|
2019-07-31 02:32:29 +03:00
|
|
|
std::move(uninitializedSlices)};
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2018-10-17 07:18:15 +03:00
|
|
|
{
|
|
|
|
const auto base_level = mBaseMipmapLevel;
|
|
|
|
mBaseMipmapLevel = 0;
|
|
|
|
|
|
|
|
ImageInfoAtFace(0, 0) = newInfo;
|
|
|
|
PopulateMipChain(levels - 1);
|
|
|
|
|
|
|
|
mBaseMipmapLevel = base_level;
|
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
mImmutable = true;
|
2019-04-02 20:00:41 +03:00
|
|
|
mImmutableLevelCount = AutoAssertCast(levels);
|
2018-10-17 07:18:15 +03:00
|
|
|
ClampLevelBaseAndMax();
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////////
|
|
|
|
// Tex(Sub)Image
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
// TexSubImage iff `!respectFormat`
|
2020-07-22 01:57:01 +03:00
|
|
|
void WebGLTexture::TexImage(uint32_t level, GLenum respecFormat,
|
|
|
|
const uvec3& offset, const webgl::PackingInfo& pi,
|
|
|
|
const webgl::TexUnpackBlobDesc& src) {
|
2024-02-29 03:37:29 +03:00
|
|
|
const auto blob = webgl::TexUnpackBlob::Create(src);
|
2020-07-22 01:57:01 +03:00
|
|
|
if (!blob) {
|
|
|
|
MOZ_ASSERT(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto imageTarget = blob->mDesc.imageTarget;
|
|
|
|
auto size = blob->mDesc.size;
|
|
|
|
|
|
|
|
if (!IsTarget3D(imageTarget)) {
|
|
|
|
size.z = 1;
|
|
|
|
}
|
2020-01-09 01:19:23 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Get dest info
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
const auto& fua = mContext->mFormatUsage;
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto fnValidateUnpackEnums = [&]() {
|
|
|
|
if (!fua->AreUnpackEnumsValid(pi.format, pi.type)) {
|
|
|
|
mContext->ErrorInvalidEnum("Invalid unpack format/type: %s/%s",
|
|
|
|
EnumString(pi.format).c_str(),
|
|
|
|
EnumString(pi.type).c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
webgl::ImageInfo* imageInfo;
|
|
|
|
const webgl::FormatUsageInfo* dstUsage;
|
|
|
|
if (respecFormat) {
|
|
|
|
if (!ValidateTexImageSpecification(imageTarget, level, size, &imageInfo))
|
|
|
|
return;
|
|
|
|
MOZ_ASSERT(imageInfo);
|
|
|
|
|
|
|
|
if (!fua->IsInternalFormatEnumValid(respecFormat)) {
|
|
|
|
mContext->ErrorInvalidValue("Invalid internalformat: 0x%04x",
|
|
|
|
respecFormat);
|
2015-11-25 07:15:29 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
dstUsage = fua->GetSizedTexUsage(respecFormat);
|
|
|
|
if (!dstUsage) {
|
|
|
|
if (respecFormat != pi.format) {
|
|
|
|
/* GL ES Version 3.0.4 - 3.8.3 Texture Image Specification
|
|
|
|
* "Specifying a combination of values for format, type, and
|
|
|
|
* internalformat that is not listed as a valid combination
|
|
|
|
* in tables 3.2 or 3.3 generates the error INVALID_OPERATION."
|
|
|
|
*/
|
|
|
|
if (!fnValidateUnpackEnums()) return;
|
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"Unsized internalFormat must match"
|
|
|
|
" unpack format.");
|
|
|
|
return;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
dstUsage = fua->GetUnsizedTexUsage(pi);
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!dstUsage) {
|
|
|
|
if (!fnValidateUnpackEnums()) return;
|
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"Invalid internalformat/format/type:"
|
|
|
|
" 0x%04x/0x%04x/0x%04x",
|
|
|
|
respecFormat, pi.format, pi.type);
|
|
|
|
return;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto& dstFormat = dstUsage->format;
|
2022-10-03 16:27:12 +03:00
|
|
|
if (!ValidateFormatAndSize(mContext, imageTarget, dstFormat, size)) return;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!mContext->IsWebGL2() && dstFormat->d) {
|
|
|
|
if (imageTarget != LOCAL_GL_TEXTURE_2D || blob->HasData() || level != 0) {
|
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"With format %s, this function may only"
|
|
|
|
" be called with target=TEXTURE_2D,"
|
|
|
|
" data=null, and level=0.",
|
|
|
|
dstFormat->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!ValidateTexImageSelection(imageTarget, level, offset, size,
|
|
|
|
&imageInfo)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(imageInfo);
|
|
|
|
dstUsage = imageInfo->mFormat;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto& dstFormat = dstUsage->format;
|
|
|
|
if (!mContext->IsWebGL2() && dstFormat->d) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidOperation(
|
2020-01-09 01:19:16 +03:00
|
|
|
"Function may not be called on a texture of"
|
|
|
|
" format %s.",
|
2018-07-27 07:46:33 +03:00
|
|
|
dstFormat->name);
|
2015-11-25 07:15:29 +03:00
|
|
|
return;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Get source info
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
const webgl::DriverUnpackInfo* driverUnpackInfo;
|
2016-06-22 20:28:11 +03:00
|
|
|
if (!dstUsage->IsUnpackValid(pi, &driverUnpackInfo)) {
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!fnValidateUnpackEnums()) return;
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"Mismatched internalFormat and format/type:"
|
2020-01-09 01:19:16 +03:00
|
|
|
" 0x%04x and 0x%04x/0x%04x",
|
|
|
|
respecFormat, pi.format, pi.type);
|
2015-11-25 07:15:29 +03:00
|
|
|
return;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:23 +03:00
|
|
|
if (!blob->Validate(mContext, pi)) return;
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Do the thing!
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
Maybe<webgl::ImageInfo> newImageInfo;
|
|
|
|
bool isRespec = false;
|
|
|
|
if (respecFormat) {
|
|
|
|
// It's tempting to do allocation first, and TexSubImage second, but this is
|
|
|
|
// generally slower.
|
|
|
|
newImageInfo = Some(webgl::ImageInfo{dstUsage, size.x, size.y, size.z});
|
|
|
|
if (!blob->HasData()) {
|
|
|
|
newImageInfo->mUninitializedSlices =
|
2020-07-22 01:57:01 +03:00
|
|
|
Some(std::vector<bool>(size.z, true));
|
2020-01-09 01:19:16 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:23 +03:00
|
|
|
isRespec = (imageInfo->mWidth != newImageInfo->mWidth ||
|
|
|
|
imageInfo->mHeight != newImageInfo->mHeight ||
|
|
|
|
imageInfo->mDepth != newImageInfo->mDepth ||
|
|
|
|
imageInfo->mFormat != newImageInfo->mFormat);
|
2020-01-09 01:19:16 +03:00
|
|
|
} else {
|
2020-01-09 01:19:23 +03:00
|
|
|
if (!blob->HasData()) {
|
|
|
|
mContext->ErrorInvalidValue("`source` cannot be null.");
|
|
|
|
return;
|
|
|
|
}
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!EnsureImageDataInitializedForUpload(this, imageTarget, level, offset,
|
|
|
|
size, imageInfo)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2022-02-12 05:20:42 +03:00
|
|
|
webgl::PixelPackingState{}.AssertCurrentUnpack(*mContext->gl,
|
|
|
|
mContext->IsWebGL2());
|
2020-08-23 09:09:33 +03:00
|
|
|
|
2022-02-12 05:20:42 +03:00
|
|
|
blob->mDesc.unpacking.ApplyUnpack(*mContext->gl, mContext->IsWebGL2(), size);
|
2020-08-23 09:09:33 +03:00
|
|
|
const auto revertUnpacking = MakeScopeExit([&]() {
|
2022-02-12 05:20:42 +03:00
|
|
|
webgl::PixelPackingState{}.ApplyUnpack(*mContext->gl, mContext->IsWebGL2(),
|
|
|
|
size);
|
2020-08-23 09:09:33 +03:00
|
|
|
});
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const bool isSubImage = !respecFormat;
|
2021-01-14 04:23:06 +03:00
|
|
|
GLenum glError = 0;
|
2020-07-22 01:57:01 +03:00
|
|
|
if (!blob->TexOrSubImage(isSubImage, isRespec, this, level, driverUnpackInfo,
|
|
|
|
offset.x, offset.y, offset.z, pi, &glError)) {
|
2016-11-16 04:52:41 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (glError == LOCAL_GL_OUT_OF_MEMORY) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorOutOfMemory("Driver ran out of memory during upload.");
|
2019-06-14 06:26:18 +03:00
|
|
|
Truncate();
|
2015-11-25 07:15:29 +03:00
|
|
|
return;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (glError) {
|
2018-07-27 07:46:33 +03:00
|
|
|
const auto enumStr = EnumString(glError);
|
|
|
|
const nsPrintfCString dui(
|
|
|
|
"Unexpected error %s during upload. (dui: %x/%x/%x)", enumStr.c_str(),
|
|
|
|
driverUnpackInfo->internalFormat, driverUnpackInfo->unpackFormat,
|
|
|
|
driverUnpackInfo->unpackType);
|
|
|
|
mContext->ErrorInvalidOperation("%s", dui.BeginReading());
|
|
|
|
gfxCriticalError() << mContext->FuncName() << ": " << dui.BeginReading();
|
2015-11-25 07:15:29 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Update our specification data?
|
2020-01-09 01:19:16 +03:00
|
|
|
|
|
|
|
if (respecFormat) {
|
|
|
|
mContext->OnDataAllocCall();
|
|
|
|
*imageInfo = *newImageInfo;
|
|
|
|
InvalidateCaches();
|
|
|
|
}
|
2015-11-24 08:55:59 +03:00
|
|
|
}
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////////
|
|
|
|
// CompressedTex(Sub)Image
|
|
|
|
|
|
|
|
static inline bool IsSubImageBlockAligned(
|
|
|
|
const webgl::CompressedFormatInfo* compression,
|
2018-10-17 07:18:15 +03:00
|
|
|
const webgl::ImageInfo* imageInfo, GLint xOffset, GLint yOffset,
|
2015-11-25 07:15:29 +03:00
|
|
|
uint32_t width, uint32_t height) {
|
|
|
|
if (xOffset % compression->blockWidth != 0 ||
|
|
|
|
yOffset % compression->blockHeight != 0) {
|
|
|
|
return false;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (width % compression->blockWidth != 0 &&
|
|
|
|
xOffset + width != imageInfo->mWidth)
|
|
|
|
return false;
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (height % compression->blockHeight != 0 &&
|
|
|
|
yOffset + height != imageInfo->mHeight)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
// CompressedTexSubImage iff `sub`
|
|
|
|
void WebGLTexture::CompressedTexImage(bool sub, GLenum imageTarget,
|
|
|
|
uint32_t level, GLenum formatEnum,
|
|
|
|
const uvec3& offset, const uvec3& size,
|
|
|
|
const Range<const uint8_t>& src,
|
|
|
|
const uint32_t pboImageSize,
|
2020-01-09 01:19:23 +03:00
|
|
|
const Maybe<uint64_t>& pboOffset) {
|
2020-01-09 01:19:16 +03:00
|
|
|
auto imageSize = pboImageSize;
|
|
|
|
if (pboOffset) {
|
|
|
|
const auto& buffer =
|
|
|
|
mContext->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER);
|
|
|
|
if (!buffer) return;
|
|
|
|
auto availBytes = buffer->ByteLength();
|
|
|
|
if (*pboOffset > availBytes) {
|
|
|
|
mContext->GenerateError(
|
|
|
|
LOCAL_GL_INVALID_OPERATION,
|
|
|
|
"`offset` (%llu) must be <= PIXEL_UNPACK_BUFFER size (%llu).",
|
|
|
|
*pboOffset, availBytes);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
availBytes -= *pboOffset;
|
|
|
|
if (availBytes < pboImageSize) {
|
|
|
|
mContext->GenerateError(
|
|
|
|
LOCAL_GL_INVALID_OPERATION,
|
|
|
|
"PIXEL_UNPACK_BUFFER size minus `offset` (%llu) too small for"
|
|
|
|
" `pboImageSize` (%u).",
|
|
|
|
availBytes, pboImageSize);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mContext->mBoundPixelUnpackBuffer) {
|
|
|
|
mContext->GenerateError(LOCAL_GL_INVALID_OPERATION,
|
|
|
|
"PIXEL_UNPACK_BUFFER is non-null.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
imageSize = src.length();
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
// -
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto usage = mContext->mFormatUsage->GetSizedTexUsage(formatEnum);
|
|
|
|
if (!usage || !usage->format->compression) {
|
|
|
|
mContext->ErrorInvalidEnumArg("format", formatEnum);
|
2015-08-04 00:34:46 +03:00
|
|
|
return;
|
2015-11-24 06:27:13 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
webgl::ImageInfo* imageInfo;
|
|
|
|
if (!sub) {
|
|
|
|
if (!ValidateTexImageSpecification(imageTarget, level, size, &imageInfo)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(imageInfo);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2022-10-03 16:27:12 +03:00
|
|
|
if (!ValidateFormatAndSize(mContext, imageTarget, usage->format, size))
|
|
|
|
return;
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!ValidateCompressedTexImageRestrictions(mContext, imageTarget, level,
|
|
|
|
usage->format, size)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!ValidateTexImageSelection(imageTarget, level, offset, size,
|
|
|
|
&imageInfo))
|
|
|
|
return;
|
|
|
|
MOZ_ASSERT(imageInfo);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto dstUsage = imageInfo->mFormat;
|
|
|
|
if (usage != dstUsage) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidOperation(
|
2020-01-09 01:19:16 +03:00
|
|
|
"`format` must match the format of the"
|
|
|
|
" existing texture image.");
|
2015-11-25 07:15:29 +03:00
|
|
|
return;
|
2020-01-09 01:19:16 +03:00
|
|
|
}
|
2015-11-24 08:55:59 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto& format = usage->format;
|
|
|
|
switch (format->compression->family) {
|
|
|
|
// Forbidden:
|
|
|
|
case webgl::CompressionFamily::ETC1:
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidOperation(
|
2020-01-09 01:19:16 +03:00
|
|
|
"Format does not allow sub-image"
|
2018-07-27 07:46:33 +03:00
|
|
|
" updates.");
|
2015-08-04 00:34:46 +03:00
|
|
|
return;
|
2017-07-18 21:02:57 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
// Block-aligned:
|
2020-01-09 01:19:23 +03:00
|
|
|
case webgl::CompressionFamily::ES3: // Yes, the ES3 formats don't match
|
|
|
|
// the ES3
|
2020-01-09 01:19:16 +03:00
|
|
|
case webgl::CompressionFamily::S3TC: // default behavior.
|
|
|
|
case webgl::CompressionFamily::BPTC:
|
|
|
|
case webgl::CompressionFamily::RGTC:
|
|
|
|
if (!IsSubImageBlockAligned(format->compression, imageInfo, offset.x,
|
|
|
|
offset.y, size.x, size.y)) {
|
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"Format requires block-aligned sub-image"
|
|
|
|
" updates.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Full-only: (The ES3 default)
|
2020-11-10 01:04:21 +03:00
|
|
|
case webgl::CompressionFamily::ASTC:
|
|
|
|
case webgl::CompressionFamily::PVRTC:
|
2020-01-09 01:19:16 +03:00
|
|
|
if (offset.x || offset.y || size.x != imageInfo->mWidth ||
|
|
|
|
size.y != imageInfo->mHeight) {
|
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"Format does not allow partial sub-image"
|
|
|
|
" updates.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2020-11-10 01:04:21 +03:00
|
|
|
switch (usage->format->compression->family) {
|
|
|
|
case webgl::CompressionFamily::BPTC:
|
|
|
|
case webgl::CompressionFamily::RGTC:
|
|
|
|
if (level == 0) {
|
|
|
|
if (size.x % 4 != 0 || size.y % 4 != 0) {
|
|
|
|
mContext->ErrorInvalidOperation(
|
|
|
|
"For level == 0, width and height must be multiples of 4.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!ValidateCompressedTexUnpack(mContext, size, usage->format, imageSize))
|
|
|
|
return;
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Do the thing!
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (sub) {
|
|
|
|
if (!EnsureImageDataInitializedForUpload(this, imageTarget, level, offset,
|
|
|
|
size, imageInfo)) {
|
|
|
|
return;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2017-07-18 21:02:57 +03:00
|
|
|
const ScopedLazyBind bindPBO(mContext->gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
|
|
|
|
mContext->mBoundPixelUnpackBuffer);
|
2020-01-09 01:19:16 +03:00
|
|
|
GLenum error;
|
|
|
|
const void* ptr;
|
|
|
|
if (pboOffset) {
|
|
|
|
ptr = reinterpret_cast<const void*>(*pboOffset);
|
|
|
|
} else {
|
|
|
|
ptr = reinterpret_cast<const void*>(src.begin().get());
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!sub) {
|
|
|
|
error = DoCompressedTexImage(mContext->gl, imageTarget, level, formatEnum,
|
|
|
|
size.x, size.y, size.z, imageSize, ptr);
|
|
|
|
} else {
|
|
|
|
error = DoCompressedTexSubImage(mContext->gl, imageTarget, level, offset.x,
|
|
|
|
offset.y, offset.z, size.x, size.y, size.z,
|
|
|
|
formatEnum, imageSize, ptr);
|
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
if (error == LOCAL_GL_OUT_OF_MEMORY) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorOutOfMemory("Ran out of memory during upload.");
|
2019-06-14 06:26:18 +03:00
|
|
|
Truncate();
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
if (error) {
|
2019-02-20 07:43:32 +03:00
|
|
|
mContext->GenerateError(error, "Unexpected error from driver.");
|
2020-01-09 01:19:16 +03:00
|
|
|
nsCString call;
|
|
|
|
if (!sub) {
|
|
|
|
call = nsPrintfCString(
|
|
|
|
"DoCompressedTexImage(0x%04x, %u, 0x%04x, %u,%u,%u, %u, %p)",
|
|
|
|
imageTarget, level, formatEnum, size.x, size.y, size.z, imageSize,
|
|
|
|
ptr);
|
|
|
|
} else {
|
|
|
|
call = nsPrintfCString(
|
|
|
|
"DoCompressedTexSubImage(0x%04x, %u, %u,%u,%u, %u,%u,%u, 0x%04x, %u, "
|
2020-01-09 01:19:23 +03:00
|
|
|
"%p)",
|
2020-01-09 01:19:16 +03:00
|
|
|
imageTarget, level, offset.x, offset.y, offset.z, size.x, size.y,
|
|
|
|
size.z, formatEnum, imageSize, ptr);
|
|
|
|
}
|
|
|
|
gfxCriticalError() << "Unexpected error " << gfx::hexa(error)
|
|
|
|
<< " from driver: " << call.BeginReading();
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Update our specification data?
|
2020-01-09 01:19:16 +03:00
|
|
|
|
|
|
|
if (!sub) {
|
|
|
|
const auto uninitializedSlices = Nothing();
|
|
|
|
const webgl::ImageInfo newImageInfo{usage, size.x, size.y, size.z,
|
|
|
|
uninitializedSlices};
|
|
|
|
*imageInfo = newImageInfo;
|
|
|
|
InvalidateCaches();
|
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
}
|
2015-11-24 08:55:59 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////////
|
|
|
|
// CopyTex(Sub)Image
|
2015-11-24 08:55:59 +03:00
|
|
|
|
2018-07-27 07:46:33 +03:00
|
|
|
static bool ValidateCopyTexImageFormats(WebGLContext* webgl,
|
2015-11-25 07:15:29 +03:00
|
|
|
const webgl::FormatInfo* srcFormat,
|
|
|
|
const webgl::FormatInfo* dstFormat) {
|
|
|
|
MOZ_ASSERT(!srcFormat->compression);
|
|
|
|
if (dstFormat->compression) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidEnum(
|
|
|
|
"Specified destination must not have a compressed"
|
|
|
|
" format.");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (dstFormat->effectiveFormat == webgl::EffectiveFormat::RGB9_E5) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidOperation(
|
|
|
|
"RGB9_E5 is an invalid destination for"
|
|
|
|
" CopyTex(Sub)Image. (GLES 3.0.4 p145)");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
2015-11-24 06:27:13 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (!DoChannelsMatchForCopyTexImage(srcFormat, dstFormat)) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidOperation(
|
|
|
|
"Destination channels must be compatible with"
|
|
|
|
" source channels. (GLES 3.0.4 p140 Table 3.16)");
|
2015-11-25 07:15:29 +03:00
|
|
|
return false;
|
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
return true;
|
2015-11-24 08:55:59 +03:00
|
|
|
}
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
class ScopedCopyTexImageSource {
|
|
|
|
WebGLContext* const mWebGL;
|
|
|
|
GLuint mRB;
|
|
|
|
GLuint mFB;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
public:
|
2018-07-27 07:46:33 +03:00
|
|
|
ScopedCopyTexImageSource(WebGLContext* webgl, uint32_t srcWidth,
|
2015-11-25 07:15:29 +03:00
|
|
|
uint32_t srcHeight,
|
|
|
|
const webgl::FormatInfo* srcFormat,
|
|
|
|
const webgl::FormatUsageInfo* dstUsage);
|
|
|
|
~ScopedCopyTexImageSource();
|
|
|
|
};
|
|
|
|
|
|
|
|
ScopedCopyTexImageSource::ScopedCopyTexImageSource(
|
|
|
|
WebGLContext* webgl, uint32_t srcWidth, uint32_t srcHeight,
|
|
|
|
const webgl::FormatInfo* srcFormat, const webgl::FormatUsageInfo* dstUsage)
|
|
|
|
: mWebGL(webgl), mRB(0), mFB(0) {
|
|
|
|
switch (dstUsage->format->unsizedFormat) {
|
|
|
|
case webgl::UnsizedFormat::L:
|
|
|
|
case webgl::UnsizedFormat::A:
|
|
|
|
case webgl::UnsizedFormat::LA:
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->GenerateWarning(
|
|
|
|
"Copying to a LUMINANCE, ALPHA, or LUMINANCE_ALPHA"
|
2015-11-25 07:15:29 +03:00
|
|
|
" is deprecated, and has severely reduced performance"
|
2018-07-27 07:46:33 +03:00
|
|
|
" on some platforms.");
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
default:
|
|
|
|
MOZ_ASSERT(!dstUsage->textureSwizzleRGBA);
|
|
|
|
return;
|
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (!dstUsage->textureSwizzleRGBA) return;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl::GLContext* gl = webgl->gl;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
GLenum sizedFormat;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
switch (srcFormat->componentType) {
|
|
|
|
case webgl::ComponentType::NormUInt:
|
|
|
|
sizedFormat = LOCAL_GL_RGBA8;
|
|
|
|
break;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
case webgl::ComponentType::Float:
|
|
|
|
if (webgl->IsExtensionEnabled(
|
|
|
|
WebGLExtensionID::WEBGL_color_buffer_float)) {
|
|
|
|
sizedFormat = LOCAL_GL_RGBA32F;
|
2019-07-09 08:21:30 +03:00
|
|
|
webgl->WarnIfImplicit(WebGLExtensionID::WEBGL_color_buffer_float);
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (webgl->IsExtensionEnabled(
|
|
|
|
WebGLExtensionID::EXT_color_buffer_half_float)) {
|
|
|
|
sizedFormat = LOCAL_GL_RGBA16F;
|
2019-07-09 08:21:30 +03:00
|
|
|
webgl->WarnIfImplicit(WebGLExtensionID::EXT_color_buffer_half_float);
|
2015-11-25 07:15:29 +03:00
|
|
|
break;
|
|
|
|
}
|
2016-06-06 18:17:23 +03:00
|
|
|
MOZ_CRASH("GFX: Should be able to request CopyTexImage from Float.");
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
default:
|
2016-06-06 18:17:23 +03:00
|
|
|
MOZ_CRASH("GFX: Should be able to request CopyTexImage from this type.");
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl::ScopedTexture scopedTex(gl);
|
|
|
|
gl::ScopedBindTexture scopedBindTex(gl, scopedTex.Texture(),
|
|
|
|
LOCAL_GL_TEXTURE_2D);
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
|
|
|
|
LOCAL_GL_NEAREST);
|
|
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
|
|
|
|
LOCAL_GL_NEAREST);
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
GLint blitSwizzle[4] = {LOCAL_GL_ZERO};
|
|
|
|
switch (dstUsage->format->unsizedFormat) {
|
|
|
|
case webgl::UnsizedFormat::L:
|
|
|
|
blitSwizzle[0] = LOCAL_GL_RED;
|
|
|
|
break;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
case webgl::UnsizedFormat::A:
|
|
|
|
blitSwizzle[0] = LOCAL_GL_ALPHA;
|
|
|
|
break;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
case webgl::UnsizedFormat::LA:
|
|
|
|
blitSwizzle[0] = LOCAL_GL_RED;
|
|
|
|
blitSwizzle[1] = LOCAL_GL_ALPHA;
|
|
|
|
break;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
default:
|
2016-06-06 18:17:23 +03:00
|
|
|
MOZ_CRASH("GFX: Unhandled unsizedFormat.");
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R,
|
|
|
|
blitSwizzle[0]);
|
|
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G,
|
|
|
|
blitSwizzle[1]);
|
|
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B,
|
|
|
|
blitSwizzle[2]);
|
|
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A,
|
|
|
|
blitSwizzle[3]);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, sizedFormat, 0, 0, srcWidth,
|
|
|
|
srcHeight, 0);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
// Now create the swizzled FB we'll be exposing.
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
GLuint rgbaRB = 0;
|
|
|
|
GLuint rgbaFB = 0;
|
2019-06-17 09:40:27 +03:00
|
|
|
{
|
|
|
|
gl->fGenRenderbuffers(1, &rgbaRB);
|
|
|
|
gl::ScopedBindRenderbuffer scopedRB(gl, rgbaRB);
|
|
|
|
gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, sizedFormat, srcWidth,
|
|
|
|
srcHeight);
|
|
|
|
|
|
|
|
gl->fGenFramebuffers(1, &rgbaFB);
|
|
|
|
gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, rgbaFB);
|
|
|
|
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
|
|
|
|
LOCAL_GL_COLOR_ATTACHMENT0,
|
|
|
|
LOCAL_GL_RENDERBUFFER, rgbaRB);
|
|
|
|
|
|
|
|
const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
|
|
|
|
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
MOZ_CRASH("GFX: Temp framebuffer is not complete.");
|
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
// Draw-blit rgbaTex into rgbaFB.
|
|
|
|
const gfx::IntSize srcSize(srcWidth, srcHeight);
|
2017-07-18 05:17:41 +03:00
|
|
|
{
|
|
|
|
const gl::ScopedBindFramebuffer bindFB(gl, rgbaFB);
|
|
|
|
gl->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex.Texture(), srcSize,
|
|
|
|
srcSize);
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
// Leave RB and FB alive, and FB bound.
|
|
|
|
mRB = rgbaRB;
|
|
|
|
mFB = rgbaFB;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
template <typename T>
|
|
|
|
static inline GLenum ToGLHandle(const T& obj) {
|
|
|
|
return (obj ? obj->mGLName : 0);
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
ScopedCopyTexImageSource::~ScopedCopyTexImageSource() {
|
|
|
|
if (!mFB) {
|
|
|
|
MOZ_ASSERT(!mRB);
|
|
|
|
return;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
MOZ_ASSERT(mRB);
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl::GLContext* gl = mWebGL->gl;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
// If we're swizzling, it's because we're on a GL core (3.2+) profile, which
|
|
|
|
// has split framebuffer support.
|
|
|
|
gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
|
|
|
|
ToGLHandle(mWebGL->mBoundDrawFramebuffer));
|
|
|
|
gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
|
|
|
|
ToGLHandle(mWebGL->mBoundReadFramebuffer));
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fDeleteFramebuffers(1, &mFB);
|
|
|
|
gl->fDeleteRenderbuffers(1, &mRB);
|
2015-11-24 08:55:59 +03:00
|
|
|
}
|
2015-11-24 06:27:13 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-06-06 23:42:50 +03:00
|
|
|
static bool GetUnsizedFormatForCopy(GLenum internalFormat,
|
|
|
|
webgl::UnsizedFormat* const out) {
|
|
|
|
switch (internalFormat) {
|
|
|
|
case LOCAL_GL_RED:
|
|
|
|
*out = webgl::UnsizedFormat::R;
|
|
|
|
break;
|
|
|
|
case LOCAL_GL_RG:
|
|
|
|
*out = webgl::UnsizedFormat::RG;
|
|
|
|
break;
|
|
|
|
case LOCAL_GL_RGB:
|
|
|
|
*out = webgl::UnsizedFormat::RGB;
|
|
|
|
break;
|
|
|
|
case LOCAL_GL_RGBA:
|
|
|
|
*out = webgl::UnsizedFormat::RGBA;
|
|
|
|
break;
|
|
|
|
case LOCAL_GL_LUMINANCE:
|
|
|
|
*out = webgl::UnsizedFormat::L;
|
|
|
|
break;
|
|
|
|
case LOCAL_GL_ALPHA:
|
|
|
|
*out = webgl::UnsizedFormat::A;
|
|
|
|
break;
|
|
|
|
case LOCAL_GL_LUMINANCE_ALPHA:
|
|
|
|
*out = webgl::UnsizedFormat::LA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const webgl::FormatUsageInfo* ValidateCopyDestUsage(
|
|
|
|
WebGLContext* webgl, const webgl::FormatInfo* srcFormat,
|
|
|
|
GLenum internalFormat) {
|
|
|
|
const auto& fua = webgl->mFormatUsage;
|
|
|
|
|
2019-02-02 11:37:22 +03:00
|
|
|
switch (internalFormat) {
|
|
|
|
case LOCAL_GL_R8_SNORM:
|
|
|
|
case LOCAL_GL_RG8_SNORM:
|
|
|
|
case LOCAL_GL_RGB8_SNORM:
|
|
|
|
case LOCAL_GL_RGBA8_SNORM:
|
|
|
|
webgl->ErrorInvalidEnum("SNORM formats are invalid for CopyTexImage.");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-06-06 23:42:50 +03:00
|
|
|
auto dstUsage = fua->GetSizedTexUsage(internalFormat);
|
|
|
|
if (!dstUsage) {
|
|
|
|
// Ok, maybe it's unsized.
|
|
|
|
webgl::UnsizedFormat unsizedFormat;
|
|
|
|
if (!GetUnsizedFormatForCopy(internalFormat, &unsizedFormat)) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidEnumInfo("internalFormat", internalFormat);
|
2016-06-06 23:42:50 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto dstFormat = srcFormat->GetCopyDecayFormat(unsizedFormat);
|
2018-10-24 18:31:43 +03:00
|
|
|
if (dstFormat) {
|
2018-07-27 07:46:33 +03:00
|
|
|
dstUsage = fua->GetUsage(dstFormat->effectiveFormat);
|
2016-06-06 23:42:50 +03:00
|
|
|
}
|
|
|
|
if (!dstUsage) {
|
|
|
|
webgl->ErrorInvalidOperation(
|
2018-07-27 07:46:33 +03:00
|
|
|
"0x%04x is not a valid unsized format for"
|
2016-06-06 23:42:50 +03:00
|
|
|
" source format %s.",
|
2018-07-27 07:46:33 +03:00
|
|
|
internalFormat, srcFormat->name);
|
2016-06-06 23:42:50 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dstUsage;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2016-06-06 23:42:50 +03:00
|
|
|
// Alright, it's sized.
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-12-08 03:56:52 +03:00
|
|
|
const auto dstFormat = dstUsage->format;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-10-24 18:31:43 +03:00
|
|
|
if (dstFormat->componentType != srcFormat->componentType) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidOperation(
|
|
|
|
"For sized internalFormats, source and dest"
|
2016-06-06 23:42:50 +03:00
|
|
|
" component types must match. (source: %s, dest:"
|
2018-11-30 13:46:48 +03:00
|
|
|
" %s)",
|
2018-07-27 07:46:33 +03:00
|
|
|
srcFormat->name, dstFormat->name);
|
2016-06-06 23:42:50 +03:00
|
|
|
return nullptr;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2016-06-06 23:42:50 +03:00
|
|
|
bool componentSizesMatch = true;
|
|
|
|
if (dstFormat->r) {
|
|
|
|
componentSizesMatch &= (dstFormat->r == srcFormat->r);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2016-06-06 23:42:50 +03:00
|
|
|
if (dstFormat->g) {
|
|
|
|
componentSizesMatch &= (dstFormat->g == srcFormat->g);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2016-06-06 23:42:50 +03:00
|
|
|
if (dstFormat->b) {
|
|
|
|
componentSizesMatch &= (dstFormat->b == srcFormat->b);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2016-06-06 23:42:50 +03:00
|
|
|
if (dstFormat->a) {
|
|
|
|
componentSizesMatch &= (dstFormat->a == srcFormat->a);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2016-06-06 23:42:50 +03:00
|
|
|
if (!componentSizesMatch) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorInvalidOperation(
|
|
|
|
"For sized internalFormats, source and dest"
|
2016-06-06 23:42:50 +03:00
|
|
|
" component sizes must match exactly. (source: %s,"
|
|
|
|
" dest: %s)",
|
2018-07-27 07:46:33 +03:00
|
|
|
srcFormat->name, dstFormat->name);
|
2016-06-06 23:42:50 +03:00
|
|
|
return nullptr;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2016-06-06 23:42:50 +03:00
|
|
|
return dstUsage;
|
|
|
|
}
|
|
|
|
|
2019-05-23 23:48:21 +03:00
|
|
|
static bool ValidateCopyTexImageForFeedback(const WebGLContext& webgl,
|
|
|
|
const WebGLTexture& tex,
|
2020-01-09 01:19:23 +03:00
|
|
|
const uint32_t mipLevel,
|
2019-05-23 23:48:21 +03:00
|
|
|
const uint32_t zLayer) {
|
|
|
|
const auto& fb = webgl.BoundReadFb();
|
2016-07-13 10:48:55 +03:00
|
|
|
if (fb) {
|
2019-05-23 23:48:21 +03:00
|
|
|
MOZ_ASSERT(fb->ColorReadBuffer());
|
|
|
|
const auto& attach = *fb->ColorReadBuffer();
|
|
|
|
MOZ_ASSERT(attach.ZLayerCount() ==
|
|
|
|
1); // Multiview invalid for copyTexImage.
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2019-05-23 23:48:21 +03:00
|
|
|
if (attach.Texture() == &tex && attach.Layer() == zLayer &&
|
|
|
|
attach.MipLevel() == mipLevel) {
|
2016-07-13 10:48:55 +03:00
|
|
|
// Note that the TexImageTargets *don't* have to match for this to be
|
|
|
|
// undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL.
|
2019-05-23 23:48:21 +03:00
|
|
|
webgl.ErrorInvalidOperation(
|
2018-07-27 07:46:33 +03:00
|
|
|
"Feedback loop detected, as this texture"
|
2016-07-13 10:48:55 +03:00
|
|
|
" is already attached to READ_FRAMEBUFFER's"
|
|
|
|
" READ_BUFFER-selected COLOR_ATTACHMENT%u.",
|
2019-05-23 23:48:21 +03:00
|
|
|
attach.mAttachmentPoint);
|
2016-07-13 10:48:55 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-27 07:46:33 +03:00
|
|
|
static bool DoCopyTexOrSubImage(WebGLContext* webgl, bool isSubImage,
|
2020-03-31 04:00:50 +03:00
|
|
|
bool needsInit, WebGLTexture* const tex,
|
2019-06-21 19:52:50 +03:00
|
|
|
const TexImageTarget target, GLint level,
|
|
|
|
GLint xWithinSrc, GLint yWithinSrc,
|
2016-12-08 03:56:52 +03:00
|
|
|
uint32_t srcTotalWidth, uint32_t srcTotalHeight,
|
|
|
|
const webgl::FormatUsageInfo* srcUsage,
|
|
|
|
GLint xOffset, GLint yOffset, GLint zOffset,
|
2017-02-28 02:24:14 +03:00
|
|
|
uint32_t dstWidth, uint32_t dstHeight,
|
2018-07-27 07:46:33 +03:00
|
|
|
const webgl::FormatUsageInfo* dstUsage) {
|
2016-12-08 03:56:52 +03:00
|
|
|
const auto& gl = webgl->gl;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
|
|
////
|
|
|
|
|
2016-12-08 03:56:52 +03:00
|
|
|
int32_t readX, readY;
|
2017-02-28 02:24:14 +03:00
|
|
|
int32_t writeX, writeY;
|
|
|
|
int32_t rwWidth, rwHeight;
|
|
|
|
if (!Intersect(srcTotalWidth, xWithinSrc, dstWidth, &readX, &writeX,
|
2016-12-09 01:58:23 +03:00
|
|
|
&rwWidth) ||
|
|
|
|
!Intersect(srcTotalHeight, yWithinSrc, dstHeight, &readY, &writeY,
|
|
|
|
&rwHeight)) {
|
2018-07-27 07:46:33 +03:00
|
|
|
webgl->ErrorOutOfMemory("Bad subrect selection.");
|
2016-12-08 03:56:52 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-01-05 04:07:53 +03:00
|
|
|
writeX += xOffset;
|
2016-12-08 03:56:52 +03:00
|
|
|
writeY += yOffset;
|
|
|
|
|
2018-11-30 13:46:48 +03:00
|
|
|
////
|
2016-12-08 03:56:52 +03:00
|
|
|
|
|
|
|
GLenum error = 0;
|
2019-02-20 07:43:32 +03:00
|
|
|
nsCString errorText;
|
2018-11-30 13:46:48 +03:00
|
|
|
do {
|
2016-12-08 03:56:52 +03:00
|
|
|
const auto& idealUnpack = dstUsage->idealUnpack;
|
2020-03-31 04:00:50 +03:00
|
|
|
const auto& pi = idealUnpack->ToPacking();
|
|
|
|
|
|
|
|
UniqueBuffer zeros;
|
|
|
|
const bool fullOverwrite =
|
|
|
|
(uint32_t(rwWidth) == dstWidth && uint32_t(rwHeight) == dstHeight);
|
|
|
|
if (needsInit && !fullOverwrite) {
|
|
|
|
CheckedInt<size_t> byteCount = BytesPerPixel(pi);
|
|
|
|
byteCount *= dstWidth;
|
|
|
|
byteCount *= dstHeight;
|
|
|
|
|
|
|
|
if (byteCount.isValid()) {
|
2021-12-25 03:31:48 +03:00
|
|
|
zeros = UniqueBuffer::Take(calloc(1u, byteCount.value()));
|
2020-03-31 04:00:50 +03:00
|
|
|
}
|
2016-12-20 04:53:52 +03:00
|
|
|
|
2020-03-31 04:00:50 +03:00
|
|
|
if (!zeros.get()) {
|
|
|
|
webgl->ErrorOutOfMemory("Ran out of memory allocating zeros.");
|
|
|
|
return false;
|
2016-12-08 03:56:52 +03:00
|
|
|
}
|
2020-03-31 04:00:50 +03:00
|
|
|
}
|
2016-12-08 03:56:52 +03:00
|
|
|
|
2020-03-31 04:00:50 +03:00
|
|
|
if (!isSubImage || zeros) {
|
2022-02-12 05:20:42 +03:00
|
|
|
webgl::PixelPackingState{}.AssertCurrentUnpack(*gl, webgl->IsWebGL2());
|
2020-08-23 09:09:33 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
|
2020-07-22 01:57:01 +03:00
|
|
|
const auto revert = MakeScopeExit(
|
|
|
|
[&]() { gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); });
|
2020-03-31 04:00:50 +03:00
|
|
|
if (!isSubImage) {
|
|
|
|
error = DoTexImage(gl, target, level, idealUnpack, dstWidth, dstHeight,
|
|
|
|
1, nullptr);
|
|
|
|
if (error) {
|
|
|
|
errorText = nsPrintfCString(
|
|
|
|
"DoTexImage(0x%04x, %i, {0x%04x, 0x%04x, 0x%04x}, %u,%u,1) -> "
|
|
|
|
"0x%04x",
|
|
|
|
target.get(), level, idealUnpack->internalFormat,
|
|
|
|
idealUnpack->unpackFormat, idealUnpack->unpackType, dstWidth,
|
|
|
|
dstHeight, error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (zeros) {
|
|
|
|
error = DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset,
|
|
|
|
dstWidth, dstHeight, 1, pi, zeros.get());
|
|
|
|
if (error) {
|
|
|
|
errorText = nsPrintfCString(
|
|
|
|
"DoTexSubImage(0x%04x, %i, %i,%i,%i, %u,%u,1, {0x%04x, 0x%04x}) "
|
|
|
|
"-> "
|
|
|
|
"0x%04x",
|
|
|
|
target.get(), level, xOffset, yOffset, zOffset, dstWidth,
|
|
|
|
dstHeight, idealUnpack->unpackFormat, idealUnpack->unpackType,
|
|
|
|
error);
|
|
|
|
break;
|
|
|
|
}
|
2019-02-20 07:43:32 +03:00
|
|
|
}
|
2016-06-22 20:28:11 +03:00
|
|
|
}
|
|
|
|
|
2018-07-27 07:46:33 +03:00
|
|
|
if (!rwWidth || !rwHeight) {
|
2015-11-25 07:15:29 +03:00
|
|
|
// There aren't any pixels to copy, so we're 'done'.
|
|
|
|
return true;
|
2016-12-08 03:56:52 +03:00
|
|
|
}
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2016-12-08 03:56:52 +03:00
|
|
|
const auto& srcFormat = srcUsage->format;
|
2018-07-27 07:46:33 +03:00
|
|
|
ScopedCopyTexImageSource maybeSwizzle(webgl, srcTotalWidth, srcTotalHeight,
|
|
|
|
srcFormat, dstUsage);
|
2022-10-05 01:38:13 +03:00
|
|
|
|
|
|
|
error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX,
|
|
|
|
readY, rwWidth, rwHeight);
|
2019-02-20 07:43:32 +03:00
|
|
|
if (error) {
|
|
|
|
errorText = nsPrintfCString(
|
2022-10-05 01:38:13 +03:00
|
|
|
"DoCopyTexSubImage(0x%04x, %i, %i,%i,%i, %i,%i, %u,%u) -> 0x%04x",
|
2019-02-20 07:43:32 +03:00
|
|
|
target.get(), level, writeX, writeY, zOffset, readX, readY, rwWidth,
|
|
|
|
rwHeight, error);
|
|
|
|
break;
|
|
|
|
}
|
2015-11-24 08:55:59 +03:00
|
|
|
|
2015-08-04 00:34:46 +03:00
|
|
|
return true;
|
2016-12-08 03:56:52 +03:00
|
|
|
} while (false);
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2017-06-22 21:32:00 +03:00
|
|
|
if (error == LOCAL_GL_OUT_OF_MEMORY) {
|
|
|
|
webgl->ErrorOutOfMemory("Ran out of memory during texture copy.");
|
2019-06-14 06:26:18 +03:00
|
|
|
tex->Truncate();
|
2017-06-22 21:32:00 +03:00
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2017-06-22 21:32:00 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
if (gl->IsANGLE() && error == LOCAL_GL_INVALID_OPERATION) {
|
|
|
|
webgl->ErrorImplementationBug(
|
|
|
|
"ANGLE is particular about CopyTexSubImage"
|
|
|
|
" formats matching exactly.");
|
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2015-11-25 07:15:29 +03:00
|
|
|
|
2019-02-20 07:43:32 +03:00
|
|
|
webgl->GenerateError(error, "Unexpected error from driver.");
|
|
|
|
gfxCriticalError() << "Unexpected error from driver: "
|
|
|
|
<< errorText.BeginReading();
|
2018-10-17 07:18:15 +03:00
|
|
|
return false;
|
2015-08-04 00:34:46 +03:00
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
// CopyTexSubImage if `!respecFormat`
|
|
|
|
void WebGLTexture::CopyTexImage(GLenum imageTarget, uint32_t level,
|
|
|
|
GLenum respecFormat, const uvec3& dstOffset,
|
|
|
|
const ivec2& srcOffset, const uvec2& size2) {
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Get source info
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-10-17 07:18:15 +03:00
|
|
|
const webgl::FormatUsageInfo* srcUsage;
|
2015-11-25 07:15:29 +03:00
|
|
|
uint32_t srcTotalWidth;
|
|
|
|
uint32_t srcTotalHeight;
|
2016-07-09 00:57:18 +03:00
|
|
|
if (!mContext->BindCurFBForColorRead(&srcUsage, &srcTotalWidth,
|
2018-07-27 07:46:33 +03:00
|
|
|
&srcTotalHeight)) {
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto& srcFormat = srcUsage->format;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:23 +03:00
|
|
|
if (!ValidateCopyTexImageForFeedback(*mContext, *this, level, dstOffset.z))
|
2019-05-23 23:48:21 +03:00
|
|
|
return;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto size = uvec3{size2.x, size2.y, 1};
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
2020-01-09 01:19:16 +03:00
|
|
|
// Get dest info
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
webgl::ImageInfo* imageInfo;
|
|
|
|
const webgl::FormatUsageInfo* dstUsage;
|
|
|
|
if (respecFormat) {
|
|
|
|
if (!ValidateTexImageSpecification(imageTarget, level, size, &imageInfo))
|
|
|
|
return;
|
|
|
|
MOZ_ASSERT(imageInfo);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
dstUsage = ValidateCopyDestUsage(mContext, srcFormat, respecFormat);
|
|
|
|
if (!dstUsage) return;
|
2015-08-04 00:34:46 +03:00
|
|
|
|
2022-10-03 16:27:12 +03:00
|
|
|
if (!ValidateFormatAndSize(mContext, imageTarget, dstUsage->format, size))
|
2020-01-09 01:19:16 +03:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
if (!ValidateTexImageSelection(imageTarget, level, dstOffset, size,
|
|
|
|
&imageInfo)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(imageInfo);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
dstUsage = imageInfo->mFormat;
|
|
|
|
MOZ_ASSERT(dstUsage);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
const auto& dstFormat = dstUsage->format;
|
2016-07-09 00:57:18 +03:00
|
|
|
if (!mContext->IsWebGL2() && dstFormat->d) {
|
2018-07-27 07:46:33 +03:00
|
|
|
mContext->ErrorInvalidOperation(
|
2020-01-09 01:19:16 +03:00
|
|
|
"Function may not be called with format %s.", dstFormat->name);
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Check that source and dest info are compatible
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-07-27 07:46:33 +03:00
|
|
|
if (!ValidateCopyTexImageFormats(mContext, srcFormat, dstFormat)) return;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Do the thing!
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2020-03-31 04:00:50 +03:00
|
|
|
const bool isSubImage = !respecFormat;
|
|
|
|
bool expectsInit = true;
|
|
|
|
if (isSubImage) {
|
2020-01-09 01:19:16 +03:00
|
|
|
if (!EnsureImageDataInitializedForUpload(this, imageTarget, level,
|
2020-03-31 04:00:50 +03:00
|
|
|
dstOffset, size, imageInfo,
|
|
|
|
&expectsInit)) {
|
2020-01-09 01:19:16 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2020-03-31 04:00:50 +03:00
|
|
|
if (!DoCopyTexOrSubImage(mContext, isSubImage, expectsInit, this, imageTarget,
|
|
|
|
level, srcOffset.x, srcOffset.y, srcTotalWidth,
|
2020-01-09 01:19:16 +03:00
|
|
|
srcTotalHeight, srcUsage, dstOffset.x, dstOffset.y,
|
|
|
|
dstOffset.z, size.x, size.y, dstUsage)) {
|
2023-10-25 03:21:00 +03:00
|
|
|
Truncate();
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
mContext->OnDataAllocCall();
|
|
|
|
|
2015-11-25 07:15:29 +03:00
|
|
|
////////////////////////////////////
|
|
|
|
// Update our specification data?
|
2020-01-09 01:19:14 +03:00
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (respecFormat) {
|
|
|
|
const auto uninitializedSlices = Nothing();
|
|
|
|
const webgl::ImageInfo newImageInfo{dstUsage, size.x, size.y, size.z,
|
|
|
|
uninitializedSlices};
|
|
|
|
*imageInfo = newImageInfo;
|
|
|
|
InvalidateCaches();
|
2020-01-09 01:19:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-04 00:34:46 +03:00
|
|
|
} // namespace mozilla
|