зеркало из https://github.com/mozilla/gecko-dev.git
589 строки
14 KiB
C++
589 строки
14 KiB
C++
/* -*- 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 "GeckoProfiler.h" // for profiler_*
|
|
#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 "LayersLogging.h"
|
|
#include "UtilityMLGPU.h"
|
|
#include "mozilla/layers/Diagnostics.h"
|
|
#include "mozilla/layers/TextRenderer.h"
|
|
|
|
#ifdef XP_WIN
|
|
#include "mozilla/widget/WinCompositorWidget.h"
|
|
#include "mozilla/gfx/DeviceManagerDx.h"
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
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;
|
|
|
|
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();
|
|
|
|
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<BorderLayer>
|
|
LayerManagerMLGPU::CreateBorderLayer()
|
|
{
|
|
MOZ_ASSERT_UNREACHABLE("Not yet implemented");
|
|
return nullptr;
|
|
}
|
|
|
|
already_AddRefed<TextLayer>
|
|
LayerManagerMLGPU::CreateTextLayer()
|
|
{
|
|
MOZ_ASSERT_UNREACHABLE("Not yet implemented");
|
|
return nullptr;
|
|
}
|
|
|
|
already_AddRefed<CanvasLayer>
|
|
LayerManagerMLGPU::CreateCanvasLayer()
|
|
{
|
|
return MakeAndAddRef<CanvasLayerMLGPU>(this);
|
|
}
|
|
|
|
TextureFactoryIdentifier
|
|
LayerManagerMLGPU::GetTextureFactoryIdentifier()
|
|
{
|
|
TextureFactoryIdentifier ident;
|
|
if (mDevice) {
|
|
ident = mDevice->GetTextureFactoryIdentifier();
|
|
}
|
|
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()
|
|
{
|
|
MOZ_ASSERT(!mTarget);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
LayerManagerMLGPU::BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget,
|
|
const gfx::IntRect& aRect)
|
|
{
|
|
MOZ_ASSERT(!mTarget);
|
|
|
|
mTarget = aTarget;
|
|
mTargetRect = aRect;
|
|
return;
|
|
}
|
|
|
|
// Helper class for making sure textures are unlocked.
|
|
class MOZ_STACK_CLASS AutoUnlockAllTextures
|
|
{
|
|
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);
|
|
|
|
SetCompositionTime(aTimeStamp);
|
|
|
|
TextureSourceProvider::AutoReadUnlockTextures unlock(mTextureSourceProvider);
|
|
|
|
if (!mRoot || (aFlags & END_NO_IMMEDIATE_REDRAW) || !mWidget) {
|
|
return;
|
|
}
|
|
|
|
mCompositionStartTime = TimeStamp::Now();
|
|
|
|
IntSize windowSize = mWidget->GetClientSize().ToUnknownSize();
|
|
if (windowSize.IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// Resize the window if needed.
|
|
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 = gfxPrefs::LayersDrawFPS() && !mTarget;
|
|
mUsingInvalidation = gfxPrefs::AdvancedLayersUseInvalidation();
|
|
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()
|
|
{
|
|
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(Move(mInvalidRegion), diagnosticRect)) {
|
|
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);
|
|
}
|
|
|
|
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();
|
|
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,
|
|
30,
|
|
600,
|
|
TextRenderer::FontType::FixedWidth);
|
|
if (!texture) {
|
|
return;
|
|
}
|
|
|
|
if (mUsingInvalidation &&
|
|
(texture->GetSize().width > kDebugOverlayMaxWidth ||
|
|
texture->GetSize().height > kDebugOverlayMaxHeight))
|
|
{
|
|
gfxCriticalNote << "Diagnostic overlay exceeds invalidation area: %s" << Stringify(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 = 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 (gfxPrefs::LayersDrawFPS()) {
|
|
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());
|
|
mWidgetContext = Nothing();
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|