diff options
Diffstat (limited to 'dom/canvas/WebGLContext.cpp')
-rw-r--r-- | dom/canvas/WebGLContext.cpp | 318 |
1 files changed, 210 insertions, 108 deletions
diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index 4bd189c46c..669e131bd4 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -894,63 +894,62 @@ void WebGLContext::BlitBackbufferToCurDriverFB( if (mScissorTestEnabled) { gl->fDisable(LOCAL_GL_SCISSOR_TEST); } - - [&]() { - // If a MozFramebuffer is supplied, ensure that a WebGLFramebuffer is not - // used since it might not have completeness info, while the MozFramebuffer - // can still supply the needed information. - MOZ_ASSERT(!(srcAsMozFb && srcAsWebglFb)); - const auto* mozFb = srcAsMozFb ? srcAsMozFb : mDefaultFB.get(); - GLuint fbo = 0; - gfx::IntSize size; - if (srcAsWebglFb) { - fbo = srcAsWebglFb->mGLName; - const auto* info = srcAsWebglFb->GetCompletenessInfo(); - MOZ_ASSERT(info); - size = gfx::IntSize(info->width, info->height); - } else { - fbo = mozFb->mFB; - size = mozFb->mSize; - } - - // If no format conversion is necessary, then attempt to directly blit - // between framebuffers. Otherwise, if we need to convert to RGBA from - // the source format, then we will need to use the texture blit path - // below. - if (!srcIsBGRA) { - if (gl->IsSupported(gl::GLFeature::framebuffer_blit)) { - gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo); - gl->fBlitFramebuffer(0, 0, size.width, size.height, 0, 0, size.width, - size.height, LOCAL_GL_COLOR_BUFFER_BIT, - LOCAL_GL_NEAREST); - return; - } - if (mDefaultFB->mSamples && - gl->IsExtensionSupported( - gl::GLContext::APPLE_framebuffer_multisample)) { - gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo); - gl->fResolveMultisampleFramebufferAPPLE(); - return; - } + const auto cleanup = MakeScopeExit([&]() { + if (mScissorTestEnabled) { + gl->fEnable(LOCAL_GL_SCISSOR_TEST); } + }); - GLuint colorTex = 0; - if (srcAsWebglFb) { - const auto& attach = srcAsWebglFb->ColorAttachment0(); - MOZ_ASSERT(attach.Texture()); - colorTex = attach.Texture()->mGLName; - } else { - colorTex = mozFb->ColorTex(); + // If a MozFramebuffer is supplied, ensure that a WebGLFramebuffer is not + // used since it might not have completeness info, while the MozFramebuffer + // can still supply the needed information. + MOZ_ASSERT(!(srcAsMozFb && srcAsWebglFb)); + const auto* mozFb = srcAsMozFb ? srcAsMozFb : mDefaultFB.get(); + GLuint fbo = 0; + gfx::IntSize size; + if (srcAsWebglFb) { + fbo = srcAsWebglFb->mGLName; + const auto* info = srcAsWebglFb->GetCompletenessInfo(); + MOZ_ASSERT(info); + size = gfx::IntSize(info->width, info->height); + } else { + fbo = mozFb->mFB; + size = mozFb->mSize; + } + + // If no format conversion is necessary, then attempt to directly blit + // between framebuffers. Otherwise, if we need to convert to RGBA from + // the source format, then we will need to use the texture blit path + // below. + if (!srcIsBGRA) { + if (gl->IsSupported(gl::GLFeature::framebuffer_blit)) { + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo); + gl->fBlitFramebuffer(0, 0, size.width, size.height, 0, 0, size.width, + size.height, LOCAL_GL_COLOR_BUFFER_BIT, + LOCAL_GL_NEAREST); + return; } + if (mDefaultFB->mSamples && + gl->IsExtensionSupported( + gl::GLContext::APPLE_framebuffer_multisample)) { + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo); + gl->fResolveMultisampleFramebufferAPPLE(); + return; + } + } - // DrawBlit handles ColorMask itself. - gl->BlitHelper()->DrawBlitTextureToFramebuffer( - colorTex, size, size, LOCAL_GL_TEXTURE_2D, srcIsBGRA); - }(); - - if (mScissorTestEnabled) { - gl->fEnable(LOCAL_GL_SCISSOR_TEST); + GLuint colorTex = 0; + if (srcAsWebglFb) { + const auto& attach = srcAsWebglFb->ColorAttachment0(); + MOZ_ASSERT(attach.Texture()); + colorTex = attach.Texture()->mGLName; + } else { + colorTex = mozFb->ColorTex(); } + + // DrawBlit handles ColorMask itself. + gl->BlitHelper()->DrawBlitTextureToFramebuffer( + colorTex, size, size, LOCAL_GL_TEXTURE_2D, srcIsBGRA); } // - @@ -960,19 +959,34 @@ constexpr auto MakeArray(Args... args) -> std::array<T, sizeof...(Args)> { return {{static_cast<T>(args)...}}; } -inline gfx::ColorSpace2 ToColorSpace2(const WebGLContextOptions& options) { - auto ret = gfx::ColorSpace2::UNKNOWN; - if (true) { - ret = gfx::ColorSpace2::SRGB; - } - if (!options.ignoreColorSpace) { - ret = gfx::ToColorSpace2(options.colorSpace); +inline gfx::ColorSpace2 ToColorSpace2ForOutput( + const std::optional<dom::PredefinedColorSpace> chosenCspace) { + const auto cmsMode = GfxColorManagementMode(); + switch (cmsMode) { + case CMSMode::Off: + return gfx::ColorSpace2::Display; + case CMSMode::TaggedOnly: + if (!chosenCspace) { + return gfx::ColorSpace2::Display; + } + break; + case CMSMode::All: + if (!chosenCspace) { + return gfx::ColorSpace2::SRGB; + } + break; } - return ret; + return gfx::ToColorSpace2(*chosenCspace); } // - +template <class T> +GLuint GLNameOrZero(const T& t) { + if (t) return t->mGLName; + return 0; +} + // For an overview of how WebGL compositing works, see: // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing bool WebGLContext::PresentInto(gl::SwapChain& swapChain) { @@ -980,46 +994,100 @@ bool WebGLContext::PresentInto(gl::SwapChain& swapChain) { if (!ValidateAndInitFB(nullptr)) return false; - { - const auto colorSpace = ToColorSpace2(mOptions); - auto presenter = swapChain.Acquire(mDefaultFB->mSize, colorSpace); + const auto size = mDefaultFB->mSize; + + const auto error = [&]() -> std::optional<std::string> { + const auto canvasCspace = ToColorSpace2ForOutput(mOptions.colorSpace); + auto presenter = swapChain.Acquire(size, canvasCspace); if (!presenter) { - GenerateWarning("Swap chain surface creation failed."); - LoseContext(); - return false; + return "Swap chain surface creation failed."; } - + const auto outputCspace = presenter->BackBuffer()->mDesc.colorSpace; const auto destFb = presenter->Fb(); - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb); - BlitBackbufferToCurDriverFB(); + // - - if (!mOptions.preserveDrawingBuffer) { - if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) { - gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB); - constexpr auto attachments = MakeArray<GLenum>( - LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); - gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, - attachments.size(), attachments.data()); - } - mDefaultFB_IsInvalid = true; + bool colorManage = (canvasCspace != gfx::ColorSpace2::Display); + if (canvasCspace == outputCspace) { + colorManage = false; + } + if (!gl->IsSupported(gl::GLFeature::texture_3D)) { + NS_WARNING("Missing GLFeature::texture_3D => colorManage = false."); + colorManage = false; } -#ifdef DEBUG - if (!mOptions.alpha) { - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb); - gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4); - if (IsWebGL2()) { - gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0); - gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0); - gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0); + auto colorLut = std::shared_ptr<gl::Texture>{}; + if (colorManage) { + MOZ_ASSERT(canvasCspace != gfx::ColorSpace2::Display); + colorLut = gl->BlitHelper()->GetColorLutTex(gl::GLBlitHelper::ColorLutKey{ + .src = canvasCspace, .dst = outputCspace}); + if (!colorLut) { + NS_WARNING("GetColorLutTex() -> nullptr => colorManage = false."); + colorManage = false; } - uint32_t pixel = 0xffbadbad; - gl->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, - &pixel); - MOZ_ASSERT((pixel & 0xff000000) == 0xff000000); } -#endif + + if (!colorManage) { + gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFb); + BlitBackbufferToCurDriverFB(); + return {}; + } + + // - + + const auto canvasFb = GetDefaultFBForRead({.endOfFrame = true}); + if (!canvasFb) { + return "[WebGLContext::PresentInto] BindDefaultFBForRead failed."; + } + + const auto& blitter = gl->BlitHelper()->GetDrawBlitProg({ + .fragHeader = gl::kFragHeader_Tex2D, + .fragParts = {gl::kFragSample_OnePlane, gl::kFragConvert_ColorLut3d}, + }); + + constexpr uint8_t texUnit_src = 0; + constexpr uint8_t texUnit_lut = 1; + gl->BindSamplerTexture(texUnit_src, SamplerLinear(), LOCAL_GL_TEXTURE_2D, + canvasFb->ColorTex()); + gl->BindSamplerTexture(texUnit_lut, SamplerLinear(), LOCAL_GL_TEXTURE_3D, + colorLut->name); + const auto texCleanup = MakeScopeExit([&]() { + gl->BindSamplerTexture( + texUnit_src, GLNameOrZero(mBoundSamplers[texUnit_src]), + LOCAL_GL_TEXTURE_2D, GLNameOrZero(mBound2DTextures[texUnit_src])); + gl->BindSamplerTexture( + texUnit_lut, GLNameOrZero(mBoundSamplers[texUnit_lut]), + LOCAL_GL_TEXTURE_3D, GLNameOrZero(mBound3DTextures[texUnit_lut])); + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture); + }); + + gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFb); + + gl->fUseProgram(blitter.mProg); + const auto cleanupProg = MakeScopeExit( + [&]() { gl->fUseProgram(GLNameOrZero(mCurrentProgram)); }); + + gl->fUniform1i(blitter.mLoc_uColorLut, texUnit_lut); + + blitter.Draw({ + .texMatrix0 = gl::Mat3::I(), + .yFlip = false, + .destSize = size, + .destRect = {}, + }); + + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, canvasFb->mFB); + return {}; + }(); + if (error) { + GenerateWarning("%s", error->c_str()); + LoseContext(); + return false; + } + + if (!mOptions.preserveDrawingBuffer) { + gl->InvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER); + mDefaultFB_IsInvalid = true; } return true; @@ -1029,7 +1097,7 @@ bool WebGLContext::PresentIntoXR(gl::SwapChain& swapChain, const gl::MozFramebuffer& fb) { OnEndOfFrame(); - const auto colorSpace = ToColorSpace2(mOptions); + const auto colorSpace = ToColorSpace2ForOutput(mOptions.colorSpace); auto presenter = swapChain.Acquire(fb.mSize, colorSpace); if (!presenter) { GenerateWarning("Swap chain surface creation failed."); @@ -1058,7 +1126,7 @@ bool WebGLContext::PresentIntoXR(gl::SwapChain& swapChain, // Initialize a swap chain's surface factory given the desired surface type. void InitSwapChain(gl::GLContext& gl, gl::SwapChain& swapChain, - const layers::TextureType consumerType) { + const layers::TextureType consumerType, bool useAsync) { if (!swapChain.mFactory) { auto typedFactory = gl::SurfaceFactory::Create(&gl, consumerType); if (typedFactory) { @@ -1070,6 +1138,11 @@ void InitSwapChain(gl::GLContext& gl, gl::SwapChain& swapChain, swapChain.mFactory = MakeUnique<gl::SurfaceFactory_Basic>(gl); } MOZ_ASSERT(swapChain.mFactory); + if (useAsync) { + // RemoteTextureMap will handle recycling any surfaces, so don't rely on the + // SwapChain's internal pooling. + swapChain.DisablePool(); + } } void WebGLContext::Present(WebGLFramebuffer* const xrFb, @@ -1090,7 +1163,10 @@ void WebGLContext::Present(WebGLFramebuffer* const xrFb, mResolvedDefaultFB = nullptr; } - InitSwapChain(*gl, *swapChain, consumerType); + bool useAsync = options.remoteTextureOwnerId.IsValid() && + options.remoteTextureId.IsValid(); + + InitSwapChain(*gl, *swapChain, consumerType, useAsync); bool valid = maybeFB ? PresentIntoXR(*swapChain, *maybeFB) : PresentInto(*swapChain); @@ -1099,8 +1175,6 @@ void WebGLContext::Present(WebGLFramebuffer* const xrFb, return; } - bool useAsync = options.remoteTextureOwnerId.IsValid() && - options.remoteTextureId.IsValid(); if (useAsync) { PushRemoteTexture(nullptr, *swapChain, swapChain->FrontBuffer(), options); } @@ -1137,10 +1211,11 @@ bool WebGLContext::CopyToSwapChain( } gfx::IntSize size(info->width, info->height); - InitSwapChain(*gl, srcFb->mSwapChain, consumerType); - bool useAsync = options.remoteTextureOwnerId.IsValid() && options.remoteTextureId.IsValid(); + + InitSwapChain(*gl, srcFb->mSwapChain, consumerType, useAsync); + // If we're using async present and if there is no way to serialize surfaces, // then a readback is required to do the copy. In this case, there's no reason // to copy into a separate shared surface for the front buffer. Just directly @@ -1153,7 +1228,7 @@ bool WebGLContext::CopyToSwapChain( { // ColorSpace will need to be part of SwapChainOptions for DTWebgl. - const auto colorSpace = ToColorSpace2(mOptions); + const auto colorSpace = ToColorSpace2ForOutput(mOptions.colorSpace); auto presenter = srcFb->mSwapChain.Acquire(size, colorSpace); if (!presenter) { GenerateWarning("Swap chain surface creation failed."); @@ -1697,12 +1772,12 @@ bool WebGLContext::BindCurFBForColorRead( return true; } -bool WebGLContext::BindDefaultFBForRead() { - if (!ValidateAndInitFB(nullptr)) return false; +const gl::MozFramebuffer* WebGLContext::GetDefaultFBForRead( + const GetDefaultFBForReadDesc& desc) { + if (!ValidateAndInitFB(nullptr)) return nullptr; if (!mDefaultFB->mSamples) { - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB); - return true; + return mDefaultFB.get(); } if (!mResolvedDefaultFB) { @@ -1710,14 +1785,24 @@ bool WebGLContext::BindDefaultFBForRead() { gl::MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false); if (!mResolvedDefaultFB) { gfxCriticalNote << FuncName() << ": Failed to create mResolvedDefaultFB."; - return false; + return nullptr; } } - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB); + gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, mResolvedDefaultFB->mFB); BlitBackbufferToCurDriverFB(); - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB); + if (desc.endOfFrame && !mOptions.preserveDrawingBuffer) { + gl->InvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER); + } + + return mResolvedDefaultFB.get(); +} + +bool WebGLContext::BindDefaultFBForRead() { + const auto fb = GetDefaultFBForRead(); + if (!fb) return false; + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb->mFB); return true; } @@ -2351,7 +2436,7 @@ webgl::LinkActiveInfo GetLinkActiveInfo( ret.activeUniforms.push_back(std::move(info)); } // for i - } // anon + } // anon if (webgl2) { // ------------------------------------- @@ -2397,7 +2482,7 @@ webgl::LinkActiveInfo GetLinkActiveInfo( ret.activeUniformBlocks.push_back(std::move(info)); } // for i - } // anon + } // anon // ------------------------------------- // active tf varyings @@ -2661,4 +2746,21 @@ webgl::ExplicitPixelPackingState::ForUseWith( return {{state, metrics}}; } +GLuint WebGLContext::SamplerLinear() const { + if (!mSamplerLinear) { + mSamplerLinear = std::make_unique<gl::Sampler>(*gl); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_MAG_FILTER, + LOCAL_GL_LINEAR); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_MIN_FILTER, + LOCAL_GL_LINEAR); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_S, + LOCAL_GL_CLAMP_TO_EDGE); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_T, + LOCAL_GL_CLAMP_TO_EDGE); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_R, + LOCAL_GL_CLAMP_TO_EDGE); + } + return mSamplerLinear->name; +} + } // namespace mozilla |