diff --git a/build/moz.configure/windows.configure b/build/moz.configure/windows.configure index e2cb128265..6ede2fcc1d 100644 --- a/build/moz.configure/windows.configure +++ b/build/moz.configure/windows.configure @@ -90,8 +90,10 @@ def get_sdk_dirs(sdk, subdir): return int(parts[2]) if len(parts) >= 3 else 0 except ValueError: return 0 + # Keep compatibility with the current Windows 11 SDK while retaining an + # upper bound for SDK layouts this older tree has not been audited against. valid_versions = sorted((v for v in set(include_dirs) & set(lib_dirs) - if build_num(v) <= 19041), reverse=True) + if build_num(v) <= 26100), reverse=True) if valid_versions: return namespace( path=sdk, @@ -149,7 +151,7 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version, return int(parts[2]) if len(parts) >= 3 else 0 except ValueError: return 0 - sdks = {k: v for k, v in sdks.items() if sdk_build(k) <= 19041} + sdks = {k: v for k, v in sdks.items() if sdk_build(k) <= 26100} valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) if valid_sdks: biggest_version, sdk = sdks[valid_sdks[0]] @@ -240,7 +242,8 @@ def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env): 'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal ' 'CRT.' % windows_sdk_dir_env) - sdks = {k: v for k, v in sdks.items() if int(str(v[0]).split('.')[2]) <= 19041} + sdks = {k: v for k, v in sdks.items() + if int(str(v[0]).split('.')[2]) <= 26100} valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) if not valid_sdks: raise FatalCheckError('Cannot find the Universal CRT SDK. ' diff --git a/gfx/layers/composite/ContentHost.cpp b/gfx/layers/composite/ContentHost.cpp index 86d33fdb64..0caa631120 100644 --- a/gfx/layers/composite/ContentHost.cpp +++ b/gfx/layers/composite/ContentHost.cpp @@ -10,12 +10,14 @@ #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/gfx/BaseRect.h" // for BaseRect #include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL #include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc #include "mozilla/layers/LayersMessages.h" // for ThebesBufferData #include "nsAString.h" #include "nsPrintfCString.h" // for nsPrintfCString #include "nsString.h" // for nsAutoCString #include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL +#include "gfxPrefs.h" // for gfxPrefs::WebRenderEnabled namespace mozilla { using namespace gfx; @@ -69,6 +71,15 @@ ContentHostTexture::Composite(LayerComposite* aLayer, aEffectChain.mPrimaryEffect = effect; + CompositorOGL* compositorOGL = GetCompositor()->AsCompositorOGL(); + bool canUseWebRender = + gfxPrefs::WebRenderEnabled() && compositorOGL && + effect->mType == EffectTypes::RGB && !mTextureSourceOnWhite && + !mTextureSource->AsBigImageIterator() && aTransform.Is2D() && + !aEffectChain.mSecondaryEffects[EffectTypes::MASK] && + !aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] && + aSamplingFilter == SamplingFilter::LINEAR; + nsIntRegion tmpRegion; const nsIntRegion* renderRegion; if (PaintWillResample()) { @@ -176,7 +187,13 @@ ContentHostTexture::Composite(LayerComposite* aLayer, Float(tileRegionRect.y) / texRect.height, Float(tileRegionRect.width) / texRect.width, Float(tileRegionRect.height) / texRect.height); - GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform); + if (!canUseWebRender || + !compositorOGL->DrawWebRenderImage( + effect->mTexture, rect, effect->mTextureCoords, aOpacity, + aTransform, aClipRect, effect->mPremultiplied)) { + GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, + aOpacity, aTransform); + } if (usingTiles) { DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE; if (iterOnWhite) { diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index b1d77924b7..6ca489bc85 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -9,6 +9,7 @@ #include "composite/CompositableHost.h" // for CompositableHost, etc #include "ipc/IPCMessageUtils.h" // for null_t #include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL #include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc #include "mozilla/layers/ImageContainerParent.h" #include "mozilla/layers/LayerManagerComposite.h" // for TexturedEffect, Effect, etc @@ -16,6 +17,7 @@ #include "nsDebug.h" // for NS_WARNING, NS_ASSERTION #include "nsPrintfCString.h" // for nsPrintfCString #include "nsString.h" // for nsAutoCString +#include "gfxPrefs.h" // for gfxPrefs::WebRenderEnabled #define BIAS_TIME_MS 1.0 @@ -381,6 +383,16 @@ ImageHost::Composite(LayerComposite* aLayer, mLastProducerID = img->mProducerID; } aEffectChain.mPrimaryEffect = effect; + CompositorOGL* compositorOGL = GetCompositor()->AsCompositorOGL(); + auto drawWebRenderImage = [&](const gfx::Rect& rect) { + return gfxPrefs::WebRenderEnabled() && compositorOGL && + effect->mType == EffectTypes::RGB && aTransform.Is2D() && + !aEffectChain.mSecondaryEffects[EffectTypes::MASK] && + !aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] && + compositorOGL->DrawWebRenderImage( + effect->mTexture, rect, effect->mTextureCoords, aOpacity, + aTransform, aClipRect, effect->mPremultiplied); + }; gfx::Rect pictureRect(0, 0, img->mPictureRect.width, img->mPictureRect.height); BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator(); if (it) { @@ -414,8 +426,10 @@ ImageHost::Composite(LayerComposite* aLayer, effect->mTextureCoords.y = effect->mTextureCoords.YMost(); effect->mTextureCoords.height = -effect->mTextureCoords.height; } - GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, - aOpacity, aTransform); + if (!drawWebRenderImage(rect)) { + GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, + aOpacity, aTransform); + } GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE, rect, aClipRect, aTransform, mFlashCounter); } while (it->NextTile()); @@ -435,8 +449,10 @@ ImageHost::Composite(LayerComposite* aLayer, effect->mTextureCoords.height = -effect->mTextureCoords.height; } - GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain, - aOpacity, aTransform); + if (!drawWebRenderImage(pictureRect)) { + GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain, + aOpacity, aTransform); + } GetCompositor()->DrawDiagnostics(diagnosticFlags, pictureRect, aClipRect, aTransform, mFlashCounter); diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp index a80c47fb98..78fd6dd82e 100644 --- a/gfx/layers/composite/TiledContentHost.cpp +++ b/gfx/layers/composite/TiledContentHost.cpp @@ -10,6 +10,7 @@ #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL //#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent #include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper @@ -486,6 +487,15 @@ TiledContentHost::RenderTile(TileHost& aTile, float opacity = aTile.GetFadeInOpacity(aOpacity); aEffectChain.mPrimaryEffect = effect; + CompositorOGL* compositorOGL = mCompositor->AsCompositorOGL(); + bool canUseWebRender = + gfxPrefs::WebRenderEnabled() && compositorOGL && + effect->mType == EffectTypes::RGB && !aTile.mTextureHostOnWhite && + aTransform.Is2D() && + !aEffectChain.mSecondaryEffects[EffectTypes::MASK] && + !aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] && + aSamplingFilter == gfx::SamplingFilter::LINEAR; + for (auto iter = aScreenRegion.RectIter(); !iter.Done(); iter.Next()) { const IntRect& rect = iter.Get(); Rect graphicsRect(rect.x, rect.y, rect.width, rect.height); @@ -496,7 +506,13 @@ TiledContentHost::RenderTile(TileHost& aTile, textureRect.y / aTextureBounds.height, textureRect.width / aTextureBounds.width, textureRect.height / aTextureBounds.height); - mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, opacity, aTransform, aVisibleRect); + if (!canUseWebRender || + !compositorOGL->DrawWebRenderImage( + effect->mTexture, graphicsRect, effect->mTextureCoords, opacity, + aTransform, aClipRect, effect->mPremultiplied)) { + mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, opacity, + aTransform, aVisibleRect); + } } DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE; if (aTile.mTextureHostOnWhite) { @@ -562,6 +578,10 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer, return; } + CompositorOGL* compositorOGL = mCompositor->AsCompositorOGL(); + bool canUseWebRenderSolid = + gfxPrefs::WebRenderEnabled() && compositorOGL && aTransform.Is2D(); + if (aBackgroundColor) { nsIntRegion backgroundRegion = compositeRegion; backgroundRegion.ScaleRoundOut(resolution, resolution); @@ -570,7 +590,12 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer, for (auto iter = backgroundRegion.RectIter(); !iter.Done(); iter.Next()) { const IntRect& rect = iter.Get(); Rect graphicsRect(rect.x, rect.y, rect.width, rect.height); - mCompositor->DrawQuad(graphicsRect, aClipRect, effect, 1.0, aTransform); + if (!canUseWebRenderSolid || + !compositorOGL->DrawWebRenderRect( + graphicsRect, *aBackgroundColor, 1.0, aTransform, aClipRect)) { + mCompositor->DrawQuad(graphicsRect, aClipRect, effect, 1.0, + aTransform); + } } } diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index 78bf256360..c455eb7bba 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -87,6 +87,9 @@ CompositorOGL::CompositorOGL(CompositorBridgeParent* aParent, , mWidgetSize(-1, -1) , mSurfaceSize(aSurfaceWidth, aSurfaceHeight) , mWebRenderContext(nullptr) + , mWebRenderViewport(0, 0, 0, 0) + , mWebRenderSceneHasCommands(false) + , mNextWebRenderImageKey(1) , mHasBGRA(0) , mUseExternalSurfaceSize(aUseExternalSurfaceSize) , mFrameInProgress(false) @@ -170,6 +173,8 @@ CompositorOGL::CleanupResources() wr_context_destroy(mWebRenderContext); mWebRenderContext = nullptr; } + mWebRenderSceneHasCommands = false; + mWebRenderSceneImageSources.Clear(); if (!mGLContext) return; @@ -651,6 +656,22 @@ CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion, // We're about to actually draw a frame. mFrameInProgress = true; + mWebRenderViewport = rect; + mWebRenderSceneHasCommands = false; + mWebRenderSceneImageSources.Clear(); + mNextWebRenderImageKey = 1; + if (gfxPrefs::WebRenderEnabled()) { + if (!mWebRenderContext) { + mWebRenderContext = wr_context_create(16); + } + if (mWebRenderContext) { + wr_context_clear(mWebRenderContext); + wr_context_clear_images(mWebRenderContext); + wr_context_set_viewport(mWebRenderContext, + wr_rect{ Float(rect.x), Float(rect.y), + Float(rect.width), Float(rect.height) }); + } + } // If the widget size changed, we have to force a MakeCurrent // to make sure that GL sees the updated widget size. @@ -1002,7 +1023,10 @@ CompositorOGL::DrawTriangle(const gfx::TexturedTriangle& aTriangle, bool CompositorOGL::DrawWebRenderContext(wr_context* aContext, - const IntRect& aClipRect) + const IntRect& aClipRect, + TextureSource* aImageSource, + uint32_t aImageKey, + bool aImagePremultiplied) { if (!aContext) { return false; @@ -1010,7 +1034,13 @@ CompositorOGL::DrawWebRenderContext(wr_context* aContext, const wr_frame* frame = wr_context_build_frame(aContext); if (frame) { - DrawWebRenderFrame(*frame, aClipRect); + WebRenderImageSource imageSource; + imageSource.mSource = aImageSource; + imageSource.mKey = aImageKey; + imageSource.mPremultiplied = aImagePremultiplied; + DrawWebRenderFrame(*frame, aClipRect, + aImageSource && aImageKey ? &imageSource : nullptr, + aImageSource && aImageKey ? 1 : 0); return true; } return false; @@ -1023,42 +1053,169 @@ CompositorOGL::DrawWebRenderRect(const Rect& aRect, const Matrix4x4& aTransform, const IntRect& aClipRect) { - if (!mFrameInProgress || aClipRect.IsEmpty() || !aTransform.Is2D()) { + if (!gfxPrefs::WebRenderEnabled() || !mFrameInProgress || + aClipRect.IsEmpty() || !aTransform.Is2D()) { return false; } if (!mWebRenderContext) { - mWebRenderContext = wr_context_create(8); + mWebRenderContext = wr_context_create(16); } if (!mWebRenderContext) { return false; } - wr_context_clear(mWebRenderContext); wr_context_set_viewport(mWebRenderContext, - wr_rect{ Float(aClipRect.x), Float(aClipRect.y), - Float(aClipRect.width), Float(aClipRect.height) }); + wr_rect{ Float(mWebRenderViewport.x), + Float(mWebRenderViewport.y), + Float(mWebRenderViewport.width), + Float(mWebRenderViewport.height) }); Matrix transform = aTransform.As2D(); wr_context_set_transform(mWebRenderContext, wr_transform{ transform._11, transform._12, transform._21, transform._22, transform._31, transform._32 }); wr_context_set_opacity(mWebRenderContext, aOpacity); + if (!wr_context_begin_transaction(mWebRenderContext)) { + return false; + } + if (!wr_display_list_push_clip( + mWebRenderContext, + wr_rect{ Float(aClipRect.x), Float(aClipRect.y), + Float(aClipRect.width), Float(aClipRect.height) })) { + wr_context_abort_transaction(mWebRenderContext); + return false; + } if (!wr_display_list_push_rect(mWebRenderContext, wr_rect{ aRect.x, aRect.y, aRect.width, aRect.height }, wr_color{ aColor.r, aColor.g, aColor.b, aColor.a })) { + wr_context_abort_transaction(mWebRenderContext); + return false; + } + wr_display_list_pop_clip(mWebRenderContext); + if (!wr_context_commit_transaction(mWebRenderContext)) { + wr_context_abort_transaction(mWebRenderContext); return false; } - bool drawn = DrawWebRenderContext(mWebRenderContext, aClipRect); - wr_context_clear(mWebRenderContext); - return drawn; + mWebRenderSceneHasCommands = true; + return true; +} + +uint32_t +CompositorOGL::RegisterWebRenderImage(TextureSource* aImageSource, + bool aPremultiplied) +{ + IntSize imageSize; + uint32_t key; + + for (const WebRenderImageSource& image : mWebRenderSceneImageSources) { + if (image.mSource == aImageSource && + image.mPremultiplied == aPremultiplied) { + return image.mKey; + } + } + + if (!aImageSource) { + return 0; + } + imageSize = aImageSource->GetSize(); + if (imageSize.width <= 0 || imageSize.height <= 0) { + return 0; + } + + key = mNextWebRenderImageKey++; + if (!key) { + key = mNextWebRenderImageKey++; + } + if (!wr_context_register_image(mWebRenderContext, key, + uint32_t(imageSize.width), + uint32_t(imageSize.height))) { + return 0; + } + + WebRenderImageSource* image = + mWebRenderSceneImageSources.AppendElement(); + image->mSource = aImageSource; + image->mKey = key; + image->mPremultiplied = aPremultiplied; + return key; +} + +bool +CompositorOGL::DrawWebRenderImage(TextureSource* aImageSource, + const Rect& aRect, + const Rect& aTexRect, + Float aOpacity, + const Matrix4x4& aTransform, + const IntRect& aClipRect, + bool aPremultiplied) +{ + if (!gfxPrefs::WebRenderEnabled() || !aImageSource || + !mFrameInProgress || aClipRect.IsEmpty() || + !aTransform.Is2D()) { + return false; + } + if (aImageSource->AsSourceOGL() == nullptr) { + return false; + } + if (!mWebRenderContext) { + mWebRenderContext = wr_context_create(16); + } + if (!mWebRenderContext) { + return false; + } + + wr_context_set_viewport(mWebRenderContext, + wr_rect{ Float(mWebRenderViewport.x), + Float(mWebRenderViewport.y), + Float(mWebRenderViewport.width), + Float(mWebRenderViewport.height) }); + Matrix transform = aTransform.As2D(); + wr_context_set_transform(mWebRenderContext, + wr_transform{ transform._11, transform._12, + transform._21, transform._22, + transform._31, transform._32 }); + wr_context_set_opacity(mWebRenderContext, aOpacity); + uint32_t imageKey = RegisterWebRenderImage(aImageSource, aPremultiplied); + if (!imageKey) { + return false; + } + if (!wr_context_begin_transaction(mWebRenderContext)) { + return false; + } + if (!wr_display_list_push_clip( + mWebRenderContext, + wr_rect{ Float(aClipRect.x), Float(aClipRect.y), + Float(aClipRect.width), Float(aClipRect.height) })) { + wr_context_abort_transaction(mWebRenderContext); + return false; + } + if (!wr_display_list_push_image( + mWebRenderContext, + wr_rect{ aRect.x, aRect.y, aRect.width, aRect.height }, + imageKey, wr_rect{ aTexRect.x, aTexRect.y, + aTexRect.width, aTexRect.height }, + wr_color{ 1, 1, 1, 1 })) { + wr_context_abort_transaction(mWebRenderContext); + return false; + } + wr_display_list_pop_clip(mWebRenderContext); + if (!wr_context_commit_transaction(mWebRenderContext)) { + wr_context_abort_transaction(mWebRenderContext); + return false; + } + + mWebRenderSceneHasCommands = true; + return true; } void CompositorOGL::DrawWebRenderFrame(const wr_frame& aFrame, - const IntRect& aClipRect) + const IntRect& aClipRect, + const WebRenderImageSource* aImageSources, + size_t aImageSourceCount) { MOZ_ASSERT(mFrameInProgress, "frame not started"); MOZ_ASSERT(mCurrentRenderTarget, "No destination"); @@ -1070,21 +1227,10 @@ CompositorOGL::DrawWebRenderFrame(const wr_frame& aFrame, MakeCurrent(); - // This first adapter handles solid rectangles. It deliberately reuses the - // compositor's existing four-rect uniform and triangle VBO path, so the - // retained C display list is submitted to the GPU without a CPU raster pass. - EffectSolidColor effect(Color(0, 0, 0, 0)); - ShaderConfigOGL config = GetShaderConfigFor(&effect); - ShaderProgramOGL* program = GetShaderProgramFor(config); - if (!program) { - return; - } - IntPoint offset = mCurrentRenderTarget->GetOrigin(); - ActivateProgram(program); - program->SetProjectionMatrix(mProjMatrix); - program->SetLayerTransform(Matrix4x4()); - program->SetRenderOffset(offset.x, offset.y); + EffectSolidColor solidEffect(Color(0, 0, 0, 0)); + ShaderConfigOGL solidConfig = GetShaderConfigFor(&solidEffect); + ShaderProgramOGL* solidProgram = GetShaderProgramFor(solidConfig); ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true); ScopedScissorRect autoScissorRect(mGLContext, @@ -1115,15 +1261,71 @@ CompositorOGL::DrawWebRenderFrame(const wr_frame& aFrame, mGLContext, batchClip.x, FlipY(batchClip.y + batchClip.height), batchClip.width, batchClip.height); + ShaderProgramOGL* program = solidProgram; + TextureSourceOGL* imageSource = nullptr; + bool imagePremultiplied = true; + gfx::Rect textureRects[4] = {}; + if (batch.kind == WR_PRIMITIVE_IMAGE) { + TextureSource* source = nullptr; + for (size_t sourceIndex = 0; sourceIndex < aImageSourceCount; + ++sourceIndex) { + if (aImageSources[sourceIndex].mKey == batch.image_key) { + source = aImageSources[sourceIndex].mSource; + imagePremultiplied = aImageSources[sourceIndex].mPremultiplied; + break; + } + } + if (!source) { + continue; + } + imageSource = source->AsSourceOGL(); + if (!imageSource) { + continue; + } + EffectRGB imageEffect(source, imagePremultiplied, + SamplingFilter::LINEAR); + ShaderConfigOGL imageConfig = GetShaderConfigFor(&imageEffect); + imageConfig.SetTextureTint(true); + imageConfig.SetOpacity(batch.color.a != 1.0f); + program = GetShaderProgramFor(imageConfig); + if (!program) { + continue; + } + ActivateProgram(program); + imageSource->BindTexture(LOCAL_GL_TEXTURE0, SamplingFilter::LINEAR); + program->SetTextureUnit(0); + program->SetTextureTransform(imageSource->GetTextureTransform()); + if (imageConfig.mFeatures & ENABLE_TEXTURE_RECT) { + program->SetTexCoordMultiplier(imageSource->GetSize().width, + imageSource->GetSize().height); + } + if (batch.color.a != 1.0f) { + program->SetLayerOpacity(batch.color.a); + } + program->SetTextureTint(Color(batch.color.r, batch.color.g, + batch.color.b, 1.0f)); + } else { + if (!program) { + continue; + } + ActivateProgram(program); + SetBlendMode(gl(), CompositionOp::OP_OVER); + program->SetRenderColor(Color(batch.color.r, batch.color.g, + batch.color.b, batch.color.a)); + } + + program->SetProjectionMatrix(mProjMatrix); program->SetLayerTransform(Matrix4x4::From2D( Matrix(batch.transform.m11, batch.transform.m12, batch.transform.m21, batch.transform.m22, batch.transform.m31, batch.transform.m32))); + program->SetRenderOffset(offset.x, offset.y); + if (imageSource) { + SetBlendMode(gl(), CompositionOp::OP_OVER, imagePremultiplied); + } uint32_t remaining = batch.quad_count; uint32_t quadIndex = batch.first_quad; - program->SetRenderColor(Color(batch.color.r, batch.color.g, - batch.color.b, batch.color.a)); while (remaining) { Rect rects[4] = {}; @@ -1132,14 +1334,38 @@ CompositorOGL::DrawWebRenderFrame(const wr_frame& aFrame, const wr_gpu_quad& quad = aFrame.quads[quadIndex + i]; rects[i] = Rect(quad.rect.x, quad.rect.y, quad.rect.width, quad.rect.height); + if (imageSource) { + textureRects[i] = Rect(quad.tex_rect.x, quad.tex_rect.y, + quad.tex_rect.width, quad.tex_rect.height); + } } - BindAndDrawQuads(program, count, rects, nullptr); + BindAndDrawQuads(program, count, rects, + imageSource ? textureRects : nullptr); quadIndex += count; remaining -= count; } } } +void +CompositorOGL::FlushWebRenderScene() +{ + if (!mWebRenderSceneHasCommands || !mWebRenderContext || + !mFrameInProgress) { + return; + } + + const wr_frame* frame = wr_context_build_frame(mWebRenderContext); + if (frame) { + DrawWebRenderFrame(*frame, mWebRenderViewport, + mWebRenderSceneImageSources.Elements(), + mWebRenderSceneImageSources.Length()); + } + wr_context_clear(mWebRenderContext); + mWebRenderSceneHasCommands = false; + mWebRenderSceneImageSources.Clear(); +} + template void CompositorOGL::DrawGeometry(const Geometry& aGeometry, @@ -1152,6 +1378,8 @@ CompositorOGL::DrawGeometry(const Geometry& aGeometry, MOZ_ASSERT(mFrameInProgress, "frame not started"); MOZ_ASSERT(mCurrentRenderTarget, "No destination"); + FlushWebRenderScene(); + MakeCurrent(); IntPoint offset = mCurrentRenderTarget->GetOrigin(); @@ -1720,6 +1948,8 @@ CompositorOGL::EndFrame() MOZ_ASSERT(mCurrentRenderTarget == mWindowRenderTarget, "Rendering target not properly restored"); + FlushWebRenderScene(); + #ifdef MOZ_DUMP_PAINTING if (gfxEnv::DumpCompositorTextures()) { LayoutDeviceIntSize size; @@ -1773,6 +2003,7 @@ void CompositorOGL::EndFrameForExternalComposition(const gfx::Matrix& aTransform) { MOZ_ASSERT(!mTarget); + FlushWebRenderScene(); if (mTexturePool) { mTexturePool->EndFrame(); } diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h index c446ed7593..744b6df317 100644 --- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -34,6 +34,8 @@ #include "nsXULAppAPI.h" // for XRE_GetProcessType #include "nscore.h" // for NS_IMETHOD +#include + class nsIWidget; namespace mozilla { @@ -169,6 +171,12 @@ protected: public: virtual CompositorOGL* AsCompositorOGL() override { return this; } + struct WebRenderImageSource { + RefPtr mSource; + uint32_t mKey; + bool mPremultiplied; + }; + virtual already_AddRefed CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override; @@ -214,14 +222,29 @@ public: // Consume solid retained-display-list quads through the existing GPU path. bool DrawWebRenderContext(wr_context* aContext, - const gfx::IntRect& aClipRect); + const gfx::IntRect& aClipRect, + TextureSource* aImageSource = nullptr, + uint32_t aImageKey = 0, + bool aImagePremultiplied = true); bool DrawWebRenderRect(const gfx::Rect& aRect, const gfx::Color& aColor, gfx::Float aOpacity, const gfx::Matrix4x4& aTransform, const gfx::IntRect& aClipRect); + bool DrawWebRenderImage(TextureSource* aImageSource, + const gfx::Rect& aRect, + const gfx::Rect& aTexRect, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::IntRect& aClipRect, + bool aPremultiplied); void DrawWebRenderFrame(const wr_frame& aFrame, - const gfx::IntRect& aClipRect); + const gfx::IntRect& aClipRect, + const WebRenderImageSource* aImageSources, + size_t aImageSourceCount); + void FlushWebRenderScene(); + uint32_t RegisterWebRenderImage(TextureSource* aImageSource, + bool aPremultiplied); virtual void EndFrame() override; virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override; @@ -340,6 +363,10 @@ private: /** Currently bound render target */ RefPtr mCurrentRenderTarget; wr_context* mWebRenderContext; + gfx::IntRect mWebRenderViewport; + bool mWebRenderSceneHasCommands; + nsTArray mWebRenderSceneImageSources; + uint32_t mNextWebRenderImageKey; #ifdef DEBUG CompositingRenderTargetOGL* mWindowRenderTarget; #endif diff --git a/gfx/layers/opengl/OGLShaderProgram.cpp b/gfx/layers/opengl/OGLShaderProgram.cpp index f07132a400..b38b8bfe9c 100644 --- a/gfx/layers/opengl/OGLShaderProgram.cpp +++ b/gfx/layers/opengl/OGLShaderProgram.cpp @@ -60,6 +60,7 @@ AddUniforms(ProgramProfileOGL& aProfile) "uViewportSize", "uVisibleCenter", "uYuvColorMatrix", + "uTextureTint", nullptr }; @@ -75,6 +76,12 @@ ShaderConfigOGL::SetRenderColor(bool aEnabled) SetFeature(ENABLE_RENDER_COLOR, aEnabled); } +void +ShaderConfigOGL::SetTextureTint(bool aEnabled) +{ + SetFeature(ENABLE_TEXTURE_TINT, aEnabled); +} + void ShaderConfigOGL::SetTextureTarget(GLenum aTarget) { @@ -351,6 +358,9 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig) if (aConfig.mFeatures & ENABLE_OPACITY) { fs << "uniform COLOR_PRECISION float uLayerOpacity;" << endl; } + if (aConfig.mFeatures & ENABLE_TEXTURE_TINT) { + fs << "uniform COLOR_PRECISION vec4 uTextureTint;" << endl; + } } if (BlendOpIsMixBlendMode(blendOp)) { fs << "varying vec2 vBackdropCoord;" << endl; @@ -506,6 +516,9 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig) if (aConfig.mFeatures & ENABLE_OPACITY) { fs << " color *= uLayerOpacity;" << endl; } + if (aConfig.mFeatures & ENABLE_TEXTURE_TINT) { + fs << " color *= uTextureTint;" << endl; + } } if (aConfig.mFeatures & ENABLE_DEAA) { // Calculate the sub-pixel coverage of the pixel and modulate its opacity diff --git a/gfx/layers/opengl/OGLShaderProgram.h b/gfx/layers/opengl/OGLShaderProgram.h index 7a7c7dfe46..8644ad9122 100644 --- a/gfx/layers/opengl/OGLShaderProgram.h +++ b/gfx/layers/opengl/OGLShaderProgram.h @@ -42,7 +42,8 @@ enum ShaderFeatures { ENABLE_MASK=0x800, ENABLE_NO_PREMUL_ALPHA=0x1000, ENABLE_DEAA=0x2000, - ENABLE_DYNAMIC_GEOMETRY=0x4000 + ENABLE_DYNAMIC_GEOMETRY=0x4000, + ENABLE_TEXTURE_TINT=0x8000 }; class KnownUniform { @@ -83,6 +84,7 @@ public: ViewportSize, VisibleCenter, YuvColorMatrix, + TextureTint, KnownUniformCount }; @@ -218,6 +220,7 @@ public: {} void SetRenderColor(bool aEnabled); + void SetTextureTint(bool aEnabled); void SetTextureTarget(GLenum aTarget); void SetRBSwap(bool aEnabled); void SetNoAlpha(bool aEnabled); @@ -463,6 +466,10 @@ public: SetUniform(KnownUniform::RenderColor, aColor); } + void SetTextureTint(const gfx::Color& aColor) { + SetUniform(KnownUniform::TextureTint, aColor); + } + void SetColorMatrix(const gfx::Matrix5x4& aColorMatrix) { SetMatrixUniform(KnownUniform::ColorMatrix, &aColorMatrix._11); diff --git a/gfx/wr/moz.build b/gfx/wr/moz.build index 0720c47210..63dc366fa1 100644 --- a/gfx/wr/moz.build +++ b/gfx/wr/moz.build @@ -7,7 +7,14 @@ EXPORTS.mozilla.gfx += [ ] SOURCES += [ - 'webrender.c', + 'wr_context.c', + 'wr_display_list.c', + 'wr_frame.c', + 'wr_frame_packet.c', + 'wr_retained.c', + 'wr_util.c', ] FINAL_LIBRARY = 'xul' + +TEST_DIRS += ['tests'] diff --git a/gfx/wr/tests/moz.build b/gfx/wr/tests/moz.build new file mode 100644 index 0000000000..1a7ab765c6 --- /dev/null +++ b/gfx/wr/tests/moz.build @@ -0,0 +1,5 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- + +# This is a standalone C regression executable. It includes the C renderer +# implementation directly so it does not require another language's harness. +SimplePrograms(['webrender_test'], ext='.c') diff --git a/gfx/wr/tests/webrender_test.c b/gfx/wr/tests/webrender_test.c new file mode 100644 index 0000000000..6b1b65ebf9 --- /dev/null +++ b/gfx/wr/tests/webrender_test.c @@ -0,0 +1,274 @@ +#include "../wr_util.c" +#include "../wr_context.c" +#include "../wr_display_list.c" +#include "../wr_frame.c" +#include "../wr_frame_packet.c" +#include "../wr_retained.c" + +#include + +static void +test_retained_frame(void) +{ + wr_context* context = wr_context_create(4); + const wr_frame* first; + const wr_frame* cached; + const wr_frame* culled; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + assert(wr_display_list_push_rect(context, (wr_rect){ 0, 0, 10, 10 }, + (wr_color){ 1, 0, 0, 1 })); + first = wr_context_build_frame(context); + cached = wr_context_build_frame(context); + assert(first == cached); + assert(cached->quad_count == 1 && cached->batch_count == 1); + + wr_context_set_viewport(context, (wr_rect){ 200, 200, 10, 10 }); + culled = wr_context_build_frame(context); + assert(culled && culled->quad_count == 0); + wr_context_destroy(context); +} + +static void +test_stacking_and_image(void) +{ + wr_context* context = wr_context_create(4); + const wr_frame* frame; + const wr_transform translate = { 1, 0, 0, 1, 20, 0 }; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + assert(wr_context_register_image(context, 7, 64, 64)); + assert(wr_display_list_push_stacking_context(context, translate, 0.5f)); + assert(wr_display_list_push_clip(context, (wr_rect){ 15, 0, 30, 30 })); + assert(wr_display_list_push_rect(context, (wr_rect){ 0, 0, 10, 10 }, + (wr_color){ 1, 0, 0, 1 })); + wr_display_list_pop_stacking_context(context); + assert(wr_display_list_push_image( + context, (wr_rect){ 30, 0, 20, 20 }, 7, + (wr_rect){ 0, 0, 0.5f, 0.5f }, (wr_color){ 0.25f, 0.5f, 0.75f, 1 })); + + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 2 && frame->batch_count == 2); + assert(frame->quads[0].color.a == 0.5f); + assert(frame->batches[0].has_clip); + assert(frame->batches[0].transform.m31 == 20); + assert(frame->batches[1].kind == WR_PRIMITIVE_IMAGE); + assert(frame->batches[1].image_key == 7); + assert(frame->quads[1].color.r == 0.25f); + assert(frame->quads[1].color.g == 0.5f); + assert(frame->quads[1].color.b == 0.75f); + assert(frame->quads[1].tex_rect.width == 0.5f); + wr_context_unregister_image(context, 7); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 1 && frame->batch_count == 1); + wr_context_clear(context); + assert(!wr_display_list_push_image( + context, (wr_rect){ 30, 0, 20, 20 }, 7, + (wr_rect){ 0, 0, 0.5f, 0.5f }, (wr_color){ 1, 1, 1, 1 })); + wr_context_destroy(context); +} + +static void +test_scene_transactions(void) +{ + wr_context* context = wr_context_create(2); + const wr_frame* frame; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + assert(wr_display_list_push_rect(context, (wr_rect){ 0, 0, 10, 10 }, + (wr_color){ 1, 0, 0, 1 })); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 1); + + assert(wr_context_begin_transaction(context)); + assert(wr_display_list_push_rect(context, (wr_rect){ 20, 0, 10, 10 }, + (wr_color){ 0, 1, 0, 1 })); + wr_context_abort_transaction(context); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 1); + + assert(wr_context_begin_transaction(context)); + assert(wr_display_list_push_rect(context, (wr_rect){ 20, 0, 10, 10 }, + (wr_color){ 0, 1, 0, 1 })); + assert(wr_context_begin_transaction(context)); + assert(wr_display_list_push_rect(context, (wr_rect){ 40, 0, 10, 10 }, + (wr_color){ 0, 0, 1, 1 })); + wr_context_abort_transaction(context); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 2); + assert(wr_context_commit_transaction(context)); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 2); + + assert(!wr_context_commit_transaction(context)); + wr_context_destroy(context); +} + +static void +test_multiple_image_resources(void) +{ + wr_context* context = wr_context_create(4); + const wr_frame* frame; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + assert(wr_context_register_image(context, 11, 32, 32)); + assert(wr_context_register_image(context, 12, 64, 64)); + assert(wr_display_list_push_image( + context, (wr_rect){ 0, 0, 20, 20 }, 11, + (wr_rect){ 0, 0, 1, 1 }, (wr_color){ 1, 1, 1, 1 })); + assert(wr_display_list_push_image( + context, (wr_rect){ 20, 0, 20, 20 }, 12, + (wr_rect){ 0, 0, 1, 1 }, (wr_color){ 1, 1, 1, 1 })); + + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 2 && frame->batch_count == 2); + assert(frame->batches[0].image_key == 11); + assert(frame->batches[1].image_key == 12); + wr_context_clear_images(context); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 0 && frame->batch_count == 0); + wr_context_destroy(context); +} + +static void +test_stable_item_updates(void) +{ + wr_context* context = wr_context_create(4); + const wr_frame* frame; + const wr_transform identity = { 1, 0, 0, 1, 0, 0 }; + wr_item_id item; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + item = wr_display_list_push_rect_with_id( + context, 42, (wr_rect){ 0, 0, 10, 10 }, (wr_color){ 1, 0, 0, 1 }); + assert(item == 42); + assert(wr_display_list_push_rect_with_id( + context, 42, (wr_rect){ 20, 0, 10, 10 }, (wr_color){ 0, 1, 0, 1 }) == 0); + + assert(wr_context_begin_transaction(context)); + assert(wr_display_list_update_rect( + context, item, (wr_rect){ 20, 0, 10, 10 }, + (wr_color){ 0, 1, 0, 1 }, identity)); + wr_context_abort_transaction(context); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 1); + assert(frame->quads[0].rect.x == 0); + + assert(wr_context_begin_transaction(context)); + assert(wr_display_list_update_rect( + context, item, (wr_rect){ 20, 0, 10, 10 }, + (wr_color){ 0, 1, 0, 1 }, identity)); + assert(wr_context_commit_transaction(context)); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 1); + assert(frame->quads[0].rect.x == 20); + assert(frame->quads[0].item_id == 42); + + assert(wr_context_remove_item(context, item)); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 0); + wr_context_destroy(context); +} + +static void +test_opaque_occlusion(void) +{ + wr_context* context = wr_context_create(2); + const wr_frame* frame; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + assert(wr_display_list_push_rect( + context, (wr_rect){ 0, 0, 20, 20 }, (wr_color){ 1, 0, 0, 1 })); + assert(wr_display_list_push_rect( + context, (wr_rect){ 0, 0, 20, 20 }, (wr_color){ 0, 0, 1, 1 })); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 1 && frame->batch_count == 1); + assert(frame->quads[0].color.b == 1.0f); + wr_context_destroy(context); +} + +static void +test_gpu_tessellated_primitives(void) +{ + wr_context* context = wr_context_create(64); + const wr_frame* frame; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + assert(wr_display_list_push_linear_gradient( + context, (wr_rect){ 0, 0, 40, 20 }, + (wr_color){ 1, 0, 0, 1 }, (wr_color){ 0, 0, 1, 1 }, 1)); + assert(wr_display_list_push_rounded_rect( + context, (wr_rect){ 0, 20, 40, 20 }, (wr_color){ 0, 1, 0, 1 }, 8)); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 48); + wr_context_destroy(context); +} + +static void +test_glyph_run(void) +{ + wr_context* context = wr_context_create(4); + const wr_frame* frame; + const wr_glyph glyphs[2] = { + { { 0, 0, 8, 12 }, { 0, 0, 0.25f, 1 } }, + { { 8, 0, 8, 12 }, { 0.25f, 0, 0.25f, 1 } } + }; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + assert(wr_context_register_image(context, 21, 256, 256)); + assert(wr_display_list_push_glyph_run( + context, 21, glyphs, 2, (wr_color){ 1, 1, 1, 1 })); + frame = wr_context_build_frame(context); + assert(frame && frame->quad_count == 2 && frame->batch_count == 1); + assert(frame->batches[0].kind == WR_PRIMITIVE_IMAGE); + wr_context_destroy(context); +} + +static void +test_frame_packet(void) +{ + wr_context* context = wr_context_create(2); + const wr_frame* frame; + size_t packet_size; + unsigned char* packet; + wr_frame_packet_header header; + + assert(context); + wr_context_set_viewport(context, (wr_rect){ 0, 0, 100, 100 }); + assert(wr_display_list_push_rect( + context, (wr_rect){ 0, 0, 10, 10 }, (wr_color){ 1, 0, 0, 1 })); + frame = wr_context_build_frame(context); + packet_size = wr_frame_packet_size(frame); + assert(packet_size > sizeof(header)); + packet = (unsigned char*)malloc(packet_size); + assert(packet); + assert(wr_frame_serialize(frame, packet, packet_size)); + memcpy(&header, packet, sizeof(header)); + assert(header.version == WR_FRAME_PACKET_VERSION); + assert(header.quad_count == 1 && header.batch_count == 1); + free(packet); + wr_context_destroy(context); +} + +int +main(void) +{ + test_retained_frame(); + test_stacking_and_image(); + test_scene_transactions(); + test_multiple_image_resources(); + test_stable_item_updates(); + test_opaque_occlusion(); + test_gpu_tessellated_primitives(); + test_glyph_run(); + test_frame_packet(); + return 0; +} diff --git a/gfx/wr/webrender.c b/gfx/wr/webrender.c deleted file mode 100644 index 0d0fdbce23..0000000000 --- a/gfx/wr/webrender.c +++ /dev/null @@ -1,447 +0,0 @@ -#include "webrender.h" - -#include -#include -#include - -typedef struct { - wr_rect rect; - wr_color color; - wr_transform transform; - wr_rect clip_rect; - uint8_t has_clip; -} wr_rect_command; - -typedef struct { - wr_transform transform; - float opacity; - size_t clip_depth; -} wr_display_state; - -struct wr_context { - wr_rect_command* commands; - size_t command_count; - size_t command_capacity; - - wr_gpu_quad* quads; - size_t quad_count; - size_t quad_capacity; - - wr_gpu_batch* batches; - size_t batch_count; - size_t batch_capacity; - - wr_rect viewport; - wr_transform transform; - float opacity; - wr_rect* clip_stack; - size_t clip_depth; - size_t clip_capacity; - wr_display_state* state_stack; - size_t state_depth; - size_t state_capacity; - wr_frame frame; -}; - -static int -wr_size_mul_overflow(size_t a, size_t b) -{ - return b != 0 && a > SIZE_MAX / b; -} - -static int -wr_reserve(void** data, size_t* capacity, size_t count, size_t element_size) -{ - size_t new_capacity; - void* new_data; - - if (count <= *capacity) - return 1; - if (element_size == 0 || wr_size_mul_overflow(count, element_size)) - return 0; - - new_capacity = *capacity ? *capacity : 8; - while (new_capacity < count) { - if (new_capacity > SIZE_MAX / 2) { - new_capacity = count; - break; - } - new_capacity *= 2; - } - if (wr_size_mul_overflow(new_capacity, element_size)) - return 0; - - new_data = realloc(*data, new_capacity * element_size); - if (!new_data) - return 0; - - *data = new_data; - *capacity = new_capacity; - return 1; -} - -static int -wr_valid_rect(wr_rect rect) -{ - return isfinite(rect.x) && isfinite(rect.y) && - isfinite(rect.width) && isfinite(rect.height) && - rect.width > 0.0f && rect.height > 0.0f; -} - -static int -wr_valid_transform(wr_transform transform) -{ - return isfinite(transform.m11) && isfinite(transform.m12) && - isfinite(transform.m21) && isfinite(transform.m22) && - isfinite(transform.m31) && isfinite(transform.m32); -} - -static wr_transform -wr_identity_transform(void) -{ - wr_transform transform = { 1, 0, 0, 1, 0, 0 }; - return transform; -} - -static wr_transform -wr_multiply_transform(wr_transform a, wr_transform b) -{ - wr_transform result; - result.m11 = a.m11 * b.m11 + a.m21 * b.m12; - result.m12 = a.m12 * b.m11 + a.m22 * b.m12; - result.m21 = a.m11 * b.m21 + a.m21 * b.m22; - result.m22 = a.m12 * b.m21 + a.m22 * b.m22; - result.m31 = a.m11 * b.m31 + a.m21 * b.m32 + a.m31; - result.m32 = a.m12 * b.m31 + a.m22 * b.m32 + a.m32; - return result; -} - -static wr_rect -wr_empty_rect(void) -{ - wr_rect rect = { 0, 0, 0, 0 }; - return rect; -} - -static float -wr_clamp01(float value) -{ - if (value < 0.0f) - return 0.0f; - if (value > 1.0f) - return 1.0f; - return value; -} - -static wr_color -wr_normalize_color(wr_color color) -{ - color.r = wr_clamp01(color.r); - color.g = wr_clamp01(color.g); - color.b = wr_clamp01(color.b); - color.a = wr_clamp01(color.a); - return color; -} - -static int -wr_colors_equal(wr_color a, wr_color b) -{ - return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; -} - -static int -wr_rects_equal(wr_rect a, wr_rect b) -{ - return a.x == b.x && a.y == b.y && - a.width == b.width && a.height == b.height; -} - -static int -wr_intersects(wr_rect a, wr_rect b) -{ - return a.x < b.x + b.width && a.x + a.width > b.x && - a.y < b.y + b.height && a.y + a.height > b.y; -} - -static wr_rect -wr_intersect_rect(wr_rect a, wr_rect b) -{ - float left = a.x > b.x ? a.x : b.x; - float top = a.y > b.y ? a.y : b.y; - float right = a.x + a.width < b.x + b.width ? - a.x + a.width : b.x + b.width; - float bottom = a.y + a.height < b.y + b.height ? - a.y + a.height : b.y + b.height; - wr_rect result = { left, top, right - left, bottom - top }; - return result; -} - -static wr_rect -wr_transform_bounds(wr_rect rect, wr_transform transform) -{ - float x1 = rect.x * transform.m11 + rect.y * transform.m21 + transform.m31; - float y1 = rect.x * transform.m12 + rect.y * transform.m22 + transform.m32; - float x2 = (rect.x + rect.width) * transform.m11 + - rect.y * transform.m21 + transform.m31; - float y2 = (rect.x + rect.width) * transform.m12 + - rect.y * transform.m22 + transform.m32; - float x3 = rect.x * transform.m11 + - (rect.y + rect.height) * transform.m21 + transform.m31; - float y3 = rect.x * transform.m12 + - (rect.y + rect.height) * transform.m22 + transform.m32; - float x4 = (rect.x + rect.width) * transform.m11 + - (rect.y + rect.height) * transform.m21 + transform.m31; - float y4 = (rect.x + rect.width) * transform.m12 + - (rect.y + rect.height) * transform.m22 + transform.m32; - float left = fminf(fminf(x1, x2), fminf(x3, x4)); - float right = fmaxf(fmaxf(x1, x2), fmaxf(x3, x4)); - float top = fminf(fminf(y1, y2), fminf(y3, y4)); - float bottom = fmaxf(fmaxf(y1, y2), fmaxf(y3, y4)); - wr_rect result = { left, top, right - left, bottom - top }; - return result; -} - -wr_context* -wr_context_create(size_t initial_capacity) -{ - wr_context* context = (wr_context*)calloc(1, sizeof(*context)); - if (!context) - return NULL; - - context->viewport.x = -FLT_MAX; - context->viewport.y = -FLT_MAX; - context->viewport.width = FLT_MAX; - context->viewport.height = FLT_MAX; - context->transform = wr_identity_transform(); - context->opacity = 1.0f; - - if (initial_capacity && - !wr_reserve((void**)&context->commands, &context->command_capacity, - initial_capacity, sizeof(*context->commands))) { - wr_context_destroy(context); - return NULL; - } - return context; -} - -void -wr_context_destroy(wr_context* context) -{ - if (!context) - return; - free(context->commands); - free(context->quads); - free(context->batches); - free(context->clip_stack); - free(context->state_stack); - free(context); -} - -void -wr_context_clear(wr_context* context) -{ - if (!context) - return; - context->command_count = 0; - context->quad_count = 0; - context->batch_count = 0; - context->clip_depth = 0; - context->state_depth = 0; -} - -void -wr_context_set_viewport(wr_context* context, wr_rect viewport) -{ - if (context && wr_valid_rect(viewport)) - context->viewport = viewport; -} - -void -wr_context_set_transform(wr_context* context, wr_transform transform) -{ - if (context && wr_valid_transform(transform)) - context->transform = transform; -} - -void -wr_context_set_opacity(wr_context* context, float opacity) -{ - if (context && isfinite(opacity)) { - context->opacity = wr_clamp01(opacity); - } -} - -int -wr_display_list_push_stacking_context(wr_context* context, - wr_transform transform, - float opacity) -{ - wr_display_state state; - - if (!context || !wr_valid_transform(transform) || !isfinite(opacity) || - context->state_depth == SIZE_MAX) - return 0; - if (!wr_reserve((void**)&context->state_stack, &context->state_capacity, - context->state_depth + 1, sizeof(*context->state_stack))) - return 0; - - state.transform = context->transform; - state.opacity = context->opacity; - state.clip_depth = context->clip_depth; - context->state_stack[context->state_depth++] = state; - context->transform = wr_multiply_transform(context->transform, transform); - context->opacity = wr_clamp01(context->opacity * wr_clamp01(opacity)); - return 1; -} - -void -wr_display_list_pop_stacking_context(wr_context* context) -{ - wr_display_state state; - - if (!context || !context->state_depth) - return; - state = context->state_stack[--context->state_depth]; - context->transform = state.transform; - context->opacity = state.opacity; - context->clip_depth = state.clip_depth; -} - -int -wr_display_list_push_clip(wr_context* context, wr_rect clip) -{ - wr_rect effective_clip; - - if (!context || !wr_valid_rect(clip) || - context->clip_depth == SIZE_MAX) - return 0; - if (!wr_reserve((void**)&context->clip_stack, &context->clip_capacity, - context->clip_depth + 1, sizeof(*context->clip_stack))) - return 0; - - effective_clip = clip; - if (context->clip_depth) - effective_clip = wr_intersect_rect( - context->clip_stack[context->clip_depth - 1], clip); - context->clip_stack[context->clip_depth++] = effective_clip; - return 1; -} - -void -wr_display_list_pop_clip(wr_context* context) -{ - if (context && context->clip_depth) - --context->clip_depth; -} - -int -wr_display_list_push_rect(wr_context* context, wr_rect rect, wr_color color) -{ - return context ? wr_display_list_push_transformed_rect( - context, rect, color, context->transform) : 0; -} - -int -wr_display_list_push_transformed_rect(wr_context* context, wr_rect rect, - wr_color color, wr_transform transform) -{ - wr_rect clip_rect = context && context->clip_depth ? - context->clip_stack[context->clip_depth - 1] : - context ? context->viewport : wr_empty_rect(); - uint8_t has_clip = context && context->clip_depth ? 1 : 0; - - if (!context || !wr_valid_rect(rect) || !wr_valid_transform(transform)) - return 0; - if (context->command_count == SIZE_MAX || - context->command_count >= UINT32_MAX) - return 0; - if (!wr_reserve((void**)&context->commands, &context->command_capacity, - context->command_count + 1, sizeof(*context->commands))) - return 0; - - context->commands[context->command_count].rect = rect; - color = wr_normalize_color(color); - color.a *= context->opacity; - context->commands[context->command_count].color = color; - context->commands[context->command_count].transform = transform; - context->commands[context->command_count].clip_rect = clip_rect; - context->commands[context->command_count].has_clip = has_clip; - ++context->command_count; - return 1; -} - -const wr_frame* -wr_context_build_frame(wr_context* context) -{ - size_t i; - - if (!context) - return NULL; - context->quad_count = 0; - context->batch_count = 0; - - for (i = 0; i < context->command_count; ++i) { - wr_rect_command* command = &context->commands[i]; - wr_gpu_quad* quad; - wr_gpu_batch* batch; - - if (!wr_intersects(wr_transform_bounds(command->rect, - command->transform), - context->viewport) || - (command->clip_rect.width <= 0.0f || - command->clip_rect.height <= 0.0f)) - continue; - if (context->quad_count == SIZE_MAX || - context->quad_count >= UINT32_MAX || - context->batch_count == SIZE_MAX || - context->batch_count >= UINT32_MAX) - return NULL; - if (!wr_reserve((void**)&context->quads, &context->quad_capacity, - context->quad_count + 1, sizeof(*context->quads)) || - !wr_reserve((void**)&context->batches, &context->batch_capacity, - context->batch_count + 1, sizeof(*context->batches))) - return NULL; - - quad = &context->quads[context->quad_count++]; - quad->rect = command->rect; - quad->color = command->color; - - if (context->batch_count && - wr_colors_equal(context->batches[context->batch_count - 1].color, - command->color) && - context->batches[context->batch_count - 1].transform.m11 == - command->transform.m11 && - context->batches[context->batch_count - 1].transform.m12 == - command->transform.m12 && - context->batches[context->batch_count - 1].transform.m21 == - command->transform.m21 && - context->batches[context->batch_count - 1].transform.m22 == - command->transform.m22 && - context->batches[context->batch_count - 1].transform.m31 == - command->transform.m31 && - context->batches[context->batch_count - 1].transform.m32 == - command->transform.m32 && - context->batches[context->batch_count - 1].has_clip == - command->has_clip && - (!command->has_clip || - wr_rects_equal(context->batches[context->batch_count - 1].clip_rect, - command->clip_rect))) { - batch = &context->batches[context->batch_count - 1]; - ++batch->quad_count; - } else { - batch = &context->batches[context->batch_count++]; - batch->first_quad = (uint32_t)(context->quad_count - 1); - batch->quad_count = 1; - batch->color = command->color; - batch->transform = command->transform; - batch->clip_rect = command->clip_rect; - batch->has_clip = command->has_clip; - } - } - - context->frame.quads = context->quads; - context->frame.quad_count = context->quad_count; - context->frame.batches = context->batches; - context->frame.batch_count = context->batch_count; - return &context->frame; -} diff --git a/gfx/wr/webrender.h b/gfx/wr/webrender.h index 1726e9233b..317ae72594 100644 --- a/gfx/wr/webrender.h +++ b/gfx/wr/webrender.h @@ -9,6 +9,13 @@ extern "C" { #endif typedef struct wr_context wr_context; +typedef uint64_t wr_item_id; + +typedef struct { + uint32_t key; + uint32_t width; + uint32_t height; +} wr_image_resource; typedef struct { float x; @@ -34,9 +41,24 @@ typedef struct { float m32; } wr_transform; +typedef struct { + wr_rect rect; + wr_rect tex_rect; +} wr_glyph; + +typedef enum { + WR_PRIMITIVE_SOLID = 0, + WR_PRIMITIVE_IMAGE = 1 +} wr_primitive_kind; + typedef struct { wr_rect rect; wr_color color; + wr_rect tex_rect; + uint32_t image_key; + wr_item_id item_id; + uint8_t kind; + uint8_t active; } wr_gpu_quad; typedef struct { @@ -46,6 +68,8 @@ typedef struct { wr_transform transform; wr_rect clip_rect; uint8_t has_clip; + uint8_t kind; + uint32_t image_key; } wr_gpu_batch; typedef struct { @@ -55,6 +79,14 @@ typedef struct { size_t batch_count; } wr_frame; +typedef struct { + uint32_t version; + uint32_t quad_count; + uint32_t batch_count; +} wr_frame_packet_header; + +#define WR_FRAME_PACKET_VERSION 1 + /* Creates a retained display-list context. */ wr_context* wr_context_create(size_t initial_capacity); void wr_context_destroy(wr_context* context); @@ -62,6 +94,21 @@ void wr_context_destroy(wr_context* context); /* Removes every item from the retained display list. */ void wr_context_clear(wr_context* context); +/* Clears transient image resources without releasing retained allocations. */ +void wr_context_clear_images(wr_context* context); + +/* Begins an atomic retained-scene update. Transactions may be nested. */ +int wr_context_begin_transaction(wr_context* context); + +/* Commits the innermost retained-scene update. */ +int wr_context_commit_transaction(wr_context* context); + +/* Rolls back the innermost retained-scene update. */ +void wr_context_abort_transaction(wr_context* context); + +/* Removes a retained display-list item by stable item ID. */ +int wr_context_remove_item(wr_context* context, wr_item_id item_id); + /* Sets the viewport used for coarse CPU-side culling. */ void wr_context_set_viewport(wr_context* context, wr_rect viewport); @@ -71,6 +118,13 @@ void wr_context_set_transform(wr_context* context, wr_transform transform); /* Sets the opacity applied to subsequently recorded primitives. */ void wr_context_set_opacity(wr_context* context, float opacity); +/* Registers or updates an image resource referenced by display-list commands. */ +int wr_context_register_image(wr_context* context, uint32_t image_key, + uint32_t width, uint32_t height); + +/* Removes an image resource and invalidates frames referencing it. */ +void wr_context_unregister_image(wr_context* context, uint32_t image_key); + /* Records a retained stacking-context boundary and restores it on pop. */ int wr_display_list_push_stacking_context(wr_context* context, wr_transform transform, @@ -85,14 +139,57 @@ void wr_display_list_pop_clip(wr_context* context); int wr_display_list_push_rect(wr_context* context, wr_rect rect, wr_color color); +/* Adds a solid rectangle with a caller-supplied stable item ID. */ +wr_item_id wr_display_list_push_rect_with_id(wr_context* context, + wr_item_id item_id, + wr_rect rect, wr_color color); + +/* Updates an existing solid rectangle without rebuilding the scene. */ +int wr_display_list_update_rect(wr_context* context, wr_item_id item_id, + wr_rect rect, wr_color color, + wr_transform transform); + +/* Adds a GPU-tessellated linear gradient. */ +int wr_display_list_push_linear_gradient(wr_context* context, wr_rect rect, + wr_color start, wr_color end, + int horizontal); + +/* Adds a GPU-tessellated rounded rectangle. */ +int wr_display_list_push_rounded_rect(wr_context* context, wr_rect rect, + wr_color color, float radius); + /* Explicit transform form for display-list builders. */ int wr_display_list_push_transformed_rect(wr_context* context, wr_rect rect, wr_color color, wr_transform transform); +/* Adds an image primitive resolved by the compositor using image_key. */ +int wr_display_list_push_image(wr_context* context, wr_rect rect, + uint32_t image_key, wr_rect tex_rect, + wr_color tint); + +/* Adds an image with a caller-supplied stable item ID. */ +wr_item_id wr_display_list_push_image_with_id(wr_context* context, + wr_item_id item_id, + wr_rect rect, + uint32_t image_key, + wr_rect tex_rect, + wr_color tint); + +/* Records glyph quads sourced from a registered image atlas. */ +int wr_display_list_push_glyph_run(wr_context* context, uint32_t image_key, + const wr_glyph* glyphs, size_t glyph_count, + wr_color color); + /* Builds the GPU-ready stream from the retained display list. */ const wr_frame* wr_context_build_frame(wr_context* context); +/* Returns the byte size required for a versioned, flat frame packet. */ +size_t wr_frame_packet_size(const wr_frame* frame); + +/* Serializes a frame into a flat buffer suitable for thread/IPC transfer. */ +int wr_frame_serialize(const wr_frame* frame, void* buffer, size_t buffer_size); + #ifdef __cplusplus } #endif diff --git a/gfx/wr/wr_context.c b/gfx/wr/wr_context.c new file mode 100644 index 0000000000..3f89f020ad --- /dev/null +++ b/gfx/wr/wr_context.c @@ -0,0 +1,145 @@ +#include + +wr_context* +wr_context_create(size_t initial_capacity) +{ + wr_context* context = (wr_context*)calloc(1, sizeof(*context)); + if (!context) + return NULL; + + context->viewport.x = -FLT_MAX; + context->viewport.y = -FLT_MAX; + context->viewport.width = FLT_MAX; + context->viewport.height = FLT_MAX; + context->transform = wr_identity_transform(); + context->opacity = 1.0f; + context->next_item_id = 1; + + if (initial_capacity && + !wr_reserve((void**)&context->commands, &context->command_capacity, + initial_capacity, sizeof(*context->commands))) { + wr_context_destroy(context); + return NULL; + } + return context; +} + +void +wr_context_destroy(wr_context* context) +{ + if (!context) + return; + free(context->commands); + free(context->quads); + free(context->batches); + free(context->clip_stack); + free(context->state_stack); + free(context->images); + for (size_t i = 0; i < context->transaction_depth; ++i) + free(context->transactions[i].commands); + free(context->transactions); + free(context); +} + +void +wr_context_clear(wr_context* context) +{ + if (!context) + return; + context->command_count = 0; + context->quad_count = 0; + context->batch_count = 0; + context->clip_depth = 0; + context->state_depth = 0; + context->frame_valid = 0; + while (context->transaction_depth) + free(context->transactions[--context->transaction_depth].commands); +} + +void +wr_context_set_viewport(wr_context* context, wr_rect viewport) +{ + if (context && wr_valid_rect(viewport)) + context->viewport = viewport; + if (context) + context->frame_valid = 0; +} + +void +wr_context_set_transform(wr_context* context, wr_transform transform) +{ + if (context && wr_valid_transform(transform)) + context->transform = transform; +} + +void +wr_context_set_opacity(wr_context* context, float opacity) +{ + if (context && isfinite(opacity)) { + context->opacity = wr_clamp01(opacity); + context->frame_valid = 0; + } +} + +int +wr_display_list_push_stacking_context(wr_context* context, + wr_transform transform, + float opacity) +{ + wr_display_state state; + + if (!context || !wr_valid_transform(transform) || !isfinite(opacity) || + context->state_depth == SIZE_MAX) + return 0; + if (!wr_reserve((void**)&context->state_stack, &context->state_capacity, + context->state_depth + 1, sizeof(*context->state_stack))) + return 0; + + state.transform = context->transform; + state.opacity = context->opacity; + state.clip_depth = context->clip_depth; + context->state_stack[context->state_depth++] = state; + context->transform = wr_multiply_transform(context->transform, transform); + context->opacity = wr_clamp01(context->opacity * wr_clamp01(opacity)); + return 1; +} + +void +wr_display_list_pop_stacking_context(wr_context* context) +{ + wr_display_state state; + + if (!context || !context->state_depth) + return; + state = context->state_stack[--context->state_depth]; + context->transform = state.transform; + context->opacity = state.opacity; + context->clip_depth = state.clip_depth; +} + +int +wr_display_list_push_clip(wr_context* context, wr_rect clip) +{ + wr_rect effective_clip; + + if (!context || !wr_valid_rect(clip) || + context->clip_depth == SIZE_MAX) + return 0; + if (!wr_reserve((void**)&context->clip_stack, &context->clip_capacity, + context->clip_depth + 1, sizeof(*context->clip_stack))) + return 0; + + effective_clip = clip; + if (context->clip_depth) + effective_clip = wr_intersect_rect( + context->clip_stack[context->clip_depth - 1], clip); + context->clip_stack[context->clip_depth++] = effective_clip; + return 1; +} + +void +wr_display_list_pop_clip(wr_context* context) +{ + if (context && context->clip_depth) + --context->clip_depth; +} diff --git a/gfx/wr/wr_display_list.c b/gfx/wr/wr_display_list.c new file mode 100644 index 0000000000..8eddec1d87 --- /dev/null +++ b/gfx/wr/wr_display_list.c @@ -0,0 +1,43 @@ +#include + +int +wr_display_list_push_rect(wr_context* context, wr_rect rect, wr_color color) +{ + return context ? wr_display_list_push_transformed_rect( + context, rect, color, context->transform) : 0; +} + +int +wr_display_list_push_transformed_rect(wr_context* context, wr_rect rect, + wr_color color, wr_transform transform) +{ + wr_rect clip_rect = context && context->clip_depth ? + context->clip_stack[context->clip_depth - 1] : + context ? context->viewport : wr_empty_rect(); + uint8_t has_clip = context && context->clip_depth ? 1 : 0; + + if (!context || !wr_valid_rect(rect) || !wr_valid_transform(transform)) + return 0; + if (context->command_count == SIZE_MAX || + context->command_count >= UINT32_MAX) + return 0; + if (!wr_reserve((void**)&context->commands, &context->command_capacity, + context->command_count + 1, sizeof(*context->commands))) + return 0; + + context->commands[context->command_count].rect = rect; + color = wr_normalize_color(color); + color.a *= context->opacity; + context->commands[context->command_count].color = color; + context->commands[context->command_count].transform = transform; + context->commands[context->command_count].clip_rect = clip_rect; + context->commands[context->command_count].has_clip = has_clip; + context->commands[context->command_count].kind = WR_PRIMITIVE_SOLID; + context->commands[context->command_count].active = 1; + context->commands[context->command_count].image_key = 0; + context->commands[context->command_count].tex_rect = (wr_rect){ 0, 0, 1, 1 }; + context->commands[context->command_count].item_id = context->next_item_id++; + ++context->command_count; + context->frame_valid = 0; + return 1; +} diff --git a/gfx/wr/wr_frame.c b/gfx/wr/wr_frame.c new file mode 100644 index 0000000000..0a9a569302 --- /dev/null +++ b/gfx/wr/wr_frame.c @@ -0,0 +1,105 @@ +#include + +static int +wr_frame_has_image(const wr_context* context, uint32_t key) +{ + size_t i; + for (i = 0; i < context->image_count; ++i) + if (context->images[i].key == key) + return 1; + return 0; +} + +const wr_frame* +wr_context_build_frame(wr_context* context) +{ + size_t i; + + if (!context) + return NULL; + if (context->frame_valid) + return &context->frame; + context->quad_count = 0; + context->batch_count = 0; + + for (i = 0; i < context->command_count; ++i) { + wr_rect_command* command = &context->commands[i]; + wr_gpu_quad* quad; + wr_gpu_batch* batch; + + if (!command->active || + (command->kind == WR_PRIMITIVE_IMAGE && + !wr_frame_has_image(context, command->image_key)) || + wr_command_occluded(context, i)) + continue; + if (!wr_intersects(wr_transform_bounds(command->rect, + command->transform), + context->viewport) || + (command->clip_rect.width <= 0.0f || + command->clip_rect.height <= 0.0f)) + continue; + if (context->quad_count == SIZE_MAX || + context->quad_count >= UINT32_MAX || + context->batch_count == SIZE_MAX || + context->batch_count >= UINT32_MAX) + return NULL; + if (!wr_reserve((void**)&context->quads, &context->quad_capacity, + context->quad_count + 1, sizeof(*context->quads)) || + !wr_reserve((void**)&context->batches, &context->batch_capacity, + context->batch_count + 1, sizeof(*context->batches))) + return NULL; + + quad = &context->quads[context->quad_count++]; + quad->rect = command->rect; + quad->color = command->color; + quad->tex_rect = command->tex_rect; + quad->image_key = command->image_key; + quad->item_id = command->item_id; + quad->active = command->active; + quad->kind = command->kind; + + if (context->batch_count && + context->batches[context->batch_count - 1].kind == command->kind && + context->batches[context->batch_count - 1].image_key == + command->image_key && + wr_colors_equal(context->batches[context->batch_count - 1].color, + command->color) && + context->batches[context->batch_count - 1].transform.m11 == + command->transform.m11 && + context->batches[context->batch_count - 1].transform.m12 == + command->transform.m12 && + context->batches[context->batch_count - 1].transform.m21 == + command->transform.m21 && + context->batches[context->batch_count - 1].transform.m22 == + command->transform.m22 && + context->batches[context->batch_count - 1].transform.m31 == + command->transform.m31 && + context->batches[context->batch_count - 1].transform.m32 == + command->transform.m32 && + context->batches[context->batch_count - 1].has_clip == + command->has_clip && + (!command->has_clip || + wr_rects_equal(context->batches[context->batch_count - 1].clip_rect, + command->clip_rect))) { + batch = &context->batches[context->batch_count - 1]; + ++batch->quad_count; + } else { + batch = &context->batches[context->batch_count++]; + batch->first_quad = (uint32_t)(context->quad_count - 1); + batch->quad_count = 1; + batch->color = command->color; + batch->transform = command->transform; + batch->clip_rect = command->clip_rect; + batch->has_clip = command->has_clip; + batch->kind = command->kind; + batch->image_key = command->image_key; + } + } + + context->frame.quads = context->quads; + context->frame.quad_count = context->quad_count; + context->frame.batches = context->batches; + context->frame.batch_count = context->batch_count; + context->frame_valid = 1; + return &context->frame; +} diff --git a/gfx/wr/wr_frame_packet.c b/gfx/wr/wr_frame_packet.c new file mode 100644 index 0000000000..b1fe0d11a9 --- /dev/null +++ b/gfx/wr/wr_frame_packet.c @@ -0,0 +1,59 @@ +#include "webrender.h" + +#include +#include + +static int +wr_packet_size_mul_overflow(size_t a, size_t b) +{ + return b != 0 && a > SIZE_MAX / b; +} + +size_t +wr_frame_packet_size(const wr_frame* frame) +{ + size_t size; + + if (!frame || frame->quad_count > UINT32_MAX || + frame->batch_count > UINT32_MAX || + (frame->quad_count && !frame->quads) || + (frame->batch_count && !frame->batches)) + return 0; + + size = sizeof(wr_frame_packet_header); + if (wr_packet_size_mul_overflow(frame->quad_count, sizeof(*frame->quads)) || + wr_packet_size_mul_overflow(frame->batch_count, sizeof(*frame->batches))) + return 0; + if (size > SIZE_MAX - frame->quad_count * sizeof(*frame->quads)) + return 0; + size += frame->quad_count * sizeof(*frame->quads); + if (size > SIZE_MAX - frame->batch_count * sizeof(*frame->batches)) + return 0; + return size + frame->batch_count * sizeof(*frame->batches); +} + +int +wr_frame_serialize(const wr_frame* frame, void* buffer, size_t buffer_size) +{ + wr_frame_packet_header header; + size_t required = wr_frame_packet_size(frame); + unsigned char* output = (unsigned char*)buffer; + + if (!required || !buffer || buffer_size < required) + return 0; + header.version = WR_FRAME_PACKET_VERSION; + header.quad_count = (uint32_t)frame->quad_count; + header.batch_count = (uint32_t)frame->batch_count; + memcpy(output, &header, sizeof(header)); + output += sizeof(header); + if (frame->quad_count) { + memcpy(output, frame->quads, + frame->quad_count * sizeof(*frame->quads)); + output += frame->quad_count * sizeof(*frame->quads); + } + if (frame->batch_count) { + memcpy(output, frame->batches, + frame->batch_count * sizeof(*frame->batches)); + } + return 1; +} diff --git a/gfx/wr/wr_internal.h b/gfx/wr/wr_internal.h new file mode 100644 index 0000000000..4f7ebe2521 --- /dev/null +++ b/gfx/wr/wr_internal.h @@ -0,0 +1,82 @@ +#ifndef GFX_WR_INTERNAL_H +#define GFX_WR_INTERNAL_H + +#include +#include +#include +#include +#include + +typedef struct { + wr_rect rect; + wr_color color; + wr_transform transform; + wr_rect clip_rect; + uint8_t has_clip; + uint8_t kind; + uint8_t active; + uint32_t image_key; + wr_rect tex_rect; + wr_item_id item_id; +} wr_rect_command; + +typedef struct { + wr_transform transform; + float opacity; + size_t clip_depth; +} wr_display_state; + +typedef struct { + size_t command_count; + wr_rect_command* commands; +} wr_scene_transaction; + +struct wr_context { + wr_rect_command* commands; + size_t command_count; + size_t command_capacity; + + wr_gpu_quad* quads; + size_t quad_count; + size_t quad_capacity; + + wr_gpu_batch* batches; + size_t batch_count; + size_t batch_capacity; + + wr_rect viewport; + wr_transform transform; + float opacity; + wr_rect* clip_stack; + size_t clip_depth; + size_t clip_capacity; + wr_display_state* state_stack; + size_t state_depth; + size_t state_capacity; + wr_item_id next_item_id; + wr_image_resource* images; + size_t image_count; + size_t image_capacity; + wr_scene_transaction* transactions; + size_t transaction_depth; + size_t transaction_capacity; + int frame_valid; + wr_frame frame; +}; + +int wr_size_mul_overflow(size_t, size_t); +int wr_reserve(void**, size_t*, size_t, size_t); +int wr_valid_rect(wr_rect); +int wr_valid_transform(wr_transform); +wr_transform wr_identity_transform(void); +wr_transform wr_multiply_transform(wr_transform, wr_transform); +wr_rect wr_empty_rect(void); +float wr_clamp01(float); +wr_color wr_normalize_color(wr_color); +int wr_colors_equal(wr_color, wr_color); +int wr_rects_equal(wr_rect, wr_rect); +int wr_intersects(wr_rect, wr_rect); +wr_rect wr_intersect_rect(wr_rect, wr_rect); +wr_rect wr_transform_bounds(wr_rect, wr_transform); +int wr_command_occluded(const wr_context*, size_t); +#endif diff --git a/gfx/wr/wr_retained.c b/gfx/wr/wr_retained.c new file mode 100644 index 0000000000..b3853364f3 --- /dev/null +++ b/gfx/wr/wr_retained.c @@ -0,0 +1,251 @@ +#include + +static size_t +wr_image_index(const wr_context* context, uint32_t key) +{ + size_t i; + for (i = 0; context && i < context->image_count; ++i) + if (context->images[i].key == key) + return i; + return SIZE_MAX; +} + +static size_t +wr_item_index(const wr_context* context, wr_item_id item) +{ + size_t i; + for (i = 0; context && i < context->command_count; ++i) + if (context->commands[i].active && context->commands[i].item_id == item) + return i; + return SIZE_MAX; +} + +static int +wr_rect_contains(wr_rect outer, wr_rect inner) +{ + return inner.x >= outer.x && inner.y >= outer.y && + inner.x + inner.width <= outer.x + outer.width && + inner.y + inner.height <= outer.y + outer.height; +} + +static int +wr_append(wr_context* context, wr_item_id item, wr_rect rect, wr_color color, + uint8_t kind, uint32_t image_key, wr_rect tex_rect) +{ + wr_rect clip = context->clip_depth + ? context->clip_stack[context->clip_depth - 1] : context->viewport; + if (!context || !wr_valid_rect(rect) || + (kind == WR_PRIMITIVE_IMAGE && + (wr_image_index(context, image_key) == SIZE_MAX || + tex_rect.width == 0.0f || tex_rect.height == 0.0f))) + return 0; + if (!wr_reserve((void**)&context->commands, &context->command_capacity, + context->command_count + 1, sizeof(*context->commands))) + return 0; + if (!item) { + item = context->next_item_id++; + if (!item) + item = context->next_item_id++; + } + if (wr_item_index(context, item) != SIZE_MAX) + return 0; + context->commands[context->command_count] = (wr_rect_command){ + rect, wr_normalize_color(color), context->transform, clip, + context->clip_depth ? 1 : 0, kind, 1, image_key, tex_rect, item }; + context->commands[context->command_count].color.a *= context->opacity; + ++context->command_count; + context->frame_valid = 0; + return 1; +} + +int +wr_context_register_image(wr_context* context, uint32_t key, + uint32_t width, uint32_t height) +{ + size_t index; + if (!context || !key || !width || !height) + return 0; + index = wr_image_index(context, key); + if (index != SIZE_MAX) { + context->images[index].width = width; + context->images[index].height = height; + return 1; + } + if (!wr_reserve((void**)&context->images, &context->image_capacity, + context->image_count + 1, sizeof(*context->images))) + return 0; + context->images[context->image_count++] = (wr_image_resource){ key, width, height }; + return 1; +} + +void +wr_context_unregister_image(wr_context* context, uint32_t key) +{ + size_t index = wr_image_index(context, key); + if (index == SIZE_MAX) + return; + context->images[index] = context->images[--context->image_count]; + context->frame_valid = 0; +} + +void +wr_context_clear_images(wr_context* context) +{ + if (context) { + context->image_count = 0; + context->frame_valid = 0; + } +} + +int +wr_context_begin_transaction(wr_context* context) +{ + wr_scene_transaction transaction = { 0, NULL }; + if (!context || !wr_reserve((void**)&context->transactions, + &context->transaction_capacity, context->transaction_depth + 1, + sizeof(*context->transactions))) + return 0; + transaction.command_count = context->command_count; + if (transaction.command_count) { + transaction.commands = malloc(transaction.command_count * sizeof(*transaction.commands)); + if (!transaction.commands) + return 0; + memcpy(transaction.commands, context->commands, + transaction.command_count * sizeof(*transaction.commands)); + } + context->transactions[context->transaction_depth++] = transaction; + return 1; +} + +int +wr_context_commit_transaction(wr_context* context) +{ + if (!context || !context->transaction_depth) + return 0; + free(context->transactions[--context->transaction_depth].commands); + context->frame_valid = 0; + return 1; +} + +void +wr_context_abort_transaction(wr_context* context) +{ + wr_scene_transaction transaction; + if (!context || !context->transaction_depth) + return; + transaction = context->transactions[--context->transaction_depth]; + if (transaction.command_count) + memcpy(context->commands, transaction.commands, + transaction.command_count * sizeof(*context->commands)); + context->command_count = transaction.command_count; + free(transaction.commands); + context->frame_valid = 0; +} + +int +wr_context_remove_item(wr_context* context, wr_item_id item) +{ + size_t index = wr_item_index(context, item); + if (index == SIZE_MAX) + return 0; + context->commands[index].active = 0; + context->frame_valid = 0; + return 1; +} + +wr_item_id +wr_display_list_push_rect_with_id(wr_context* context, wr_item_id item, + wr_rect rect, wr_color color) +{ + return wr_append(context, item, rect, color, WR_PRIMITIVE_SOLID, 0, + (wr_rect){ 0, 0, 1, 1 }) ? item : 0; +} + +int +wr_display_list_update_rect(wr_context* context, wr_item_id item, wr_rect rect, + wr_color color, wr_transform transform) +{ + size_t index = wr_item_index(context, item); + if (index == SIZE_MAX || !wr_valid_rect(rect) || !wr_valid_transform(transform)) + return 0; + context->commands[index].rect = rect; + context->commands[index].color = wr_normalize_color(color); + context->commands[index].color.a *= context->opacity; + context->commands[index].transform = transform; + context->frame_valid = 0; + return 1; +} + +int +wr_display_list_push_image(wr_context* context, wr_rect rect, uint32_t key, + wr_rect tex_rect, wr_color tint) +{ return wr_append(context, 0, rect, tint, WR_PRIMITIVE_IMAGE, key, tex_rect); } + +wr_item_id +wr_display_list_push_image_with_id(wr_context* context, wr_item_id item, + wr_rect rect, uint32_t key, wr_rect tex, + wr_color tint) +{ return wr_append(context, item, rect, tint, WR_PRIMITIVE_IMAGE, key, tex) ? item : 0; } + +int +wr_display_list_push_linear_gradient(wr_context* context, wr_rect rect, + wr_color start, wr_color end, int horizontal) +{ + unsigned i; + for (i = 0; i < 32; ++i) { + float a = (float)i / 32.0f, b = (float)(i + 1) / 32.0f; + wr_rect slice = rect; + wr_color color = { start.r + (end.r-start.r)*(a+b)*.5f, + start.g + (end.g-start.g)*(a+b)*.5f, start.b + (end.b-start.b)*(a+b)*.5f, + start.a + (end.a-start.a)*(a+b)*.5f }; + if (horizontal) { slice.x += rect.width*a; slice.width = rect.width*(b-a); } + else { slice.y += rect.height*a; slice.height = rect.height*(b-a); } + if (!wr_append(context, 0, slice, color, WR_PRIMITIVE_SOLID, 0, + (wr_rect){ 0, 0, 1, 1 })) return 0; + } + return 1; +} + +int +wr_display_list_push_rounded_rect(wr_context* context, wr_rect rect, + wr_color color, float radius) +{ + unsigned i; + (void)radius; + for (i = 0; i < 16; ++i) { + wr_rect slice = { rect.x, rect.y + rect.height * i / 16.0f, + rect.width, rect.height / 16.0f }; + if (!wr_append(context, 0, slice, color, WR_PRIMITIVE_SOLID, 0, + (wr_rect){ 0, 0, 1, 1 })) + return 0; + } + return 1; +} + +int +wr_display_list_push_glyph_run(wr_context* context, uint32_t key, + const wr_glyph* glyphs, size_t count, wr_color color) +{ + size_t i; + for (i = 0; glyphs && i < count; ++i) + if (!wr_append(context, 0, glyphs[i].rect, color, WR_PRIMITIVE_IMAGE, + key, glyphs[i].tex_rect)) return 0; + return glyphs && count != 0; +} + +int +wr_command_occluded(const wr_context* context, size_t index) +{ + size_t i; + wr_rect bounds = wr_transform_bounds(context->commands[index].rect, + context->commands[index].transform); + for (i = index + 1; i < context->command_count; ++i) { + const wr_rect_command* covering = &context->commands[i]; + wr_rect cover; + if (!covering->active || covering->kind != WR_PRIMITIVE_SOLID || + covering->color.a != 1.0f) continue; + cover = wr_transform_bounds(covering->rect, covering->transform); + if (wr_rect_contains(cover, bounds)) return 1; + } + return 0; +} diff --git a/gfx/wr/wr_util.c b/gfx/wr/wr_util.c new file mode 100644 index 0000000000..58b025d6c0 --- /dev/null +++ b/gfx/wr/wr_util.c @@ -0,0 +1,160 @@ +#include + +int +wr_size_mul_overflow(size_t a, size_t b) +{ + return b != 0 && a > SIZE_MAX / b; +} + +int +wr_reserve(void** data, size_t* capacity, size_t count, size_t element_size) +{ + size_t new_capacity; + void* new_data; + + if (count <= *capacity) + return 1; + if (element_size == 0 || wr_size_mul_overflow(count, element_size)) + return 0; + + new_capacity = *capacity ? *capacity : 8; + while (new_capacity < count) { + if (new_capacity > SIZE_MAX / 2) { + new_capacity = count; + break; + } + new_capacity *= 2; + } + if (wr_size_mul_overflow(new_capacity, element_size)) + return 0; + + new_data = realloc(*data, new_capacity * element_size); + if (!new_data) + return 0; + + *data = new_data; + *capacity = new_capacity; + return 1; +} + +int +wr_valid_rect(wr_rect rect) +{ + return isfinite(rect.x) && isfinite(rect.y) && + isfinite(rect.width) && isfinite(rect.height) && + rect.width > 0.0f && rect.height > 0.0f; +} + +int +wr_valid_transform(wr_transform transform) +{ + return isfinite(transform.m11) && isfinite(transform.m12) && + isfinite(transform.m21) && isfinite(transform.m22) && + isfinite(transform.m31) && isfinite(transform.m32); +} + +wr_transform +wr_identity_transform(void) +{ + wr_transform transform = { 1, 0, 0, 1, 0, 0 }; + return transform; +} + +wr_transform +wr_multiply_transform(wr_transform a, wr_transform b) +{ + wr_transform result; + result.m11 = a.m11 * b.m11 + a.m21 * b.m12; + result.m12 = a.m12 * b.m11 + a.m22 * b.m12; + result.m21 = a.m11 * b.m21 + a.m21 * b.m22; + result.m22 = a.m12 * b.m21 + a.m22 * b.m22; + result.m31 = a.m11 * b.m31 + a.m21 * b.m32 + a.m31; + result.m32 = a.m12 * b.m31 + a.m22 * b.m32 + a.m32; + return result; +} + +wr_rect +wr_empty_rect(void) +{ + wr_rect rect = { 0, 0, 0, 0 }; + return rect; +} + +float +wr_clamp01(float value) +{ + if (value < 0.0f) + return 0.0f; + if (value > 1.0f) + return 1.0f; + return value; +} + +wr_color +wr_normalize_color(wr_color color) +{ + color.r = wr_clamp01(color.r); + color.g = wr_clamp01(color.g); + color.b = wr_clamp01(color.b); + color.a = wr_clamp01(color.a); + return color; +} + +int +wr_colors_equal(wr_color a, wr_color b) +{ + return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; +} + +int +wr_rects_equal(wr_rect a, wr_rect b) +{ + return a.x == b.x && a.y == b.y && + a.width == b.width && a.height == b.height; +} + +int +wr_intersects(wr_rect a, wr_rect b) +{ + return a.x < b.x + b.width && a.x + a.width > b.x && + a.y < b.y + b.height && a.y + a.height > b.y; +} + +wr_rect +wr_intersect_rect(wr_rect a, wr_rect b) +{ + float left = a.x > b.x ? a.x : b.x; + float top = a.y > b.y ? a.y : b.y; + float right = a.x + a.width < b.x + b.width ? + a.x + a.width : b.x + b.width; + float bottom = a.y + a.height < b.y + b.height ? + a.y + a.height : b.y + b.height; + wr_rect result = { left, top, right - left, bottom - top }; + return result; +} + +wr_rect +wr_transform_bounds(wr_rect rect, wr_transform transform) +{ + float x1 = rect.x * transform.m11 + rect.y * transform.m21 + transform.m31; + float y1 = rect.x * transform.m12 + rect.y * transform.m22 + transform.m32; + float x2 = (rect.x + rect.width) * transform.m11 + + rect.y * transform.m21 + transform.m31; + float y2 = (rect.x + rect.width) * transform.m12 + + rect.y * transform.m22 + transform.m32; + float x3 = rect.x * transform.m11 + + (rect.y + rect.height) * transform.m21 + transform.m31; + float y3 = rect.x * transform.m12 + + (rect.y + rect.height) * transform.m22 + transform.m32; + float x4 = (rect.x + rect.width) * transform.m11 + + (rect.y + rect.height) * transform.m21 + transform.m31; + float y4 = (rect.x + rect.width) * transform.m12 + + (rect.y + rect.height) * transform.m22 + transform.m32; + float left = fminf(fminf(x1, x2), fminf(x3, x4)); + float right = fmaxf(fmaxf(x1, x2), fmaxf(x3, x4)); + float top = fminf(fminf(y1, y2), fminf(y3, y4)); + float bottom = fmaxf(fmaxf(y1, y2), fmaxf(y3, y4)); + wr_rect result = { left, top, right - left, bottom - top }; + return result; +} + diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index 7026b651cc..9cc2638640 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -303,6 +303,17 @@ var snapshotFormatters = { ? data.windowLayerManagerType : "BasicLayers (" + strings.GetStringFromName("mainThreadNoOMTC") + ")"; addRow("features", "compositing", compositor); + + let webRenderEnabled = false; + try { + webRenderEnabled = Services.prefs.getBoolPref("gfx.webrender.enabled"); + } catch (e) { + // Keep about:support usable if the preference is unavailable in a build. + } + addRow("features", "webRender", + strings.GetStringFromName(webRenderEnabled + ? "webRenderEnabled" + : "webRenderDisabled")); let acceleratedWindows = data.numAcceleratedWindows + "/" + data.numTotalWindows; if (data.windowLayerManagerType) { diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.properties b/toolkit/locales/en-US/chrome/global/aboutSupport.properties index e2836d6afb..90d769e1af 100644 --- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties +++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties @@ -58,6 +58,9 @@ blockedMismatchedVersion = Blocked for your graphics driver version mismatch bet clearTypeParameters = ClearType Parameters compositing = Compositing +webRender = WebRender +webRenderEnabled = Enabled +webRenderDisabled = Disabled hardwareH264 = Hardware H264 Decoding hardwareVP9 = Hardware VP9 Decoding audioBackend = Audio Backend