diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/mlgpu/LayerManagerMLGPU.cpp | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | gfx/layers/mlgpu/LayerManagerMLGPU.cpp | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/gfx/layers/mlgpu/LayerManagerMLGPU.cpp b/gfx/layers/mlgpu/LayerManagerMLGPU.cpp new file mode 100644 index 0000000000..0379ffe785 --- /dev/null +++ b/gfx/layers/mlgpu/LayerManagerMLGPU.cpp @@ -0,0 +1,588 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "LayerManagerMLGPU.h" +#include "LayerTreeInvalidation.h" +#include "PaintedLayerMLGPU.h" +#include "ImageLayerMLGPU.h" +#include "CanvasLayerMLGPU.h" +#include "ContainerLayerMLGPU.h" +#include "GeckoProfiler.h" // for profiler_* +#include "gfxEnv.h" // for gfxEnv +#include "MLGDevice.h" +#include "RenderPassMLGPU.h" +#include "RenderViewMLGPU.h" +#include "ShaderDefinitionsMLGPU.h" +#include "SharedBufferMLGPU.h" +#include "UnitTransforms.h" +#include "TextureSourceProviderMLGPU.h" +#include "TreeTraversal.h" +#include "FrameBuilder.h" +#include "UtilityMLGPU.h" +#include "CompositionRecorder.h" +#include "mozilla/layers/Diagnostics.h" +#include "mozilla/layers/TextRenderer.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/ToString.h" + +#ifdef XP_WIN +# include "mozilla/widget/WinCompositorWidget.h" +# include "mozilla/gfx/DeviceManagerDx.h" +#endif + +namespace mozilla { +namespace layers { + +using namespace gfx; + +static const int kDebugOverlayX = 2; +static const int kDebugOverlayY = 5; +static const int kDebugOverlayMaxWidth = 600; +static const int kDebugOverlayMaxHeight = 96; + +class RecordedFrameMLGPU : public RecordedFrame { + public: + RecordedFrameMLGPU(MLGDevice* aDevice, MLGTexture* aTexture, + const TimeStamp& aTimestamp) + : RecordedFrame(aTimestamp), mDevice(aDevice) { + mSoftTexture = + aDevice->CreateTexture(aTexture->GetSize(), SurfaceFormat::B8G8R8A8, + MLGUsage::Staging, MLGTextureFlags::None); + + aDevice->CopyTexture(mSoftTexture, IntPoint(), aTexture, + IntRect(IntPoint(), aTexture->GetSize())); + } + + ~RecordedFrameMLGPU() { + if (mIsMapped) { + mDevice->Unmap(mSoftTexture); + } + } + + virtual already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override { + if (mDataSurf) { + return RefPtr<DataSourceSurface>(mDataSurf).forget(); + } + MLGMappedResource map; + if (!mDevice->Map(mSoftTexture, MLGMapType::READ, &map)) { + return nullptr; + } + + mIsMapped = true; + mDataSurf = Factory::CreateWrappingDataSourceSurface( + map.mData, map.mStride, mSoftTexture->GetSize(), + SurfaceFormat::B8G8R8A8); + return RefPtr<DataSourceSurface>(mDataSurf).forget(); + } + + private: + RefPtr<MLGDevice> mDevice; + // Software texture in VRAM. + RefPtr<MLGTexture> mSoftTexture; + RefPtr<DataSourceSurface> mDataSurf; + bool mIsMapped = false; +}; + +LayerManagerMLGPU::LayerManagerMLGPU(widget::CompositorWidget* aWidget) + : mWidget(aWidget), + mDrawDiagnostics(false), + mUsingInvalidation(false), + mCurrentFrame(nullptr), + mDebugFrameNumber(0) { + if (!aWidget) { + return; + } + +#ifdef WIN32 + mDevice = DeviceManagerDx::Get()->GetMLGDevice(); +#endif + if (!mDevice || !mDevice->IsValid()) { + gfxWarning() << "Could not acquire an MLGDevice!"; + return; + } + + mSwapChain = mDevice->CreateSwapChainForWidget(aWidget); + if (!mSwapChain) { + gfxWarning() << "Could not acquire an MLGSwapChain!"; + return; + } + + mDiagnostics = MakeUnique<Diagnostics>(); + mTextRenderer = new TextRenderer(); +} + +LayerManagerMLGPU::~LayerManagerMLGPU() { + if (mTextureSourceProvider) { + mTextureSourceProvider->Destroy(); + } +} + +bool LayerManagerMLGPU::Initialize() { + if (!mDevice || !mSwapChain) { + return false; + } + + mTextureSourceProvider = new TextureSourceProviderMLGPU(this, mDevice); + return true; +} + +void LayerManagerMLGPU::Destroy() { + if (IsDestroyed()) { + return; + } + + LayerManager::Destroy(); + mProfilerScreenshotGrabber.Destroy(); + + if (mDevice && mDevice->IsValid()) { + mDevice->Flush(); + } + if (mSwapChain) { + mSwapChain->Destroy(); + mSwapChain = nullptr; + } + if (mTextureSourceProvider) { + mTextureSourceProvider->Destroy(); + mTextureSourceProvider = nullptr; + } + mWidget = nullptr; + mDevice = nullptr; +} + +void LayerManagerMLGPU::ForcePresent() { + if (!mDevice->IsValid()) { + return; + } + + IntSize windowSize = mWidget->GetClientSize().ToUnknownSize(); + if (mSwapChain->GetSize() != windowSize) { + return; + } + + mSwapChain->ForcePresent(); +} + +already_AddRefed<ContainerLayer> LayerManagerMLGPU::CreateContainerLayer() { + return MakeAndAddRef<ContainerLayerMLGPU>(this); +} + +already_AddRefed<ColorLayer> LayerManagerMLGPU::CreateColorLayer() { + return MakeAndAddRef<ColorLayerMLGPU>(this); +} + +already_AddRefed<RefLayer> LayerManagerMLGPU::CreateRefLayer() { + return MakeAndAddRef<RefLayerMLGPU>(this); +} + +already_AddRefed<PaintedLayer> LayerManagerMLGPU::CreatePaintedLayer() { + return MakeAndAddRef<PaintedLayerMLGPU>(this); +} + +already_AddRefed<ImageLayer> LayerManagerMLGPU::CreateImageLayer() { + return MakeAndAddRef<ImageLayerMLGPU>(this); +} + +already_AddRefed<CanvasLayer> LayerManagerMLGPU::CreateCanvasLayer() { + return MakeAndAddRef<CanvasLayerMLGPU>(this); +} + +TextureFactoryIdentifier LayerManagerMLGPU::GetTextureFactoryIdentifier() { + TextureFactoryIdentifier ident; + if (mDevice) { + ident = mDevice->GetTextureFactoryIdentifier(mWidget); + } + ident.mUsingAdvancedLayers = true; + return ident; +} + +LayersBackend LayerManagerMLGPU::GetBackendType() { + return mDevice ? mDevice->GetLayersBackend() : LayersBackend::LAYERS_NONE; +} + +void LayerManagerMLGPU::SetRoot(Layer* aLayer) { mRoot = aLayer; } + +bool LayerManagerMLGPU::BeginTransaction(const nsCString& aURL) { return true; } + +void LayerManagerMLGPU::BeginTransactionWithDrawTarget( + gfx::DrawTarget* aTarget, const gfx::IntRect& aRect) { + MOZ_ASSERT(!mTarget); + + mTarget = aTarget; + mTargetRect = aRect; +} + +// Helper class for making sure textures are unlocked. +class MOZ_STACK_CLASS AutoUnlockAllTextures final { + public: + explicit AutoUnlockAllTextures(MLGDevice* aDevice) : mDevice(aDevice) {} + ~AutoUnlockAllTextures() { mDevice->UnlockAllTextures(); } + + private: + RefPtr<MLGDevice> mDevice; +}; + +void LayerManagerMLGPU::EndTransaction(const TimeStamp& aTimeStamp, + EndTransactionFlags aFlags) { + AUTO_PROFILER_LABEL("LayerManager::EndTransaction", GRAPHICS); + + TextureSourceProvider::AutoReadUnlockTextures unlock(mTextureSourceProvider); + + if (!mRoot || (aFlags & END_NO_IMMEDIATE_REDRAW) || !mWidget) { + return; + } + + if (!mDevice->IsValid()) { + // Waiting device reset handling. + return; + } + + mCompositionOpportunityId = mCompositionOpportunityId.Next(); + SetCompositionTime(aTimeStamp); + + mCompositionStartTime = TimeStamp::Now(); + + IntSize windowSize = mWidget->GetClientSize().ToUnknownSize(); + if (windowSize.IsEmpty()) { + return; + } + + // Resize the window if needed. +#ifdef XP_WIN + mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary(); +#endif + if (mSwapChain->GetSize() != windowSize) { + // Note: all references to the backbuffer must be cleared. + mDevice->SetRenderTarget(nullptr); + if (!mSwapChain->ResizeBuffers(windowSize)) { + gfxCriticalNote << "Could not resize the swapchain (" + << hexa(windowSize.width) << "," + << hexa(windowSize.height) << ")"; + return; + } + } + + // Don't draw the diagnostic overlay if we want to snapshot the output. + mDrawDiagnostics = StaticPrefs::layers_acceleration_draw_fps() && !mTarget; + mUsingInvalidation = StaticPrefs::layers_mlgpu_enable_invalidation(); + mDebugFrameNumber++; + + AL_LOG("--- Compositing frame %d ---\n", mDebugFrameNumber); + + // Compute transforms - and the changed area, if enabled. + mRoot->ComputeEffectiveTransforms(Matrix4x4()); + ComputeInvalidRegion(); + + // Build and execute draw commands, and present. + if (PreRender()) { + Composite(); + PostRender(); + } + + mTextureSourceProvider->FlushPendingNotifyNotUsed(); + + // Finish composition. + mLastCompositionEndTime = TimeStamp::Now(); +} + +void LayerManagerMLGPU::Composite() { + if (gfxEnv::SkipComposition()) { + return; + } + + AUTO_PROFILER_LABEL("LayerManagerMLGPU::Composite", GRAPHICS); + + // Don't composite if we're minimized/hidden, or if there is nothing to draw. + if (mWidget->IsHidden()) { + return; + } + + // Make sure the diagnostic area gets invalidated. We do this now, rather than + // earlier, so we don't accidentally cause extra composites. + Maybe<IntRect> diagnosticRect; + if (mDrawDiagnostics) { + diagnosticRect = + Some(IntRect(kDebugOverlayX, kDebugOverlayY, kDebugOverlayMaxWidth, + kDebugOverlayMaxHeight)); + } + + AL_LOG("Computed invalid region: %s\n", Stringify(mInvalidRegion).c_str()); + + // Now that we have the final invalid region, give it to the swap chain which + // will tell us if we still need to render. + if (!mSwapChain->ApplyNewInvalidRegion(std::move(mInvalidRegion), + diagnosticRect)) { + mProfilerScreenshotGrabber.NotifyEmptyFrame(); + + // Discard the current payloads. These payloads did not require a composite + // (they caused no changes to anything visible), so we don't want to measure + // their latency. + mPayload.Clear(); + + return; + } + + AutoUnlockAllTextures autoUnlock(mDevice); + + mDevice->BeginFrame(); + + RenderLayers(); + + if (mDrawDiagnostics) { + DrawDebugOverlay(); + } + + if (mTarget) { + mSwapChain->CopyBackbuffer(mTarget, mTargetRect); + mTarget = nullptr; + mTargetRect = IntRect(); + } + mSwapChain->Present(); + + // We call this here to mimic the behavior in LayerManagerComposite, as to + // not change what Talos measures. That is, we do not record an empty frame + // as a frame, since we short-circuit at the top of this function. + RecordFrame(); + + mDevice->EndFrame(); + + // Free the old cloned property tree, then clone a new one. Note that we do + // this after compositing, since layer preparation actually mutates the layer + // tree (for example, ImageHost::mLastFrameID). We want the property tree to + // pick up these changes. Similarly, we are careful to not mutate the tree + // in any way that we *don't* want LayerProperties to catch, lest we cause + // extra invalidation. + // + // Note that the old Compositor performs occlusion culling directly on the + // shadow visible region, and does this *before* cloning layer tree + // properties. Advanced Layers keeps the occlusion region separate and + // performs invalidation against the clean layer tree. + mClonedLayerTreeProperties = nullptr; + mClonedLayerTreeProperties = LayerProperties::CloneFrom(mRoot); + + PayloadPresented(TimeStamp::Now()); + + mPayload.Clear(); +} + +void LayerManagerMLGPU::RenderLayers() { + AUTO_PROFILER_LABEL("LayerManagerMLGPU::RenderLayers", GRAPHICS); + + // Traverse the layer tree and assign each layer to a render target. + FrameBuilder builder(this, mSwapChain); + mCurrentFrame = &builder; + + if (!builder.Build()) { + return; + } + + if (mDrawDiagnostics) { + mDiagnostics->RecordPrepareTime( + (TimeStamp::Now() - mCompositionStartTime).ToMilliseconds()); + } + + // Make sure we acquire/release the sync object. + if (!mDevice->Synchronize()) { + // Catastrophic failure - probably a device reset. + return; + } + + TimeStamp start = TimeStamp::Now(); + + // Upload shared buffers. + mDevice->FinishSharedBufferUse(); + + // Prepare the pipeline. + if (mDrawDiagnostics) { + IntSize size = mSwapChain->GetBackBufferInvalidRegion().GetBounds().Size(); + uint32_t numPixels = size.width * size.height; + mDevice->StartDiagnostics(numPixels); + } + + // Execute all render passes. + builder.Render(); + + mProfilerScreenshotGrabber.MaybeGrabScreenshot( + mDevice, builder.GetWidgetRT()->GetTexture()); + + if (mCompositionRecorder) { + bool hasContentPaint = false; + for (CompositionPayload& payload : mPayload) { + if (payload.mType == CompositionPayloadType::eContentPaint) { + hasContentPaint = true; + break; + } + } + + if (hasContentPaint) { + RefPtr<RecordedFrame> frame = new RecordedFrameMLGPU( + mDevice, builder.GetWidgetRT()->GetTexture(), TimeStamp::Now()); + mCompositionRecorder->RecordFrame(frame); + } + } + mCurrentFrame = nullptr; + + if (mDrawDiagnostics) { + mDiagnostics->RecordCompositeTime( + (TimeStamp::Now() - start).ToMilliseconds()); + mDevice->EndDiagnostics(); + } +} + +void LayerManagerMLGPU::DrawDebugOverlay() { + IntSize windowSize = mSwapChain->GetSize(); + + GPUStats stats; + mDevice->GetDiagnostics(&stats); + stats.mScreenPixels = windowSize.width * windowSize.height; + + std::string text = mDiagnostics->GetFrameOverlayString(stats); + RefPtr<TextureSource> texture = mTextRenderer->RenderText( + mTextureSourceProvider, text, 600, TextRenderer::FontType::FixedWidth); + if (!texture) { + return; + } + + if (mUsingInvalidation && + (texture->GetSize().width > kDebugOverlayMaxWidth || + texture->GetSize().height > kDebugOverlayMaxHeight)) { + gfxCriticalNote << "Diagnostic overlay exceeds invalidation area: %s" + << ToString(texture->GetSize()).c_str(); + } + + struct DebugRect { + Rect bounds; + Rect texCoords; + }; + + if (!mDiagnosticVertices) { + DebugRect rect; + rect.bounds = + Rect(Point(kDebugOverlayX, kDebugOverlayY), Size(texture->GetSize())); + rect.texCoords = Rect(0.0, 0.0, 1.0, 1.0); + + VertexStagingBuffer instances; + if (!instances.AppendItem(rect)) { + return; + } + + mDiagnosticVertices = mDevice->CreateBuffer( + MLGBufferType::Vertex, instances.NumItems() * instances.SizeOfItem(), + MLGUsage::Immutable, instances.GetBufferStart()); + if (!mDiagnosticVertices) { + return; + } + } + + // Note: we rely on the world transform being correctly left bound by the + // outermost render view. + mDevice->SetScissorRect(Nothing()); + mDevice->SetDepthTestMode(MLGDepthTestMode::Disabled); + mDevice->SetTopology(MLGPrimitiveTopology::UnitQuad); + mDevice->SetVertexShader(VertexShaderID::DiagnosticText); + mDevice->SetVertexBuffer(1, mDiagnosticVertices, sizeof(DebugRect)); + mDevice->SetPixelShader(PixelShaderID::DiagnosticText); + mDevice->SetBlendState(MLGBlendState::Over); + mDevice->SetPSTexture(0, texture); + mDevice->SetSamplerMode(0, SamplerMode::Point); + mDevice->DrawInstanced(4, 1, 0, 0); +} + +void LayerManagerMLGPU::ComputeInvalidRegion() { + // If invalidation is disabled, throw away cloned properties and redraw the + // whole target area. + if (!mUsingInvalidation) { + mInvalidRegion = mTarget ? mTargetRect : mRenderBounds; + mNextFrameInvalidRegion.SetEmpty(); + return; + } + + nsIntRegion changed; + if (mClonedLayerTreeProperties) { + if (!mClonedLayerTreeProperties->ComputeDifferences(mRoot, changed, + nullptr)) { + changed = mRenderBounds; + } + } else { + changed = mRenderBounds; + } + + // We compute the change region, but if we're painting to a target, we save + // it for the next frame instead. + if (mTarget) { + mInvalidRegion = mTargetRect; + mNextFrameInvalidRegion.OrWith(changed); + } else { + mInvalidRegion = std::move(mNextFrameInvalidRegion); + mInvalidRegion.OrWith(changed); + } +} + +void LayerManagerMLGPU::AddInvalidRegion(const nsIntRegion& aRegion) { + mNextFrameInvalidRegion.OrWith(aRegion); +} + +TextureSourceProvider* LayerManagerMLGPU::GetTextureSourceProvider() const { + return mTextureSourceProvider; +} + +bool LayerManagerMLGPU::IsCompositingToScreen() const { return !mTarget; } + +bool LayerManagerMLGPU::AreComponentAlphaLayersEnabled() { + return LayerManager::AreComponentAlphaLayersEnabled(); +} + +bool LayerManagerMLGPU::BlendingRequiresIntermediateSurface() { return true; } + +void LayerManagerMLGPU::EndTransaction(DrawPaintedLayerCallback aCallback, + void* aCallbackData, + EndTransactionFlags aFlags) { + MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)"); +} + +void LayerManagerMLGPU::ClearCachedResources(Layer* aSubtree) { + Layer* root = aSubtree ? aSubtree : mRoot.get(); + if (!root) { + return; + } + + ForEachNode<ForwardIterator>(root, [](Layer* aLayer) { + LayerMLGPU* layer = aLayer->AsHostLayer()->AsLayerMLGPU(); + if (!layer) { + return; + } + layer->ClearCachedResources(); + }); +} + +void LayerManagerMLGPU::NotifyShadowTreeTransaction() { + if (StaticPrefs::layers_acceleration_draw_fps()) { + mDiagnostics->AddTxnFrame(); + } +} + +void LayerManagerMLGPU::UpdateRenderBounds(const gfx::IntRect& aRect) { + mRenderBounds = aRect; +} + +bool LayerManagerMLGPU::PreRender() { + AUTO_PROFILER_LABEL("LayerManagerMLGPU::PreRender", GRAPHICS); + + widget::WidgetRenderingContext context; + if (!mWidget->PreRender(&context)) { + return false; + } + mWidgetContext = Some(context); + return true; +} + +void LayerManagerMLGPU::PostRender() { + mWidget->PostRender(mWidgetContext.ptr()); + mProfilerScreenshotGrabber.MaybeProcessQueue(); + mWidgetContext = Nothing(); +} + +} // namespace layers +} // namespace mozilla |