Bug 1769747 - Support non-RGBA video-to-texture fastpath. r=gfx-reviewers,nical

Also scale back some asserts from debug-fatal to just warnings, because
I kept hitting them during testing because of our poor surface lifetime
handling.

Differential Revision: https://phabricator.services.mozilla.com/D157993
This commit is contained in:
Kelsey Gilbert 2022-10-04 20:31:42 +00:00
Родитель 8d369ed0b3
Коммит 5e0abe1b97
12 изменённых файлов: 268 добавлений и 100 удалений

Просмотреть файл

@ -4049,6 +4049,35 @@ void webgl::TexUnpackBlobDesc::Shrink(const webgl::PackingInfo& pi) {
// -
// Because often you need a null-terminated copy for printf.
inline std::string ToString(std::string_view s) { return std::string{s}; }
std::string_view ToString(const layers::SurfaceDescriptor::Type t) {
switch (t) {
#define _(X) \
case layers::SurfaceDescriptor::X: \
return #X;
_(T__None)
_(TSurfaceDescriptorBuffer)
_(TSurfaceDescriptorD3D10)
_(TSurfaceDescriptorDXGIYCbCr)
_(TSurfaceDescriptorDMABuf)
_(TSurfaceTextureDescriptor)
_(TSurfaceDescriptorAndroidHardwareBuffer)
_(TEGLImageDescriptor)
_(TSurfaceDescriptorMacIOSurface)
_(TSurfaceDescriptorSharedGLTexture)
_(TSurfaceDescriptorGPUVideo)
_(TSurfaceDescriptorRecorded)
_(TSurfaceDescriptorRemoteTexture)
_(TSurfaceDescriptorDcompSurface)
_(Tnull_t)
#undef _
}
MOZ_ASSERT_UNREACHABLE();
}
void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
GLint level, GLenum respecFormat,
const ivec3& offset,
@ -4246,7 +4275,8 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
const bool canUploadViaSd = contextInfo.uploadableSdTypes[sdType];
if (!canUploadViaSd) {
const nsPrintfCString msg(
"Fast uploads for resource type %i not implemented.", int(sdType));
"Fast uploads for resource type %s not implemented.",
ToString(ToString(sdType)).c_str());
return Some(ToString(msg));
}

Просмотреть файл

@ -393,8 +393,8 @@ bool TexUnpackBlob::ConvertIfNeeded(
if (srcFormat != dstFormat) {
webgl->GeneratePerfWarning(
"Conversion requires pixel reformatting. (%u->%u)", uint32_t(srcFormat),
uint32_t(dstFormat));
"Conversion requires pixel reformatting. (%s->%s)",
ToString(srcFormat).c_str(), ToString(dstFormat).c_str());
} else if (fnHasPremultMismatch()) {
webgl->GeneratePerfWarning(
"Conversion requires change in"
@ -664,48 +664,61 @@ Maybe<std::string> BlitPreventReason(const int32_t level, const ivec3& offset,
const auto& size = desc.size;
const auto& unpacking = desc.unpacking;
const auto ret = [&]() -> const char* {
if (size.z != 1) {
return "depth is not 1";
}
if (offset.x != 0 || offset.y != 0 || offset.z != 0) {
return "x/y/zOffset is not 0";
}
if (unpacking.skipPixels || unpacking.skipRows || unpacking.skipImages) {
return "non-zero UNPACK_SKIP_* not yet supported";
}
const auto premultReason = [&]() -> const char* {
if (desc.srcAlphaType == gfxAlphaType::Opaque) return nullptr;
const bool srcIsPremult = (desc.srcAlphaType == gfxAlphaType::Premult);
const auto& dstIsPremult = unpacking.premultiplyAlpha;
if (srcIsPremult == dstIsPremult) return nullptr;
if (dstIsPremult) {
return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
} else {
return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
}
}();
if (premultReason) return premultReason;
if (pi.format != LOCAL_GL_RGBA) {
return "`format` is not RGBA";
}
if (pi.type != LOCAL_GL_UNSIGNED_BYTE) {
return "`type` is not UNSIGNED_BYTE";
}
return nullptr;
}();
if (ret) {
return Some(std::string(ret));
if (size.z != 1) {
return Some(std::string("depth is not 1"));
}
if (offset.x != 0 || offset.y != 0 || offset.z != 0) {
return Some(std::string("x/y/zOffset is not 0"));
}
if (unpacking.skipPixels || unpacking.skipRows || unpacking.skipImages) {
return Some(std::string("non-zero UNPACK_SKIP_* not yet supported"));
}
const auto premultReason = [&]() -> const char* {
if (desc.srcAlphaType == gfxAlphaType::Opaque) return nullptr;
const bool srcIsPremult = (desc.srcAlphaType == gfxAlphaType::Premult);
const auto& dstIsPremult = unpacking.premultiplyAlpha;
if (srcIsPremult == dstIsPremult) return nullptr;
if (dstIsPremult) {
return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
} else {
return "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
}
}();
if (premultReason) return Some(std::string(premultReason));
// RGBA8 spec'd as renderable.
// RGB8 we can probably guarantee to be renderable, with an init-time test.
// RG8 (etc) via draw-to-RGBA8 then copyTexSubImage.
switch (pi.format) {
case LOCAL_GL_RGBA:
case LOCAL_GL_RGB:
break;
default:
return Some(std::string("`format` is ") + EnumString(pi.format) +
", not RGB/RGBA");
}
if (pi.type != LOCAL_GL_UNSIGNED_BYTE) {
// Eventually, we can upload other types, so long as we know that
// (RGBA, type) is renderable.
return Some(std::string("`type` is ") + EnumString(pi.type) +
", not UNSIGNED_BYTE");
}
return {};
}
} // namespace webgl
void CopyTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level,
GLint xOffset, GLint yOffset, GLint zOffset, GLint x,
GLint y, GLsizei width, GLsizei height);
namespace webgl {
bool TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec,
WebGLTexture* tex, GLint level,
const webgl::DriverUnpackInfo* dui,
@ -747,19 +760,63 @@ bool TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec,
gl::ScopedFramebuffer scopedFB(gl);
gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());
{
gl::GLContext::LocalErrorScope errorScope(*gl);
MOZ_RELEASE_ASSERT(pi.type == LOCAL_GL_UNSIGNED_BYTE);
gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_COLOR_ATTACHMENT0, target,
tex->mGLName, level);
UniquePtr<gl::MozFramebuffer> intermediate;
auto blitDestTarget = target;
auto blitDestTex = tex->mGLName;
auto blitDestLevel = level;
const auto err = errorScope.GetError();
MOZ_ALWAYS_TRUE(!err);
const auto AttachToFb = [&]() {
switch (blitDestTarget) {
case LOCAL_GL_TEXTURE_2D:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_COLOR_ATTACHMENT0, blitDestTarget,
blitDestTex, blitDestLevel);
break;
case LOCAL_GL_TEXTURE_3D:
case LOCAL_GL_TEXTURE_2D_ARRAY:
gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_COLOR_ATTACHMENT0, blitDestTex,
blitDestLevel, zOffset);
break;
default:
MOZ_ASSERT_UNREACHABLE("blitDestTarget");
}
const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
return false;
}
return true;
};
auto fbOk = AttachToFb();
if (!fbOk) {
// If not renderable, create intermediate rgba8.
webgl->GeneratePerfWarning(
"Copying to (%s,%s) requires an intermediate RGBA8 renderable.",
EnumString(pi.format).c_str(), EnumString(pi.type).c_str());
intermediate = gl::MozFramebuffer::Create(gl, {size.x, size.y}, 0, false);
blitDestTarget = LOCAL_GL_TEXTURE_2D;
blitDestTex = intermediate->ColorTex();
blitDestLevel = 0;
fbOk = AttachToFb();
}
MOZ_ASSERT(fbOk);
if (!fbOk) {
webgl->ErrorImplementationBug("Texture upload gpu-copy fastpath failed!");
tex->Truncate();
return false;
}
const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
MOZ_ALWAYS_TRUE(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
const auto dstOrigin =
(unpacking.flipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft);
@ -776,6 +833,12 @@ bool TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec,
"Fast Tex(Sub)Image upload failed without recourse, clearing to "
"[0.2, 0.0, 0.2, 1.0]. Please file a bug!");
}
if (intermediate) {
gl->fBindTexture(target, tex->mGLName);
CopyTexSubImage(gl, target, level, xOffset, yOffset, zOffset, 0, 0,
size.x, size.y);
}
}
return true;

Просмотреть файл

@ -691,12 +691,9 @@ GLenum DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target,
return errorScope.GetError();
}
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);
void CopyTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level,
GLint xOffset, GLint yOffset, GLint zOffset, GLint x,
GLint y, GLsizei width, GLsizei height) {
if (IsTarget3D(target)) {
gl->fCopyTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, x, y,
width, height);
@ -705,8 +702,6 @@ static inline GLenum DoCopyTexSubImage(gl::GLContext* gl, TexImageTarget target,
gl->fCopyTexSubImage2D(target.get(), level, xOffset, yOffset, x, y, width,
height);
}
return errorScope.GetError();
}
//////////////////////////////////////////////////////////////////////////////////////////
@ -1791,12 +1786,15 @@ static bool DoCopyTexOrSubImage(WebGLContext* webgl, bool isSubImage,
const auto& srcFormat = srcUsage->format;
ScopedCopyTexImageSource maybeSwizzle(webgl, srcTotalWidth, srcTotalHeight,
srcFormat, dstUsage);
error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX,
readY, rwWidth, rwHeight);
{
auto errorScope = gl::GLContext::LocalErrorScope(*gl);
CopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX, readY,
rwWidth, rwHeight);
error = errorScope.GetError();
}
if (error) {
errorText = nsPrintfCString(
"DoCopyTexSubImage(0x%04x, %i, %i,%i,%i, %i,%i, %u,%u) -> 0x%04x",
"CopyTexSubImage(0x%04x, %i, %i,%i,%i, %i,%i, %u,%u) -> 0x%04x",
target.get(), level, writeX, writeY, zOffset, readX, readY, rwWidth,
rwHeight, error);
break;

54
dom/canvas/WebGLTypes.cpp Normal file
Просмотреть файл

@ -0,0 +1,54 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "WebGLTypes.h"
// -
namespace mozilla {
std::string ToString(const WebGLTexelFormat format) {
switch (format) {
#define _(X) case WebGLTexelFormat::X: return #X;
_(None)
_(FormatNotSupportingAnyConversion)
_(Auto)
// 1-channel formats
_(A8)
_(A16F)
_(A32F)
_(R8)
_(R16F)
_(R32F)
// 2-channel formats
_(RA8)
_(RA16F)
_(RA32F)
_(RG8)
_(RG16F)
_(RG32F)
// 3-channel formats
_(RGB8)
_(RGB565)
_(RGB11F11F10F)
_(RGB16F)
_(RGB32F)
// 4-channel formats
_(RGBA8)
_(RGBA5551)
_(RGBA4444)
_(RGBA16F)
_(RGBA32F)
// DOM element source only formats.
_(RGBX8)
_(BGRX8)
_(BGRA8)
#undef _
}
MOZ_ASSERT_UNREACHABLE();
}
} // namespace mozilla

Просмотреть файл

@ -209,6 +209,8 @@ enum class WebGLTexelFormat : uint8_t {
BGRA8
};
std::string ToString(WebGLTexelFormat);
enum class WebGLTexImageFunc : uint8_t {
TexImage,
TexSubImage,

Просмотреть файл

@ -159,6 +159,7 @@ UNIFIED_SOURCES += [
"WebGLTexture.cpp",
"WebGLTextureUpload.cpp",
"WebGLTransformFeedback.cpp",
"WebGLTypes.cpp",
"WebGLValidateStrings.cpp",
"WebGLVertexArray.cpp",
"WebGLVertexArrayFake.cpp",

Просмотреть файл

@ -488,7 +488,7 @@ DrawBlitProg::~DrawBlitProg() {
gl->fDeleteProgram(mProg);
}
void DrawBlitProg::Draw(const BaseArgs& args,
bool DrawBlitProg::Draw(const BaseArgs& args,
const YUVArgs* const argsYUV) const {
const auto& gl = mParent.mGL;
@ -546,6 +546,7 @@ void DrawBlitProg::Draw(const BaseArgs& args,
default:
gfxCriticalError()
<< "Bad mType_uColorMatrix: " << gfx::hexa(mType_uColorMatrix);
return false;
}
}
}
@ -581,22 +582,41 @@ void DrawBlitProg::Draw(const BaseArgs& args,
gl->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0);
}
gl->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
if (mParent.mQuadVAO) {
gl->fBindVertexArray(oldVAO);
} else {
if (vaa0Enabled) {
gl->fEnableVertexAttribArray(0);
const auto restore = MakeScopeExit([&]() {
if (mParent.mQuadVAO) {
gl->fBindVertexArray(oldVAO);
} else {
gl->fDisableVertexAttribArray(0);
if (vaa0Enabled) {
gl->fEnableVertexAttribArray(0);
} else {
gl->fDisableVertexAttribArray(0);
}
// The current VERTEX_ARRAY_BINDING is not necessarily the same as the
// buffer set for vaa0Buffer.
const ScopedBindArrayBuffer bindVBO(gl, vaa0Buffer);
gl->fVertexAttribPointer(0, vaa0Size, vaa0Type, bool(vaa0Normalized),
vaa0Stride, vaa0Pointer);
}
});
{
auto errorScope = GLContext::LocalErrorScope(*gl);
gl->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
const auto err = errorScope.GetError();
if (err) {
MOZ_ASSERT(err ==
LOCAL_GL_INVALID_OPERATION); // ANGLE does this if we hand it
// an invalid share handle.
gfxCriticalNote << "glDrawArrays generated "
<< GLContext::GLErrorToString(err)
<< " in DrawBlitProg::Draw.";
return false;
}
// The current VERTEX_ARRAY_BINDING is not necessarily the same as the
// buffer set for vaa0Buffer.
const ScopedBindArrayBuffer bindVBO(gl, vaa0Buffer);
gl->fVertexAttribPointer(0, vaa0Size, vaa0Type, bool(vaa0Normalized),
vaa0Stride, vaa0Pointer);
}
return true;
}
// --
@ -980,6 +1000,11 @@ bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref& surfaceTexture,
const ScopedBindTexture savedTex(mGL, surfaceTexture->GetTexName(),
LOCAL_GL_TEXTURE_EXTERNAL);
surfaceTexture->UpdateTexImage();
const auto scopedRelease = MakeScopeExit([&]() {
if (surfaceTexture->IsSingleBuffer()) {
surfaceTexture->ReleaseTexImage();
}
});
const auto transform3 = Mat3::I();
const auto srcOrigin = OriginPos::TopLeft;
const bool yFlip = (srcOrigin != destOrigin);
@ -987,13 +1012,7 @@ bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref& surfaceTexture,
{kFragHeader_TexExt, {kFragSample_OnePlane, kFragConvert_None}});
const DrawBlitProg::BaseArgs baseArgs = {transform3, yFlip, destSize,
Nothing()};
prog->Draw(baseArgs, nullptr);
if (surfaceTexture->IsSingleBuffer()) {
surfaceTexture->ReleaseTexImage();
}
return true;
return prog->Draw(baseArgs, nullptr);
}
#endif
@ -1129,8 +1148,7 @@ bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData,
yFlip, destSize, Nothing()};
const DrawBlitProg::YUVArgs yuvArgs = {
SubRectMat3(clipRect, uvTexSize, divisors), Some(yuvData.mYUVColorSpace)};
prog->Draw(baseArgs, &yuvArgs);
return true;
return prog->Draw(baseArgs, &yuvArgs);
}
// -------------------------------------
@ -1275,8 +1293,7 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf,
kFragHeader_Tex2DRect,
{fragSample, kFragConvert_ColorMatrix},
});
prog->Draw(baseArgs, pYuvArgs);
return true;
return prog->Draw(baseArgs, pYuvArgs);
}
#endif
@ -1315,7 +1332,7 @@ void GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex,
const bool yFlip = false;
const DrawBlitProg::BaseArgs baseArgs = {texMatrix0, yFlip, destSize,
Nothing()};
prog->Draw(baseArgs);
(void)prog->Draw(baseArgs);
}
// -----------------------------------------------------------------------------
@ -1515,9 +1532,7 @@ bool GLBlitHelper::Blit(DMABufSurface* surface, const gfx::IntSize& destSize,
const auto& prog =
GetDrawBlitProg({kFragHeader_Tex2D, {fragSample, fragConvert}});
prog->Draw(baseArgs, pYuvArgs);
return true;
return prog->Draw(baseArgs, pYuvArgs);
}
bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage* srcImage,

Просмотреть файл

@ -161,7 +161,8 @@ class DrawBlitProg final {
Maybe<gfx::YUVColorSpace> colorSpaceForMatrix;
};
void Draw(const BaseArgs& args, const YUVArgs* argsYUV = nullptr) const;
[[nodiscard]] bool Draw(const BaseArgs& args,
const YUVArgs* argsYUV = nullptr) const;
};
class ScopedSaveMultiTex final {

Просмотреть файл

@ -263,7 +263,8 @@ bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc,
tex = OpenSharedTexture(d3d, handle);
}
if (!tex) {
MOZ_GL_ASSERT(mGL, false); // Get a nullptr from OpenSharedResource.
gfxCriticalNote
<< "Failed to open ID3D11Texture2D in GLBlitHelper::BlitDescriptor.";
return false;
}
const RefPtr<ID3D11Texture2D> texList[2] = {tex, tex};
@ -319,8 +320,7 @@ bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc,
const auto& prog = GetDrawBlitProg(
{kFragHeader_TexExt, {kFragSample_TwoPlane, kFragConvert_ColorMatrix}});
prog->Draw(baseArgs, &yuvArgs);
return true;
return prog->Draw(baseArgs, &yuvArgs);
}
bool GLBlitHelper::BlitDescriptor(
@ -370,8 +370,7 @@ bool GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3],
const auto& prog = GetDrawBlitProg(
{kFragHeader_TexExt, {kFragSample_ThreePlane, kFragConvert_ColorMatrix}});
prog->Draw(baseArgs, &yuvArgs);
return true;
return prog->Draw(baseArgs, &yuvArgs);
}
} // namespace gl

Просмотреть файл

@ -580,6 +580,10 @@ class GLContext : public GenericAtomicRefCounted, public SupportsWeakPtr {
mOldTop = mGL.GetError();
}
private:
LocalErrorScope(const LocalErrorScope&) = default;
public:
/// Never returns CONTEXT_LOST.
GLenum GetError() {
MOZ_ASSERT(!mHasBeenChecked);

Просмотреть файл

@ -56,7 +56,8 @@ class MozFramebuffer final {
const GLuint mColorName;
public:
// Create a new framebuffer with the specified properties.
// Create a new RGBA8 framebuffer with the specified properties.
// samples:0 for texture, samples:1 is subtly-different 1xMSAA.
static UniquePtr<MozFramebuffer> Create(GLContext* gl,
const gfx::IntSize& size,
uint32_t samples, bool depthStencil);

Просмотреть файл

@ -1949,7 +1949,7 @@ Maybe<HANDLE> GpuProcessD3D11TextureMap::GetSharedHandleOfCopiedTexture(
auto it = textures->find(aTextureId);
if (it == textures->end()) {
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
gfxCriticalNoteOnce << "Didn't find id in mD3D11TexturesById.";
return Nothing();
}