Initial import of Advanced Layers. (bug 1365879 part 20, r=bas)

This commit is contained in:
David Anderson 2017-06-23 14:23:12 -07:00
parent 3115836c5c
commit 6376e2c6bb
76 changed files with 11758 additions and 3 deletions

313
gfx/doc/AdvancedLayers.md Normal file
View File

@ -0,0 +1,313 @@
Advanced Layers
==============
Advanced Layers is a new method of compositing layers in Gecko. This document serves as a technical
overview and provides a short walk-through of its source code.
Overview
-------------
Advanced Layers attempts to group as many GPU operations as it can into a single draw call. This is
a common technique in GPU-based rendering called "batching". It is not always trivial, as a
batching algorithm can easily waste precious CPU resources trying to build optimal draw calls.
Advanced Layers reuses the existing Gecko layers system as much as possible. Huge layer trees do
not currently scale well (see the future work section), so opportunities for batching are currently
limited without expending unnecessary resources elsewhere. However, Advanced Layers has a few
benefits:
* It submits smaller GPU workloads and buffer uploads than the existing compositor.
* It needs only a single pass over the layer tree.
* It uses occlusion information more intelligently.
* It is easier to add new specialized rendering paths and new layer types.
* It separates compositing logic from device logic, unlike the existing compositor.
* It is much faster at rendering 3d scenes or complex layer trees.
* It has experimental code to use the z-buffer for occlusion culling.
Because of these benefits we hope that it provides a significant improvement over the existing
compositor.
Advanced Layers uses the acronym "MLG" and "MLGPU" in many places. This stands for "Mid-Level
Graphics", the idea being that it is optimized for Direct3D 11-style rendering systems as opposed
to Direct3D 12 or Vulkan.
LayerManagerMLGPU
------------------------------
Advanced layers does not change client-side rendering at all. Content still uses Direct2D (when
possible), and creates identical layer trees as it would with a normal Direct3D 11 compositor. In
fact, Advanced Layers re-uses all of the existing texture handling and video infrastructure as
well, replacing only the composite-side layer types.
Advanced Layers does not create a `LayerManagerComposite` - instead, it creates a
`LayerManagerMLGPU`. This layer manager does not have a `Compositor` - instead, it has an
`MLGDevice`, which roughly abstracts the Direct3D 11 API. (The hope is that this API is easily
interchangeable for something else when cross-platform or software support is needed.)
`LayerManagerMLGPU` also dispenses with the old "composite" layers for new layer types. For
example, `ColorLayerComposite` becomes `ColorLayerMLGPU`. Since these layer types implement
`HostLayer`, they integrate with `LayerTransactionParent` as normal composite layers would.
Rendering Overview
----------------------------
The steps for rendering are described in more detail below, but roughly the process is:
1. Sort layers front-to-back.
2. Create a dependency tree of render targets (called "views").
3. Accumulate draw calls for all layers in each view.
4. Upload draw call buffers to the GPU.
5. Execute draw commands for each view.
Advanced Layers divides the layer tree into "views" (`RenderViewMLGPU`), which correspond to a
render target. The root layer is represented by a view corresponding to the screen. Layers that
require intermediate surfaces have temporary views. Layers are analyzed front-to-back, and rendered
back-to-front within a view. Views themselves are rendered front-to-back, to minimize render target
switching.
Each view contains one or more rendering passes (`RenderPassMLGPU`). A pass represents a single
draw command with one or more rendering items attached to it. For example, a `SolidColorPass` item
contains a rectangle and an RGBA value, and many of these can be drawn with a single GPU call.
When considering a layer, views will first try to find an existing rendering batch that can support
it. If so, that pass will accumulate another draw item for the layer. Otherwise, a new pass will be
added.
When trying to find a matching pass for a layer, there is a tradeoff in CPU time versus the GPU
time saved by not issuing another draw commands. We generally care more about CPU time, so we do
not try too hard in matching items to an existing batch.
After all layers have been processed, there is a "prepare" step. This copies all accumulated draw
data and uploads it into vertex and constant buffers in the GPU.
Finally, we execute rendering commands. At the end of the frame, all batches and (most) constant
buffers are thrown away.
Shaders Overview
-------------------------------------
Advanced Layers currently has five layer-related shader pipelines:
- Textured (PaintedLayer, ImageLayer, CanvasLayer)
- ComponentAlpha (PaintedLayer with component-alpha)
- YCbCr (ImageLayer with YCbCr video)
- Color (ColorLayers)
- Blend (ContainerLayers with mix-blend modes)
There are also three special shader pipelines:
- MaskCombiner, which is used to combine mask layers into a single texture.
- Clear, which is used for fast region-based clears when not directly supported by the GPU.
- Diagnostic, which is used to display the diagnostic overlay texture.
The basic layer shaders follow a unified structure. Each pipeline has a vertex and pixel shader.
The vertex shader takes a layers ID, a z-buffer depth, a vertex (in a triangle list), and any
ancillary data needed for the pixel shader. Often this ancillary data is just an index into
a constant buffer (more on this below).
The vertex shader applies transforms and sends data down to the pixel shader, which performs
clipping and masking.
Most of the time, layers have simple rectangular clips with simple rectilinear transforms, and
pixel shaders do not need to perform masking or clipping. We take advantage of this for common
layer types, and use a second set of vertex and pixel shaders. These shaders have a unified
input layout: a draw rect, a layers ID, and a z-buffer depth. The pipeline uses a unit quad
as input. Ancillary data is stored in a constant buffer, which is indexed by the instance ID.
This path performs clipping in the vertex shader, and the pixel shader does not support masks.
Most shader types use ancillary data (such as texture coordinates, or a color value). This is
stored in a constant buffer, which is bound to the vertex shader. Unit quad shaders use
instancing to access the buffer. Full-fledged, triangle list shaders store the index in each
vertex.
All of the shader-specific data is modelled in ShaderDefinitionsMLGPU.h.
CPU Occlusion Culling
-------------------------------------
By default, Advanced Layers performs occlusion culling on the CPU. Since layers are visited
front-to-back, this is simply a matter of accumulating the visible region of opaque layers, and
subtracting it from the visible region of subsequent layers. There is a major difference
between this occlusion culling and PostProcessLayers of the old compositor: AL performs culling
after invalidation, not before. Completely valid layers will have an empty visible region.
Most layer types (with the exception of images) will intelligently split their draw calls into a
batch of individual rectangles, based on their visible region.
Z-Buffering and Occlusion
-------------------------------------
Advanced Layers also supports occlusion culling on the GPU, using a z-buffer. This is disabled by
default currently since it is significantly costly on integrated GPUs. When using the z-buffer, we
separate opaque layers into a separate list of passes. The render process then uses the following
steps:
1. The depth buffer is set to read-write.
2. Opaque batches are executed.,
3. The depth buffer is set to read-only.
4. Transparent batches are executed.
The problem we have observed is that the depth buffer increases writes to the GPU, and on
integrated GPUs this is expensive - we have seen draw call times increase by 20-30%, which is the
wrong direction we want to take on battery life. In particular on a full screen video, the call to
ClearDepthStencilView plus the actual depth buffer write of the video can double GPU time.
For now the depth-buffer is disabled until we can find a compelling case for it on non-integrated
hardware.
Clipping
-------------------------------------
Clipping is a bit tricky in Advanced Layers. We cannot use the hardware "scissor" feature, since the
clip can change from instance to instance within a batch. And if using the depth buffer, we
cannot write transparent pixels for the clipped area. As a result we always clip opaque draw rects
in the vertex shader (and sometimes even on the CPU, as is needed for sane texture coordiantes).
Only transparent items are clipped in the pixel shader. As a result, masked layers and layers with
non-rectangular transforms are always considered transparent, and use a more flexible clipping
pipeline.
Plane Splitting
---------------------
Plane splitting is when a 3D transform causes a layer to be split - for example, one transparent
layer may intersect another on a separate plane. When this happens, Gecko sorts layers using a BSP
tree and produces a list of triangles instead of draw rects.
These layers cannot use the "unit quad" shaders that support the fast clipping pipeline. Instead
they always use the full triangle-list shaders that support extended vertices and clipping.
This is the slowest path we can take when building a draw call, since we must interact with the
polygon clipping and texturing code.
Masks
---------
For each layer with a mask attached, Advanced Layers builds a `MaskOperation`. These operations
must resolve to a single mask texture, as well as a rectangular area to which the mask applies. All
batched pixel shaders will automatically clip pixels to the mask if a mask texture is bound. (Note
that we must use separate batches if the mask texture changes.)
Some layers have multiple mask textures. In this case, the MaskOperation will store the list of
masks, and right before rendering, it will invoke a shader to combine these masks into a single texture.
MaskOperations are shared across layers when possible, but are not cached across frames.
BigImage Support
--------------------------
ImageLayers and CanvasLayers can be tiled with many individual textures. This happens in rare cases
where the underlying buffer is too big for the GPU. Early on this caused problems for Advanced
Layers, since AL required one texture per layer. We implemented BigImage support by creating
temporary ImageLayers for each visible tile, and throwing those layers away at the end of the
frame.
Advanced Layers no longer has a 1:1 layer:texture restriction, but we retain the temporary layer
solution anyway. It is not much code and it means we do not have to split `TexturedLayerMLGPU`
methods into iterated and non-iterated versions.
Texture Locking
----------------------
Advanced Layers has a different texture locking scheme than the existing compositor. If a texture
needs to be locked, then it is locked by the MLGDevice automatically when bound to the current
pipeline. The MLGDevice keeps a set of the locked textures to avoid double-locking. At the end of
the frame, any textures in the locked set are unlocked.
We cannot easily replicate the locking scheme in the old compositor, since the duration of using
the texture is not scoped to when we visit the layer.
Buffer Measurements
-------------------------------
Advanced Layers uses constant buffers to send layer information and extended instance data to the
GPU. We do this by pre-allocating large constant buffers and mapping them with `MAP_DISCARD` at the
beginning of the frame. Batches may allocate into this up to the maximum bindable constant buffer
size of the device (currently, 64KB).
There are some downsides to this approach. Constant buffers are difficult to work with - they have
specific alignment requirements, and care must be taken not too run over the maximum number of
constants in a buffer. Another approach would be to store constants in a 2D texture and use vertex
shader texture fetches. Advanced Layers implemented this and benchmarked it to decide which
approach to use. Textures seemed to skew better on GPU performance, but worse on CPU, but this
varied depending on the GPU. Overall constant buffers performed best and most consistently, so we
have kept them.
Additionally, we tested different ways of performing buffer uploads. Buffer creation itself is
costly, especially on integrated GPUs, and especially so for immutable, immediate-upload buffers.
As a result we aggressively cache buffer objects and always allocate them as MAP_DISCARD unless
they are write-once and long-lived.
Buffer Types
------------
Advanced Layers has a few different classes to help build and upload buffers to the GPU. They are:
- `MLGBuffer`. This is the low-level shader resource that `MLGDevice` exposes. It is the building
block for buffer helper classes, but it can also be used to make one-off, immutable,
immediate-upload buffers. MLGBuffers, being a GPU resource, are reference counted.
- `SharedBufferMLGPU`. These are large, pre-allocated buffers that are read-only on the GPU and
write-only on the CPU. They usually exceed the maximum bindable buffer size. There are three
shared buffers created by default and they are automatically unmapped as needed: one for vertices,
one for vertex shader constants, and one for pixel shader constants. When callers allocate into a
shared buffer they get back a mapped pointer, a GPU resource, and an offset. When the underlying
device supports offsetable buffers (like `ID3D11DeviceContext1` does), this results in better GPU
utilization, as there are less resources and fewer upload commands.
- `ConstantBufferSection` and `VertexBufferSection`. These are "views" into a `SharedBufferMLGPU`.
They contain the underlying `MLGBuffer`, and when offsetting is supported, the offset
information necessary for resource binding. Sections are not reference counted.
- `StagingBuffer`. A dynamically sized CPU buffer where items can be appended in a free-form
manner. The stride of a single "item" is computed by the first item written, and successive
items must have the same stride. The buffer must be uploaded to the GPU manually. Staging buffers
are appropriate for creating general constant or vertex buffer data. They can also write items in
reverse, which is how we render back-to-front when layers are visited front-to-back. They can be
uploaded to a `SharedBufferMLGPU` or an immutabler `MLGBuffer` very easily. Staging buffers are not
reference counted.
Unsupported Features
--------------------------------
Currently, these features of the old compositor are not yet implemented.
- OpenGL and software support (currently AL only works on D3D11).
- APZ displayport overlay.
- Diagnostic/developer overlays other than the FPS/timing overlay.
- DEAA. It was never ported to the D3D11 compositor, but we would like it.
- Component alpha when used inside an opaque intermediate surface.
- Effects prefs. Possibly not needed post-B2G removal.
- Widget overlays and underlays used by macOS and Android.
- DefaultClearColor. This is Android specific, but is easy to added when needed.
- Frame uniformity info in the profiler. Possibly not needed post-B2G removal.
- LayerScope. There are no plans to make this work.
Future Work
--------------------------------
- Refactor for D3D12/Vulkan support (namely, split MLGDevice into something less stateful and something else more low-level).
- Remove "MLG" moniker and namespace everything.
- Other backends (D3D12/Vulkan, OpenGL, Software)
- Delete CompositorD3D11
- Add DEAA support
- Re-enable the depth buffer by default for fast GPUs
- Re-enable right-sizing of inaccurately sized containers
- Drop constant buffers for ancillary vertex data
- Fast shader paths for simple video/painted layer cases
History
----------
Advanced Layers has gone through four major design iterations. The initial version used tiling -
each render view divided the screen into 128x128 tiles, and layers were assigned to tiles based on
their screen-space draw area. This approach proved not to scale well to 3d transforms, and so
tiling was eliminated.
We replaced it with a simple system of accumulating draw regions to each batch, thus ensuring that
items could be assigned to batches while maintaining correct z-ordering. This second iteration also
coincided with plane-splitting support.
On large layer trees, accumulating the affected regions of batches proved to be quite expensive.
This led to a third iteration, using depth buffers and separate opaque and transparent batch lists
to achieve z-ordering and occlusion culling.
Finally, depth buffers proved to be too expensive, and we introduced a simple CPU-based occlusion
culling pass. This iteration coincided with using more precise draw rects and splitting pipelines
into unit-quad, cpu-clipped and triangle-list, gpu-clipped variants.

View File

@ -10,6 +10,7 @@
#include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/Matrix.h"
#include "mozilla/gfx/Polygon.h"
#include "nsRegion.h"
#include "nsTArray.h"
namespace mozilla {

View File

@ -9,6 +9,7 @@
#include "mozilla/dom/TabChild.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/APZChild.h"
#include "nsIContentInlines.h"
#include "InputData.h" // for InputData

View File

@ -63,6 +63,8 @@ class TextRenderer;
class CompositingRenderTarget;
struct FPSState;
class PaintCounter;
class LayerMLGPU;
class LayerManagerMLGPU;
class UiCompositorControllerParent;
static const int kVisualWarningDuration = 150; // ms
@ -123,6 +125,9 @@ public:
virtual HostLayerManager* AsHostLayerManager() override {
return this;
}
virtual LayerManagerMLGPU* AsLayerManagerMLGPU() {
return nullptr;
}
void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications)
{
@ -550,6 +555,8 @@ public:
virtual Layer* GetLayer() = 0;
virtual LayerMLGPU* AsLayerMLGPU() { return nullptr; }
virtual bool SetCompositableHost(CompositableHost*)
{
// We must handle this gracefully, see bug 967824
@ -569,6 +576,10 @@ public:
{
mShadowVisibleRegion = aRegion;
}
void SetShadowVisibleRegion(LayerIntRegion&& aRegion)
{
mShadowVisibleRegion = Move(aRegion);
}
void SetShadowOpacity(float aOpacity)
{
@ -596,11 +607,12 @@ public:
// These getters can be used anytime.
float GetShadowOpacity() { return mShadowOpacity; }
const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
const LayerIntRegion& GetShadowVisibleRegion() const { return mShadowVisibleRegion; }
const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
gfx::Matrix4x4 GetShadowTransform();
bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
LayerIntRegion&& GetShadowVisibleRegion() { return Move(mShadowVisibleRegion); }
protected:
HostLayerManager* mCompositorManager;

View File

@ -47,7 +47,7 @@ using namespace gfx;
namespace layers {
static bool CanUsePartialPresents(ID3D11Device* aDevice);
bool CanUsePartialPresents(ID3D11Device* aDevice);
const FLOAT sBlendFactor[] = { 0, 0, 0, 0 };
@ -255,7 +255,7 @@ CompositorD3D11::Initialize(nsCString* const out_failureReason)
return true;
}
static bool
bool
CanUsePartialPresents(ID3D11Device* aDevice)
{
if (gfxPrefs::PartialPresent() > 0) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,330 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_d3d11_MLGDeviceD3D11_h
#define mozilla_gfx_layers_d3d11_MLGDeviceD3D11_h
#include "mozilla/layers/MLGDevice.h"
#include "mozilla/EnumeratedArray.h"
#include "nsTHashtable.h"
#include <d3d11_1.h>
#include "nsPrintfCString.h"
namespace mozilla {
namespace layers {
struct GPUStats;
struct ShaderBytes;
class DiagnosticsD3D11;
class MLGRenderTargetD3D11 final : public MLGRenderTarget
{
public:
MLGRenderTargetD3D11(const gfx::IntSize& aSize, MLGRenderTargetFlags aFlags);
// Create with a new texture.
bool Initialize(ID3D11Device* aDevice);
// Do not create a texture - use the given one provided, which may be null.
// The depth buffer is still initialized.
bool Initialize(ID3D11Device* aDevice, ID3D11Texture2D* aTexture);
gfx::IntSize GetSize() const override;
MLGRenderTargetD3D11* AsD3D11() override { return this; }
MLGTexture* GetTexture() override;
// This is exposed only for MLGSwapChainD3D11.
bool UpdateTexture(ID3D11Texture2D* aTexture);
ID3D11DepthStencilView* GetDSV();
ID3D11RenderTargetView* GetRenderTargetView();
private:
bool CreateDepthBuffer(ID3D11Device* aDevice);
void ForgetTexture();
private:
~MLGRenderTargetD3D11() override;
private:
RefPtr<ID3D11Texture2D> mTexture;
RefPtr<ID3D11RenderTargetView> mRTView;
RefPtr<ID3D11Texture2D> mDepthBuffer;
RefPtr<ID3D11DepthStencilView> mDepthStencilView;
RefPtr<MLGTexture> mTextureSource;
gfx::IntSize mSize;
};
class MLGSwapChainD3D11 final : public MLGSwapChain
{
public:
static RefPtr<MLGSwapChainD3D11> Create(MLGDeviceD3D11* aParent,
ID3D11Device* aDevice,
widget::CompositorWidget* aWidget);
RefPtr<MLGRenderTarget> AcquireBackBuffer() override;
gfx::IntSize GetSize() const override;
bool ResizeBuffers(const gfx::IntSize& aSize) override;
void CopyBackbuffer(gfx::DrawTarget* aTarget, const gfx::IntRect& aBounds) override;
void Present() override;
void ForcePresent() override;
void Destroy() override;
private:
MLGSwapChainD3D11(MLGDeviceD3D11* aParent, ID3D11Device* aDevice);
~MLGSwapChainD3D11() override;
bool Initialize(widget::CompositorWidget* aWidget);
void UpdateBackBufferContents(ID3D11Texture2D* aBack);
private:
RefPtr<MLGDeviceD3D11> mParent;
RefPtr<ID3D11Device> mDevice;
RefPtr<IDXGISwapChain> mSwapChain;
RefPtr<IDXGISwapChain1> mSwapChain1;
RefPtr<MLGRenderTargetD3D11> mRT;
widget::CompositorWidget* mWidget;
gfx::IntSize mSize;
bool mCanUsePartialPresents;
};
class MLGResourceD3D11
{
public:
virtual ID3D11Resource* GetResource() const = 0;
};
class MLGBufferD3D11 final : public MLGBuffer, public MLGResourceD3D11
{
public:
static RefPtr<MLGBufferD3D11> Create(
ID3D11Device* aDevice,
MLGBufferType aType,
uint32_t aSize,
MLGUsage aUsage,
const void* aInitialData);
MLGBufferD3D11* AsD3D11() override {
return this;
}
ID3D11Resource* GetResource() const override {
return mBuffer;
}
ID3D11Buffer* GetBuffer() const {
return mBuffer;
}
MLGResourceD3D11* AsResourceD3D11() override {
return this;
}
size_t GetSize() const override {
return mSize;
}
protected:
MLGBufferD3D11(ID3D11Buffer* aBuffer, MLGBufferType aType, size_t aSize);
~MLGBufferD3D11() override;
private:
RefPtr<ID3D11Buffer> mBuffer;
MLGBufferType mType;
size_t mSize;
};
class MLGTextureD3D11 final : public MLGTexture, public MLGResourceD3D11
{
public:
explicit MLGTextureD3D11(ID3D11Texture2D* aTexture);
static RefPtr<MLGTextureD3D11> Create(
ID3D11Device* aDevice,
const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat,
MLGUsage aUsage,
MLGTextureFlags aFlags);
MLGTextureD3D11* AsD3D11() override {
return this;
}
MLGResourceD3D11* AsResourceD3D11() override {
return this;
}
ID3D11Texture2D* GetTexture() const {
return mTexture;
}
ID3D11Resource* GetResource() const override {
return mTexture;
}
ID3D11ShaderResourceView* GetShaderResourceView();
private:
RefPtr<ID3D11Texture2D> mTexture;
RefPtr<ID3D11ShaderResourceView> mView;
};
class MLGDeviceD3D11 final : public MLGDevice
{
public:
explicit MLGDeviceD3D11(ID3D11Device* aDevice);
~MLGDeviceD3D11() override;
bool Initialize() override;
void StartDiagnostics(uint32_t aInvalidPixels) override;
void EndDiagnostics() override;
void GetDiagnostics(GPUStats* aStats) override;
MLGDeviceD3D11* AsD3D11() override { return this; }
TextureFactoryIdentifier GetTextureFactoryIdentifier() const override;
RefPtr<MLGSwapChain> CreateSwapChainForWidget(widget::CompositorWidget* aWidget) override;
int32_t GetMaxTextureSize() const override;
LayersBackend GetLayersBackend() const override;
void EndFrame() override;
bool Map(MLGResource* aResource, MLGMapType aType, MLGMappedResource* aMap) override;
void Unmap(MLGResource* aResource) override;
void UpdatePartialResource(
MLGResource* aResource,
const gfx::IntRect* aRect,
void* aData,
uint32_t aStride) override;
void CopyTexture(MLGTexture* aDest,
const gfx::IntPoint& aTarget,
MLGTexture* aSource,
const gfx::IntRect& aRect) override;
RefPtr<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags) override;
void SetRenderTarget(MLGRenderTarget* aRT) override;
MLGRenderTarget* GetRenderTarget() override;
void SetViewport(const gfx::IntRect& aViewport) override;
void SetScissorRect(const Maybe<gfx::IntRect>& aScissorRect) override;
void SetVertexShader(VertexShaderID aVertexShader) override;
void SetPixelShader(PixelShaderID aPixelShader) override;
void SetSamplerMode(uint32_t aIndex, SamplerMode aSamplerMode) override;
void SetBlendState(MLGBlendState aBlendState) override;
void SetVertexBuffer(uint32_t aSlot, MLGBuffer* aBuffer, uint32_t aStride, uint32_t aOffset) override;
void SetPSTextures(uint32_t aSlot, uint32_t aNumTextures, TextureSource* const* aTextures) override;
void SetPSTexture(uint32_t aSlot, MLGTexture* aTexture) override;
void SetPSTexturesNV12(uint32_t aSlot, TextureSource* aTexture) override;
void SetPrimitiveTopology(MLGPrimitiveTopology aTopology) override;
void SetDepthTestMode(MLGDepthTestMode aMode) override;
void SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) override;
void SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) override;
void SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer, uint32_t aFirstConstant, uint32_t aNumConstants) override;
void SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer, uint32_t aFirstConstant, uint32_t aNumConstants) override;
RefPtr<MLGBuffer> CreateBuffer(
MLGBufferType aType,
uint32_t aSize,
MLGUsage aUsage,
const void* aInitialData) override;
RefPtr<MLGRenderTarget> CreateRenderTarget(
const gfx::IntSize& aSize,
MLGRenderTargetFlags aFlags) override;
RefPtr<MLGTexture> CreateTexture(
const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat,
MLGUsage aUsage,
MLGTextureFlags aFlags) override;
RefPtr<MLGTexture> CreateTexture(TextureSource* aSource) override;
void Clear(MLGRenderTarget* aRT, const gfx::Color& aColor) override;
void ClearDepthBuffer(MLGRenderTarget* aRT) override;
void ClearView(MLGRenderTarget* aRT, const gfx::Color& aColor, const gfx::IntRect* aRects, size_t aNumRects) override;
void Draw(uint32_t aVertexCount, uint32_t aOffset) override;
void DrawInstanced(uint32_t aVertexCountPerInstance, uint32_t aInstanceCount,
uint32_t aVertexOffset, uint32_t aInstanceOffset) override;
void Flush() override;
// This is exposed for TextureSourceProvider.
ID3D11Device* GetD3D11Device() const {
return mDevice;
}
bool Synchronize() override;
void UnlockAllTextures() override;
void InsertPresentWaitQuery();
void WaitForPreviousPresentQuery();
void HandleDeviceReset(const char* aWhere);
private:
bool InitSyncObject();
void MaybeLockTexture(ID3D11Texture2D* aTexture);
bool InitPixelShader(PixelShaderID aShaderID);
bool InitVertexShader(VertexShaderID aShaderID);
bool InitInputLayout(D3D11_INPUT_ELEMENT_DESC* aDesc,
size_t aNumElements,
const ShaderBytes& aCode,
VertexShaderID aShaderID);
bool InitRasterizerStates();
bool InitSamplerStates();
bool InitBlendStates();
bool InitDepthStencilState();
private:
RefPtr<ID3D11Device> mDevice;
RefPtr<ID3D11DeviceContext> mCtx;
RefPtr<ID3D11DeviceContext1> mCtx1;
UniquePtr<DiagnosticsD3D11> mDiagnostics;
typedef EnumeratedArray<PixelShaderID, PixelShaderID::MaxShaders, RefPtr<ID3D11PixelShader>> PixelShaderArray;
typedef EnumeratedArray<VertexShaderID, VertexShaderID::MaxShaders, RefPtr<ID3D11VertexShader>> VertexShaderArray;
typedef EnumeratedArray<VertexShaderID, VertexShaderID::MaxShaders, RefPtr<ID3D11InputLayout>> InputLayoutArray;
typedef EnumeratedArray<SamplerMode, SamplerMode::MaxModes, RefPtr<ID3D11SamplerState>> SamplerStateArray;
typedef EnumeratedArray<MLGBlendState, MLGBlendState::MaxStates, RefPtr<ID3D11BlendState>> BlendStateArray;
typedef EnumeratedArray<MLGDepthTestMode,
MLGDepthTestMode::MaxModes,
RefPtr<ID3D11DepthStencilState>> DepthStencilStateArray;
PixelShaderArray mPixelShaders;
VertexShaderArray mVertexShaders;
InputLayoutArray mInputLayouts;
SamplerStateArray mSamplerStates;
BlendStateArray mBlendStates;
DepthStencilStateArray mDepthStencilStates;
RefPtr<ID3D11RasterizerState> mRasterizerStateNoScissor;
RefPtr<ID3D11RasterizerState> mRasterizerStateScissor;
RefPtr<IDXGIResource> mSyncTexture;
HANDLE mSyncHandle;
RefPtr<MLGRenderTarget> mCurrentRT;
RefPtr<MLGBuffer> mUnitQuadVB;
RefPtr<ID3D11VertexShader> mCurrentVertexShader;
RefPtr<ID3D11InputLayout> mCurrentInputLayout;
RefPtr<ID3D11PixelShader> mCurrentPixelShader;
RefPtr<ID3D11BlendState> mCurrentBlendState;
RefPtr<ID3D11Query> mWaitForPresentQuery;
RefPtr<ID3D11Query> mNextWaitForPresentQuery;
nsTHashtable<nsRefPtrHashKey<IDXGIKeyedMutex>> mLockedTextures;
nsTHashtable<nsRefPtrHashKey<IDXGIKeyedMutex>> mLockAttemptedTextures;
typedef EnumeratedArray<PixelShaderID, PixelShaderID::MaxShaders, const ShaderBytes*> LazyPixelShaderArray;
LazyPixelShaderArray mLazyPixelShaders;
typedef EnumeratedArray<VertexShaderID, VertexShaderID::MaxShaders, const ShaderBytes*> LazyVertexShaderArray;
LazyVertexShaderArray mLazyVertexShaders;
bool mScissored;
};
} // namespace layers
} // namespace mozilla
struct ShaderBytes;
#endif // mozilla_gfx_layers_d3d11_MLGDeviceD3D11_h

View File

@ -90,6 +90,14 @@ SurfaceFormatToDXGIFormat(gfx::SurfaceFormat aFormat)
}
}
void
ReportTextureMemoryUsage(ID3D11Texture2D* aTexture, size_t aBytes)
{
aTexture->SetPrivateDataInterface(
sD3D11TextureUsage,
new TextureMemoryMeasurer(aBytes));
}
static uint32_t
GetRequiredTilesD3D11(uint32_t aSize, uint32_t aMaxSize)
{

View File

@ -483,6 +483,7 @@ inline uint32_t GetMaxTextureSizeForFeatureLevel(D3D_FEATURE_LEVEL aFeatureLevel
}
uint32_t GetMaxTextureSizeFromDevice(ID3D11Device* aDevice);
void ReportTextureMemoryUsage(ID3D11Texture2D* aTexture, size_t aBytes);
class AutoLockD3D11Texture
{

View File

@ -0,0 +1,15 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-vs.hlsl"
struct VS_BLEND_OUTPUT
{
float4 vPosition : SV_Position;
float2 vTexCoords : TEXCOORD0;
float2 vBackdropCoords : TEXCOORD1;
float2 vLocalPos : TEXCOORD2;
float3 vMaskCoords : TEXCOORD3;
nointerpolation float4 vClipRect : TEXCOORD4;
};

View File

@ -0,0 +1,540 @@
float4 BlendMultiplyPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendMultiply(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendScreenPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendScreen(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendOverlayPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendOverlay(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendDarkenPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendDarken(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendLightenPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendLighten(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendColorDodgePS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendColorDodge(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendColorBurnPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendColorBurn(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendHardLightPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= sOpacity;
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendHardLight(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendSoftLightPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendSoftLight(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendDifferencePS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendDifference(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendExclusionPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendExclusion(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendHuePS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendHue(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendSaturationPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendSaturation(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendColorPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendColor(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}
float4 BlendLuminosityPS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = BlendLuminosity(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}

View File

@ -0,0 +1,36 @@
float4 Blend{BLEND_FUNC}PS(const VS_BLEND_OUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
discard;
}
float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
// Apply masks to the source texture, not the result.
source *= ReadMask(aInput.vMaskCoords);
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// infinity into the blend function and return incorrect results.
if (backdrop.a == 0.0) {
return source;
}
if (source.a == 0.0) {
return float4(0, 0, 0, 0);
}
// The spec assumes there is no premultiplied alpha. The backdrop and
// source are both render targets and always premultiplied, so we undo
// that here.
backdrop.rgb /= backdrop.a;
source.rgb /= source.a;
float4 result;
result.rgb = Blend{BLEND_FUNC}(backdrop.rgb, source.rgb);
result.a = source.a;
// Factor backdrop alpha, then premultiply for the final OP_OVER.
result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
result.rgb *= result.a;
return result;
}

View File

@ -0,0 +1,13 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common.hlsl"
#include "common-ps.hlsl"
#include "blend-common.hlsl"
#include "../BlendingHelpers.hlslh"
Texture2D simpleTex : register(ps, t0);
Texture2D tBackdrop : register(ps, t1);
#include "blend-ps-generated.hlslh"

View File

@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-vs.hlsl"
#include "blend-common.hlsl"
#include "textured-common.hlsl"
cbuffer BlendConstants : register(b4)
{
float4x4 mBackdropTransform;
}
float2 BackdropPosition(float4 aPosition)
{
// Move the position from clip space (-1,1) into 0..1 space.
float2 pos;
pos.x = (aPosition.x + 1.0) / 2.0;
pos.y = 1.0 - (aPosition.y + 1.0) / 2.0;
return mul(mBackdropTransform, float4(pos.xy, 0, 1.0)).xy;
}
VS_BLEND_OUTPUT BlendImpl(const VertexInfo aInfo, float2 aTexCoord)
{
VS_BLEND_OUTPUT output;
output.vPosition = aInfo.worldPos;
output.vTexCoords = aTexCoord;
output.vBackdropCoords = BackdropPosition(output.vPosition);
output.vLocalPos = aInfo.screenPos;
output.vClipRect = aInfo.clipRect;
output.vMaskCoords = aInfo.maskCoords;
return output;
}
VS_BLEND_OUTPUT BlendVertexVS(const VS_TEXTUREDVERTEX aVertex)
{
VertexInfo info = ComputePosition(aVertex.vLayerPos, aVertex.vLayerId, aVertex.vDepth);
return BlendImpl(info, aVertex.vTexCoord);
}

View File

@ -0,0 +1,9 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
struct VS_CLEAR_OUT
{
float4 vPosition : SV_Position;
};

View File

@ -0,0 +1,12 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-ps.hlsl"
#include "clear-common.hlsl"
float4 ClearPS(const VS_CLEAR_OUT aVS) : SV_Target
{
return float4(0.0, 0.0, 0.0, 0.0);
}

View File

@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-vs.hlsl"
#include "clear-common.hlsl"
struct VS_CLEAR_IN
{
float2 vPos : POSITION;
int4 vRect : TEXCOORD0;
};
// Note: we use slot 2 so we don't have to rebind the layer slot (1) to run
// this shader.
cbuffer ClearConstants : register(b2) {
int sDepth : packoffset(c0.x);
};
VS_CLEAR_OUT ClearVS(const VS_CLEAR_IN aInput)
{
float4 rect = float4(aInput.vRect.x, aInput.vRect.y, aInput.vRect.z, aInput.vRect.w);
float4 screenPos = float4(UnitQuadToRect(aInput.vPos, rect), 0, 1);
float4 worldPos = mul(WorldTransform, screenPos);
worldPos.z = ComputeDepth(worldPos, sDepth);
VS_CLEAR_OUT output;
output.vPosition = worldPos;
return output;
}

View File

@ -0,0 +1,19 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
struct VS_COLOROUTPUT
{
nointerpolation float4 vColor : COLOR0;
nointerpolation float4 vClipRect : TEXCOORD0;
float4 vPosition : SV_Position;
float2 vLocalPos : TEXCOORD1;
float3 vMaskCoords : TEXCOORD2;
};
struct VS_COLOROUTPUT_CLIPPED
{
float4 vPosition : SV_Position;
nointerpolation float4 vColor : COLOR0;
};

View File

@ -0,0 +1,20 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-ps.hlsl"
#include "color-common.hlsl"
float4 ColoredQuadPS(const VS_COLOROUTPUT_CLIPPED aVS) : SV_Target
{
// Opacity is always 1.0, we premultiply it on the CPU.
return aVS.vColor;
}
float4 ColoredVertexPS(const VS_COLOROUTPUT aVS) : SV_Target
{
if (!RectContainsPoint(aVS.vClipRect, aVS.vPosition.xy)) {
return float4(0, 0, 0, 0);
}
return aVS.vColor * ReadMaskWithOpacity(aVS.vMaskCoords, 1.0);
}

View File

@ -0,0 +1,70 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-vs.hlsl"
#include "color-common.hlsl"
struct ColorItem
{
float4 color;
};
#define SIZEOF_COLORITEM 1
ColorItem GetItem(uint aIndex)
{
uint offset = aIndex * SIZEOF_COLORITEM;
ColorItem item;
item.color = sItems[offset + 0];
return item;
}
struct VS_COLORQUAD
{
float2 vPos : POSITION;
float4 vRect : TEXCOORD0;
uint vLayerId : TEXCOORD1;
int vDepth : TEXCOORD2;
uint vIndex : SV_InstanceID;
};
struct VS_COLORVERTEX
{
float2 vLayerPos : POSITION;
uint vLayerId : TEXCOORD0;
int vDepth : TEXCOORD1;
uint vIndex : TEXCOORD2;
};
VS_COLOROUTPUT ColorImpl(float4 aColor, const VertexInfo aInfo)
{
VS_COLOROUTPUT output;
output.vPosition = aInfo.worldPos;
output.vLocalPos = aInfo.screenPos;
output.vColor = aColor;
output.vClipRect = aInfo.clipRect;
output.vMaskCoords = aInfo.maskCoords;
return output;
}
VS_COLOROUTPUT_CLIPPED ColoredQuadVS(const VS_COLORQUAD aInput)
{
ColorItem item = GetItem(aInput.vIndex);
float4 worldPos = ComputeClippedPosition(
aInput.vPos,
aInput.vRect,
aInput.vLayerId,
aInput.vDepth);
VS_COLOROUTPUT_CLIPPED output;
output.vPosition = worldPos;
output.vColor = item.color;
return output;
}
VS_COLOROUTPUT ColoredVertexVS(const VS_COLORVERTEX aInput)
{
ColorItem item = GetItem(aInput.vIndex);
VertexInfo info = ComputePosition(aInput.vLayerPos, aInput.vLayerId, aInput.vDepth);
return ColorImpl(item.color, info);
}

View File

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common.hlsl"
sampler sSampler : register(ps, s0);
sampler sMaskSampler : register(ps, s1);
Texture2D tMaskTexture : register(ps, t4);
cbuffer MaskInformation : register(b0)
{
float sOpacity : packoffset(c0.x);
uint sHasMask : packoffset(c0.y);
};
float2 MaskCoordsToUV(float3 aMaskCoords)
{
return aMaskCoords.xy / aMaskCoords.z;
}
float ReadMaskWithOpacity(float3 aMaskCoords, float aOpacity)
{
if (!sHasMask) {
return aOpacity;
}
float2 uv = MaskCoordsToUV(aMaskCoords);
float r = tMaskTexture.Sample(sMaskSampler, uv).r;
return min(aOpacity, r);
}
float ReadMask(float3 aMaskCoords)
{
return ReadMaskWithOpacity(aMaskCoords, sOpacity);
}

View File

@ -0,0 +1,162 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
#ifndef mozilla_gfx_layers_d3d11_mlgshaders_common_vs_hlsl
#define mozilla_gfx_layers_d3d11_mlgshaders_common_vs_hlsl
#include "common.hlsl"
cbuffer VSBufSimple : register(b0)
{
float4x4 WorldTransform;
float2 RenderTargetOffset;
int SortIndexOffset;
float padding;
};
struct Layer {
float4x4 transform;
float4 clipRect;
uint4 info;
};
cbuffer Layers : register(b1)
{
Layer sLayers[682];
};
cbuffer Items : register(b2)
{
float4 sItems[4096];
};
cbuffer MaskRects : register(b3)
{
float4 sMaskRects[4096];
};
struct VertexInfo {
float4 worldPos;
float2 screenPos;
float3 maskCoords;
float4 clipRect;
};
float3 ComputeMaskCoords(float4 aPosition, Layer aLayer)
{
if (aLayer.info.x == 0) {
return float3(0.0, 0.0, 0.0);
}
float4 maskRect = sMaskRects[aLayer.info.x - 1];
// See the perspective comment in CompositorD3D11.hlsl.
float4x4 transform = float4x4(
1.0/maskRect.z, 0.0, 0.0, -maskRect.x/maskRect.z,
0.0, 1.0/maskRect.w, 0.0, -maskRect.y/maskRect.w,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
return float3(mul(transform, aPosition / aPosition.w).xy, 1.0) * aPosition.w;
}
float2 UnitQuadToRect(const float2 aVertex, const float4 aRect)
{
return float2(aRect.x + aVertex.x * aRect.z, aRect.y + aVertex.y * aRect.w);
}
float ComputeDepth(float4 aPosition, float aSortIndex)
{
// Note: this value should match ShaderDefinitionsMLGPU.h.
return ((aSortIndex + SortIndexOffset) / 1000000.0f) * aPosition.w;
}
// Compute the world-space, screen-space, layer-space clip, and mask
// uv-coordinates given a layer-space vertex, id, and z-index.
VertexInfo ComputePosition(float2 aVertex, uint aLayerId, float aSortIndex)
{
Layer layer = sLayers[aLayerId];
// Translate from unit vertex to layer quad vertex.
float4 position = float4(aVertex, 0, 1);
float4 clipRect = layer.clipRect;
// Transform to screen coordinates.
float4x4 transform = layer.transform;
position = mul(transform, position);
position.xyz /= position.w;
position.xy -= RenderTargetOffset.xy;
position.xyz *= position.w;
float4 worldPos = mul(WorldTransform, position);
// Depth must be computed after the world transform, since we don't want
// 3d transforms clobbering the z-value. We assume a viewport culling
// everything outside of [0, 1). Note that when depth-testing, we do not
// use sorting indices < 1.
//
// Note that we have to normalize this value to w=1, since the GPU will
// divide all values by w internally.
worldPos.z = ComputeDepth(worldPos, aSortIndex);
VertexInfo info;
info.screenPos = position.xy;
info.worldPos = worldPos;
info.maskCoords = ComputeMaskCoords(position, layer);
info.clipRect = clipRect;
return info;
}
// This function takes a unit quad position and a layer rectangle, and computes
// a clipped draw rect. It is only valid to use this function for layers with
// rectilinear transforms that do not have masks.
float4 ComputeClippedPosition(const float2 aVertex,
const float4 aRect,
uint aLayerId,
float aDepth)
{
Layer layer = sLayers[aLayerId];
float4 position = float4(UnitQuadToRect(aVertex, aRect), 0, 1);
float4x4 transform = layer.transform;
float4 clipRect = layer.clipRect;
// Transform to screen coordinates.
//
// We clamp the draw rect to the clip. This lets us use faster shaders.
// For opaque shapes, it is necessary to do this anyway since we might
// otherwrite write transparent pixels in the pixel which would also be
// written to the depth buffer. We cannot use discard in the pixel shader
// as this would break early-z tests.
//
// Note that for some shaders, like textured shaders, it is not valid to
// change the draw rect like this without also clamping the texture
// coordinates. We take care to adjust for this in our batching code.
//
// We do not need to do this for 3D transforms since we always treat those
// as transparent (they are not written to the depth buffer). 3D items
// will always use the full clip+masking shader.
position = mul(transform, position);
position.xyz /= position.w;
position.xy -= RenderTargetOffset.xy;
position.xy = clamp(position.xy, clipRect.xy, clipRect.xy + clipRect.zw);
position.xyz *= position.w;
float4 worldPos = mul(WorldTransform, position);
// Depth must be computed after the world transform, since we don't want
// 3d transforms clobbering the z-value. We assume a viewport culling
// everything outside of [0, 1). Note that when depth-testing, we do not
// use sorting indices < 1.
//
// Note that we have to normalize this value to w=1, since the GPU will
// divide all values by w internally.
worldPos.z = ComputeDepth(worldPos, aDepth);
return worldPos;
}
#endif // mozilla_gfx_layers_d3d11_mlgshaders_common_vs_hlsl

View File

@ -0,0 +1,17 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
#ifndef mozilla_gfx_layers_d3d11_mlgshaders_common_hlsl
#define mozilla_gfx_layers_d3d11_mlgshaders_common_hlsl
bool RectContainsPoint(float4 aRect, float2 aPoint)
{
return aPoint.x >= aRect.x &&
aPoint.y >= aRect.y &&
aPoint.x <= (aRect.x + aRect.z) &&
aPoint.y <= (aRect.y + aRect.w);
}
#endif // mozilla_gfx_layers_d3d11_mlgshaders_common_hlsl

View File

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common.hlsl"
#include "common-ps.hlsl"
#include "textured-common.hlsl"
Texture2D texOnBlack : register(ps, t0);
Texture2D texOnWhite : register(ps, t1);
struct PS_OUTPUT {
float4 vSrc;
float4 vAlpha;
};
PS_OUTPUT ComponentAlphaQuadPS(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
{
PS_OUTPUT result;
result.vSrc = texOnBlack.Sample(sSampler, aInput.vTexCoords);
result.vAlpha = 1.0 - texOnWhite.Sample(sSampler, aInput.vTexCoords) + result.vSrc;
result.vSrc.a = result.vAlpha.g;
result.vSrc *= sOpacity;
result.vAlpha *= sOpacity;
return result;
}
PS_OUTPUT ComponentAlphaVertexPS(const VS_SAMPLEOUTPUT aInput) : SV_Target
{
PS_OUTPUT result;
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
result.vSrc = float4(0, 0, 0, 0);
result.vAlpha = float4(0, 0, 0, 0);
return result;
}
float alpha = ReadMask(aInput.vMaskCoords);
result.vSrc = texOnBlack.Sample(sSampler, aInput.vTexCoords);
result.vAlpha = 1.0 - texOnWhite.Sample(sSampler, aInput.vTexCoords) + result.vSrc;
result.vSrc.a = result.vAlpha.g;
result.vSrc *= alpha;
result.vAlpha *= alpha;
return result;
}

View File

@ -0,0 +1,10 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
struct VS_DIAGOUTPUT
{
float4 vPosition : SV_Position;
float2 vTexCoord : TEXCOORD0;
};

View File

@ -0,0 +1,13 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-ps.hlsl"
#include "diagnostics-common.hlsl"
Texture2D sTexture: register(ps, t0);
float4 DiagnosticTextPS(const VS_DIAGOUTPUT aInput) : SV_Target
{
return sTexture.Sample(sSampler, aInput.vTexCoord);
}

View File

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-vs.hlsl"
#include "textured-common.hlsl"
#include "diagnostics-common.hlsl"
struct VS_DIAGINPUT
{
float2 vPos : POSITION;
float4 vRect : TEXCOORD0;
float4 vTexCoords : TEXCOORD1;
};
VS_DIAGOUTPUT DiagnosticTextVS(const VS_DIAGINPUT aInput)
{
float2 pos = UnitQuadToRect(aInput.vPos, aInput.vRect);
float2 texCoord = UnitQuadToRect(aInput.vPos, aInput.vTexCoords);
VS_DIAGOUTPUT output;
output.vPosition = mul(WorldTransform, float4(pos, 0, 1));
output.vTexCoord = texCoord;
return output;
}

View File

@ -0,0 +1,10 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
struct VS_MASKOUTPUT
{
float4 vPosition : SV_Position;
float2 vTexCoords : TEXCOORD0;
};

View File

@ -0,0 +1,16 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common.hlsl"
#include "mask-combiner-common.hlsl"
sampler sSampler;
Texture2D tMaskTexture : register(ps, t0);
float4 MaskCombinerPS(VS_MASKOUTPUT aInput) : SV_Target
{
float4 value = tMaskTexture.Sample(sSampler, aInput.vTexCoords);
return float4(value.r, 0, 0, value.r);
}

View File

@ -0,0 +1,26 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-vs.hlsl"
#include "mask-combiner-common.hlsl"
struct VS_MASKINPUT
{
// Note, the input is
float2 vPos : POSITION;
float4 vTexCoords : POSITION1;
};
VS_MASKOUTPUT MaskCombinerVS(VS_MASKINPUT aInput)
{
float4 position = float4(
aInput.vPos.x * 2.0f - 1.0f,
1.0f - (aInput.vPos.y * 2.0f),
0, 1);
VS_MASKOUTPUT output;
output.vPosition = position;
output.vTexCoords = UnitQuadToRect(aInput.vPos, aInput.vTexCoords);
return output;
}

View File

@ -0,0 +1,94 @@
- type: vs_4_0
file: textured-vs.hlsl
shaders:
- TexturedQuadVS
- TexturedVertexVS
- type: ps_4_0
file: textured-ps.hlsl
shaders:
- TexturedVertexRGB
- TexturedVertexRGBA
- TexturedQuadRGB
- TexturedQuadRGBA
- type: ps_4_0
file: ycbcr-ps.hlsl
shaders:
- TexturedVertexIMC4
- TexturedVertexNV12
- TexturedQuadIMC4
- TexturedQuadNV12
- type: vs_4_0
file: color-vs.hlsl
shaders:
- ColoredQuadVS
- ColoredVertexVS
- type: ps_4_0
file: color-ps.hlsl
shaders:
- ColoredQuadPS
- ColoredVertexPS
- type: ps_4_0
file: component-alpha-ps.hlsl
shaders:
- ComponentAlphaQuadPS
- ComponentAlphaVertexPS
- type: vs_4_0
file: blend-vs.hlsl
shaders:
- BlendVertexVS
- type: ps_4_0
file: blend-ps.hlsl
shaders:
- BlendMultiplyPS
- BlendScreenPS
- BlendOverlayPS
- BlendDarkenPS
- BlendLightenPS
- BlendColorDodgePS
- BlendColorBurnPS
- BlendHardLightPS
- BlendSoftLightPS
- BlendDifferencePS
- BlendExclusionPS
- BlendHuePS
- BlendSaturationPS
- BlendColorPS
- BlendLuminosityPS
- type: vs_4_0
file: clear-vs.hlsl
shaders:
- ClearVS
- type: ps_4_0
file: clear-ps.hlsl
shaders:
- ClearPS
- type: vs_4_0
file: mask-combiner-vs.hlsl
shaders:
- MaskCombinerVS
- type: ps_4_0
file: mask-combiner-ps.hlsl
shaders:
- MaskCombinerPS
- type: vs_4_0
file: diagnostics-vs.hlsl
shaders:
- DiagnosticTextVS
- type: ps_4_0
file: diagnostics-ps.hlsl
shaders:
- DiagnosticTextPS

View File

@ -0,0 +1,54 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
#ifdef VERTEX_SHADER
struct TexturedItem
{
float4 texCoords;
};
#define SIZEOF_TEXTUREDITEM 1
TexturedItem GetItem(uint aIndex)
{
uint offset = aIndex * SIZEOF_TEXTUREDITEM;
TexturedItem item;
item.texCoords = sItems[offset + 0];
return item;
}
#endif
// Instanced version.
struct VS_TEXTUREDINPUT
{
float2 vPos : POSITION;
float4 vRect : TEXCOORD0;
uint vLayerId : TEXCOORD1;
int vDepth : TEXCOORD2;
uint vIndex : SV_InstanceID;
};
// Non-instanced version.
struct VS_TEXTUREDVERTEX
{
float2 vLayerPos : POSITION;
float2 vTexCoord : TEXCOORD0;
uint vLayerId : TEXCOORD1;
int vDepth : TEXCOORD2;
};
struct VS_SAMPLEOUTPUT
{
float4 vPosition : SV_Position;
float2 vTexCoords : TEXCOORD0;
float2 vLocalPos : TEXCOORD1;
float3 vMaskCoords : TEXCOORD2;
nointerpolation float4 vClipRect : TEXCOORD3;
};
struct VS_SAMPLEOUTPUT_CLIPPED
{
float4 vPosition : SV_Position;
float2 vTexCoords : TEXCOORD0;
};

View File

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common.hlsl"
#include "common-ps.hlsl"
#include "textured-common.hlsl"
Texture2D simpleTex : register(ps, t0);
float4 FixRGBOpacity(float4 color, float alpha) {
return float4(color.rgb * alpha, alpha);
}
// Fast cases that don't require complex clipping.
float4 TexturedQuadRGBA(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
{
return simpleTex.Sample(sSampler, aInput.vTexCoords) * sOpacity;
}
float4 TexturedQuadRGB(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
{
return FixRGBOpacity(simpleTex.Sample(sSampler, aInput.vTexCoords), sOpacity);
}
// PaintedLayer common case.
float4 TexturedVertexRGBA(const VS_SAMPLEOUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
return float4(0, 0, 0, 0);
}
float alpha = ReadMask(aInput.vMaskCoords);
return simpleTex.Sample(sSampler, aInput.vTexCoords) * alpha;
}
// ImageLayers.
float4 TexturedVertexRGB(const VS_SAMPLEOUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
return float4(0, 0, 0, 0);
}
float alpha = ReadMask(aInput.vMaskCoords);
return FixRGBOpacity(simpleTex.Sample(sSampler, aInput.vTexCoords), alpha);
}

View File

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-vs.hlsl"
#include "textured-common.hlsl"
VS_SAMPLEOUTPUT TexturedQuadImpl(const VertexInfo aInfo, const float2 aTexCoord)
{
VS_SAMPLEOUTPUT output;
output.vPosition = aInfo.worldPos;
output.vTexCoords = aTexCoord;
output.vLocalPos = aInfo.screenPos;
output.vClipRect = aInfo.clipRect;
output.vMaskCoords = aInfo.maskCoords;
return output;
}
VS_SAMPLEOUTPUT_CLIPPED TexturedQuadVS(const VS_TEXTUREDINPUT aVertex)
{
TexturedItem item = GetItem(aVertex.vIndex);
float4 worldPos = ComputeClippedPosition(
aVertex.vPos,
aVertex.vRect,
aVertex.vLayerId,
aVertex.vDepth);
VS_SAMPLEOUTPUT_CLIPPED output;
output.vPosition = worldPos;
output.vTexCoords = UnitQuadToRect(aVertex.vPos, item.texCoords);
return output;
}
VS_SAMPLEOUTPUT TexturedVertexVS(const VS_TEXTUREDVERTEX aVertex)
{
VertexInfo info = ComputePosition(aVertex.vLayerPos, aVertex.vLayerId, aVertex.vDepth);
return TexturedQuadImpl(info, aVertex.vTexCoord);
}

View File

@ -0,0 +1,91 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "common-ps.hlsl"
#include "textured-common.hlsl"
Texture2D tY : register(ps, t0);
Texture2D tCb : register(ps, t1);
Texture2D tCr : register(ps, t2);
cbuffer YCbCrBuffer : register(b1) {
row_major float3x3 YuvColorMatrix;
};
/* From Rec601:
[R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
[B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
For [0,1] instead of [0,255], and to 5 places:
[R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
[B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
From Rec709:
[R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
[G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
[B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
For [0,1] instead of [0,255], and to 5 places:
[R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
[B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
*/
float4 CalculateYCbCrColor(float3 rgb)
{
return float4(
mul(YuvColorMatrix,
float3(
rgb.r - 0.06275,
rgb.g - 0.50196,
rgb.b - 0.50196)),
1.0);
}
float4 CalculateIMC4Color(const float2 aTexCoords)
{
float3 yuv = float3(
tY.Sample(sSampler, aTexCoords).r,
tCb.Sample(sSampler, aTexCoords).r,
tCr.Sample(sSampler, aTexCoords).r);
return CalculateYCbCrColor(yuv);
}
float4 CalculateNV12Color(const float2 aTexCoords)
{
float y = tY.Sample(sSampler, aTexCoords).r;
float2 cbcr = tCb.Sample(sSampler, aTexCoords).rg;
return CalculateYCbCrColor(float3(y, cbcr));
}
float4 TexturedQuadIMC4(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
{
return CalculateIMC4Color(aInput.vTexCoords) * sOpacity;
}
float4 TexturedQuadNV12(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
{
return CalculateNV12Color(aInput.vTexCoords) * sOpacity;
}
float4 TexturedVertexIMC4(const VS_SAMPLEOUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
return float4(0, 0, 0, 0);
}
float alpha = ReadMask(aInput.vMaskCoords);
return CalculateIMC4Color(aInput.vTexCoords) * alpha;
}
float4 TexturedVertexNV12(const VS_SAMPLEOUTPUT aInput) : SV_Target
{
if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
return float4(0, 0, 0, 0);
}
float alpha = ReadMask(aInput.vMaskCoords);
return CalculateNV12Color(aInput.vTexCoords) * alpha;
}

View File

@ -0,0 +1,127 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "BufferCache.h"
#include "MLGDevice.h"
#include "mozilla/MathAlgorithms.h"
namespace mozilla {
namespace layers {
BufferCache::BufferCache(MLGDevice* aDevice)
: mDevice(aDevice)
{
}
BufferCache::~BufferCache()
{
}
RefPtr<MLGBuffer>
BufferCache::GetOrCreateBuffer(size_t aBytes)
{
// Try to take a buffer from the expired frame. If none exists, make a new one.
RefPtr<MLGBuffer> buffer = mExpired.Take(aBytes);
if (!buffer) {
// Round up to the nearest size class, but not over 1024 bytes.
size_t roundedUp = std::max(std::min(RoundUpPow2(aBytes), size_t(1024)), aBytes);
buffer = mDevice->CreateBuffer(MLGBufferType::Constant, roundedUp, MLGUsage::Dynamic, nullptr);
if (!buffer) {
return nullptr;
}
}
MOZ_ASSERT(buffer->GetSize() >= aBytes);
// Assign this buffer to the current frame, so it becomes available again once
// this frame expires.
mCurrent.Put(buffer);
return buffer;
}
void
BufferCache::EndFrame()
{
BufferPool empty;
mExpired = Move(mPrevious);
mPrevious = Move(mCurrent);
mCurrent = Move(empty);
}
RefPtr<MLGBuffer>
BufferPool::Take(size_t aBytes)
{
MOZ_ASSERT(aBytes >= 16);
// We need to bump the request up to the nearest size class. For example,
// a request of 24 bytes must allocate from the 32 byte pool.
SizeClass sc = GetSizeClassFromHighBit(CeilingLog2(aBytes));
if (sc == SizeClass::Huge) {
return TakeHugeBuffer(aBytes);
}
if (mClasses[sc].IsEmpty()) {
return nullptr;
}
RefPtr<MLGBuffer> buffer = mClasses[sc].LastElement();
mClasses[sc].RemoveElementAt(mClasses[sc].Length() - 1);
return buffer.forget();
}
void
BufferPool::Put(MLGBuffer* aBuffer)
{
MOZ_ASSERT(aBuffer->GetSize() >= 16);
// When returning buffers, we bump them into a lower size class. For example
// a 24 byte buffer cannot be re-used for a 32-byte allocation, so it goes
// into the 16-byte class.
SizeClass sc = GetSizeClassFromHighBit(FloorLog2(aBuffer->GetSize()));
if (sc == SizeClass::Huge) {
mHugeBuffers.push_back(aBuffer);
} else {
mClasses[sc].AppendElement(aBuffer);
}
}
RefPtr<MLGBuffer>
BufferPool::TakeHugeBuffer(size_t aBytes)
{
static const size_t kMaxSearches = 3;
size_t numSearches = std::min(kMaxSearches, mHugeBuffers.size());
for (size_t i = 0; i < numSearches; i++) {
RefPtr<MLGBuffer> buffer = mHugeBuffers.front();
mHugeBuffers.pop_front();
// Don't pick buffers that are massively overallocated.
if (buffer->GetSize() >= aBytes && buffer->GetSize() <= aBytes * 2) {
return buffer.forget();
}
// Return the buffer to the list.
mHugeBuffers.push_back(buffer);
}
return nullptr;
}
/* static */ BufferPool::SizeClass
BufferPool::GetSizeClassFromHighBit(size_t aBit)
{
// If the size is smaller than our smallest size class (which should
// never happen), or bigger than our largest size class, we dump it
// in the catch-all "huge" list.
static const size_t kBitForFirstClass = 4;
static const size_t kBitForLastClass = kBitForFirstClass + size_t(SizeClass::Huge);
if (aBit < kBitForFirstClass || aBit >= kBitForLastClass) {
return SizeClass::Huge;
}
return SizeClass(aBit - kBitForFirstClass);
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,99 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_BufferCache_h
#define mozilla_gfx_layers_mlgpu_BufferCache_h
#include "mozilla/EnumeratedArray.h"
#include "nsTArray.h"
#include <deque>
namespace mozilla {
namespace layers {
class MLGBuffer;
class MLGDevice;
// This file defines a buffer caching mechanism for systems where constant
// buffer offset binding is not allowed. On those systems we must allocate
// new buffers each frame, and this cache allows us to re-use them.
// Track buffers based on their size class, for small buffers.
class BufferPool
{
public:
// Remove a buffer from the pool holding at least |aBytes|.
RefPtr<MLGBuffer> Take(size_t aBytes);
// Put a buffer into the pool holding at least |aBytes|.
void Put(MLGBuffer* aBuffer);
BufferPool& operator =(BufferPool&& aOther) {
mClasses = Move(aOther.mClasses);
mHugeBuffers = Move(aOther.mHugeBuffers);
return *this;
}
private:
// Try to see if we can quickly re-use any buffer that didn't fit into a
// pre-existing size class.
RefPtr<MLGBuffer> TakeHugeBuffer(size_t aBytes);
enum class SizeClass {
One, // 16+ bytes (one constant)
Two, // 32+ bytes (two constants)
Four, // 64+ bytes (four constants)
Eight, // 128+ bytes (eight constants)
Medium, // 256+ bytes (16 constants)
Large, // 512+ bytes (32 constants)
Huge // 1024+ bytes (64+ constants)
};
static SizeClass GetSizeClassFromHighBit(size_t bit);
private:
typedef nsTArray<RefPtr<MLGBuffer>> BufferList;
EnumeratedArray<SizeClass, SizeClass::Huge, BufferList> mClasses;
std::deque<RefPtr<MLGBuffer>> mHugeBuffers;
};
// Cache buffer pools based on how long ago they were last used.
class BufferCache
{
public:
explicit BufferCache(MLGDevice* aDevice);
~BufferCache();
// Get a buffer that has at least |aBytes|, or create a new one
// if none can be re-used.
RefPtr<MLGBuffer> GetOrCreateBuffer(size_t aBytes);
// Rotate buffers after a frame has been completed.
void EndFrame();
private:
// Not RefPtr since this would create a cycle.
MLGDevice* mDevice;
// We keep three active buffer pools:
// The "expired" pool, which was used two frames ago.
// The "previous" pool, which is being used by the previous frame.
// The "current" pool, which is being used for the current frame.
//
// We always allocate from the expired pool into the current pool.
// After a frame is completed, the current is moved into the previous,
// and the previous is moved into the expired. The expired buffers
// are deleted if still alive.
//
// Since Layers does not allow us to composite more than one frame
// ahead, this system ensures the expired buffers are always free.
BufferPool mExpired;
BufferPool mPrevious;
BufferPool mCurrent;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_BufferCache_h

View File

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CanvasLayerMLGPU.h"
#include "composite/CompositableHost.h" // for CompositableHost
#include "gfx2DGlue.h" // for ToFilter
#include "gfxEnv.h" // for gfxEnv, etc
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
#include "mozilla/gfx/Point.h" // for Point
#include "mozilla/gfx/Rect.h" // for Rect
#include "mozilla/layers/Compositor.h" // for Compositor
#include "mozilla/layers/Effects.h" // for EffectChain
#include "mozilla/layers/ImageHost.h"
#include "mozilla/mozalloc.h" // for operator delete
#include "nsAString.h"
#include "mozilla/RefPtr.h" // for nsRefPtr
#include "MaskOperation.h"
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
#include "nsString.h" // for nsAutoCString
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
CanvasLayerMLGPU::CanvasLayerMLGPU(LayerManagerMLGPU* aManager)
: CanvasLayer(aManager, nullptr)
, TexturedLayerMLGPU(aManager)
{
}
CanvasLayerMLGPU::~CanvasLayerMLGPU()
{
CleanupResources();
}
Layer*
CanvasLayerMLGPU::GetLayer()
{
return this;
}
gfx::SamplingFilter
CanvasLayerMLGPU::GetSamplingFilter()
{
gfx::SamplingFilter filter = mSamplingFilter;
#ifdef ANDROID
// Bug 691354
// Using the LINEAR filter we get unexplained artifacts.
// Use NEAREST when no scaling is required.
Matrix matrix;
bool is2D = GetEffectiveTransform().Is2D(&matrix);
if (is2D && !ThebesMatrix(matrix).HasNonTranslationOrFlip()) {
filter = SamplingFilter::POINT;
}
#endif
return filter;
}
void
CanvasLayerMLGPU::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
CanvasLayer::PrintInfo(aStream, aPrefix);
aStream << "\n";
if (mHost && mHost->IsAttached()) {
nsAutoCString pfx(aPrefix);
pfx += " ";
mHost->PrintInfo(aStream, pfx.get());
}
}
void
CanvasLayerMLGPU::CleanupResources()
{
if (mHost) {
mHost->Detach(this);
}
mTexture = nullptr;
mBigImageTexture = nullptr;
mHost = nullptr;
}
void
CanvasLayerMLGPU::Disconnect()
{
CleanupResources();
}
void
CanvasLayerMLGPU::ClearCachedResources()
{
CleanupResources();
}
void
CanvasLayerMLGPU::SetRegionToRender(LayerIntRegion&& aRegion)
{
aRegion.AndWith(LayerIntRect::FromUnknownRect(mPictureRect));
LayerMLGPU::SetRegionToRender(Move(aRegion));
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_CanvasLayerMLGPU_H
#define GFX_CanvasLayerMLGPU_H
#include "Layers.h" // for CanvasLayer, etc
#include "TexturedLayerMLGPU.h"
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/layers/LayerManagerMLGPU.h" // for LayerComposite, etc
#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
#include "nsDebug.h" // for NS_RUNTIMEABORT
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nscore.h" // for nsACString
namespace mozilla {
namespace layers {
class CompositableHost;
class ImageHost;
class CanvasLayerMLGPU final : public CanvasLayer,
public TexturedLayerMLGPU
{
public:
explicit CanvasLayerMLGPU(LayerManagerMLGPU* aManager);
protected:
~CanvasLayerMLGPU() override;
public:
void Initialize(const Data& aData) override {
MOZ_CRASH("Incompatibe surface type");
}
Layer* GetLayer() override;
void Disconnect() override;
HostLayer* AsHostLayer() override { return this; }
CanvasLayerMLGPU* AsCanvasLayerMLGPU() override { return this; }
gfx::SamplingFilter GetSamplingFilter() override;
void ClearCachedResources() override;
void SetRegionToRender(LayerIntRegion&& aRegion) override;
MOZ_LAYER_DECL_NAME("CanvasLayerMLGPU", TYPE_CANVAS)
protected:
void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
void CleanupResources();
};
} // namespace layers
} // namespace mozilla
#endif /* GFX_CanvasLayerMLGPU_H */

View File

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ContainerLayerMLGPU.h"
#include "gfxPrefs.h"
#include "LayersLogging.h"
#include "MLGDevice.h"
namespace mozilla {
namespace layers {
ContainerLayerMLGPU::ContainerLayerMLGPU(LayerManagerMLGPU* aManager)
: ContainerLayer(aManager, nullptr)
, LayerMLGPU(aManager)
{
}
ContainerLayerMLGPU::~ContainerLayerMLGPU()
{
while (mFirstChild) {
RemoveChild(mFirstChild);
}
}
bool
ContainerLayerMLGPU::OnPrepareToRender(FrameBuilder* aBuilder)
{
if (!UseIntermediateSurface()) {
return true;
}
mTargetOffset = GetIntermediateSurfaceRect().TopLeft().ToUnknownPoint();
mTargetSize = GetIntermediateSurfaceRect().Size().ToUnknownSize();
if (mRenderTarget && mRenderTarget->GetSize() != mTargetSize) {
mRenderTarget = nullptr;
}
IntRect viewport(IntPoint(0, 0), mTargetSize);
if (!mRenderTarget || !gfxPrefs::AdvancedLayersUseInvalidation()) {
// Fine-grained invalidation is disabled, invalidate everything.
mInvalidRect = viewport;
} else {
// Clamp the invalid rect to the viewport.
mInvalidRect = mInvalidRect.Intersect(viewport);
}
return true;
}
RefPtr<MLGRenderTarget>
ContainerLayerMLGPU::UpdateRenderTarget(MLGDevice* aDevice, MLGRenderTargetFlags aFlags)
{
if (mRenderTarget) {
return mRenderTarget;
}
mRenderTarget = aDevice->CreateRenderTarget(mTargetSize, aFlags);
if (!mRenderTarget) {
gfxWarning() << "Failed to create an intermediate render target for ContainerLayer";
return nullptr;
}
return mRenderTarget;
}
void
ContainerLayerMLGPU::SetInvalidCompositeRect(const gfx::IntRect& aRect)
{
// For simplicity we only track the bounds of the invalid area, since regions
// are expensive. We can adjust this in the future if needed.
IntRect bounds = aRect;
bounds.MoveBy(-GetTargetOffset());
// Note we add the bounds to the invalid rect from the last frame, since we
// only clear the area that we actually paint.
if (Maybe<IntRect> result = mInvalidRect.SafeUnion(bounds)) {
mInvalidRect = result.value();
} else {
mInvalidRect = IntRect(IntPoint(0, 0), GetTargetSize());
}
}
void
ContainerLayerMLGPU::ClearCachedResources()
{
mRenderTarget = nullptr;
}
bool
ContainerLayerMLGPU::IsContentOpaque()
{
if (GetMixBlendMode() != CompositionOp::OP_OVER) {
// We need to read from what's underneath us, so we consider our content to
// be not opaque.
return false;
}
return LayerMLGPU::IsContentOpaque();
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_ContainerLayerMLGPU_h
#define mozilla_gfx_layers_mlgpu_ContainerLayerMLGPU_h
#include "LayerMLGPU.h"
namespace mozilla {
namespace layers {
class ContainerLayerMLGPU final : public ContainerLayer
, public LayerMLGPU
{
public:
explicit ContainerLayerMLGPU(LayerManagerMLGPU* aManager);
~ContainerLayerMLGPU() override;
MOZ_LAYER_DECL_NAME("ContainerLayerMLGPU", TYPE_CONTAINER)
HostLayer* AsHostLayer() override { return this; }
ContainerLayerMLGPU* AsContainerLayerMLGPU() override { return this; }
Layer* GetLayer() override { return this; }
void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override {
DefaultComputeEffectiveTransforms(aTransformToSurface);
}
void SetInvalidCompositeRect(const gfx::IntRect &aRect) override;
void ClearCachedResources() override;
RefPtr<MLGRenderTarget> UpdateRenderTarget(
MLGDevice* aDevice,
MLGRenderTargetFlags aFlags);
MLGRenderTarget* GetRenderTarget() const {
return mRenderTarget;
}
gfx::IntPoint GetTargetOffset() const {
return mTargetOffset;
}
gfx::IntSize GetTargetSize() const {
return mTargetSize;
}
const gfx::IntRect& GetInvalidRect() const {
return mInvalidRect;
}
void ClearInvalidRect() {
mInvalidRect.SetEmpty();
}
bool IsContentOpaque() override;
protected:
bool OnPrepareToRender(FrameBuilder* aBuilder) override;
private:
RefPtr<MLGRenderTarget> mRenderTarget;
// We cache these since occlusion culling can change the visible region.
gfx::IntPoint mTargetOffset;
gfx::IntSize mTargetSize;
// The region of the container that needs to be recomposited if visible. We
// store this as a rectangle instead of an nsIntRegion for efficiency.
gfx::IntRect mInvalidRect;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_ContainerLayerMLGPU_h

View File

@ -0,0 +1,431 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FrameBuilder.h"
#include "ContainerLayerMLGPU.h"
#include "GeckoProfiler.h" // for profiler_*
#include "LayerMLGPU.h"
#include "LayerManagerMLGPU.h"
#include "MaskOperation.h"
#include "RenderPassMLGPU.h"
#include "RenderViewMLGPU.h"
#include "mozilla/layers/LayersHelpers.h"
namespace mozilla {
namespace layers {
using namespace mlg;
FrameBuilder::FrameBuilder(LayerManagerMLGPU* aManager, MLGSwapChain* aSwapChain)
: mManager(aManager),
mDevice(aManager->GetDevice()),
mSwapChain(aSwapChain)
{
// test_bug1124898.html has a root ColorLayer, so we don't assume the root is
// a container.
mRoot = mManager->GetRoot()->AsHostLayer()->AsLayerMLGPU();
}
FrameBuilder::~FrameBuilder()
{
}
bool
FrameBuilder::Build()
{
AUTO_PROFILER_LABEL("FrameBuilder::Build", GRAPHICS);
// AcquireBackBuffer can fail, so we check the result here.
RefPtr<MLGRenderTarget> target = mSwapChain->AcquireBackBuffer();
if (!target) {
return false;
}
// This updates the frame sequence number, so layers can quickly check if
// they've already been prepared.
LayerMLGPU::BeginFrame();
// Note: we don't clip draw calls to the invalid region per se, but instead
// the region bounds. Clipping all draw calls would incur a significant
// CPU cost on large layer trees, and would greatly complicate how draw
// rects are added in RenderPassMLGPU, since we would need to break
// each call into additional items based on the intersection with the
// invalid region.
//
// Instead we scissor to the invalid region bounds. As a result, all items
// affecting the invalid bounds are redrawn, even if not all are in the
// precise region.
const nsIntRegion& region = mSwapChain->GetBackBufferInvalidRegion();
mWidgetRenderView = new RenderViewMLGPU(this, target, region);
// Traverse the layer tree and assign each layer to tiles.
{
Maybe<Polygon> geometry;
RenderTargetIntRect clip(0, 0, target->GetSize().width, target->GetSize().height);
AssignLayer(mRoot->GetLayer(), mWidgetRenderView, clip, Move(geometry));
}
// Build the default mask buffer.
{
MaskInformation defaultMaskInfo(1.0f, false);
if (!mDevice->GetSharedPSBuffer()->Allocate(&mDefaultMaskInfo, defaultMaskInfo)) {
return false;
}
}
// Build render passes and buffer information for each pass.
mWidgetRenderView->FinishBuilding();
mWidgetRenderView->Prepare();
// Prepare masks that need to be combined.
for (const auto& pair : mCombinedTextureMasks) {
pair.second->PrepareForRendering();
}
FinishCurrentLayerBuffer();
FinishCurrentMaskRectBuffer();
return true;
}
void
FrameBuilder::Render()
{
AUTO_PROFILER_LABEL("FrameBuilder::Render", GRAPHICS);
// Render combined masks into single mask textures.
for (const auto& pair : mCombinedTextureMasks) {
pair.second->Render();
}
// Render to all targets, front-to-back.
mWidgetRenderView->Render();
}
void
FrameBuilder::AssignLayer(Layer* aLayer,
RenderViewMLGPU* aView,
const RenderTargetIntRect& aClipRect,
Maybe<Polygon>&& aGeometry)
{
LayerMLGPU* layer = aLayer->AsHostLayer()->AsLayerMLGPU();
if (ContainerLayer* container = aLayer->AsContainerLayer()) {
// This returns false if we don't need to (or can't) process the layer any
// further. This always returns false for non-leaf ContainerLayers.
if (!ProcessContainerLayer(container, aView, aClipRect, aGeometry)) {
return;
}
} else {
// Set the precomputed clip and any textures/resources that are needed.
if (!layer->PrepareToRender(this, aClipRect)) {
return;
}
}
// If we are dealing with a nested 3D context, we might need to transform
// the geometry back to the coordinate space of the current layer.
if (aGeometry) {
TransformLayerGeometry(aLayer, aGeometry);
}
// Finally, assign the layer to a rendering batch in the current render
// target.
layer->AssignToView(this, aView, Move(aGeometry));
}
bool
FrameBuilder::ProcessContainerLayer(ContainerLayer* aContainer,
RenderViewMLGPU* aView,
const RenderTargetIntRect& aClipRect,
Maybe<gfx::Polygon>& aGeometry)
{
LayerMLGPU* layer = aContainer->AsHostLayer()->AsLayerMLGPU();
// We don't want to traverse containers twice, so we only traverse them if
// they haven't been prepared yet.
bool isFirstVisit = !layer->IsPrepared();
if (isFirstVisit && !layer->PrepareToRender(this, aClipRect)) {
return false;
}
// If the container is not part of the invalid region, we don't draw it
// or traverse it. Note that we do not pass the geometry here. Otherwise
// we could decide the particular split is not visible, and because of the
// check above, never bother traversing the container again.
IntRect boundingBox = layer->GetClippedBoundingBox(aView, Nothing());
const IntRect& invalidRect = aView->GetInvalidRect();
if (boundingBox.IsEmpty() || !invalidRect.Intersects(boundingBox)) {
return false;
}
if (!aContainer->UseIntermediateSurface()) {
// In case the layer previously required an intermediate surface, we
// clear any intermediate render targets here.
layer->ClearCachedResources();
// This is a pass-through container, so we just process children and
// instruct AssignLayer to early-return.
ProcessChildList(aContainer, aView, aClipRect, aGeometry);
return false;
}
// If this is the first visit of the container this frame, and the
// container has an unpainted area, we traverse the container. Note that
// RefLayers do not have intermediate surfaces so this is guaranteed
// to be a full-fledged ContainerLayerMLGPU.
ContainerLayerMLGPU* viewContainer = layer->AsContainerLayerMLGPU();
if (isFirstVisit && !viewContainer->GetInvalidRect().IsEmpty()) {
// The RenderView constructor automatically attaches itself to the parent.
RefPtr<RenderViewMLGPU> view = new RenderViewMLGPU(this, viewContainer, aView);
ProcessChildList(aContainer, view, aClipRect, Nothing());
view->FinishBuilding();
}
return true;
}
void
FrameBuilder::ProcessChildList(ContainerLayer* aContainer,
RenderViewMLGPU* aView,
const RenderTargetIntRect& aParentClipRect,
const Maybe<Polygon>& aParentGeometry)
{
nsTArray<LayerPolygon> polygons =
aContainer->SortChildrenBy3DZOrder(ContainerLayer::SortMode::WITH_GEOMETRY);
// Visit layers in front-to-back order.
for (auto iter = polygons.rbegin(); iter != polygons.rend(); iter++) {
LayerPolygon& entry = *iter;
Layer* child = entry.layer;
if (child->IsBackfaceHidden() || !child->IsVisible()) {
continue;
}
RenderTargetIntRect clip = child->CalculateScissorRect(aParentClipRect);
if (clip.IsEmpty()) {
continue;
}
Maybe<Polygon> geometry;
if (aParentGeometry && entry.geometry) {
// Both parent and child are split.
geometry = Some(aParentGeometry->ClipPolygon(*entry.geometry));
} else if (aParentGeometry) {
geometry = aParentGeometry;
} else if (entry.geometry) {
geometry = Move(entry.geometry);
}
AssignLayer(child, aView, clip, Move(geometry));
}
}
bool
FrameBuilder::AddLayerToConstantBuffer(ItemInfo& aItem)
{
LayerMLGPU* layer = aItem.layer;
// If this layer could appear multiple times, cache it.
if (aItem.geometry) {
if (mLayerBufferMap.Get(layer, &aItem.layerIndex)) {
return true;
}
}
LayerConstants* info = AllocateLayerInfo(aItem);
if (!info) {
return false;
}
// Note we do not use GetEffectiveTransformForBuffer, since we calculate
// the correct scaling when we build texture coordinates.
Layer* baseLayer = layer->GetLayer();
const Matrix4x4& transform = baseLayer->GetEffectiveTransform();
memcpy(&info->transform, &transform._11, 64);
info->clipRect = Rect(layer->GetComputedClipRect().ToUnknownRect());
info->maskIndex = 0;
if (MaskOperation* op = layer->GetMask()) {
// Note: we use 0 as an invalid index, and so indices are offset by 1.
Rect rect = op->ComputeMaskRect(baseLayer);
AddMaskRect(rect, &info->maskIndex);
}
if (aItem.geometry) {
mLayerBufferMap.Put(layer, aItem.layerIndex);
}
return true;
}
MaskOperation*
FrameBuilder::AddMaskOperation(LayerMLGPU* aLayer)
{
Layer* layer = aLayer->GetLayer();
MOZ_ASSERT(layer->HasMaskLayers());
// Multiple masks are combined into a single mask.
if ((layer->GetMaskLayer() && layer->GetAncestorMaskLayerCount()) ||
layer->GetAncestorMaskLayerCount() > 1)
{
// Since each mask can be moved independently of the other, we must create
// a separate combined mask for every new positioning we encounter.
MaskTextureList textures;
if (Layer* maskLayer = layer->GetMaskLayer()) {
AppendToMaskTextureList(textures, maskLayer);
}
for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
AppendToMaskTextureList(textures, layer->GetAncestorMaskLayerAt(i));
}
auto iter = mCombinedTextureMasks.find(textures);
if (iter != mCombinedTextureMasks.end()) {
return iter->second;
}
RefPtr<MaskCombineOperation> op = new MaskCombineOperation(this);
op->Init(textures);
mCombinedTextureMasks[textures] = op;
return op;
}
Layer* maskLayer = layer->GetMaskLayer()
? layer->GetMaskLayer()
: layer->GetAncestorMaskLayerAt(0);
RefPtr<TextureSource> texture = GetMaskLayerTexture(maskLayer);
if (!texture) {
return nullptr;
}
RefPtr<MaskOperation> op;
mSingleTextureMasks.Get(texture, getter_AddRefs(op));
if (op) {
return op;
}
RefPtr<MLGTexture> wrapped = mDevice->CreateTexture(texture);
op = new MaskOperation(this, wrapped);
mSingleTextureMasks.Put(texture, op);
return op;
}
void
FrameBuilder::RetainTemporaryLayer(LayerMLGPU* aLayer)
{
// This should only be used with temporary layers. Temporary layers do not
// have parents.
MOZ_ASSERT(!aLayer->GetLayer()->GetParent());
mTemporaryLayers.push_back(aLayer->GetLayer());
}
LayerConstants*
FrameBuilder::AllocateLayerInfo(ItemInfo& aItem)
{
if (((mCurrentLayerBuffer.Length() + 1) * sizeof(LayerConstants)) >
mDevice->GetMaxConstantBufferBindSize())
{
FinishCurrentLayerBuffer();
mLayerBufferMap.Clear();
mCurrentLayerBuffer.ClearAndRetainStorage();
}
LayerConstants* info = mCurrentLayerBuffer.AppendElement(mozilla::fallible);
if (!info) {
return nullptr;
}
aItem.layerIndex = mCurrentLayerBuffer.Length() - 1;
return info;
}
void
FrameBuilder::FinishCurrentLayerBuffer()
{
if (mCurrentLayerBuffer.IsEmpty()) {
return;
}
// Note: we append the buffer even if we couldn't allocate one, since
// that keeps the indices sane.
ConstantBufferSection section;
mDevice->GetSharedVSBuffer()->Allocate(
&section,
mCurrentLayerBuffer.Elements(),
mCurrentLayerBuffer.Length());
mLayerBuffers.AppendElement(section);
}
size_t
FrameBuilder::CurrentLayerBufferIndex() const
{
// The mask rect buffer list doesn't contain the buffer currently being
// built, so we don't subtract 1 here.
return mLayerBuffers.Length();
}
ConstantBufferSection
FrameBuilder::GetLayerBufferByIndex(size_t aIndex) const
{
if (aIndex >= mLayerBuffers.Length()) {
return ConstantBufferSection();
}
return mLayerBuffers[aIndex];
}
bool
FrameBuilder::AddMaskRect(const gfx::Rect& aRect, uint32_t* aOutIndex)
{
if (((mCurrentMaskRectList.Length() + 1) * sizeof(gfx::Rect)) >
mDevice->GetMaxConstantBufferBindSize())
{
FinishCurrentMaskRectBuffer();
mCurrentMaskRectList.ClearAndRetainStorage();
}
mCurrentMaskRectList.AppendElement(aRect);
// Mask indices start at 1 so the shader can use 0 as a no-mask indicator.
*aOutIndex = mCurrentMaskRectList.Length();
return true;
}
void
FrameBuilder::FinishCurrentMaskRectBuffer()
{
if (mCurrentMaskRectList.IsEmpty()) {
return;
}
// Note: we append the buffer even if we couldn't allocate one, since
// that keeps the indices sane.
ConstantBufferSection section;
mDevice->GetSharedVSBuffer()->Allocate(
&section,
mCurrentMaskRectList.Elements(),
mCurrentMaskRectList.Length());
mMaskRectBuffers.AppendElement(section);
}
size_t
FrameBuilder::CurrentMaskRectBufferIndex() const
{
// The mask rect buffer list doesn't contain the buffer currently being
// built, so we don't subtract 1 here.
return mMaskRectBuffers.Length();
}
ConstantBufferSection
FrameBuilder::GetMaskRectBufferByIndex(size_t aIndex) const
{
if (aIndex >= mMaskRectBuffers.Length()) {
return ConstantBufferSection();
}
return mMaskRectBuffers[aIndex];
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,129 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_FrameBuilder_h
#define mozilla_gfx_layers_mlgpu_FrameBuilder_h
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/gfx/Types.h"
#include "MaskOperation.h"
#include "nsDataHashtable.h"
#include "nsRefPtrHashtable.h"
#include "ShaderDefinitionsMLGPU.h"
#include "SharedBufferMLGPU.h"
#include "Units.h"
#include <map>
#include <vector>
namespace mozilla {
namespace layers {
class ContainerLayer;
class ContainerLayerMLGPU;
class Layer;
class LayerMLGPU;
class LayerManagerMLGPU;
class MLGDevice;
class MLGRenderTarget;
class MLGSwapChain;
class RenderViewMLGPU;
struct ItemInfo;
class FrameBuilder final
{
public:
FrameBuilder(LayerManagerMLGPU* aManager, MLGSwapChain* aSwapChain);
~FrameBuilder();
bool Build();
void Render();
bool AddLayerToConstantBuffer(ItemInfo& aItem);
LayerManagerMLGPU* GetManager() const {
return mManager;
}
MLGDevice* GetDevice() const {
return mDevice;
}
const ConstantBufferSection& GetDefaultMaskInfo() const {
return mDefaultMaskInfo;
}
// Called during tile construction. Finds or adds a mask layer chain to the
// cache, that will be flattened as a dependency to rendering batches.
MaskOperation* AddMaskOperation(LayerMLGPU* aLayer);
// Note: These should only be called during batch construction.
size_t CurrentLayerBufferIndex() const;
size_t CurrentMaskRectBufferIndex() const;
// These are called during rendering, and may return null if a buffer
// couldn't be allocated.
ConstantBufferSection GetLayerBufferByIndex(size_t aIndex) const;
ConstantBufferSection GetMaskRectBufferByIndex(size_t aIndex) const;
// Hold a layer alive until the frame ends.
void RetainTemporaryLayer(LayerMLGPU* aLayer);
private:
void AssignLayer(Layer* aLayer,
RenderViewMLGPU* aView,
const RenderTargetIntRect& aClipRect,
Maybe<gfx::Polygon>&& aGeometry);
void ProcessChildList(ContainerLayer* aContainer,
RenderViewMLGPU* aView,
const RenderTargetIntRect& aParentClipRect,
const Maybe<gfx::Polygon>& aParentGeometry);
mlg::LayerConstants* AllocateLayerInfo(ItemInfo& aItem);
bool AddMaskRect(const gfx::Rect& aRect, uint32_t* aOutIndex);
void FinishCurrentLayerBuffer();
void FinishCurrentMaskRectBuffer();
// Returns true to continue, false to stop - false does not indicate
// failure.
bool ProcessContainerLayer(ContainerLayer* aLayer,
RenderViewMLGPU* aView,
const RenderTargetIntRect& aClipRect,
Maybe<gfx::Polygon>& aGeometry);
private:
RefPtr<LayerManagerMLGPU> mManager;
RefPtr<MLGDevice> mDevice;
RefPtr<MLGSwapChain> mSwapChain;
RefPtr<RenderViewMLGPU> mWidgetRenderView;
LayerMLGPU* mRoot;
// Each time we consume a layer in a tile, we make sure a constant buffer
// exists that contains information about the layer. The mapping is valid
// for the most recent buffer, and once the buffer fills, we begin a new
// one and clear the map.
nsTArray<ConstantBufferSection> mLayerBuffers;
nsTArray<mlg::LayerConstants> mCurrentLayerBuffer;
nsDataHashtable<nsPtrHashKey<LayerMLGPU>, uint32_t> mLayerBufferMap;
// We keep mask rects in a separate buffer since they're rare.
nsTArray<ConstantBufferSection> mMaskRectBuffers;
nsTArray<gfx::Rect> mCurrentMaskRectList;
// For values that *can* change every render pass, but almost certainly do
// not, we pre-fill and cache some buffers.
ConstantBufferSection mDefaultMaskInfo;
// Cache for MaskOperations.
nsRefPtrHashtable<nsRefPtrHashKey<TextureSource>, MaskOperation> mSingleTextureMasks;
std::map<MaskTextureList, RefPtr<MaskCombineOperation>> mCombinedTextureMasks;
// This list of temporary layers is wiped out when the frame is completed.
std::vector<RefPtr<Layer>> mTemporaryLayers;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_FrameBuilder_h

View File

@ -0,0 +1,125 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageLayerMLGPU.h"
#include "LayerManagerMLGPU.h"
namespace mozilla {
using namespace gfx;
namespace layers {
ImageLayerMLGPU::ImageLayerMLGPU(LayerManagerMLGPU* aManager)
: ImageLayer(aManager, static_cast<HostLayer*>(this))
, TexturedLayerMLGPU(aManager)
{
}
ImageLayerMLGPU::~ImageLayerMLGPU()
{
CleanupResources();
}
void
ImageLayerMLGPU::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
{
Matrix4x4 local = GetLocalTransform();
// Snap image edges to pixel boundaries.
gfxRect sourceRect(0, 0, 0, 0);
if (mHost && mHost->IsAttached()) {
IntSize size = mHost->GetImageSize();
sourceRect.SizeTo(size.width, size.height);
}
// Snap our local transform first, and snap the inherited transform as well.
// This makes our snapping equivalent to what would happen if our content
// was drawn into a PaintedLayer (gfxContext would snap using the local
// transform, then we'd snap again when compositing the PaintedLayer).
mEffectiveTransform =
SnapTransform(local, sourceRect, nullptr) *
SnapTransformTranslation(aTransformToSurface, nullptr);
mEffectiveTransformForBuffer = mEffectiveTransform;
if (mScaleMode == ScaleMode::STRETCH &&
mScaleToSize.width != 0.0 &&
mScaleToSize.height != 0.0)
{
Size scale(
sourceRect.width / mScaleToSize.width,
sourceRect.height / mScaleToSize.height);
mScale = Some(scale);
}
ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
}
gfx::SamplingFilter
ImageLayerMLGPU::GetSamplingFilter()
{
return ImageLayer::GetSamplingFilter();
}
bool
ImageLayerMLGPU::IsContentOpaque()
{
if (mPictureRect.width == 0 || mPictureRect.height == 0) {
return false;
}
if (mScaleMode == ScaleMode::STRETCH) {
return gfx::IsOpaque(mHost->CurrentTextureHost()->GetFormat());
}
return false;
}
void
ImageLayerMLGPU::SetRegionToRender(LayerIntRegion&& aRegion)
{
// See bug 1264142.
if (mScaleMode == ScaleMode::STRETCH) {
aRegion.AndWith(LayerIntRect(0, 0, mScaleToSize.width, mScaleToSize.height));
}
LayerMLGPU::SetRegionToRender(Move(aRegion));
}
void
ImageLayerMLGPU::CleanupResources()
{
if (mHost) {
mHost->CleanupResources();
mHost->Detach(this);
}
mTexture = nullptr;
mBigImageTexture = nullptr;
mHost = nullptr;
}
void
ImageLayerMLGPU::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
ImageLayer::PrintInfo(aStream, aPrefix);
if (mHost && mHost->IsAttached()) {
aStream << "\n";
nsAutoCString pfx(aPrefix);
pfx += " ";
mHost->PrintInfo(aStream, pfx.get());
}
}
void
ImageLayerMLGPU::Disconnect()
{
CleanupResources();
}
void
ImageLayerMLGPU::ClearCachedResources()
{
CleanupResources();
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,54 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_IMAGELAYERMLGPU_H
#define MOZILLA_GFX_IMAGELAYERMLGPU_H
#include "LayerManagerMLGPU.h"
#include "TexturedLayerMLGPU.h"
#include "ImageLayers.h"
#include "mozilla/layers/ImageHost.h"
namespace mozilla {
namespace layers {
class ImageLayerMLGPU final : public ImageLayer
, public TexturedLayerMLGPU
{
public:
explicit ImageLayerMLGPU(LayerManagerMLGPU* aManager);
Layer* GetLayer() override { return this; }
HostLayer* AsHostLayer() override { return this; }
ImageLayerMLGPU* AsImageLayerMLGPU() override { return this; }
void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override;
void SetRegionToRender(LayerIntRegion&& aRegion) override;
gfx::SamplingFilter GetSamplingFilter() override;
void ClearCachedResources() override;
bool IsContentOpaque() override;
void Disconnect() override;
Maybe<gfx::Size> GetPictureScale() const override {
return mScale;
}
MOZ_LAYER_DECL_NAME("ImageLayerMLGPU", TYPE_IMAGE)
protected:
~ImageLayerMLGPU() override;
void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
void CleanupResources();
private:
Maybe<gfx::Size> mScale;
};
} // namespace layers
} // namespace mozilla
#endif

View File

@ -0,0 +1,149 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "LayerManagerMLGPU.h"
#include "RenderPassMLGPU.h"
#include "RenderViewMLGPU.h"
#include "FrameBuilder.h"
#include "mozilla/layers/ImageHost.h"
namespace mozilla {
namespace layers {
using namespace gfx;
uint64_t LayerMLGPU::sFrameKey = 0;
LayerMLGPU::LayerMLGPU(LayerManagerMLGPU* aManager)
: HostLayer(aManager),
mFrameKey(0),
mPrepared(false)
{
}
/* static */ void
LayerMLGPU::BeginFrame()
{
sFrameKey++;
}
LayerManagerMLGPU*
LayerMLGPU::GetManager()
{
return static_cast<LayerManagerMLGPU*>(mCompositorManager);
}
bool
LayerMLGPU::PrepareToRender(FrameBuilder* aBuilder, const RenderTargetIntRect& aClipRect)
{
if (mFrameKey == sFrameKey) {
return mPrepared;
}
mFrameKey = sFrameKey;
mPrepared = false;
Layer* layer = GetLayer();
// Only container layers may have mixed blend modes.
MOZ_ASSERT_IF(layer->GetMixBlendMode() != CompositionOp::OP_OVER,
layer->GetType() == Layer::TYPE_CONTAINER);
mComputedClipRect = aClipRect;
if (layer->HasMaskLayers()) {
mMask = aBuilder->AddMaskOperation(this);
} else {
mMask = nullptr;
}
if (!OnPrepareToRender(aBuilder)) {
return false;
}
mComputedOpacity = layer->GetEffectiveOpacity();
mPrepared = true;
return true;
}
void
LayerMLGPU::AssignToView(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
Maybe<gfx::Polygon>&& aGeometry)
{
AddBoundsToView(aBuilder, aView, Move(aGeometry));
}
void
LayerMLGPU::AddBoundsToView(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
Maybe<gfx::Polygon>&& aGeometry)
{
IntRect bounds = GetClippedBoundingBox(aView, aGeometry);
aView->AddItem(this, bounds, Move(aGeometry));
}
IntRect
LayerMLGPU::GetClippedBoundingBox(RenderViewMLGPU* aView,
const Maybe<gfx::Polygon>& aGeometry)
{
MOZ_ASSERT(IsPrepared());
Layer* layer = GetLayer();
const Matrix4x4& transform = layer->GetEffectiveTransform();
Rect rect = aGeometry
? aGeometry->BoundingBox()
: Rect(layer->GetLocalVisibleRegion().GetBounds().ToUnknownRect());
rect = transform.TransformBounds(rect);
rect.MoveBy(-aView->GetTargetOffset());
rect = rect.Intersect(Rect(mComputedClipRect.ToUnknownRect()));
IntRect bounds;
rect.RoundOut();
rect.ToIntRect(&bounds);
return bounds;
}
void
LayerMLGPU::MarkPrepared()
{
mFrameKey = sFrameKey;
mPrepared = true;
}
bool
LayerMLGPU::IsContentOpaque()
{
return GetLayer()->IsOpaque();
}
void
LayerMLGPU::SetRegionToRender(LayerIntRegion&& aRegion)
{
SetShadowVisibleRegion(Move(aRegion));
}
RefLayerMLGPU::RefLayerMLGPU(LayerManagerMLGPU* aManager)
: RefLayer(aManager, static_cast<HostLayer*>(this))
, LayerMLGPU(aManager)
{
}
RefLayerMLGPU::~RefLayerMLGPU()
{
}
ColorLayerMLGPU::ColorLayerMLGPU(LayerManagerMLGPU* aManager)
: ColorLayer(aManager, static_cast<HostLayer*>(this))
, LayerMLGPU(aManager)
{
}
ColorLayerMLGPU::~ColorLayerMLGPU()
{
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,166 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_LayerMLGPU_h
#define mozilla_gfx_layers_mlgpu_LayerMLGPU_h
#include "Layers.h"
#include "mozilla/layers/LayerManagerComposite.h"
namespace mozilla {
namespace layers {
class CanvasLayerMLGPU;
class ColorLayerMLGPU;
class ContainerLayerMLGPU;
class FrameBuilder;
class ImageHost;
class ImageLayerMLGPU;
class LayerManagerMLGPU;
class MaskOperation;
class MLGRenderTarget;
class PaintedLayerMLGPU;
class RefLayerMLGPU;
class RenderViewMLGPU;
class TexturedLayerMLGPU;
class TextureSource;
class LayerMLGPU : public HostLayer
{
public:
LayerMLGPU* AsLayerMLGPU() override { return this; }
virtual PaintedLayerMLGPU* AsPaintedLayerMLGPU() { return nullptr; }
virtual ImageLayerMLGPU* AsImageLayerMLGPU() { return nullptr; }
virtual CanvasLayerMLGPU* AsCanvasLayerMLGPU() { return nullptr; }
virtual ContainerLayerMLGPU* AsContainerLayerMLGPU() { return nullptr; }
virtual RefLayerMLGPU* AsRefLayerMLGPU() { return nullptr; }
virtual ColorLayerMLGPU* AsColorLayerMLGPU() { return nullptr; }
virtual TexturedLayerMLGPU* AsTexturedLayerMLGPU() { return nullptr; }
static void BeginFrame();
// Ask the layer to acquire any resources or per-frame information needed
// to render. If this returns false, the layer will be skipped entirely.
bool PrepareToRender(FrameBuilder* aBuilder, const RenderTargetIntRect& aClipRect);
Layer::LayerType GetType() {
return GetLayer()->GetType();
}
const RenderTargetIntRect& GetComputedClipRect() const {
return mComputedClipRect;
}
MaskOperation* GetMask() const {
return mMask;
}
float GetComputedOpacity() const {
return mComputedOpacity;
}
// Return the bounding box of this layer in render target space, clipped to
// the computed clip rect, and rounded out to an integer rect.
gfx::IntRect GetClippedBoundingBox(RenderViewMLGPU* aView,
const Maybe<gfx::Polygon>& aGeometry);
// If this layer has already been prepared for the current frame, return
// true. This should only be used to guard against double-processing
// container layers after 3d-sorting.
bool IsPrepared() const {
return mFrameKey == sFrameKey && mPrepared;
}
// Return true if the content in this layer is opaque (not factoring in
// blend modes or opacity), false otherwise.
virtual bool IsContentOpaque();
// This is a wrapper around SetShadowVisibleRegion. Some layers have visible
// regions that extend beyond what is actually drawn. When performing CPU-
// based occlusion culling we must clamp the visible region to the actual
// area.
virtual void SetRegionToRender(LayerIntRegion&& aRegion);
virtual void AssignToView(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
Maybe<gfx::Polygon>&& aGeometry);
// Callback for when PrepareToRender has finished successfully. If this
// returns false, PrepareToRender will return false.
virtual bool OnPrepareToRender(FrameBuilder* aBuilder) {
return true;
}
virtual void ClearCachedResources() {}
virtual CompositableHost* GetCompositableHost() override {
return nullptr;
}
protected:
LayerMLGPU(LayerManagerMLGPU* aManager);
LayerManagerMLGPU* GetManager();
void AddBoundsToView(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
Maybe<gfx::Polygon>&& aGeometry);
void MarkPrepared();
private:
// This is a monotonic counter used to check whether a layer appears twice
// when 3d sorting.
static uint64_t sFrameKey;
protected:
// These are set during PrepareToRender.
RenderTargetIntRect mComputedClipRect;
RefPtr<MaskOperation> mMask;
uint64_t mFrameKey;
float mComputedOpacity;
bool mPrepared;
};
class RefLayerMLGPU final : public RefLayer
, public LayerMLGPU
{
public:
explicit RefLayerMLGPU(LayerManagerMLGPU* aManager);
~RefLayerMLGPU() override;
// Layer
HostLayer* AsHostLayer() override { return this; }
RefLayerMLGPU* AsRefLayerMLGPU() override { return this; }
Layer* GetLayer() override { return this; }
// ContainerLayer
void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
{
DefaultComputeEffectiveTransforms(aTransformToSurface);
}
MOZ_LAYER_DECL_NAME("RefLayerMLGPU", TYPE_REF)
};
class ColorLayerMLGPU final : public ColorLayer
, public LayerMLGPU
{
public:
explicit ColorLayerMLGPU(LayerManagerMLGPU* aManager);
~ColorLayerMLGPU() override;
// LayerMLGPU
bool IsContentOpaque() override {
return mColor.a >= 1.0f;
}
// Layer
HostLayer* AsHostLayer() override { return this; }
ColorLayerMLGPU* AsColorLayerMLGPU() override { return this; }
Layer* GetLayer() override { return this; }
MOZ_LAYER_DECL_NAME("ColorLayerMLGPU", TYPE_COLOR)
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_LayerMLGPU_h

View File

@ -0,0 +1,575 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "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)
{
if (!aWidget) {
return;
}
if (!mDevice) {
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.mSupportsBackdropCopyForComponentAlpha = SupportsBackdropCopyForComponentAlpha();
return ident;
}
LayersBackend
LayerManagerMLGPU::GetBackendType()
{
return mDevice ? mDevice->GetLayersBackend() : LayersBackend::LAYERS_NONE;
}
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;
}
}
mDrawDiagnostics = gfxPrefs::LayersDrawFPS();
mUsingInvalidation = gfxPrefs::AdvancedLayersUseInvalidation();
// Compute transforms - and the changed area, if enabled.
mRoot->ComputeEffectiveTransforms(Matrix4x4());
ComputeInvalidRegion();
// Build and execute draw commands, and present.
if (PreRender()) {
Composite();
PostRender();
}
// 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();
}
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) {
changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr);
} 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);
}
// Free the old cloned property tree, then clone a new one. Note that we do
// this before compositing since our CPU-based occlusion culling will update
// the visible region to contain non-occluded draw rects. If a layer will not
// be drawn, it will have no visible region. LTI might save this, and if the
// layer is removed next frame, LTI will invalidate the wrong area.
//
// Instead, we always invalidate based on the full shadow tree.
//
// Note that the old compositor performs CPU-based occlusion culling *before*
// invalidation. This maintains consistency, but we have more accurate draw
// regions.
mClonedLayerTreeProperties = nullptr;
mClonedLayerTreeProperties = LayerProperties::CloneFrom(mRoot);
}
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;
}
bool
LayerManagerMLGPU::SupportsBackdropCopyForComponentAlpha()
{
return false;
}
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

View File

@ -0,0 +1,136 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_LAYERMANAGERMLGPU_H
#define MOZILLA_GFX_LAYERMANAGERMLGPU_H
#include "Layers.h"
#include "mozilla/layers/LayerManagerComposite.h"
#include "LayerMLGPU.h"
namespace mozilla {
namespace layers {
class FrameBuilder;
class LayerManagerMLGPU;
class RenderPassMLGPU;
class SharedBufferMLGPU;
class RenderViewMLGPU;
class TextRenderer;
class TextureSourceProviderMLGPU;
class MLGBuffer;
class MLGDevice;
class MLGSwapChain;
class MLGTileBuffer;
struct LayerProperties;
class LayerManagerMLGPU final : public HostLayerManager
{
public:
explicit LayerManagerMLGPU(widget::CompositorWidget* aWidget);
~LayerManagerMLGPU();
bool Initialize();
void Destroy() override;
// LayerManager methods
bool BeginTransaction() override;
void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget, const gfx::IntRect& aRect) override;
void SetRoot(Layer* aLayer) override { mRoot = aLayer; }
already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
already_AddRefed<ContainerLayer> CreateContainerLayer() override;
already_AddRefed<ImageLayer> CreateImageLayer() override;
already_AddRefed<ColorLayer> CreateColorLayer() override;
already_AddRefed<TextLayer> CreateTextLayer() override;
already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
already_AddRefed<RefLayer> CreateRefLayer() override;
already_AddRefed<BorderLayer> CreateBorderLayer() override;
bool AreComponentAlphaLayersEnabled() override;
bool BlendingRequiresIntermediateSurface() override;
bool SupportsBackdropCopyForComponentAlpha() override;
// HostLayerManager methods
void ForcePresent() override;
TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
LayersBackend GetBackendType() override;
void AddInvalidRegion(const nsIntRegion& aRegion) override;
void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
const Maybe<uint32_t>& aPresShellId) override {}
void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
const CSSIntRegion& aRegion) override {}
void EndTransaction(const TimeStamp& aTimeStamp, EndTransactionFlags aFlags) override;
void EndTransaction(DrawPaintedLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags aFlags) override;
Compositor* GetCompositor() const override {
return nullptr;
}
bool IsCompositingToScreen() const override;
TextureSourceProvider* GetTextureSourceProvider() const override;
void ClearCachedResources(Layer* aSubtree = nullptr) override;
void NotifyShadowTreeTransaction() override;
void UpdateRenderBounds(const gfx::IntRect& aRect) override;
LayerManagerMLGPU* AsLayerManagerMLGPU() override {
return this;
}
const char* Name() const override {
return "";
}
// This should only be called while a FrameBuilder is live.
FrameBuilder* GetCurrentFrame() const {
MOZ_ASSERT(mCurrentFrame);
return mCurrentFrame;
}
MLGDevice* GetDevice() {
return mDevice;
}
TimeStamp GetLastCompositionEndTime() const {
return mLastCompositionEndTime;
}
private:
void Composite();
void ComputeInvalidRegion();
void RenderLayers();
void DrawDebugOverlay();
bool PreRender();
void PostRender();
private:
RefPtr<MLGDevice> mDevice;
RefPtr<MLGSwapChain> mSwapChain;
RefPtr<TextureSourceProviderMLGPU> mTextureSourceProvider;
RefPtr<TextRenderer> mTextRenderer;
widget::CompositorWidget* mWidget;
UniquePtr<LayerProperties> mClonedLayerTreeProperties;
nsIntRegion mNextFrameInvalidRegion;
gfx::IntRect mRenderBounds;
// These are per-frame only.
bool mDrawDiagnostics;
bool mUsingInvalidation;
nsIntRegion mInvalidRegion;
Maybe<widget::WidgetRenderingContext> mWidgetContext;
IntSize mWindowSize;
TimeStamp mCompositionStartTime;
TimeStamp mLastCompositionEndTime;
RefPtr<DrawTarget> mTarget;
gfx::IntRect mTargetRect;
FrameBuilder* mCurrentFrame;
RefPtr<MLGBuffer> mDiagnosticVertices;
};
} // namespace layers
} // namespace mozilla
#endif

View File

@ -0,0 +1,306 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MLGDevice.h"
#include "mozilla/layers/TextureHost.h"
#include "BufferCache.h"
#include "gfxPrefs.h"
#include "gfxUtils.h"
#include "ShaderDefinitionsMLGPU.h"
#include "SharedBufferMLGPU.h"
namespace mozilla {
namespace layers {
using namespace gfx;
using namespace mlg;
MLGRenderTarget::MLGRenderTarget(MLGRenderTargetFlags aFlags)
: mFlags(aFlags),
mLastDepthStart(-1)
{
}
MLGSwapChain::MLGSwapChain()
: mIsDoubleBuffered(false)
{
}
bool
MLGSwapChain::ApplyNewInvalidRegion(nsIntRegion&& aRegion, const Maybe<gfx::IntRect>& aExtraRect)
{
// We clamp the invalid region to the backbuffer size, otherwise the present
// can fail.
IntRect bounds(IntPoint(0, 0), GetSize());
nsIntRegion invalid = Move(aRegion);
invalid.AndWith(bounds);
if (invalid.IsEmpty()) {
return false;
}
if (aExtraRect) {
IntRect rect = aExtraRect.value().Intersect(bounds);
if (!rect.IsEmpty()) {
invalid.OrWith(rect);
}
}
// This area is now invalid in the back and front buffers. Note that the front
// buffer is either totally valid or totally invalid, since either the last
// paint succeeded or was thrown out due to a buffer resize. Effectively, it
// will now contain the invalid region specific to this frame.
mBackBufferInvalid.OrWith(invalid);
AL_LOG("Backbuffer invalid region: %s\n", Stringify(mBackBufferInvalid).c_str());
if (mIsDoubleBuffered) {
mFrontBufferInvalid.OrWith(invalid);
AL_LOG("Frontbuffer invalid region: %s\n", Stringify(mFrontBufferInvalid).c_str());
}
return true;
}
MLGDevice::MLGDevice()
: mTopology(MLGPrimitiveTopology::Unknown),
mIsValid(false),
mCanUseClearView(false),
mCanUseConstantBufferOffsetBinding(false),
mMaxConstantBufferBindSize(0)
{
}
MLGDevice::~MLGDevice()
{
}
bool
MLGDevice::Initialize()
{
if (!mMaxConstantBufferBindSize) {
return Fail("FEATURE_FAILURE_NO_MAX_CB_BIND_SIZE", "Failed to set a max constant buffer bind size");
}
if (mMaxConstantBufferBindSize < mlg::kMaxConstantBufferSize) {
// StagingBuffer depends on this value being accurate, so for now we just
// double-check it here.
return Fail("FEATURE_FAILURE_MIN_MAX_CB_BIND_SIZE", "Minimum constant buffer bind size not met");
}
// We allow this to be pref'd off for testing. Switching it on enables
// Direct3D 11.0/Windows 7/OpenGL-style buffer code paths.
if (!gfxPrefs::AdvancedLayersEnableBufferSharing()) {
mCanUseConstantBufferOffsetBinding = false;
}
// We allow this to be pref'd off for testing. Disabling it turns on
// ID3D11DeviceContext1::ClearView support, which is present on
// newer Windows 8+ drivers.
if (!gfxPrefs::AdvancedLayersEnableClearView()) {
mCanUseClearView = false;
}
// When compositing normal sized layer trees, we typically have small vertex
// buffers. Empirically the vertex and pixel constant buffer sizes are generally
// under 1KB and the vertex constant buffer size is under 8KB.
static const size_t kDefaultVertexBufferSize = 4096;
static const size_t kDefaultVSConstantBufferSize = 512 * kConstantBufferElementSize;
static const size_t kDefaultPSConstantBufferSize = 256 * kConstantBufferElementSize;
// Note: we create these after we've verified all the device-specific properties above.
mSharedVertexBuffer = MakeUnique<SharedVertexBuffer>(this, kDefaultVertexBufferSize);
mSharedVSBuffer = MakeUnique<SharedConstantBuffer>(this, kDefaultVSConstantBufferSize);
mSharedPSBuffer = MakeUnique<SharedConstantBuffer>(this, kDefaultPSConstantBufferSize);
if (!mSharedVertexBuffer->Init() ||
!mSharedVSBuffer->Init() ||
!mSharedPSBuffer->Init())
{
return Fail("FEATURE_FAILURE_ALLOC_SHARED_BUFFER", "Failed to allocate a shared shader buffer");
}
if (gfxPrefs::AdvancedLayersEnableBufferCache()) {
mConstantBufferCache = MakeUnique<BufferCache>(this);
}
mInitialized = true;
mIsValid = true;
return true;
}
void
MLGDevice::BeginFrame()
{
mSharedVertexBuffer->Reset();
mSharedPSBuffer->Reset();
mSharedVSBuffer->Reset();
}
void
MLGDevice::EndFrame()
{
if (mConstantBufferCache) {
mConstantBufferCache->EndFrame();
}
}
void
MLGDevice::FinishSharedBufferUse()
{
mSharedVertexBuffer->PrepareForUsage();
mSharedPSBuffer->PrepareForUsage();
mSharedVSBuffer->PrepareForUsage();
}
void
MLGDevice::SetTopology(MLGPrimitiveTopology aTopology)
{
if (mTopology == aTopology) {
return;
}
SetPrimitiveTopology(aTopology);
mTopology = aTopology;
}
void
MLGDevice::SetVertexBuffer(uint32_t aSlot, VertexBufferSection* aSection)
{
if (!aSection->IsValid()) {
return;
}
SetVertexBuffer(aSlot, aSection->GetBuffer(), aSection->Stride(), aSection->Offset());
}
void
MLGDevice::SetPSConstantBuffer(uint32_t aSlot, ConstantBufferSection* aSection)
{
if (!aSection->IsValid()) {
return;
}
MLGBuffer* buffer = aSection->GetBuffer();
if (aSection->HasOffset()) {
uint32_t first = aSection->Offset();
uint32_t numConstants = aSection->NumConstants();
SetPSConstantBuffer(aSlot, buffer, first, numConstants);
} else {
SetPSConstantBuffer(aSlot, buffer);
}
}
void
MLGDevice::SetVSConstantBuffer(uint32_t aSlot, ConstantBufferSection* aSection)
{
if (!aSection->IsValid()) {
return;
}
MLGBuffer* buffer = aSection->GetBuffer();
if (aSection->HasOffset()) {
uint32_t first = aSection->Offset();
uint32_t numConstants = aSection->NumConstants();
SetVSConstantBuffer(aSlot, buffer, first, numConstants);
} else {
SetVSConstantBuffer(aSlot, buffer);
}
}
void
MLGDevice::SetPSTexturesYUV(uint32_t aSlot, TextureSource* aTexture)
{
// Note, we don't support tiled YCbCr textures.
const int Y = 0, Cb = 1, Cr = 2;
TextureSource* textures[3] = {
aTexture->GetSubSource(Y),
aTexture->GetSubSource(Cb),
aTexture->GetSubSource(Cr)
};
MOZ_ASSERT(textures[0]);
MOZ_ASSERT(textures[1]);
MOZ_ASSERT(textures[2]);
SetPSTextures(0, 3, textures);
}
void
MLGDevice::SetPSTexture(uint32_t aSlot, TextureSource* aSource)
{
SetPSTextures(aSlot, 1, &aSource);
}
static inline SamplerMode
FilterToSamplerMode(gfx::SamplingFilter aFilter)
{
switch (aFilter) {
case gfx::SamplingFilter::POINT:
return SamplerMode::Point;
case gfx::SamplingFilter::LINEAR:
case gfx::SamplingFilter::GOOD:
return SamplerMode::LinearClamp;
default:
MOZ_ASSERT_UNREACHABLE("Unknown sampler mode");
return SamplerMode::LinearClamp;
}
}
void
MLGDevice::SetSamplerMode(uint32_t aIndex, gfx::SamplingFilter aFilter)
{
SetSamplerMode(aIndex, FilterToSamplerMode(aFilter));
}
bool
MLGDevice::Fail(const nsCString& aFailureId, const nsCString* aMessage)
{
const char* message = aMessage
? aMessage->get()
: "Failed initializing MLGDeviceD3D11";
gfxWarning() << "Failure initializing MLGDeviceD3D11: " << message;
mFailureId = aFailureId;
mFailureMessage = message;
return false;
}
void
MLGDevice::UnmapSharedBuffers()
{
mSharedVertexBuffer->Reset();
mSharedPSBuffer->Reset();
mSharedVSBuffer->Reset();
}
RefPtr<MLGBuffer>
MLGDevice::GetBufferForColorSpace(YUVColorSpace aColorSpace)
{
if (mColorSpaceBuffers[aColorSpace]) {
return mColorSpaceBuffers[aColorSpace];
}
YCbCrShaderConstants buffer;
memcpy(
&buffer.yuvColorMatrix,
gfxUtils::YuvToRgbMatrix4x3RowMajor(aColorSpace),
sizeof(buffer.yuvColorMatrix));
RefPtr<MLGBuffer> resource = CreateBuffer(
MLGBufferType::Constant,
sizeof(buffer),
MLGUsage::Immutable,
&buffer);
if (!resource) {
return nullptr;
}
mColorSpaceBuffers[aColorSpace] = resource;
return resource;
}
bool
MLGDevice::Synchronize()
{
return true;
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,476 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_MLGDevice_h
#define mozilla_gfx_layers_mlgpu_MLGDevice_h
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/EnumeratedArray.h"
#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
#include "mozilla/TypedEnumBits.h"
#include "mozilla/WidgetUtils.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/LayersTypes.h"
#include "ImageTypes.h"
#include "MLGDeviceTypes.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsPrintfCString.h"
namespace mozilla {
namespace widget {
class CompositorWidget;
} // namespace widget
namespace gfx {
class DrawTarget;
} // namespace gfx
namespace layers {
struct GPUStats;
class BufferCache;
class ConstantBufferSection;
class DataTextureSource;
class MLGBufferD3D11;
class MLGDeviceD3D11;
class MLGRenderTargetD3D11;
class MLGResourceD3D11;
class MLGTexture;
class MLGTextureD3D11;
class SharedVertexBuffer;
class SharedConstantBuffer;
class TextureSource;
class VertexBufferSection;
class MLGRenderTarget
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MLGRenderTarget)
virtual gfx::IntSize GetSize() const = 0;
virtual MLGRenderTargetD3D11* AsD3D11() { return nullptr; }
// Returns the underlying texture of the render target.
virtual MLGTexture* GetTexture() = 0;
bool HasDepthBuffer() const {
return (mFlags & MLGRenderTargetFlags::ZBuffer) == MLGRenderTargetFlags::ZBuffer;
}
int32_t GetLastDepthStart() const {
return mLastDepthStart;
}
void SetLastDepthStart(int32_t aDepthStart) {
mLastDepthStart = aDepthStart;
}
protected:
explicit MLGRenderTarget(MLGRenderTargetFlags aFlags);
virtual ~MLGRenderTarget() {}
protected:
MLGRenderTargetFlags mFlags;
// When using a depth buffer, callers can track the range of depth values
// that were last used.
int32_t mLastDepthStart;
};
class MLGSwapChain
{
protected:
virtual ~MLGSwapChain() {}
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MLGSwapChain)
virtual RefPtr<MLGRenderTarget> AcquireBackBuffer() = 0;
virtual bool ResizeBuffers(const gfx::IntSize& aSize) = 0;
virtual gfx::IntSize GetSize() const = 0;
// Present to the screen.
virtual void Present() = 0;
// Force a present without waiting for the previous frame's present to complete.
virtual void ForcePresent() = 0;
// Copy an area of the backbuffer to a draw target.
virtual void CopyBackbuffer(gfx::DrawTarget* aTarget, const gfx::IntRect& aBounds) = 0;
// Free any internal resources.
virtual void Destroy() = 0;
// Give the new invalid region to the swap chain in preparation for
// acquiring the backbuffer. If the new invalid region is empty,
// this returns false and no composite is required.
//
// The extra rect is used for the debug overlay, which is factored in
// separately to avoid causing unnecessary composites.
bool ApplyNewInvalidRegion(nsIntRegion&& aRegion, const Maybe<gfx::IntRect>& aExtraRect);
const nsIntRegion& GetBackBufferInvalidRegion() const {
return mBackBufferInvalid;
}
protected:
MLGSwapChain();
protected:
gfx::IntSize mLastPresentSize;
// The swap chain tracks the invalid region of its buffers. After presenting,
// the invalid region for the backbuffer is cleared. If using double
// buffering, it is set to the area of the non-presented buffer that was not
// painted this frame. The initial invalid region each frame comes from
// LayerManagerMLGPU, and is combined with the back buffer's invalid region
// before frame building begins.
nsIntRegion mBackBufferInvalid;
nsIntRegion mFrontBufferInvalid;
bool mIsDoubleBuffered;
};
class MLGResource
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MLGResource)
public:
enum class Type {
Buffer,
Texture
};
virtual Type GetType() const = 0;
virtual MLGResourceD3D11* AsResourceD3D11() {
return nullptr;
}
protected:
virtual ~MLGResource() {}
};
// A buffer for use as a shader input.
class MLGBuffer : public MLGResource
{
public:
Type GetType() const override {
return Type::Buffer;
}
virtual MLGBufferD3D11* AsD3D11() {
return nullptr;
}
virtual size_t GetSize() const = 0;
protected:
~MLGBuffer() override {}
};
// This is a lower-level resource than a TextureSource. It wraps
// a 2D texture.
class MLGTexture : public MLGResource
{
public:
Type GetType() const override {
return Type::Texture;
}
virtual MLGTextureD3D11* AsD3D11() {
return nullptr;
}
const gfx::IntSize& GetSize() const {
return mSize;
}
protected:
gfx::IntSize mSize;
};
enum class VertexShaderID
{
TexturedQuad,
TexturedVertex,
ColoredQuad,
ColoredVertex,
BlendVertex,
Clear,
MaskCombiner,
DiagnosticText,
MaxShaders
};
enum class PixelShaderID
{
ColoredQuad,
ColoredVertex,
TexturedQuadRGB,
TexturedQuadRGBA,
TexturedVertexRGB,
TexturedVertexRGBA,
TexturedQuadIMC4,
TexturedQuadNV12,
TexturedVertexIMC4,
TexturedVertexNV12,
ComponentAlphaQuad,
ComponentAlphaVertex,
BlendMultiply,
BlendScreen,
BlendOverlay,
BlendDarken,
BlendLighten,
BlendColorDodge,
BlendColorBurn,
BlendHardLight,
BlendSoftLight,
BlendDifference,
BlendExclusion,
BlendHue,
BlendSaturation,
BlendColor,
BlendLuminosity,
Clear,
MaskCombiner,
DiagnosticText,
MaxShaders
};
class MLGDevice
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MLGDevice)
MLGDevice();
virtual bool Initialize();
// If Initialize returns false, these may return more useful messages.
virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() const = 0;
virtual int32_t GetMaxTextureSize() const = 0;
virtual LayersBackend GetLayersBackend() const = 0;
virtual RefPtr<MLGSwapChain> CreateSwapChainForWidget(widget::CompositorWidget* aWidget) = 0;
// Markers for when we start and finish issuing "normal" (i.e., non-
// diagnostic) draw commands for the frame.
virtual void StartDiagnostics(uint32_t aInvalidPixels) = 0;
virtual void EndDiagnostics() = 0;
virtual void GetDiagnostics(GPUStats* aStats) = 0;
// Layers interaction.
virtual RefPtr<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags) = 0;
// Resource access
virtual bool Map(MLGResource* aResource, MLGMapType aType, MLGMappedResource* aMap) = 0;
virtual void Unmap(MLGResource* aResource) = 0;
virtual void UpdatePartialResource(
MLGResource* aResource,
const gfx::IntRect* aRect,
void* aData,
uint32_t aStride) = 0;
virtual void CopyTexture(
MLGTexture* aDest,
const gfx::IntPoint& aTarget,
MLGTexture* aSource,
const gfx::IntRect& aRect) = 0;
// Begin a frame. This clears and resets all shared buffers.
virtual void BeginFrame();
virtual void EndFrame();
// State setup commands.
virtual void SetRenderTarget(MLGRenderTarget* aRT) = 0;
virtual MLGRenderTarget* GetRenderTarget() = 0;
virtual void SetViewport(const gfx::IntRect& aRT) = 0;
virtual void SetScissorRect(const Maybe<gfx::IntRect>& aScissorRect) = 0;
virtual void SetVertexShader(VertexShaderID aVertexShader) = 0;
virtual void SetPixelShader(PixelShaderID aPixelShader) = 0;
virtual void SetSamplerMode(uint32_t aIndex, SamplerMode aSamplerMode) = 0;
virtual void SetBlendState(MLGBlendState aBlendState) = 0;
virtual void SetVertexBuffer(uint32_t aSlot, MLGBuffer* aBuffer, uint32_t aStride, uint32_t aOffset = 0) = 0;
virtual void SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) = 0;
virtual void SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) = 0;
virtual void SetPSTextures(uint32_t aSlot, uint32_t aNumTextures, TextureSource* const* aTextures) = 0;
virtual void SetPSTexture(uint32_t aSlot, MLGTexture* aTexture) = 0;
virtual void SetDepthTestMode(MLGDepthTestMode aMode) = 0;
// If supported, bind constant buffers at a particular offset. These can only
// be used if CanUseConstantBufferOffsetBinding returns true.
virtual void SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer, uint32_t aFirstConstant, uint32_t aNumConstants) = 0;
virtual void SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer, uint32_t aFirstConstant, uint32_t aNumConstants) = 0;
// Set the topology. No API call is made if the topology has not changed.
// The UnitQuad topology implicity binds a unit quad triangle strip as
// vertex buffer #0.
void SetTopology(MLGPrimitiveTopology aTopology);
// Set textures that have special binding logic, and bind to multiple slots.
virtual void SetPSTexturesNV12(uint32_t aSlot, TextureSource* aTexture) = 0;
void SetPSTexturesYUV(uint32_t aSlot, TextureSource* aTexture);
virtual RefPtr<MLGBuffer> CreateBuffer(
MLGBufferType aType,
uint32_t aSize,
MLGUsage aUsage,
const void* aInitialData = nullptr) = 0;
virtual RefPtr<MLGTexture> CreateTexture(
const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat,
MLGUsage aUsage,
MLGTextureFlags aFlags) = 0;
// Unwrap the underlying GPU texture in the given TextureSource, and re-wrap
// it in an MLGTexture structure.
virtual RefPtr<MLGTexture> CreateTexture(TextureSource* aSource) = 0;
virtual RefPtr<MLGRenderTarget> CreateRenderTarget(
const gfx::IntSize& aSize,
MLGRenderTargetFlags aFlags = MLGRenderTargetFlags::Default) = 0;
// Clear a render target to the given color, or clear a depth buffer.
virtual void Clear(MLGRenderTarget* aRT, const gfx::Color& aColor) = 0;
virtual void ClearDepthBuffer(MLGRenderTarget* aRT) = 0;
// This is only available if CanUseClearView() returns true.
virtual void ClearView(
MLGRenderTarget* aRT,
const gfx::Color& aColor,
const gfx::IntRect* aRects,
size_t aNumRects) = 0;
// Drawing Commands
virtual void Draw(uint32_t aVertexCount, uint32_t aOffset) = 0;
virtual void DrawInstanced(uint32_t aVertexCountPerInstance, uint32_t aInstanceCount,
uint32_t aVertexOffset, uint32_t aInstanceOffset) = 0;
virtual void Flush() = 0;
// This unlocks any textures that were implicitly locked during drawing.
virtual void UnlockAllTextures() = 0;
virtual MLGDeviceD3D11* AsD3D11() { return nullptr; }
// Helpers.
void SetVertexBuffer(uint32_t aSlot, VertexBufferSection* aSection);
void SetPSConstantBuffer(uint32_t aSlot, ConstantBufferSection* aSection);
void SetVSConstantBuffer(uint32_t aSlot, ConstantBufferSection* aSection);
void SetPSTexture(uint32_t aSlot, TextureSource* aSource);
void SetSamplerMode(uint32_t aIndex, gfx::SamplingFilter aFilter);
// This creates or returns a previously created constant buffer, containing
// a YCbCrShaderConstants instance.
RefPtr<MLGBuffer> GetBufferForColorSpace(YUVColorSpace aColorSpace);
// A shared buffer that can be used to build VertexBufferSections.
SharedVertexBuffer* GetSharedVertexBuffer() {
return mSharedVertexBuffer.get();
}
// A shared buffer that can be used to build ConstantBufferSections. Intended
// to be used with vertex shaders.
SharedConstantBuffer* GetSharedVSBuffer() {
return mSharedVSBuffer.get();
}
// A shared buffer that can be used to build ConstantBufferSections. Intended
// to be used with pixel shaders.
SharedConstantBuffer* GetSharedPSBuffer() {
return mSharedPSBuffer.get();
}
// A cache for constant buffers, used when offset-based binding is not supported.
BufferCache* GetConstantBufferCache() {
return mConstantBufferCache.get();
}
// Unmap and upload all shared buffers to the GPU.
void FinishSharedBufferUse();
// These are used to detect and report initialization failure.
virtual bool IsValid() const {
return mInitialized && mIsValid;
}
const nsCString& GetFailureId() const {
return mFailureId;
}
const nsCString& GetFailureMessage() const {
return mFailureMessage;
}
// If supported, synchronize with the SyncObject given to clients.
virtual bool Synchronize();
// If this returns true, ClearView() can be called.
bool CanUseClearView() const {
return mCanUseClearView;
}
// If this returns true, constant buffers can be bound at specific offsets for
// a given run of bytes. This is only supported on Windows 8+ for Direct3D 11.
bool CanUseConstantBufferOffsetBinding() const {
return mCanUseConstantBufferOffsetBinding;
}
// Return the maximum number of elements that can be bound to a constant
// buffer. This is different than the maximum size of a buffer (there is
// no such limit on Direct3D 11.1).
size_t GetMaxConstantBufferBindSize() const {
return mMaxConstantBufferBindSize;
}
protected:
virtual ~MLGDevice();
virtual void SetPrimitiveTopology(MLGPrimitiveTopology aTopology) = 0;
// Used during initialization to record failure reasons.
bool Fail(const nsCString& aFailureId, const nsCString* aMessage);
// Used during initialization to record failure reasons. Note: our
// MOZ_FORMAT_PRINTF macro does not work on this function, so we
// disable the warning.
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-security"
#endif
template <typename... T>
bool Fail(const char* aFailureId) {
nsCString failureId(aFailureId);
return Fail(failureId, nullptr);
}
template <typename... T>
bool Fail(const char* aFailureId,
const char* aMessage,
const T&... args)
{
nsCString failureId(aFailureId);
nsPrintfCString message(aMessage, args...);
return Fail(failureId, &message);
}
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
void UnmapSharedBuffers();
private:
MLGPrimitiveTopology mTopology;
UniquePtr<SharedVertexBuffer> mSharedVertexBuffer;
UniquePtr<SharedConstantBuffer> mSharedVSBuffer;
UniquePtr<SharedConstantBuffer> mSharedPSBuffer;
UniquePtr<BufferCache> mConstantBufferCache;
nsCString mFailureId;
nsCString mFailureMessage;
bool mInitialized;
typedef EnumeratedArray<YUVColorSpace, YUVColorSpace::UNKNOWN, RefPtr<MLGBuffer>> ColorSpaceArray;
ColorSpaceArray mColorSpaceBuffers;
protected:
bool mIsValid;
bool mCanUseClearView;
bool mCanUseConstantBufferOffsetBinding;
size_t mMaxConstantBufferBindSize;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_MLGDevice_h

View File

@ -0,0 +1,109 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_MLGDeviceTypes_h
#define mozilla_gfx_layers_mlgpu_MLGDeviceTypes_h
#include "mozilla/TypedEnumBits.h"
namespace mozilla {
namespace layers {
enum class MLGUsage
{
// GPU read-only, CPU write once on creation and read/write never.
Immutable,
// GPU read-only, CPU write-only. Must be mapped with WRITE_DISCARD.
Dynamic,
// GPU read/write-only, no CPU access.
Default,
// GPU->CPU transfer, and read from the CPU.
Staging
};
enum class MLGDepthTestMode
{
Disabled,
Write,
ReadOnly,
AlwaysWrite,
MaxModes
};
enum class MLGBufferType : uint32_t
{
Vertex,
Constant
};
enum class SamplerMode
{
// Linear filter, clamped to border.
LinearClamp = 0,
// Linear filter, clamped to transparent pixels.
LinearClampToZero,
// Point filter, clamped to border.
Point,
MaxModes
};
enum class MLGBlendState
{
Copy = 0,
Over,
OverAndPremultiply,
Min,
ComponentAlpha,
MaxStates
};
enum class MLGPrimitiveTopology
{
Unknown = 0,
TriangleStrip = 1,
TriangleList = 2,
UnitQuad = 3
};
struct MLGMappedResource
{
uint8_t* mData;
uint32_t mStride;
};
enum class MLGMapType
{
READ = 0,
WRITE,
READ_WRITE,
WRITE_DISCARD
};
enum class MLGTextureFlags
{
None,
ShaderResource,
RenderTarget
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MLGTextureFlags);
enum class MLGRenderTargetFlags : uint32_t
{
Default = 0,
ZBuffer = (1 << 0)
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MLGRenderTargetFlags);
// NVIDIA drivers crash when we supply too many rects to ClearView - it
// seems to cause a stack overflow >= 20 rects. We cap to 12 for now.
static const size_t kMaxClearViewRects = 12;
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_MLGDeviceTypes_h

View File

@ -0,0 +1,189 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MaskOperation.h"
#include "FrameBuilder.h"
#include "LayerMLGPU.h"
#include "mozilla/layers/LayersHelpers.h"
#include "MLGDevice.h"
#include "TexturedLayerMLGPU.h"
namespace mozilla {
namespace layers {
using namespace gfx;
MaskOperation::MaskOperation(FrameBuilder* aBuilder)
{
}
MaskOperation::MaskOperation(FrameBuilder* aBuilder, MLGTexture* aSource)
: mTexture(aSource)
{
}
MaskOperation::~MaskOperation()
{
}
static gfx::Rect
ComputeQuadForMaskLayer(Layer* aLayer, const IntSize& aSize)
{
const Matrix4x4& transform = aLayer->GetEffectiveTransform();
MOZ_ASSERT(transform.Is2D(), "Mask layers should not have 3d transforms");
Rect bounds(Point(0, 0), Size(aSize));
return transform.As2D().TransformBounds(bounds);
}
Rect
MaskOperation::ComputeMaskRect(Layer* aLayer) const
{
Layer* maskLayer = aLayer->GetMaskLayer()
? aLayer->GetMaskLayer()
: aLayer->GetAncestorMaskLayerAt(0);
MOZ_ASSERT((aLayer->GetAncestorMaskLayerCount() == 0 && aLayer->GetMaskLayer()) ||
(aLayer->GetAncestorMaskLayerCount() == 1 && !aLayer->GetMaskLayer()));
return ComputeQuadForMaskLayer(maskLayer, mTexture->GetSize());
}
// This is only needed for std::map.
bool
MaskTexture::operator <(const MaskTexture& aOther) const
{
if (mRect.x != aOther.mRect.x) {
return mRect.x < aOther.mRect.x;
}
if (mRect.y != aOther.mRect.y) {
return mRect.y < aOther.mRect.y;
}
if (mRect.width != aOther.mRect.width) {
return mRect.width < aOther.mRect.width;
}
if (mRect.height != aOther.mRect.height) {
return mRect.height < aOther.mRect.height;
}
return mSource < aOther.mSource;
}
RefPtr<TextureSource>
GetMaskLayerTexture(Layer* aLayer)
{
LayerMLGPU* layer = aLayer->AsHostLayer()->AsLayerMLGPU();
TexturedLayerMLGPU* texLayer = layer->AsTexturedLayerMLGPU();
if (!texLayer) {
MOZ_ASSERT_UNREACHABLE("Mask layers should be texture layers");
return nullptr;
}
RefPtr<TextureSource> source = texLayer->BindAndGetTexture();
if (!source) {
gfxWarning() << "Mask layer does not have a TextureSource";
return nullptr;
}
return source.forget();
}
MaskCombineOperation::MaskCombineOperation(FrameBuilder* aBuilder)
: MaskOperation(aBuilder),
mBuilder(aBuilder)
{
}
MaskCombineOperation::~MaskCombineOperation()
{
}
void
MaskCombineOperation::Init(const MaskTextureList& aTextures)
{
// All masks for a single layer exist in the same coordinate space. Find the
// area that covers all rects.
Rect area = aTextures[0].mRect;
for (size_t i = 1; i < aTextures.size(); i++) {
area = area.Intersect(aTextures[i].mRect);
}
// Go through and decide which areas of the textures are relevant.
for (size_t i = 0; i < aTextures.size(); i++) {
Rect rect = aTextures[i].mRect.Intersect(area);
if (rect.IsEmpty()) {
continue;
}
rect -= aTextures[i].mRect.TopLeft();
mTextures.push_back(MaskTexture(rect, aTextures[i].mSource));
}
IntRect size;
Rect bounds = area;
bounds.RoundOut();
bounds.ToIntRect(&size);
mTarget = mBuilder->GetDevice()->CreateRenderTarget(size.Size());
mArea = area;
mTexture = mTarget->GetTexture();
}
void
MaskCombineOperation::PrepareForRendering()
{
for (const auto& entry : mTextures) {
Rect texCoords = TextureRectToCoords(entry.mRect, entry.mSource->GetSize());
SharedVertexBuffer* shared = mBuilder->GetDevice()->GetSharedVertexBuffer();
VertexBufferSection section;
if (!shared->Allocate(&section, 1, sizeof(texCoords), &texCoords)) {
continue;
}
mInputBuffers.push_back(section);
}
}
void
MaskCombineOperation::Render()
{
RefPtr<MLGDevice> device = mBuilder->GetDevice();
device->SetTopology(MLGPrimitiveTopology::UnitQuad);
device->SetVertexShader(VertexShaderID::MaskCombiner);
device->SetPixelShader(PixelShaderID::MaskCombiner);
device->SetSamplerMode(0, SamplerMode::LinearClamp);
device->SetBlendState(MLGBlendState::Min);
// Since the mask operation is effectively an AND operation, we initialize
// the entire r-channel to 1.
device->Clear(mTarget, Color(1, 0, 0, 1));
device->SetScissorRect(Nothing());
device->SetRenderTarget(mTarget);
device->SetViewport(IntRect(IntPoint(0, 0), mTarget->GetSize()));
for (size_t i = 0; i < mInputBuffers.size(); i++) {
if (!mInputBuffers[i].IsValid()) {
continue;
}
device->SetVertexBuffer(1, &mInputBuffers[i]);
device->SetPSTexture(0, mTextures[i].mSource);
device->DrawInstanced(4, mInputBuffers[i].NumVertices(), 0, 0);
}
}
void
AppendToMaskTextureList(MaskTextureList& aList, Layer* aLayer)
{
RefPtr<TextureSource> source = GetMaskLayerTexture(aLayer);
if (!source) {
return;
}
gfx::Rect rect = ComputeQuadForMaskLayer(aLayer, source->GetSize());
aList.push_back(MaskTexture(rect, source));
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,96 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_MaskOperation_h
#define mozilla_gfx_layers_mlgpu_MaskOperation_h
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/Rect.h"
#include "SharedBufferMLGPU.h"
#include <vector>
namespace mozilla {
namespace layers {
class FrameBuilder;
class Layer;
class MLGDevice;
class MLGRenderTarget;
class MLGTexture;
class TextureSource;
class MaskOperation
{
NS_INLINE_DECL_REFCOUNTING(MaskOperation)
public:
// For when the exact texture is known ahead of time.
MaskOperation(FrameBuilder* aBuilder, MLGTexture* aSource);
// Return the mask rectangle in screen coordinates. This function takes a
// layer because a single-texture mask operation is not dependent on a
// specific mask transform. (Multiple mask layer operations are, and they
// ignore the layer parameter).
virtual gfx::Rect ComputeMaskRect(Layer* aLayer) const;
MLGTexture* GetTexture() const {
return mTexture;
}
protected:
explicit MaskOperation(FrameBuilder* aBuilder);
virtual ~MaskOperation();
protected:
RefPtr<MLGTexture> mTexture;
};
struct MaskTexture
{
MaskTexture() : mSource(nullptr)
{}
MaskTexture(const gfx::Rect& aRect, TextureSource* aSource)
: mRect(aRect), mSource(aSource)
{}
bool operator <(const MaskTexture& aOther) const;
gfx::Rect mRect;
RefPtr<TextureSource> mSource;
};
typedef std::vector<MaskTexture> MaskTextureList;
class MaskCombineOperation final : public MaskOperation
{
public:
explicit MaskCombineOperation(FrameBuilder* aBuilder);
~MaskCombineOperation() override;
void Init(const MaskTextureList& aTextures);
void PrepareForRendering();
void Render();
gfx::Rect ComputeMaskRect(Layer* aLayer) const override {
return mArea;
}
private:
FrameBuilder* mBuilder;
gfx::Rect mArea;
MaskTextureList mTextures;
RefPtr<MLGRenderTarget> mTarget;
std::vector<VertexBufferSection> mInputBuffers;
};
RefPtr<TextureSource> GetMaskLayerTexture(Layer* aLayer);
void AppendToMaskTextureList(MaskTextureList& aList, Layer* aLayer);
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_MaskOperation_h

View File

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PaintedLayerMLGPU.h"
#include "LayerManagerMLGPU.h"
#include "mozilla/layers/LayersHelpers.h"
namespace mozilla {
using namespace gfx;
namespace layers {
PaintedLayerMLGPU::PaintedLayerMLGPU(LayerManagerMLGPU* aManager)
: PaintedLayer(aManager, static_cast<HostLayer*>(this)),
LayerMLGPU(aManager)
{
MOZ_COUNT_CTOR(PaintedLayerMLGPU);
}
PaintedLayerMLGPU::~PaintedLayerMLGPU()
{
MOZ_COUNT_DTOR(PaintedLayerMLGPU);
CleanupResources();
}
bool
PaintedLayerMLGPU::OnPrepareToRender(FrameBuilder* aBuilder)
{
if (!mHost) {
return false;
}
mTexture = mHost->AcquireTextureSource();
if (!mTexture) {
return false;
}
mTextureOnWhite = mHost->AcquireTextureSourceOnWhite();
#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
// Note: we don't set PaintWillResample on our ContentTextureHost. The old
// compositor must do this since ContentHost is responsible for issuing
// draw calls, but in AL we can handle it directly here.
//
// Note that when AL performs CPU-based occlusion culling (the default
// behavior), we might break up the visible region again. If that turns
// out to be a problem, we can factor this into ForEachDrawRect instead.
if (MayResample()) {
LayerIntRegion visible = Move(GetShadowVisibleRegion());
visible = visible.GetBounds();
SetShadowVisibleRegion(Move(visible));
}
#endif
return true;
}
bool
PaintedLayerMLGPU::SetCompositableHost(CompositableHost* aHost)
{
switch (aHost->GetType()) {
case CompositableType::CONTENT_SINGLE:
case CompositableType::CONTENT_DOUBLE:
mHost = static_cast<ContentHostBase*>(aHost)->AsContentHostTexture();
if (!mHost) {
gfxWarning() << "ContentHostBase is not a ContentHostTexture";
}
return true;
default:
return false;
}
}
CompositableHost*
PaintedLayerMLGPU::GetCompositableHost()
{
return mHost;
}
void
PaintedLayerMLGPU::CleanupResources()
{
if (mHost) {
mHost->Detach(this);
}
mTexture = nullptr;
mTextureOnWhite = nullptr;
mHost = nullptr;
}
void
PaintedLayerMLGPU::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
PaintedLayer::PrintInfo(aStream, aPrefix);
if (mHost && mHost->IsAttached()) {
aStream << "\n";
nsAutoCString pfx(aPrefix);
pfx += " ";
mHost->PrintInfo(aStream, pfx.get());
}
}
void
PaintedLayerMLGPU::Disconnect()
{
CleanupResources();
}
bool
PaintedLayerMLGPU::IsContentOpaque()
{
return !!(GetContentFlags() & CONTENT_OPAQUE);
}
void
PaintedLayerMLGPU::CleanupCachedResources()
{
CleanupResources();
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_PAINTEDLAYERMLGPU_H
#define MOZILLA_GFX_PAINTEDLAYERMLGPU_H
#include "LayerManagerMLGPU.h"
#include "mozilla/layers/ContentHost.h"
#include "nsRegionFwd.h"
#include <functional>
namespace mozilla {
namespace layers {
class PaintedLayerMLGPU final
: public PaintedLayer,
public LayerMLGPU
{
public:
explicit PaintedLayerMLGPU(LayerManagerMLGPU* aManager);
~PaintedLayerMLGPU() override;
// Layer
HostLayer* AsHostLayer() override { return this; }
PaintedLayerMLGPU* AsPaintedLayerMLGPU() override { return this; }
virtual Layer* GetLayer() override { return this; }
bool SetCompositableHost(CompositableHost*) override;
CompositableHost* GetCompositableHost() override;
void Disconnect() override;
bool IsContentOpaque() override;
// PaintedLayer
void InvalidateRegion(const nsIntRegion& aRegion) override {
MOZ_CRASH("PaintedLayerMLGPU can't fill invalidated regions");
}
bool HasComponentAlpha() const {
return !!mTextureOnWhite;
}
TextureSource* GetTexture() const {
return mTexture;
}
TextureSource* GetTextureOnWhite() const {
MOZ_ASSERT(HasComponentAlpha());
return mTextureOnWhite;
}
ContentHostTexture* GetContentHost() const {
return mHost;
}
nsIntRegion GetRenderRegion() const {
nsIntRegion region = GetShadowVisibleRegion().ToUnknownRegion();
region.AndWith(gfx::IntRect(region.GetBounds().TopLeft(), mTexture->GetSize()));
return region;
}
MOZ_LAYER_DECL_NAME("PaintedLayerMLGPU", TYPE_PAINTED)
void CleanupCachedResources();
protected:
void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
bool OnPrepareToRender(FrameBuilder* aBuilder) override;
void ComputeDrawRegion();
void CleanupResources();
private:
RefPtr<ContentHostTexture> mHost;
RefPtr<TextureSource> mTexture;
RefPtr<TextureSource> mTextureOnWhite;
gfx::IntRegion mLocalDrawRegion;
gfx::IntRegion mTextureRegion;
bool mComputedDrawRegion;
};
} // namespace layers
} // namespace mozilla
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,532 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_RENDERPASSMLGPU_H
#define MOZILLA_GFX_RENDERPASSMLGPU_H
#include "LayerManagerMLGPU.h"
#include "ShaderDefinitionsMLGPU.h"
#include "SharedBufferMLGPU.h"
#include "StagingBuffer.h"
namespace mozilla {
namespace layers {
using namespace mlg;
class RenderViewMLGPU;
enum class RenderPassType {
ClearView,
SolidColor,
SingleTexture,
RenderView,
Video,
ComponentAlpha,
Unknown
};
enum class RenderOrder
{
// Used for all items when not using a depth buffer. Otherwise, used for
// items that may draw transparent pixels.
BackToFront,
// Only used when the depth buffer is enabled, and only for items that are
// guaranteed to only draw opaque pixels.
FrontToBack
};
static const uint32_t kInvalidResourceIndex = uint32_t(-1);
struct ItemInfo {
explicit ItemInfo(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
LayerMLGPU* aLayer,
int32_t aSortOrder,
const gfx::IntRect& aBounds,
Maybe<gfx::Polygon>&& aGeometry);
// Return true if a layer can be clipped by the vertex shader; false
// otherwise. Any kind of textured mask or non-rectilinear transform
// will cause this to return false.
bool HasRectTransformAndClip() const {
return rectilinear && !layer->GetMask();
}
RenderViewMLGPU* view;
LayerMLGPU* layer;
RenderPassType type;
uint32_t layerIndex;
int32_t sortOrder;
gfx::IntRect bounds;
RenderOrder renderOrder;
Maybe<gfx::Polygon> geometry;
// Set only when the transform is a 2D integer translation.
Maybe<gfx::IntPoint> translation;
// Set when the item bounds will occlude anything below it.
bool opaque;
// Set when the item's transform is 2D and rectilinear.
bool rectilinear;
};
// Base class for anything that can render in a batch to the GPU.
class RenderPassMLGPU
{
NS_INLINE_DECL_REFCOUNTING(RenderPassMLGPU)
public:
static RenderPassType GetPreferredPassType(FrameBuilder* aBuilder,
const ItemInfo& aInfo);
static RefPtr<RenderPassMLGPU> CreatePass(FrameBuilder* aBuilder,
const ItemInfo& aInfo);
// Return true if this pass is compatible with the given item, false
// otherwise. This does not guarantee the pass will accept the item,
// but does guarantee we can try.
virtual bool IsCompatible(const ItemInfo& aItem);
virtual RenderPassType GetType() const = 0;
// Return true if the layer was compatible with and added to this pass,
// false otherwise.
bool AcceptItem(ItemInfo& aInfo);
// Prepare constants buffers and textures.
virtual void PrepareForRendering();
// Execute this render pass to the currently selected surface.
virtual void ExecuteRendering() = 0;
virtual Maybe<MLGBlendState> GetBlendState() const {
return Nothing();
}
size_t GetLayerBufferIndex() const {
return mLayerBufferIndex;
}
Maybe<uint32_t> GetMaskRectBufferIndex() const {
return mMaskRectBufferIndex == kInvalidResourceIndex
? Nothing()
: Some(mMaskRectBufferIndex);
}
// Returns true if this pass overlaps the affected region of an item. This
// only ever returns true for transparent items and transparent batches,
// and should not be used otherwise.
bool Intersects(const ItemInfo& aItem);
// Returns true if pass has been successfully prepared.
bool IsPrepared() const {
return mPrepared;
}
protected:
RenderPassMLGPU(FrameBuilder* aBuilder, const ItemInfo& aItem);
virtual ~RenderPassMLGPU();
// Return true if the item was consumed, false otherwise.
virtual bool AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo) = 0;
protected:
enum class GeometryMode {
Unknown,
UnitQuad,
Polygon
};
protected:
FrameBuilder* mBuilder;
RefPtr<MLGDevice> mDevice;
size_t mLayerBufferIndex;
size_t mMaskRectBufferIndex;
gfx::IntRegion mAffectedRegion;
bool mPrepared;
};
// Shader-based render passes execute a draw call, vs. non-shader passes that
// use non-shader APIs (like ClearView).
class ShaderRenderPass : public RenderPassMLGPU
{
public:
ShaderRenderPass(FrameBuilder* aBuilder, const ItemInfo& aItem);
// Used by ShaderDefinitions for writing traits.
VertexStagingBuffer* GetVertices() {
return &mVertices;
}
VertexStagingBuffer* GetInstances() {
return &mInstances;
}
ConstantStagingBuffer* GetItems() {
return &mItems;
}
bool IsCompatible(const ItemInfo& aItem) override;
void PrepareForRendering() override;
void ExecuteRendering() override;
virtual Maybe<MLGBlendState> GetBlendState() const override{
return Some(MLGBlendState::Over);
}
protected:
// If this batch has a uniform opacity, return it here. Otherwise this should
// return 1.0.
virtual float GetOpacity() const = 0;
// Set any components of the pipeline that won't be handled by
// ExecuteRendering. This is called only once even if multiple draw calls
// are issued.
virtual void SetupPipeline() = 0;
protected:
// Set the geometry this pass will use. This must be called by every
// derived constructor. Use GeometryMode::Unknown to pick the default
// behavior: UnitQuads for rectilinear transform+clips, and polygons
// otherwise.
void SetGeometry(const ItemInfo& aItem, GeometryMode aMode);
void SetDefaultGeometry(const ItemInfo& aItem) {
SetGeometry(aItem, GeometryMode::Unknown);
}
// Called after PrepareForRendering() has finished. If this returns false,
// PrepareForRendering() will return false.
virtual bool OnPrepareBuffers() {
return true;
}
// Prepare the buffer bound to "sItems" in shaders. This is used for opaque
// batches when the items can be drawn in front-to-back order.
bool PrepareItemBuffer();
// Prepare the vertex buffer, if not using a unit quad. This is used for
// opaque batches when the items can be drawn in front-to-back order.
bool PrepareVertexBuffer();
bool PrepareInstanceBuffer();
// Prepare the mask/opacity buffer bound in most pixel shaders.
bool SetupPSBuffer0(float aOpacity);
bool HasMask() const {
return !!mMask;
}
MaskOperation* GetMask() const {
return mMask;
}
protected:
GeometryMode mGeometry;
RefPtr<MaskOperation> mMask;
bool mHasRectTransformAndClip;
VertexStagingBuffer mVertices;
VertexBufferSection mVertexBuffer;
VertexStagingBuffer mInstances;
VertexBufferSection mInstanceBuffer;
ConstantStagingBuffer mItems;
ConstantBufferSection mItemBuffer;
ConstantBufferSection mPSBuffer0;
};
// This contains various helper functions for building vertices and shader
// inputs for layers.
template <typename Traits>
class BatchRenderPass : public ShaderRenderPass
{
public:
BatchRenderPass(FrameBuilder* aBuilder, const ItemInfo& aItem)
: ShaderRenderPass(aBuilder, aItem)
{}
protected:
// It is tricky to determine ahead of time whether or not we'll have enough
// room in our buffers to hold all draw commands for a layer, especially
// since layers can have multiple draw rects. We don't want to draw one rect,
// reject the item, then redraw the same rect again in another batch.
// To deal with this we use a transaction approach and reject the transaction
// if we couldn't add everything.
class Txn {
public:
explicit Txn(BatchRenderPass* aPass)
: mPass(aPass),
mPrevVertexPos(aPass->mVertices.GetPosition()),
mPrevItemPos(aPass->mItems.GetPosition()),
mPrevInstancePos(aPass->mInstances.GetPosition())
{}
// Add an item based on a draw rect, layer, and optional geometry. The Traits
// must contain, at minimum:
//
// - An "mRect" member as a gfx::Rect, containing the draw rect.
// - An "AddInstanceTo" method, which adds instance data for
// shaders using unit-quad vertices.
// - An "AddVerticesTo" method, which adds triangle list vertices
// to a batch's shader data, with optional geometry.
// - An "AddItemTo" method, which adds constant buffer data if
// needed.
//
bool Add(const Traits& aTraits, const ItemInfo& aInfo) {
// If this succeeds, but we clip the polygon below, that's okay.
// Polygons do not use instanced rendering so this won't break
// ordering.
if (!aTraits.AddItemTo(mPass)) {
return false;
}
if (mPass->mGeometry == GeometryMode::Polygon) {
size_t itemIndex = mPass->GetItems()->NumItems() - 1;
if (aInfo.geometry) {
gfx::Polygon polygon = aInfo.geometry->ClipPolygon(aTraits.mRect);
if (polygon.IsEmpty()) {
return true;
}
return aTraits.AddVerticesTo(mPass, aInfo, itemIndex, &polygon);
}
return aTraits.AddVerticesTo(mPass, aInfo, itemIndex);
}
return aTraits.AddInstanceTo(mPass, aInfo);
}
bool Fail() {
MOZ_ASSERT(!mStatus.isSome() || !mStatus.value());
mStatus = Some(false);
return false;
}
bool Commit() {
MOZ_ASSERT(!mStatus.isSome() || !mStatus.value());
if (mStatus.isSome()) {
return false;
}
mStatus = Some(true);
return true;
}
~Txn() {
if (!mStatus.isSome() || !mStatus.value()) {
mPass->mVertices.RestorePosition(mPrevVertexPos);
mPass->mInstances.RestorePosition(mPrevInstancePos);
mPass->mItems.RestorePosition(mPrevItemPos);
}
}
private:
BatchRenderPass* mPass;
VertexStagingBuffer::Position mPrevVertexPos;
VertexStagingBuffer::Position mPrevItemPos;
ConstantStagingBuffer::Position mPrevInstancePos;
Maybe<bool> mStatus;
};
};
// Shaders which sample from a texture should inherit from this.
class TexturedRenderPass : public BatchRenderPass<TexturedTraits>
{
public:
explicit TexturedRenderPass(FrameBuilder* aBuilder, const ItemInfo& aItem);
protected:
// Add a set of draw rects based on a visible region. The texture size and
// scaling factor are used to compute uv-coordinates.
//
// The origin is the offset from the draw rect to the layer bounds. You can
// also think of it as the translation from layer space into texture space,
// pre-scaling. For example, ImageLayers use the texture bounds as their
// draw rect, so the origin will be (0, 0). ContainerLayer intermediate
// surfaces, on the other hand, are relative to the target offset of the
// layer. In all cases the visible region may be partially occluded, so
// knowing the true origin is important.
bool AddItems(Txn& aTxn,
const ItemInfo& aInfo,
const nsIntRegion& aDrawRects,
const gfx::IntPoint& aDestOrigin,
const gfx::IntSize& aTextureSize,
const Maybe<gfx::Size>& aScale = Nothing())
{
gfx::Point origin(aDestOrigin);
for (auto iter = aDrawRects.RectIter(); !iter.Done(); iter.Next()) {
gfx::Rect drawRect = gfx::Rect(iter.Get());
if (!AddItem(aTxn, aInfo, drawRect, origin, aTextureSize, aScale)) {
return false;
}
}
return true;
}
private:
// Add a draw instance to the given destination rect. Texture coordinates
// are built from the given texture size, optional scaling factor, and
// texture origin relative to the draw rect. This will ultimately call
// AddClippedItem, potentially clipping the draw rect if needed.
bool AddItem(Txn& aTxn,
const ItemInfo& aInfo,
const gfx::Rect& aDrawRect,
const gfx::Point& aDestOrigin,
const gfx::IntSize& aTextureSize,
const Maybe<gfx::Size>& aTextureScale = Nothing());
// Add an item that has gone through any necessary clipping already. This
// is the final destination for handling textured items.
bool AddClippedItem(Txn& aTxn,
const ItemInfo& aInfo,
const gfx::Rect& aDrawRect,
const gfx::Point& aDestOrigin,
const gfx::IntSize& aTextureSize,
const Maybe<gfx::Size>& aScale);
protected:
TextureFlags mTextureFlags;
};
// This is only available when MLGDevice::CanUseClearView returns true.
class ClearViewPass final : public RenderPassMLGPU
{
public:
ClearViewPass(FrameBuilder* aBuilder, const ItemInfo& aItem);
bool IsCompatible(const ItemInfo& aItem) override;
void ExecuteRendering() override;
RenderPassType GetType() const override {
return RenderPassType::ClearView;
}
private:
bool AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo) override;
private:
// Note: Not a RefPtr since this would create a cycle.
RenderViewMLGPU* mView;
gfx::Color mColor;
nsTArray<gfx::IntRect> mRects;
};
// SolidColorPass is used when ClearViewPass is not available, or when
// the layer has masks, or subpixel or complex transforms.
class SolidColorPass final : public BatchRenderPass<ColorTraits>
{
public:
explicit SolidColorPass(FrameBuilder* aBuilder, const ItemInfo& aItem);
RenderPassType GetType() const override {
return RenderPassType::SolidColor;
}
private:
bool AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo) override;
void SetupPipeline() override;
float GetOpacity() const override;
};
class SingleTexturePass final : public TexturedRenderPass
{
public:
explicit SingleTexturePass(FrameBuilder* aBuilder, const ItemInfo& aItem);
RenderPassType GetType() const override {
return RenderPassType::SingleTexture;
}
private:
bool AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo) override;
void SetupPipeline() override;
float GetOpacity() const override {
return mOpacity;
}
Maybe<MLGBlendState> GetBlendState() const override;
private:
RefPtr<TextureSource> mTexture;
gfx::SamplingFilter mFilter;
float mOpacity;
};
class ComponentAlphaPass final : public TexturedRenderPass
{
public:
explicit ComponentAlphaPass(FrameBuilder* aBuilder, const ItemInfo& aItem);
RenderPassType GetType() const override {
return RenderPassType::ComponentAlpha;
}
private:
bool AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo) override;
void SetupPipeline() override;
float GetOpacity() const override;
Maybe<MLGBlendState> GetBlendState() const override {
return Some(MLGBlendState::ComponentAlpha);
}
private:
PaintedLayerMLGPU* mAssignedLayer;
RefPtr<TextureSource> mTextureOnBlack;
RefPtr<TextureSource> mTextureOnWhite;
};
class VideoRenderPass final : public TexturedRenderPass
{
public:
explicit VideoRenderPass(FrameBuilder* aBuilder, const ItemInfo& aItem);
RenderPassType GetType() const override {
return RenderPassType::Video;
}
private:
bool AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo) override;
void SetupPipeline() override;
float GetOpacity() const override {
return mOpacity;
}
private:
RefPtr<TextureHost> mHost;
RefPtr<TextureSource> mTexture;
gfx::SamplingFilter mFilter;
float mOpacity;
};
class RenderViewPass final : public TexturedRenderPass
{
public:
RenderViewPass(FrameBuilder* aBuilder, const ItemInfo& aItem);
RenderPassType GetType() const override {
return RenderPassType::RenderView;
}
private:
bool AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo) override;
void SetupPipeline() override;
bool OnPrepareBuffers() override;
float GetOpacity() const override;
bool PrepareBlendState();
private:
ConstantBufferSection mBlendConstants;
ContainerLayerMLGPU* mAssignedLayer;
RefPtr<MLGRenderTarget> mSource;
// Note: we don't use RefPtr here since that would cause a cycle. RenderViews
// and RenderPasses are both scoped to the frame anyway.
RenderViewMLGPU* mParentView;
gfx::IntRect mBackdropCopyRect;
Maybe<gfx::CompositionOp> mBlendMode;
};
} // namespace layers
} // namespace mozilla
#endif

View File

@ -0,0 +1,549 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RenderViewMLGPU.h"
#include "ContainerLayerMLGPU.h"
#include "FrameBuilder.h"
#include "gfxPrefs.h"
#include "LayersHelpers.h"
#include "LayersLogging.h"
#include "MLGDevice.h"
#include "RenderPassMLGPU.h"
#include "ShaderDefinitionsMLGPU.h"
#include "UtilityMLGPU.h"
namespace mozilla {
namespace layers {
using namespace gfx;
RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder,
MLGRenderTarget* aTarget,
const nsIntRegion& aInvalidRegion)
: RenderViewMLGPU(aBuilder, nullptr)
{
mTarget = aTarget;
mInvalidBounds = aInvalidRegion.GetBounds();
AL_LOG("RenderView %p root with invalid area %s\n",
this,
Stringify(mInvalidBounds).c_str());
}
RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder,
ContainerLayerMLGPU* aContainer,
RenderViewMLGPU* aParent)
: RenderViewMLGPU(aBuilder, aParent)
{
mContainer = aContainer;
mTargetOffset = aContainer->GetTargetOffset();
mInvalidBounds = aContainer->GetInvalidRect();
MOZ_ASSERT(!mInvalidBounds.IsEmpty());
AL_LOG("RenderView %p starting with container %p and invalid area %s\n",
this,
aContainer->GetLayer(),
Stringify(mInvalidBounds).c_str());
}
RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder, RenderViewMLGPU* aParent)
: mBuilder(aBuilder),
mDevice(aBuilder->GetDevice()),
mParent(aParent),
mContainer(nullptr),
mFinishedBuilding(false),
mCurrentLayerBufferIndex(kInvalidResourceIndex),
mCurrentMaskRectBufferIndex(kInvalidResourceIndex),
mNextSortIndex(1),
mUseDepthBuffer(gfxPrefs::AdvancedLayersEnableDepthBuffer()),
mDepthBufferNeedsClear(false)
{
if (aParent) {
aParent->AddChild(this);
}
}
RenderViewMLGPU::~RenderViewMLGPU()
{
for (const auto& child : mChildren) {
child->mParent = nullptr;
}
}
IntSize
RenderViewMLGPU::GetSize() const
{
MOZ_ASSERT(mFinishedBuilding);
return mTarget->GetSize();
}
MLGRenderTarget*
RenderViewMLGPU::GetRenderTarget() const
{
MOZ_ASSERT(mFinishedBuilding);
return mTarget;
}
void
RenderViewMLGPU::AddChild(RenderViewMLGPU* aParent)
{
mChildren.push_back(aParent);
}
void
RenderViewMLGPU::Render()
{
// We render tiles front-to-back, depth-first, to minimize render target switching.
for (const auto& child : mChildren) {
child->Render();
}
ExecuteRendering();
}
void
RenderViewMLGPU::FinishBuilding()
{
MOZ_ASSERT(!mFinishedBuilding);
mFinishedBuilding = true;
if (mContainer) {
MOZ_ASSERT(!mTarget);
MLGRenderTargetFlags flags = MLGRenderTargetFlags::Default;
if (mUseDepthBuffer) {
flags |= MLGRenderTargetFlags::ZBuffer;
}
mTarget = mContainer->UpdateRenderTarget(mDevice, flags);
}
}
void
RenderViewMLGPU::AddItem(LayerMLGPU* aItem,
const IntRect& aRect,
Maybe<Polygon>&& aGeometry)
{
AL_LOG("RenderView %p analyzing layer %p\n", this, aItem->GetLayer());
// If the item is not visible at all, skip it.
if (aItem->GetComputedOpacity() == 0.0f) {
AL_LOG("RenderView %p culling item %p with no opacity\n",
this,
aItem->GetLayer());
return;
}
// When using the depth buffer, the z-index for items is important.
//
// Sort order starts at 1 and goes to positive infinity, with smaller values
// being closer to the screen. Our viewport is the same, with anything
// outside of [0.0, 1.0] being culled, and lower values occluding higher
// values. To make this work our projection transform scales the z-axis.
// Note that we do not use 0 as a sorting index (when depth-testing is
// enabled) because this would result in a z-value of 1.0, which would be
// culled.
ItemInfo info(mBuilder, this, aItem, mNextSortIndex++, aRect, Move(aGeometry));
// If the item is not visible, or we can't add it to the layer constant
// buffer for some reason, bail out.
if (!UpdateVisibleRegion(info) || !mBuilder->AddLayerToConstantBuffer(info)) {
AL_LOG("RenderView %p culled item %p!\n", this, aItem->GetLayer());
return;
}
// We support all layer types now.
MOZ_ASSERT(info.type != RenderPassType::Unknown);
if (info.renderOrder == RenderOrder::FrontToBack) {
AddItemFrontToBack(aItem, info);
} else {
AddItemBackToFront(aItem, info);
}
}
bool
RenderViewMLGPU::UpdateVisibleRegion(ItemInfo& aItem)
{
// If the item has some kind of complex transform, we perform a very
// simple occlusion test and move on. We using a depth buffer we skip
// CPU-based occlusion culling as well, since the GPU will do most of our
// culling work for us.
if (mUseDepthBuffer ||
!aItem.translation ||
!gfxPrefs::AdvancedLayersEnableCPUOcclusion())
{
// Update the render region even if we won't compute visibility, since some
// layer types (like Canvas and Image) need to have the visible region
// clamped.
LayerIntRegion region = Move(aItem.layer->GetShadowVisibleRegion());
aItem.layer->SetRegionToRender(Move(region));
AL_LOG("RenderView %p simple occlusion test, bounds=%s, translation?=%d\n",
this,
Stringify(aItem.bounds).c_str(),
aItem.translation ? 1 : 0);
return mInvalidBounds.Intersects(aItem.bounds);
}
MOZ_ASSERT(aItem.rectilinear);
AL_LOG("RenderView %p starting visibility tests:\n", this);
AL_LOG(" occluded=%s\n", Stringify(mOccludedRegion).c_str());
// Compute the translation into render target space.
LayerIntPoint translation =
LayerIntPoint::FromUnknownPoint(aItem.translation.value() - mTargetOffset);
AL_LOG(" translation=%s\n", Stringify(translation).c_str());
IntRect clip = aItem.layer->GetComputedClipRect().ToUnknownRect();
AL_LOG(" clip=%s\n", Stringify(translation).c_str());
LayerIntRegion region = Move(aItem.layer->GetShadowVisibleRegion());
region.MoveBy(translation);
AL_LOG(" effective-visible=%s\n", Stringify(region).c_str());
region.SubOut(mOccludedRegion);
region.AndWith(LayerIntRect::FromUnknownRect(mInvalidBounds));
region.AndWith(LayerIntRect::FromUnknownRect(clip));
if (region.IsEmpty()) {
return false;
}
// Move the visible region back into layer space.
region.MoveBy(-translation);
AL_LOG(" new-local-visible=%s\n", Stringify(region).c_str());
aItem.layer->SetRegionToRender(Move(region));
// Apply the new occluded area. We do another dance with the translation to
// avoid copying the region. We do this after the SetRegionToRender call to
// accomodate the possiblity of a layer changing its visible region.
if (aItem.opaque) {
mOccludedRegion.MoveBy(-translation);
mOccludedRegion.OrWith(aItem.layer->GetShadowVisibleRegion());
mOccludedRegion.MoveBy(translation);
AL_LOG(" new-occluded=%s\n", Stringify(mOccludedRegion).c_str());
// If the occluded region gets too complicated, we reset it.
if (mOccludedRegion.GetNumRects() >= 32) {
mOccludedRegion.SetEmpty();
AL_LOG(" clear-occluded, too many rects\n");
}
}
return true;
}
void
RenderViewMLGPU::AddItemFrontToBack(LayerMLGPU* aLayer, ItemInfo& aItem)
{
// We receive items in front-to-back order. Ideally we want to push items
// as far back into batches impossible, to ensure the GPU can do a good
// job at culling. However we also want to make sure we actually batch
// items versus drawing one primitive per pass.
//
// As a compromise we look at the most 3 recent batches and then give up.
// This can be tweaked in the future.
static const size_t kMaxSearch = 3;
size_t iterations = 0;
for (auto iter = mFrontToBack.rbegin(); iter != mFrontToBack.rend(); iter++) {
RenderPassMLGPU* pass = (*iter);
if (pass->IsCompatible(aItem) && pass->AcceptItem(aItem)) {
AL_LOG("RenderView %p added layer %p to pass %p (%d)\n",
this, aLayer->GetLayer(), pass, int(pass->GetType()));
return;
}
if (++iterations > kMaxSearch) {
break;
}
}
RefPtr<RenderPassMLGPU> pass = RenderPassMLGPU::CreatePass(mBuilder, aItem);
if (!pass || !pass->AcceptItem(aItem)) {
MOZ_ASSERT_UNREACHABLE("Could not build a pass for item!");
return;
}
AL_LOG("RenderView %p added layer %p to new pass %p (%d)\n",
this, aLayer->GetLayer(), pass.get(), int(pass->GetType()));
mFrontToBack.push_back(pass);
}
void
RenderViewMLGPU::AddItemBackToFront(LayerMLGPU* aLayer, ItemInfo& aItem)
{
// We receive layers in front-to-back order, but there are two cases when we
// actually draw back-to-front: when the depth buffer is disabled, or when
// using the depth buffer and the item has transparent pixels (and therefore
// requires blending). In these cases we will build vertex and constant
// buffers in reverse, as well as execute batches in reverse, to ensure the
// correct ordering.
//
// Note: We limit the number of batches we search through, since it's better
// to add new draw calls than spend too much time finding compatible
// batches further down.
static const size_t kMaxSearch = 10;
size_t iterations = 0;
for (auto iter = mBackToFront.begin(); iter != mBackToFront.end(); iter++) {
RenderPassMLGPU* pass = (*iter);
if (pass->IsCompatible(aItem) && pass->AcceptItem(aItem)) {
AL_LOG("RenderView %p added layer %p to pass %p (%d)\n",
this, aLayer->GetLayer(), pass, int(pass->GetType()));
return;
}
if (pass->Intersects(aItem)) {
break;
}
if (++iterations > kMaxSearch) {
break;
}
}
RefPtr<RenderPassMLGPU> pass = RenderPassMLGPU::CreatePass(mBuilder, aItem);
if (!pass || !pass->AcceptItem(aItem)) {
MOZ_ASSERT_UNREACHABLE("Could not build a pass for item!");
return;
}
AL_LOG("RenderView %p added layer %p to new pass %p (%d)\n",
this, aLayer->GetLayer(), pass.get(), int(pass->GetType()));
mBackToFront.push_front(pass);
}
void
RenderViewMLGPU::Prepare()
{
if (!mTarget) {
return;
}
// Prepare front-to-back passes. These are only present when using the depth
// buffer, and they contain only opaque data.
for (RefPtr<RenderPassMLGPU>& pass : mFrontToBack) {
pass->PrepareForRendering();
}
// Prepare the Clear buffer, which will fill the render target with transparent
// pixels. This must happen before we set up world constants, since it can
// create new z-indices.
PrepareClear();
// Prepare the world constant buffer. This must be called after we've
// finished allocating all z-indices.
{
WorldConstants vsConstants;
Matrix4x4 projection = Matrix4x4::Translation(-1.0, 1.0, 0.0);
projection.PreScale(2.0 / float(mTarget->GetSize().width),
2.0 / float(mTarget->GetSize().height),
1.0f);
projection.PreScale(1.0f, -1.0f, 1.0f);
memcpy(vsConstants.projection, &projection._11, 64);
vsConstants.targetOffset = Point(mTargetOffset);
vsConstants.sortIndexOffset = PrepareDepthBuffer();
SharedConstantBuffer* shared = mDevice->GetSharedVSBuffer();
if (!shared->Allocate(&mWorldConstants, vsConstants)) {
return;
}
}
// Prepare back-to-front passes. In depth buffer mode, these contain draw
// calls that might produce transparent pixels. When using CPU-based occlusion
// culling, all draw calls are back-to-front.
for (RefPtr<RenderPassMLGPU>& pass : mBackToFront) {
pass->PrepareForRendering();
}
// Now, process children.
for (const auto& iter : mChildren) {
iter->Prepare();
}
}
void
RenderViewMLGPU::ExecuteRendering()
{
if (!mTarget) {
return;
}
mDevice->SetRenderTarget(mTarget);
mDevice->SetViewport(IntRect(IntPoint(0, 0), mTarget->GetSize()));
mDevice->SetScissorRect(Some(mInvalidBounds));
if (!mWorldConstants.IsValid()) {
gfxWarning() << "Failed to allocate constant buffer for world transform";
return;
}
mDevice->SetVSConstantBuffer(kWorldConstantBufferSlot, &mWorldConstants);
// If using the depth buffer, clear it (if needed) and enable writes.
if (mUseDepthBuffer) {
if (mDepthBufferNeedsClear) {
mDevice->ClearDepthBuffer(mTarget);
}
mDevice->SetDepthTestMode(MLGDepthTestMode::Write);
}
// Opaque items, rendered front-to-back.
for (auto iter = mFrontToBack.begin(); iter != mFrontToBack.end(); iter++) {
ExecutePass(*iter);
}
if (mUseDepthBuffer) {
// From now on we might be rendering transparent pixels, so we disable
// writing to the z-buffer.
mDevice->SetDepthTestMode(MLGDepthTestMode::ReadOnly);
}
// Clear any pixels that are not occluded, and therefore might require
// blending.
DrawClear();
// Render back-to-front passes.
for (auto iter = mBackToFront.begin(); iter != mBackToFront.end(); iter++) {
ExecutePass(*iter);
}
// We repaint the entire invalid region, even if it is partially occluded.
// Thus it's safe for us to clear the invalid area here. If we ever switch
// to nsIntRegions, we will have to take the difference between the paitned
// area and the invalid area.
if (mContainer) {
mContainer->ClearInvalidRect();
}
}
void
RenderViewMLGPU::ExecutePass(RenderPassMLGPU* aPass)
{
if (!aPass->IsPrepared()) {
return;
}
// Change the layer buffer if needed.
if (aPass->GetLayerBufferIndex() != mCurrentLayerBufferIndex) {
mCurrentLayerBufferIndex = aPass->GetLayerBufferIndex();
ConstantBufferSection section = mBuilder->GetLayerBufferByIndex(mCurrentLayerBufferIndex);
mDevice->SetVSConstantBuffer(kLayerBufferSlot, &section);
}
// Change the mask rect buffer if needed.
if (aPass->GetMaskRectBufferIndex() &&
aPass->GetMaskRectBufferIndex().value() != mCurrentMaskRectBufferIndex)
{
mCurrentMaskRectBufferIndex = aPass->GetMaskRectBufferIndex().value();
ConstantBufferSection section = mBuilder->GetMaskRectBufferByIndex(mCurrentMaskRectBufferIndex);
mDevice->SetVSConstantBuffer(kMaskBufferSlot, &section);
}
// Change the blend state if needed.
if (Maybe<MLGBlendState> blendState = aPass->GetBlendState()) {
mDevice->SetBlendState(blendState.value());
}
aPass->ExecuteRendering();
}
int32_t
RenderViewMLGPU::PrepareDepthBuffer()
{
if (!mUseDepthBuffer) {
return 0;
}
// Rather than clear the depth buffer every frame, we offset z-indices each
// frame, starting with indices far away from the screen and moving toward
// the user each successive frame. This ensures that frames can re-use the
// depth buffer but never collide with previously written values.
//
// Once a frame runs out of sort indices, we finally clear the depth buffer
// and start over again.
// Note: the lowest sort index (kDepthLimit) is always occluded since it will
// resolve to the clear value - kDepthLimit / kDepthLimit == 1.0.
//
// If we don't have any more indices to allocate, we need to clear the depth
// buffer and start fresh.
int32_t highestIndex = mTarget->GetLastDepthStart();
if (highestIndex < mNextSortIndex) {
mDepthBufferNeedsClear = true;
highestIndex = kDepthLimit;
}
// We should not have more than kDepthLimit layers to draw. The last known
// sort index might appear in the depth buffer and occlude something, so
// we subtract 1. This ensures all our indices will compare less than all
// old indices.
int32_t sortOffset = highestIndex - mNextSortIndex - 1;
MOZ_ASSERT(sortOffset >= 0);
mTarget->SetLastDepthStart(sortOffset);
return sortOffset;
}
void
RenderViewMLGPU::PrepareClear()
{
// Get the list of rects to clear. If using the depth buffer, we don't
// care if it's accurate since the GPU will do occlusion testing for us.
// If not using the depth buffer, we subtract out the occluded region.
LayerIntRegion region = LayerIntRect::FromUnknownRect(mInvalidBounds);
if (!mUseDepthBuffer) {
// Don't let the clear region become too complicated.
region.SubOut(mOccludedRegion);
region.SimplifyOutward(kMaxClearViewRects);
}
for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
mClearRects.AppendElement(iter.Get().ToUnknownRect());
}
// If ClearView is supported and we're not using a depth buffer, we
// can stop here. We'll use the rects as inputs to ClearView.
if (mClearRects.IsEmpty() || (mDevice->CanUseClearView() && !mUseDepthBuffer)) {
return;
}
// Set up vertices for a shader-based clear.
mDevice->GetSharedVertexBuffer()->Allocate(
&mClearInput,
mClearRects.Length(),
sizeof(IntRect),
mClearRects.Elements());
mClearRects.Clear();
// Note that we use the lowest available sorting index, to ensure that when
// using the z-buffer, we don't draw over already-drawn content.
ClearConstants consts(mNextSortIndex++);
mDevice->GetSharedVSBuffer()->Allocate(&mClearConstants, consts);
}
void
RenderViewMLGPU::DrawClear()
{
// If we've set up vertices for a shader-based clear, execute that now.
if (mClearInput.IsValid()) {
mDevice->SetTopology(MLGPrimitiveTopology::UnitQuad);
mDevice->SetVertexShader(VertexShaderID::Clear);
mDevice->SetVertexBuffer(1, &mClearInput);
mDevice->SetVSConstantBuffer(kClearConstantBufferSlot, &mClearConstants);
mDevice->SetBlendState(MLGBlendState::Copy);
mDevice->SetPixelShader(PixelShaderID::Clear);
mDevice->DrawInstanced(4, mClearInput.NumVertices(), 0, 0);
return;
}
// Otherwise, if we have a normal rect list, we wanted to use the faster
// ClearView.
if (!mClearRects.IsEmpty()) {
Color color(0.0, 0.0, 0.0, 0.0);
mDevice->ClearView(mTarget, color, mClearRects.Elements(), mClearRects.Length());
}
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,135 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_RenderViewMLGPU_h
#define mozilla_gfx_layers_mlgpu_RenderViewMLGPU_h
#include "LayerManagerMLGPU.h"
#include "RenderPassMLGPU.h"
#include "Units.h"
#include <deque>
namespace mozilla {
namespace layers {
class FrameBuilder;
class ContainerLayerMLGPU;
class MLGRenderTarget;
class RenderViewMLGPU
{
public:
NS_INLINE_DECL_REFCOUNTING(RenderViewMLGPU)
// Constructor for the widget render target.
RenderViewMLGPU(FrameBuilder* aBuilder,
MLGRenderTarget* aTarget,
const nsIntRegion& aInvalidRegion);
// Constructor for intermediate surfaces.
RenderViewMLGPU(FrameBuilder* aBuilder,
ContainerLayerMLGPU* aContainer,
RenderViewMLGPU* aParent);
void Prepare();
void Render();
void AddChild(RenderViewMLGPU* aParent);
void AddItem(LayerMLGPU* aItem,
const gfx::IntRect& aBounds,
Maybe<gfx::Polygon>&& aGeometry);
void FinishBuilding();
const gfx::IntPoint& GetTargetOffset() const {
return mTargetOffset;
}
RenderViewMLGPU* GetParent() const {
return mParent;
}
bool HasDepthBuffer() const {
return mUseDepthBuffer;
}
// The size and render target cannot be read until the view has finished
// building, since we try to right-size the render target to the visible
// region.
MLGRenderTarget* GetRenderTarget() const;
gfx::IntSize GetSize() const;
gfx::IntRect GetInvalidRect() const {
return mInvalidBounds;
}
private:
RenderViewMLGPU(FrameBuilder* aBuilder, RenderViewMLGPU* aParent);
~RenderViewMLGPU();
void ExecuteRendering();
bool UpdateVisibleRegion(ItemInfo& aItem);
void AddItemFrontToBack(LayerMLGPU* aLayer, ItemInfo& aItem);
void AddItemBackToFront(LayerMLGPU* aLayer, ItemInfo& aItem);
void PrepareClear();
void DrawClear();
void ExecutePass(RenderPassMLGPU* aPass);
// Return the sorting index offset to use.
int32_t PrepareDepthBuffer();
private:
std::deque<RefPtr<RenderPassMLGPU>> mFrontToBack;
std::deque<RefPtr<RenderPassMLGPU>> mBackToFront;
FrameBuilder* mBuilder;
RefPtr<MLGDevice> mDevice;
RenderViewMLGPU* mParent;
std::vector<RefPtr<RenderViewMLGPU>> mChildren;
// Shader data.
ConstantBufferSection mWorldConstants;
// If using ClearView-based clears.
nsTArray<gfx::IntRect> mClearRects;
// If using shader-based clears.
VertexBufferSection mClearInput;
ConstantBufferSection mClearConstants;
// Either an MLGSwapChain-derived render target, or an intermediate surface.
RefPtr<MLGRenderTarget> mTarget;
// For intermediate render targets only, this is the layer owning the render target.
ContainerLayerMLGPU* mContainer;
// The offset adjustment from container layer space to render target space.
// This is 0,0 for the root view.
gfx::IntPoint mTargetOffset;
// The invalid bounds as computed by LayerTreeInvalidation. This is the initial
// render bounds size, if invalidation is disabled.
gfx::IntRect mInvalidBounds;
// The occluded region, which is updated every time we process an opaque,
// rectangular item. This is not actually in LayerPixels, we do this to
// avoid FromUnknownRegion which has array copies.
LayerIntRegion mOccludedRegion;
// True if we've finished adding layers to the view.
bool mFinishedBuilding;
// This state is used to avoid changing buffers while we execute batches.
size_t mCurrentLayerBufferIndex;
size_t mCurrentMaskRectBufferIndex;
// Depth-buffer tracking.
int32_t mNextSortIndex;
bool mUseDepthBuffer;
bool mDepthBufferNeedsClear;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_RenderViewMLGPU_h

View File

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _include_gfx_layers_mlgpu_ShaderDefinitions_inl_h
#define _include_gfx_layers_mlgpu_ShaderDefinitions_inl_h
namespace mozilla {
namespace layers {
namespace mlg {
// This is a helper class for writing vertices for unit-quad based
// shaders, since they all share the same input layout.
struct SimpleVertex
{
SimpleVertex(const gfx::Rect& aRect,
uint32_t aLayerIndex,
int aDepth)
: rect(aRect),
layerIndex(aLayerIndex),
depth(aDepth)
{}
gfx::Rect rect;
uint32_t layerIndex;
int depth;
};
bool
SimpleTraits::AddInstanceTo(ShaderRenderPass* aPass, const ItemInfo& aItem) const
{
return aPass->GetInstances()->AddItem(SimpleVertex(
mRect, aItem.layerIndex, aItem.sortOrder));
}
inline bool
ColorTraits::AddItemTo(ShaderRenderPass* aPass) const
{
return aPass->GetItems()->AddItem(mColor);
}
inline bool
TexturedTraits::AddItemTo(ShaderRenderPass* aPass) const
{
return aPass->GetItems()->AddItem(mTexCoords);
}
} // namespace mlg
} // namespace layers
} // namespace mozilla
#endif // _include_gfx_layers_mlgpu_ShaderDefinitions_inl_h

View File

@ -0,0 +1,127 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ShaderDefinitionsMLGPU.h"
#include "RenderPassMLGPU.h"
namespace mozilla {
namespace layers {
namespace mlg {
using namespace gfx;
// Helper function for adding triangle vertices to shader buffers.
struct TriangleVertex
{
TriangleVertex(const gfx::Point& aPoint,
const ItemInfo& aItem,
uint32_t aItemIndex)
: point(aPoint),
layerIndex(aItem.layerIndex),
depth(aItem.sortOrder),
itemIndex(aItemIndex)
{}
gfx::Point point;
uint32_t layerIndex;
int depth;
uint32_t itemIndex;
};
bool
SimpleTraits::AddVerticesTo(ShaderRenderPass* aPass,
const ItemInfo& aItem,
uint32_t aItemIndex,
const gfx::Polygon* aGeometry) const
{
VertexStagingBuffer* vertices = aPass->GetVertices();
// If we don't have geometry, take a fast path where we can hardcode
// the set of triangles.
if (!aGeometry) {
if (!vertices->PrependItem(TriangleVertex(mRect.BottomLeft(), aItem, aItemIndex)) ||
!vertices->PrependItem(TriangleVertex(mRect.TopLeft(), aItem, aItemIndex)) ||
!vertices->PrependItem(TriangleVertex(mRect.TopRight(), aItem, aItemIndex)) ||
!vertices->PrependItem(TriangleVertex(mRect.TopRight(), aItem, aItemIndex)) ||
!vertices->PrependItem(TriangleVertex(mRect.BottomRight(), aItem, aItemIndex)) ||
!vertices->PrependItem(TriangleVertex(mRect.BottomLeft(), aItem, aItemIndex)))
{
return false;
}
return true;
}
// Slow path: full-fledged geometry.
nsTArray<Triangle> triangles = aGeometry->ToTriangles();
for (const Triangle& t : triangles) {
if (!vertices->PrependItem(TriangleVertex(t.p1, aItem, aItemIndex)) ||
!vertices->PrependItem(TriangleVertex(t.p2, aItem, aItemIndex)) ||
!vertices->PrependItem(TriangleVertex(t.p3, aItem, aItemIndex)))
{
return false;
}
}
return true;
}
struct TexturedTriangleVertex
{
TexturedTriangleVertex(const gfx::Point& aPoint,
const gfx::Point& aTexCoord,
const ItemInfo& aItem)
: point(aPoint),
texCoord(aTexCoord),
layerIndex(aItem.layerIndex),
depth(aItem.sortOrder)
{}
gfx::Point point;
gfx::Point texCoord;
uint32_t layerIndex;
int depth;
};
bool
TexturedTraits::AddVerticesTo(ShaderRenderPass* aPass,
const ItemInfo& aItem,
uint32_t aItemIndex,
const gfx::Polygon* aGeometry) const
{
VertexStagingBuffer* vertices = aPass->GetVertices();
using Vertex = TexturedTriangleVertex;
// If we don't have geometry, take a fast path where we can hardcode
// the set of triangles.
if (!aGeometry) {
if (!vertices->PrependItem(Vertex(mRect.BottomLeft(), mTexCoords.BottomLeft(), aItem)) ||
!vertices->PrependItem(Vertex(mRect.TopLeft(), mTexCoords.TopLeft(), aItem)) ||
!vertices->PrependItem(Vertex(mRect.TopRight(), mTexCoords.TopRight(), aItem)) ||
!vertices->PrependItem(Vertex(mRect.TopRight(), mTexCoords.TopRight(), aItem)) ||
!vertices->PrependItem(Vertex(mRect.BottomRight(), mTexCoords.BottomRight(), aItem)) ||
!vertices->PrependItem(Vertex(mRect.BottomLeft(), mTexCoords.BottomLeft(), aItem)))
{
return false;
}
return true;
}
// Slow path: full-fledged geometry.
nsTArray<TexturedTriangle> triangles =
GenerateTexturedTriangles(*aGeometry, mRect, mTexCoords);
for (const TexturedTriangle& t: triangles) {
if (!vertices->PrependItem(Vertex(t.p1, t.textureCoords.p1, aItem)) ||
!vertices->PrependItem(Vertex(t.p2, t.textureCoords.p2, aItem)) ||
!vertices->PrependItem(Vertex(t.p3, t.textureCoords.p3, aItem)))
{
return false;
}
}
return true;
}
} // namespace mlg
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,144 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_SHADERDEFINITIONSMLGPU_H
#define MOZILLA_GFX_SHADERDEFINITIONSMLGPU_H
#include "mozilla/gfx/Point.h"
#include "mozilla/gfx/Triangle.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/layers/LayersHelpers.h"
#include "nsTArray.h"
namespace mozilla {
namespace layers {
struct ItemInfo;
class ShaderRenderPass;
namespace mlg {
// These may need to move into run-time values determined by MLGDevice.
static const size_t kConstantBufferElementSize = 16;
static const size_t kMaxConstantBufferSize = 4096 * kConstantBufferElementSize;
// Vertex shader slots. We reverse the first two slots across all shaders,
// and the first three slots free across all RenderPass shaders, for
// uniformity.
static const uint32_t kWorldConstantBufferSlot = 0;
static const uint32_t kLayerBufferSlot = 1;
static const uint32_t kItemBufferSlot = 2;
static const uint32_t kMaskBufferSlot = 3;
static const uint32_t kBlendConstantBufferSlot = 4;
static const uint32_t kClearConstantBufferSlot = 2;
// This is specified in common-ps.hlsl.
static const uint32_t kMaskLayerTextureSlot = 4;
static const uint32_t kDefaultSamplerSlot = 0;
static const uint32_t kMaskSamplerSlot = 1;
// These are the maximum slot numbers we bind. We assert that no binding
// happens above the max slot, since we try to clear buffer bindings at
// the end of each frame.
static const uint32_t kMaxVertexShaderConstantBuffers = 5;
static const uint32_t kMaxPixelShaderConstantBuffers = 2;
// Maximum depth in the depth buffer. This must match common-vs.hlsl.
static const int32_t kDepthLimit = 1000000;
struct WorldConstants
{
float projection[4][4];
gfx::Point targetOffset;
int sortIndexOffset;
float padding;
};
struct ClearConstants
{
explicit ClearConstants(int aDepth) : depth(aDepth)
{}
int depth;
int padding[3];
};
struct LayerConstants
{
float transform[4][4];
gfx::Rect clipRect;
uint32_t maskIndex;
uint32_t padding[3];
};
struct MaskCombineInput
{
float texCoords[4];
};
struct MaskInformation
{
MaskInformation(float aOpacity, bool aHasMask)
: opacity(aOpacity),
hasMask(aHasMask ? 1 : 0)
{}
float opacity;
uint32_t hasMask;
uint32_t padding[2];
};
struct YCbCrShaderConstants {
float yuvColorMatrix[3][4];
};
struct BlendVertexShaderConstants {
float backdropTransform[4][4];
};
struct SimpleTraits
{
explicit SimpleTraits(const gfx::Rect& aRect)
: mRect(aRect)
{}
bool AddInstanceTo(ShaderRenderPass* aPass, const ItemInfo& aItem) const;
bool AddVerticesTo(ShaderRenderPass* aPass,
const ItemInfo& aItem,
uint32_t aItemIndex,
const gfx::Polygon* aGeometry = nullptr) const;
gfx::Rect mRect;
};
struct ColorTraits : public SimpleTraits
{
ColorTraits(const gfx::Rect& aRect, const gfx::Color& aColor)
: SimpleTraits(aRect), mColor(aColor)
{}
bool AddItemTo(ShaderRenderPass* aPass) const;
gfx::Color mColor;
};
struct TexturedTraits : public SimpleTraits
{
TexturedTraits(const gfx::Rect& aRect, const gfx::Rect& aTexCoords)
: SimpleTraits(aRect), mTexCoords(aTexCoords)
{}
bool AddVerticesTo(ShaderRenderPass* aPass,
const ItemInfo& aItem,
uint32_t aItemIndex,
const gfx::Polygon* aGeometry = nullptr) const;
bool AddItemTo(ShaderRenderPass* aPass) const;
gfx::Rect mTexCoords;
};
} // namespace mlg
} // namespace layers
} // namespace mozilla
#endif

View File

@ -0,0 +1,204 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SharedBufferMLGPU.h"
#include "BufferCache.h"
#include "MLGDevice.h"
using namespace std;
namespace mozilla {
namespace layers {
SharedBufferMLGPU::SharedBufferMLGPU(MLGDevice* aDevice, MLGBufferType aType, size_t aDefaultSize)
: mDevice(aDevice),
mType(aType),
mDefaultSize(aDefaultSize),
mCanUseOffsetAllocation(true),
mCurrentPosition(0),
mMaxSize(0),
mMapped(false),
mBytesUsedThisFrame(0),
mNumSmallFrames(0)
{
MOZ_COUNT_CTOR(SharedBufferMLGPU);
}
SharedBufferMLGPU::~SharedBufferMLGPU()
{
MOZ_COUNT_DTOR(SharedBufferMLGPU);
Unmap();
}
bool
SharedBufferMLGPU::Init()
{
// If we can't use buffer offset binding, we never allocated shared buffers.
if (!mCanUseOffsetAllocation) {
return true;
}
// If we can use offset binding, allocate an initial shared buffer now.
if (!GrowBuffer(mDefaultSize)) {
return false;
}
return true;
}
void
SharedBufferMLGPU::Reset()
{
// We shouldn't be mapped here, but just in case, unmap now.
Unmap();
mBytesUsedThisFrame = 0;
// If we allocated a large buffer for a particularly heavy layer tree,
// but have not used most of the buffer again for many frames, we
// discard the buffer. This is to prevent having to perform large
// pointless uploads after visiting a single havy page - it also
// lessens ping-ponging between large and small buffers.
if (mBuffer &&
(mBuffer->GetSize() > mDefaultSize * 4) &&
mNumSmallFrames >= 10)
{
mBuffer = nullptr;
}
// Note that we do not aggressively map a new buffer. There's no reason to,
// and it'd cause unnecessary uploads when painting empty frames.
}
bool
SharedBufferMLGPU::EnsureMappedBuffer(size_t aBytes)
{
if (!mBuffer || (mMaxSize - mCurrentPosition < aBytes)) {
if (!GrowBuffer(aBytes)) {
return false;
}
}
if (!mMapped && !Map()) {
return false;
}
return true;
}
// We don't want to cache large buffers, since it results in larger uploads
// that might not be needed.
static const size_t kMaxCachedBufferSize = 128 * 1024;
bool
SharedBufferMLGPU::GrowBuffer(size_t aBytes)
{
// We only pre-allocate buffers if we can use offset allocation.
MOZ_ASSERT(mCanUseOffsetAllocation);
// Unmap the previous buffer. This will retain mBuffer, but free up the
// address space used by its mapping.
Unmap();
size_t maybeSize = mDefaultSize;
if (mBuffer) {
// Try to first grow the previous allocation size.
maybeSize = std::min(kMaxCachedBufferSize, mBuffer->GetSize() * 2);
}
size_t bytes = std::max(aBytes, maybeSize);
mBuffer = mDevice->CreateBuffer(mType, bytes, MLGUsage::Dynamic);
if (!mBuffer) {
return false;
}
mCurrentPosition = 0;
mMaxSize = mBuffer->GetSize();
return true;
}
void
SharedBufferMLGPU::PrepareForUsage()
{
Unmap();
if (mBytesUsedThisFrame <= mDefaultSize) {
mNumSmallFrames++;
} else {
mNumSmallFrames = 0;
}
}
bool
SharedBufferMLGPU::Map()
{
MOZ_ASSERT(mBuffer);
MOZ_ASSERT(!mMapped);
if (!mDevice->Map(mBuffer, MLGMapType::WRITE_DISCARD, &mMap)) {
// Don't retain the buffer, it's useless if we can't map it.
mBuffer = nullptr;
return false;
}
mCurrentPosition = 0;
mMapped = true;
return true;
}
void
SharedBufferMLGPU::Unmap()
{
if (!mMapped) {
return;
}
mBytesUsedThisFrame += mCurrentPosition;
mDevice->Unmap(mBuffer);
mMap = MLGMappedResource();
mMapped = false;
}
SharedVertexBuffer::SharedVertexBuffer(MLGDevice* aDevice, size_t aDefaultSize)
: SharedBufferMLGPU(aDevice, MLGBufferType::Vertex, aDefaultSize)
{
}
SharedConstantBuffer::SharedConstantBuffer(MLGDevice* aDevice, size_t aDefaultSize)
: SharedBufferMLGPU(aDevice, MLGBufferType::Constant, aDefaultSize)
{
mMaxConstantBufferBindSize = aDevice->GetMaxConstantBufferBindSize();
mCanUseOffsetAllocation = aDevice->CanUseConstantBufferOffsetBinding();
}
uint8_t*
SharedConstantBuffer::AllocateNewBuffer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer)
{
RefPtr<MLGBuffer> buffer;
if (BufferCache* cache = mDevice->GetConstantBufferCache()) {
buffer = cache->GetOrCreateBuffer(aBytes);
} else {
buffer = mDevice->CreateBuffer(MLGBufferType::Constant, aBytes, MLGUsage::Dynamic);
}
if (!buffer) {
return nullptr;
}
MLGMappedResource map;
if (!mDevice->Map(buffer, MLGMapType::WRITE_DISCARD, &map)) {
return nullptr;
}
// Signal that offsetting is not supported.
*aOutOffset = -1;
*aOutBuffer = buffer;
return reinterpret_cast<uint8_t*>(map.mData);
}
void
AutoBufferUploadBase::UnmapBuffer()
{
mDevice->Unmap(mBuffer);
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,372 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h
#define mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h
#include "ShaderDefinitionsMLGPU.h"
#include "MLGDeviceTypes.h"
#include "StagingBuffer.h"
#include "mozilla/gfx/Logging.h"
namespace mozilla {
namespace layers {
class MLGBuffer;
class SharedBufferMLGPU
{
public:
virtual ~SharedBufferMLGPU();
bool Init();
// Call before starting a new frame.
void Reset();
// Call to finish any pending uploads.
void PrepareForUsage();
protected:
SharedBufferMLGPU(MLGDevice* aDevice, MLGBufferType aType, size_t aDefaultSize);
bool EnsureMappedBuffer(size_t aBytes);
bool GrowBuffer(size_t aBytes);
void ForgetBuffer();
bool Map();
void Unmap();
uint8_t* GetBufferPointer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer) {
if (!EnsureMappedBuffer(aBytes)) {
return nullptr;
}
ptrdiff_t newPos = mCurrentPosition + aBytes;
MOZ_ASSERT(size_t(newPos) <= mMaxSize);
*aOutOffset = mCurrentPosition;
*aOutBuffer = mBuffer;
uint8_t* ptr = reinterpret_cast<uint8_t*>(mMap.mData) + mCurrentPosition;
mCurrentPosition = newPos;
return ptr;
}
protected:
// Note: RefPtr here would cause a cycle. Only MLGDevice should own
// SharedBufferMLGPU objects for now.
MLGDevice* mDevice;
MLGBufferType mType;
size_t mDefaultSize;
bool mCanUseOffsetAllocation;
// When |mBuffer| is non-null, mMaxSize is the buffer size. If mapped, the
// position is between 0 and mMaxSize, otherwise it is always 0.
RefPtr<MLGBuffer> mBuffer;
ptrdiff_t mCurrentPosition;
size_t mMaxSize;
MLGMappedResource mMap;
bool mMapped;
// These are used to track how many frames come in under the default
// buffer size in a row.
size_t mBytesUsedThisFrame;
size_t mNumSmallFrames;
};
class VertexBufferSection final
{
friend class SharedVertexBuffer;
public:
VertexBufferSection()
: mOffset(-1),
mNumVertices(0),
mStride(0)
{}
uint32_t Stride() const {
return mStride;
}
MLGBuffer* GetBuffer() const {
return mBuffer;
}
ptrdiff_t Offset() const {
MOZ_ASSERT(IsValid());
return mOffset;
}
size_t NumVertices() const {
return mNumVertices;
}
bool IsValid() const {
return !!mBuffer;
}
protected:
void Init(MLGBuffer* aBuffer, ptrdiff_t aOffset, size_t aNumVertices, size_t aStride) {
mBuffer = aBuffer;
mOffset = aOffset;
mNumVertices = aNumVertices;
mStride = aStride;
}
protected:
RefPtr<MLGBuffer> mBuffer;
ptrdiff_t mOffset;
size_t mNumVertices;
size_t mStride;
};
class ConstantBufferSection final
{
friend class SharedConstantBuffer;
public:
ConstantBufferSection()
: mOffset(-1)
{}
uint32_t NumConstants() const {
return NumConstantsForBytes(mNumBytes);
}
size_t NumItems() const {
return mNumItems;
}
uint32_t Offset() const {
MOZ_ASSERT(IsValid());
return mOffset / 16;
}
MLGBuffer* GetBuffer() const {
return mBuffer;
}
bool IsValid() const {
return !!mBuffer;
}
bool HasOffset() const {
return mOffset != -1;
}
protected:
static constexpr size_t NumConstantsForBytes(size_t aBytes) {
return (aBytes + ((256 - (aBytes % 256)) % 256)) / 16;
}
void Init(MLGBuffer* aBuffer, ptrdiff_t aOffset, size_t aBytes, size_t aNumItems) {
mBuffer = aBuffer;
mOffset = aOffset;
mNumBytes = aBytes;
mNumItems = aNumItems;
}
protected:
RefPtr<MLGBuffer> mBuffer;
ptrdiff_t mOffset;
size_t mNumBytes;
size_t mNumItems;
};
// Vertex buffers don't need special alignment.
typedef StagingBuffer<0> VertexStagingBuffer;
class SharedVertexBuffer final : public SharedBufferMLGPU
{
public:
SharedVertexBuffer(MLGDevice* aDevice, size_t aDefaultSize);
// Allocate a buffer that can be uploaded immediately.
bool Allocate(VertexBufferSection* aHolder, const VertexStagingBuffer& aStaging) {
return Allocate(aHolder,
aStaging.NumItems(),
aStaging.SizeOfItem(),
aStaging.GetBufferStart());
}
// Allocate a buffer that can be uploaded immediately. This is the
// direct access version, for cases where a StagingBuffer is not
// needed.
bool Allocate(VertexBufferSection* aHolder,
size_t aNumItems,
size_t aSizeOfItem,
const void* aData)
{
RefPtr<MLGBuffer> buffer;
ptrdiff_t offset;
size_t bytes = aSizeOfItem * aNumItems;
uint8_t* ptr = GetBufferPointer(bytes, &offset, &buffer);
if (!ptr) {
return false;
}
memcpy(ptr, aData, bytes);
aHolder->Init(buffer, offset, aNumItems, aSizeOfItem);
return true;
}
template <typename T>
bool Allocate(VertexBufferSection* aHolder, const T& aItem) {
return Allocate(aHolder, 1, sizeof(T), &aItem);
}
};
// To support older Direct3D versions, we need to support one-off MLGBuffers,
// where data is uploaded immediately rather than at the end of all batch
// preparation. We achieve this through a small helper class.
//
// Note: the unmap is not inline sincce we don't include MLGDevice.h.
class MOZ_STACK_CLASS AutoBufferUploadBase
{
public:
AutoBufferUploadBase() : mPtr(nullptr) {}
~AutoBufferUploadBase() {
if (mBuffer) {
UnmapBuffer();
}
}
void Init(void* aPtr) {
MOZ_ASSERT(!mPtr && aPtr);
mPtr = aPtr;
}
void Init(void* aPtr, MLGDevice* aDevice, MLGBuffer* aBuffer) {
MOZ_ASSERT(!mPtr && aPtr);
mPtr = aPtr;
mDevice = aDevice;
mBuffer = aBuffer;
}
void* get() {
return const_cast<void*>(mPtr);
}
private:
void UnmapBuffer();
protected:
RefPtr<MLGDevice> mDevice;
RefPtr<MLGBuffer> mBuffer;
void* mPtr;
};
// This is a typed helper for AutoBufferUploadBase.
template <typename T>
class AutoBufferUpload : public AutoBufferUploadBase
{
public:
AutoBufferUpload()
{}
T* operator ->() const {
return reinterpret_cast<T*>(mPtr);
}
};
class SharedConstantBuffer final : public SharedBufferMLGPU
{
public:
SharedConstantBuffer(MLGDevice* aDevice, size_t aDefaultSize);
// Allocate a buffer that can be immediately uploaded.
bool Allocate(ConstantBufferSection* aHolder, const ConstantStagingBuffer& aStaging) {
MOZ_ASSERT(aStaging.NumItems() * aStaging.SizeOfItem() == aStaging.NumBytes());
return Allocate(aHolder, aStaging.NumItems(), aStaging.SizeOfItem(), aStaging.GetBufferStart());
}
// Allocate a buffer of one item that can be immediately uploaded.
template <typename T>
bool Allocate(ConstantBufferSection* aHolder, const T& aItem) {
return Allocate(aHolder, 1, sizeof(aItem), &aItem);
}
// Allocate a buffer of N items that can be immediately uploaded.
template <typename T>
bool Allocate(ConstantBufferSection* aHolder, const T* aItems, size_t aNumItems) {
return Allocate(aHolder, aNumItems, sizeof(T), aItems);
}
// Allocate a buffer that is uploaded after the caller has finished writing
// to it. This should method should generally not be used unless copying T
// is expensive, since the default immediate-upload version has an implicit
// extra copy to the GPU. This version exposes the mapped memory directly.
template <typename T>
bool Allocate(ConstantBufferSection* aHolder, AutoBufferUpload<T>* aPtr) {
MOZ_ASSERT(sizeof(T) % 16 == 0, "Items must be padded to 16 bytes");
return Allocate(aHolder, aPtr, 1, sizeof(T));
}
private:
bool Allocate(ConstantBufferSection* aHolder,
size_t aNumItems,
size_t aSizeOfItem,
const void* aData)
{
AutoBufferUploadBase ptr;
if (!Allocate(aHolder, &ptr, aNumItems, aSizeOfItem)) {
return false;
}
memcpy(ptr.get(), aData, aNumItems * aSizeOfItem);
return true;
}
bool Allocate(ConstantBufferSection* aHolder,
AutoBufferUploadBase* aPtr,
size_t aNumItems,
size_t aSizeOfItem)
{
MOZ_ASSERT(aSizeOfItem % 16 == 0, "Items must be padded to 16 bytes");
size_t bytes = aNumItems * aSizeOfItem;
if (bytes > mMaxConstantBufferBindSize) {
gfxWarning() << "Attempted to allocate too many bytes into a constant buffer";
return false;
}
RefPtr<MLGBuffer> buffer;
ptrdiff_t offset;
if (!GetBufferPointer(aPtr, bytes, &offset, &buffer)) {
return false;
}
aHolder->Init(buffer, offset, bytes, aNumItems);
return true;
}
bool GetBufferPointer(AutoBufferUploadBase* aPtr,
size_t aBytes,
ptrdiff_t* aOutOffset,
RefPtr<MLGBuffer>* aOutBuffer)
{
if (!mCanUseOffsetAllocation) {
uint8_t* ptr = AllocateNewBuffer(aBytes, aOutOffset, aOutBuffer);
if (!ptr) {
return false;
}
aPtr->Init(ptr, mDevice, *aOutBuffer);
return true;
}
// Align up the allocation to 256 bytes, since D3D11 requires that
// constant buffers start at multiples of 16 elements.
size_t alignedBytes = AlignUp<256>::calc(aBytes);
uint8_t* ptr = SharedBufferMLGPU::GetBufferPointer(alignedBytes, aOutOffset, aOutBuffer);
if (!ptr) {
return false;
}
aPtr->Init(ptr);
return true;
}
uint8_t* AllocateNewBuffer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer);
private:
size_t mMaxConstantBufferBindSize;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h

View File

@ -0,0 +1,19 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "StagingBuffer.h"
#include "MLGDevice.h"
#include "ShaderDefinitionsMLGPU.h"
namespace mozilla {
namespace layers {
ConstantStagingBuffer::ConstantStagingBuffer(MLGDevice* aDevice)
: StagingBuffer(mlg::kMaxConstantBufferSize)
{
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,251 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_StagingBuffer_h
#define mozilla_gfx_layers_mlgpu_StagingBuffer_h
#include "mozilla/UniquePtr.h"
#include "UtilityMLGPU.h"
#include <algorithm>
#include <utility>
#include <limits.h>
namespace mozilla {
namespace layers {
class MLGDevice;
// A StagingBuffer is a writable memory buffer for arbitrary contents.
template <size_t Alignment = 0>
class StagingBuffer
{
public:
StagingBuffer()
: StagingBuffer(0)
{}
// By default, staging buffers operate in "forward" mode: items are added to
// the end of the buffer. In "reverse" mode the cursor is at the end of the
// buffer, and items are added to the beginning.
//
// This must be called before the buffer is written.
void SetReversed() {
MOZ_ASSERT(IsEmpty());
mReversed = true;
}
// Write a series of components as a single item. When this is first used, the
// buffer records the initial item size and requires that all future items be
// the exact same size.
//
// This directs to either AppendItem or PrependItem depending on the buffer
// state.
template <typename T>
bool AddItem(const T& aItem) {
if (mReversed) {
return PrependItem(aItem);
}
return AppendItem(aItem);
}
// This may only be called on forward buffers.
template <typename T>
bool AppendItem(const T& aItem) {
MOZ_ASSERT(!mReversed);
size_t alignedBytes = AlignUp<Alignment>::calc(sizeof(aItem));
if (!mUniformSize) {
mUniformSize = alignedBytes;
}
if (!EnsureForwardRoomFor(alignedBytes)) {
return false;
}
if (mUniformSize != alignedBytes) {
MOZ_ASSERT_UNREACHABLE("item of incorrect size added!");
return false;
}
*reinterpret_cast<T*>(mPos) = aItem;
mPos += alignedBytes;
MOZ_ASSERT(mPos <= mEnd);
mNumItems++;
return true;
}
// This may only be called on reversed buffers.
template <typename T>
bool PrependItem(const T& aItem) {
MOZ_ASSERT(mReversed);
size_t alignedBytes = AlignUp<Alignment>::calc(sizeof(aItem));
if (!mUniformSize) {
mUniformSize = alignedBytes;
}
if (!EnsureBackwardRoomFor(alignedBytes)) {
return false;
}
if (mUniformSize != alignedBytes) {
MOZ_ASSERT_UNREACHABLE("item of incorrect size added!");
return false;
}
mPos -= alignedBytes;
*reinterpret_cast<T*>(mPos) = aItem;
MOZ_ASSERT(mPos >= mBuffer.get());
mNumItems++;
return true;
}
size_t NumBytes() const {
return mReversed
? mEnd - mPos
: mPos - mBuffer.get();
}
uint8_t* GetBufferStart() const {
return mReversed
? mPos
: mBuffer.get();
}
size_t SizeOfItem() const {
return mUniformSize;
}
size_t NumItems() const {
return mNumItems;
}
void Reset() {
mPos = mReversed ? mEnd : mBuffer.get();
mUniformSize = 0;
mNumItems = 0;
}
// RestorePosition must only be called with a previous call to
// GetPosition.
typedef std::pair<size_t,size_t> Position;
Position GetPosition() const {
return std::make_pair(NumBytes(), mNumItems);
}
void RestorePosition(const Position& aPosition) {
mPos = mBuffer.get() + aPosition.first;
mNumItems = aPosition.second;
if (mNumItems == 0) {
mUniformSize = 0;
}
// Make sure the buffer is still coherent.
MOZ_ASSERT(mPos >= mBuffer.get() && mPos <= mEnd);
MOZ_ASSERT(mNumItems * mUniformSize == NumBytes());
}
bool IsEmpty() const {
return mNumItems == 0;
}
protected:
explicit StagingBuffer(size_t aMaxSize)
: mPos(nullptr),
mEnd(nullptr),
mUniformSize(0),
mNumItems(0),
mMaxSize(aMaxSize),
mReversed(false)
{}
static const size_t kDefaultSize = 8;
bool EnsureForwardRoomFor(size_t aAlignedBytes) {
if (size_t(mEnd - mPos) < aAlignedBytes) {
return GrowBuffer(aAlignedBytes);
}
return true;
}
bool EnsureBackwardRoomFor(size_t aAlignedBytes) {
if (size_t(mPos - mBuffer.get()) < aAlignedBytes) {
return GrowBuffer(aAlignedBytes);
}
return true;
}
bool GrowBuffer(size_t aAlignedBytes) {
// We should not be writing items that are potentially bigger than the
// maximum constant buffer size, that's crazy. An assert should be good
// enough since the size of writes is static - and shader compilers
// would explode anyway.
MOZ_ASSERT_IF(mMaxSize, aAlignedBytes < mMaxSize);
MOZ_ASSERT_IF(mMaxSize, kDefaultSize * Alignment < mMaxSize);
if (!mBuffer) {
size_t newSize = std::max(kDefaultSize * Alignment, aAlignedBytes);
MOZ_ASSERT_IF(mMaxSize, newSize < mMaxSize);
mBuffer = MakeUnique<uint8_t[]>(newSize);
mEnd = mBuffer.get() + newSize;
mPos = mReversed ? mEnd : mBuffer.get();
return true;
}
// Take the bigger of exact-fit or 1.5x the previous size, and make sure
// the new size doesn't overflow size_t. If needed, clamp to the max
// size.
size_t oldSize = mEnd - mBuffer.get();
size_t trySize = std::max(oldSize + aAlignedBytes, oldSize + oldSize / 2);
size_t newSize = mMaxSize ? std::min(trySize, mMaxSize) : trySize;
if (newSize < oldSize || newSize - oldSize < aAlignedBytes) {
return false;
}
UniquePtr<uint8_t[]> newBuffer = MakeUnique<uint8_t[]>(newSize);
if (!newBuffer) {
return false;
}
// When the buffer is in reverse mode, we have to copy from the end of the
// buffer, not the beginning.
if (mReversed) {
size_t usedBytes = mEnd - mPos;
size_t newPos = newSize - usedBytes;
MOZ_RELEASE_ASSERT(newPos + usedBytes <= newSize);
memcpy(newBuffer.get() + newPos, mPos, usedBytes);
mPos = newBuffer.get() + newPos;
} else {
size_t usedBytes = mPos - mBuffer.get();
MOZ_RELEASE_ASSERT(usedBytes <= newSize);
memcpy(newBuffer.get(), mBuffer.get(), usedBytes);
mPos = newBuffer.get() + usedBytes;
}
mEnd = newBuffer.get() + newSize;
mBuffer = Move(newBuffer);
MOZ_RELEASE_ASSERT(mPos >= mBuffer.get() && mPos <= mEnd);
return true;
}
protected:
UniquePtr<uint8_t[]> mBuffer;
uint8_t* mPos;
uint8_t* mEnd;
size_t mUniformSize;
size_t mNumItems;
size_t mMaxSize;
bool mReversed;
};
class ConstantStagingBuffer : public StagingBuffer<16>
{
public:
explicit ConstantStagingBuffer(MLGDevice* aDevice);
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_StagingBuffer_h

View File

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TextureSourceProviderMLGPU.h"
#include "LayerManagerMLGPU.h"
#include "MLGDevice.h"
#ifdef XP_WIN
# include "mozilla/layers/MLGDeviceD3D11.h"
#endif
namespace mozilla {
namespace layers {
TextureSourceProviderMLGPU::TextureSourceProviderMLGPU(LayerManagerMLGPU* aLayerManager, MLGDevice* aDevice)
: mLayerManager(aLayerManager),
mDevice(aDevice)
{
}
TextureSourceProviderMLGPU::~TextureSourceProviderMLGPU()
{
}
int32_t
TextureSourceProviderMLGPU::GetMaxTextureSize() const
{
if (!mDevice) {
return 0;
}
return mDevice->GetMaxTextureSize();
}
bool
TextureSourceProviderMLGPU::SupportsEffect(EffectTypes aEffect)
{
switch (aEffect) {
case EffectTypes::YCBCR:
return true;
default:
MOZ_ASSERT_UNREACHABLE("NYI");
}
return false;
}
bool
TextureSourceProviderMLGPU::IsValid() const
{
return !!mLayerManager;
}
void
TextureSourceProviderMLGPU::Destroy()
{
mLayerManager = nullptr;
mDevice = nullptr;
TextureSourceProvider::Destroy();
}
#ifdef XP_WIN
ID3D11Device*
TextureSourceProviderMLGPU::GetD3D11Device() const
{
if (!mDevice) {
return nullptr;
}
return mDevice->AsD3D11()->GetD3D11Device();
}
#endif
TimeStamp
TextureSourceProviderMLGPU::GetLastCompositionEndTime() const
{
if (!mLayerManager) {
return TimeStamp();
}
return mLayerManager->GetLastCompositionEndTime();
}
already_AddRefed<DataTextureSource>
TextureSourceProviderMLGPU::CreateDataTextureSource(TextureFlags aFlags)
{
RefPtr<DataTextureSource> texture = mDevice->CreateDataTextureSource(aFlags);
return texture.forget();
}
already_AddRefed<DataTextureSource>
TextureSourceProviderMLGPU::CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface)
{
MOZ_ASSERT_UNREACHABLE("NYI");
return nullptr;
}
bool
TextureSourceProviderMLGPU::NotifyNotUsedAfterComposition(TextureHost* aTextureHost)
{
if (!mDevice) {
return false;
}
return TextureSourceProvider::NotifyNotUsedAfterComposition(aTextureHost);
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_TextureSourceProviderMLGPU_h
#define mozilla_gfx_layers_mlgpu_TextureSourceProviderMLGPU_h
#include "mozilla/layers/TextureSourceProvider.h"
namespace mozilla {
namespace layers {
class MLGDevice;
class LayerManagerMLGPU;
class TextureSourceProviderMLGPU final : public TextureSourceProvider
{
public:
TextureSourceProviderMLGPU(LayerManagerMLGPU* aLayerManager, MLGDevice* aDevice);
~TextureSourceProviderMLGPU() override;
already_AddRefed<DataTextureSource>
CreateDataTextureSource(TextureFlags aFlags) override;
already_AddRefed<DataTextureSource>
CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override;
bool NotifyNotUsedAfterComposition(TextureHost* aTextureHost) override;
int32_t GetMaxTextureSize() const override;
TimeStamp GetLastCompositionEndTime() const override;
bool SupportsEffect(EffectTypes aEffect) override;
bool IsValid() const override;
#ifdef XP_WIN
virtual ID3D11Device* GetD3D11Device() const override;
#endif
void ReadUnlockTextures() {
TextureSourceProvider::ReadUnlockTextures();
}
// Release references to the layer manager.
void Destroy() override;
private:
// Using RefPtr<> here would be a circular reference.
LayerManagerMLGPU* mLayerManager;
RefPtr<MLGDevice> mDevice;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_TextureSourceProviderMLGPU_h

View File

@ -0,0 +1,210 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TexturedLayerMLGPU.h"
#include "LayerManagerMLGPU.h"
#include "RenderViewMLGPU.h"
#include "FrameBuilder.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/layers/ImageHost.h"
#include "UnitTransforms.h"
namespace mozilla {
namespace layers {
using namespace gfx;
TexturedLayerMLGPU::TexturedLayerMLGPU(LayerManagerMLGPU* aManager)
: LayerMLGPU(aManager)
{
}
TexturedLayerMLGPU::~TexturedLayerMLGPU()
{
// Note: we have to cleanup resources in derived classes, since we can't
// easily tell in our destructor if we have a TempImageLayerMLGPU, which
// should not have its compositable detached, and we can't call GetLayer
// here.
}
bool
TexturedLayerMLGPU::SetCompositableHost(CompositableHost* aHost)
{
switch (aHost->GetType()) {
case CompositableType::IMAGE:
mHost = aHost->AsImageHost();
return true;
default:
return false;
}
}
CompositableHost*
TexturedLayerMLGPU::GetCompositableHost()
{
if (mHost && mHost->IsAttached()) {
return mHost.get();
}
return nullptr;
}
RefPtr<TextureSource>
TexturedLayerMLGPU::BindAndGetTexture()
{
if (!mHost) {
return nullptr;
}
LayerManagerMLGPU* lm = GetLayerManager()->AsLayerManagerMLGPU();
// Note: we don't call FinishRendering since mask layers do not need
// composite notifications or bias updates. (This function should
// not be called for non-mask-layers).
ImageHost::RenderInfo info;
if (!mHost->PrepareToRender(lm->GetTextureSourceProvider(), &info)) {
return nullptr;
}
RefPtr<TextureSource> source = mHost->AcquireTextureSource(info);
if (!source) {
return nullptr;
}
mTexture = source;
return source;
}
bool
TexturedLayerMLGPU::OnPrepareToRender(FrameBuilder* aBuilder)
{
if (!mHost) {
return false;
}
LayerManagerMLGPU* lm = GetLayerManager()->AsLayerManagerMLGPU();
ImageHost::RenderInfo info;
if (!mHost->PrepareToRender(lm->GetTextureSourceProvider(), &info)) {
return false;
}
RefPtr<TextureSource> source = mHost->AcquireTextureSource(info);
if (!source) {
return false;
}
if (source->AsBigImageIterator()) {
mBigImageTexture = source;
mTexture = nullptr;
} else {
mTexture = source;
}
mPictureRect = IntRect(0, 0, info.img->mPictureRect.width, info.img->mPictureRect.height);
mHost->FinishRendering(info);
return true;
}
void
TexturedLayerMLGPU::AssignToView(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
Maybe<Polygon>&& aGeometry)
{
if (mBigImageTexture) {
BigImageIterator* iter = mBigImageTexture->AsBigImageIterator();
iter->BeginBigImageIteration();
AssignBigImage(aBuilder, aView, iter, aGeometry);
iter->EndBigImageIteration();
} else {
LayerMLGPU::AssignToView(aBuilder, aView, Move(aGeometry));
}
}
void
TexturedLayerMLGPU::AssignBigImage(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
BigImageIterator* aIter,
const Maybe<Polygon>& aGeometry)
{
const Matrix4x4& transform = GetLayer()->GetEffectiveTransformForBuffer();
// Note that we don't need to assign these in any particular order, since
// they do not overlap.
do {
IntRect tileRect = aIter->GetTileRect();
IntRect rect = tileRect.Intersect(mPictureRect);
if (rect.IsEmpty()) {
continue;
}
{
Rect screenRect = transform.TransformBounds(Rect(rect));
screenRect.MoveBy(-aView->GetTargetOffset());
screenRect = screenRect.Intersect(Rect(mComputedClipRect.ToUnknownRect()));
if (screenRect.IsEmpty()) {
// This tile is not in the clip region, so skip it.
continue;
}
}
RefPtr<TextureSource> tile = mBigImageTexture->ExtractCurrentTile();
if (!tile) {
continue;
}
// Create a temporary item.
RefPtr<TempImageLayerMLGPU> item = new TempImageLayerMLGPU(aBuilder->GetManager());
item->Init(this, tile, rect);
Maybe<Polygon> geometry = aGeometry;
item->AddBoundsToView(aBuilder, aView, Move(geometry));
// Since the layer tree is not holding this alive, we have to ask the
// FrameBuilder to do it for us.
aBuilder->RetainTemporaryLayer(item);
} while (aIter->NextTile());
}
TempImageLayerMLGPU::TempImageLayerMLGPU(LayerManagerMLGPU* aManager)
: ImageLayer(aManager, static_cast<HostLayer*>(this)),
TexturedLayerMLGPU(aManager)
{
}
TempImageLayerMLGPU::~TempImageLayerMLGPU()
{
}
void
TempImageLayerMLGPU::Init(TexturedLayerMLGPU* aSource,
const RefPtr<TextureSource>& aTexture,
const gfx::IntRect& aPictureRect)
{
// ImageLayer properties.
mEffectiveTransform = aSource->GetLayer()->GetEffectiveTransform();
mEffectiveTransformForBuffer = aSource->GetLayer()->GetEffectiveTransformForBuffer();
// Base LayerMLGPU properties.
mComputedClipRect = aSource->GetComputedClipRect();
mMask = aSource->GetMask();
mComputedOpacity = aSource->GetComputedOpacity();
// TexturedLayerMLGPU properties.
mHost = aSource->GetImageHost();
mTexture = aTexture;
mPictureRect = aPictureRect;
// Local properties.
mFilter = aSource->GetSamplingFilter();
mShadowVisibleRegion = aSource->GetShadowVisibleRegion();
mIsOpaque = aSource->IsContentOpaque();
// Set this layer to prepared so IsPrepared() assertions don't fire.
MarkPrepared();
}
} // namespace layers
} // namespace mozilla

View File

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_TexturedLayerMLGPU_h
#define mozilla_gfx_layers_mlgpu_TexturedLayerMLGPU_h
#include "LayerMLGPU.h"
#include "ImageLayers.h"
#include "mozilla/layers/ImageHost.h"
namespace mozilla {
namespace layers {
// This is the base class for canvas and image layers.
class TexturedLayerMLGPU : public LayerMLGPU
{
public:
TexturedLayerMLGPU* AsTexturedLayerMLGPU() override { return this; }
virtual gfx::SamplingFilter GetSamplingFilter() = 0;
bool SetCompositableHost(CompositableHost* aHost) override;
CompositableHost* GetCompositableHost() override;
void AssignToView(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
Maybe<gfx::Polygon>&& aGeometry) override;
TextureSource* GetTexture() const {
return mTexture;
}
ImageHost* GetImageHost() const {
return mHost;
}
// Return the scale factor from the texture source to the picture rect.
virtual Maybe<gfx::Size> GetPictureScale() const {
return Nothing();
}
// Mask layers aren't prepared like normal layers. They are bound as
// mask operations are built. Mask layers are never tiled (they are
// scaled to a lower resolution if too big), so this pathway returns
// a TextureSource.
RefPtr<TextureSource> BindAndGetTexture();
protected:
explicit TexturedLayerMLGPU(LayerManagerMLGPU* aManager);
virtual ~TexturedLayerMLGPU() override;
void AssignBigImage(FrameBuilder* aBuilder,
RenderViewMLGPU* aView,
BigImageIterator* aIter,
const Maybe<gfx::Polygon>& aGeometry);
bool OnPrepareToRender(FrameBuilder* aBuilder) override;
protected:
RefPtr<ImageHost> mHost;
RefPtr<TextureSource> mTexture;
RefPtr<TextureSource> mBigImageTexture;
gfx::IntRect mPictureRect;
};
// This is a pseudo layer that wraps a tile in an ImageLayer backed by a
// BigImage. Without this, we wouldn't have anything sensible to add to
// RenderPasses. In the future we could potentially consume the source
// layer more intelligently instead (for example, having it compute
// which textures are relevant for a given tile).
class TempImageLayerMLGPU final : public ImageLayer,
public TexturedLayerMLGPU
{
public:
explicit TempImageLayerMLGPU(LayerManagerMLGPU* aManager);
// Layer
HostLayer* AsHostLayer() override { return this; }
gfx::SamplingFilter GetSamplingFilter() override {
return mFilter;
}
bool IsContentOpaque() override {
return mIsOpaque;
}
void Init(TexturedLayerMLGPU* aSource,
const RefPtr<TextureSource>& aTexture,
const gfx::IntRect& aPictureRect);
// HostLayer
Layer* GetLayer() override { return this; }
protected:
~TempImageLayerMLGPU() override;
private:
gfx::SamplingFilter mFilter;
bool mIsOpaque;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_mlgpu_TexturedLayerMLGPU_h

View File

@ -0,0 +1,39 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_layers_mlgpu_UtilityMLGPU_h
#define mozilla_gfx_layers_mlgpu_UtilityMLGPU_h
#include "mozilla/Assertions.h"
#include "mozilla/MathAlgorithms.h"
namespace mozilla {
namespace layers {
template <size_t T> struct AlignUp
{
static inline size_t calc(size_t aAmount) {
MOZ_ASSERT(IsPowerOfTwo(T), "alignment must be a power of two");
return aAmount + ((T - (aAmount % T)) % T);
}
};
template <> struct AlignUp<0>
{
static inline size_t calc(size_t aAmount) {
return aAmount;
}
};
} // namespace layers
} // namespace mozilla
#ifdef ENABLE_AL_LOGGING
# define AL_LOG(...) printf_stderr("AL: " __VA_ARGS__)
#else
# define AL_LOG(...)
#endif
#endif // mozilla_gfx_layers_mlgpu_UtilityMLGPU_h

View File

@ -66,12 +66,14 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'd3d11/DeviceAttachmentsD3D11.h',
'd3d11/DiagnosticsD3D11.h',
'd3d11/HelpersD3D11.h',
'd3d11/MLGDeviceD3D11.h',
'd3d11/ReadbackManagerD3D11.h',
'd3d11/ShaderDefinitionsD3D11.h',
'd3d11/TextureD3D11.h',
]
UNIFIED_SOURCES += [
'd3d11/DiagnosticsD3D11.cpp',
'd3d11/MLGDeviceD3D11.cpp',
'd3d11/TextureD3D11.cpp',
]
SOURCES += [
@ -148,6 +150,7 @@ EXPORTS.mozilla.layers += [
'composite/ImageLayerComposite.h',
'composite/LayerManagerComposite.h',
'composite/PaintedLayerComposite.h',
'composite/TextRenderer.h',
'composite/TextureHost.h',
'composite/TiledContentHost.h',
'Compositor.h',
@ -195,6 +198,12 @@ EXPORTS.mozilla.layers += [
'LayerMetricsWrapper.h',
'LayersHelpers.h',
'LayersTypes.h',
'mlgpu/LayerManagerMLGPU.h',
'mlgpu/LayerMLGPU.h',
'mlgpu/MLGDevice.h',
'mlgpu/MLGDeviceTypes.h',
'mlgpu/ShaderDefinitionsMLGPU.h',
'mlgpu/UtilityMLGPU.h',
'opengl/CompositingRenderTargetOGL.h',
'opengl/CompositorOGL.h',
'opengl/MacIOSurfaceTextureClientOGL.h',
@ -394,6 +403,23 @@ UNIFIED_SOURCES += [
'LayersLogging.cpp',
'LayerSorter.cpp',
'LayersTypes.cpp',
'mlgpu/BufferCache.cpp',
'mlgpu/CanvasLayerMLGPU.cpp',
'mlgpu/ContainerLayerMLGPU.cpp',
'mlgpu/FrameBuilder.cpp',
'mlgpu/ImageLayerMLGPU.cpp',
'mlgpu/LayerManagerMLGPU.cpp',
'mlgpu/LayerMLGPU.cpp',
'mlgpu/MaskOperation.cpp',
'mlgpu/MLGDevice.cpp',
'mlgpu/PaintedLayerMLGPU.cpp',
'mlgpu/RenderPassMLGPU.cpp',
'mlgpu/RenderViewMLGPU.cpp',
'mlgpu/ShaderDefinitionsMLGPU.cpp',
'mlgpu/SharedBufferMLGPU.cpp',
'mlgpu/StagingBuffer.cpp',
'mlgpu/TexturedLayerMLGPU.cpp',
'mlgpu/TextureSourceProviderMLGPU.cpp',
'opengl/CompositingRenderTargetOGL.cpp',
'opengl/CompositorOGL.cpp',
'opengl/GLBlitTextureImageHelper.cpp',
@ -481,11 +507,15 @@ include('/ipc/chromium/chromium-config.mozbuild')
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
GENERATED_FILES = [
'CompositorD3D11Shaders.h',
'MLGShaders.h',
]
d3d11_shaders = GENERATED_FILES['CompositorD3D11Shaders.h']
d3d11_shaders.script = 'd3d11/genshaders.py'
d3d11_shaders.inputs = ['d3d11/shaders.manifest']
mlg_shaders = GENERATED_FILES['MLGShaders.h']
mlg_shaders.script = 'd3d11/genshaders.py'
mlg_shaders.inputs = ['d3d11/mlgshaders/shaders.manifest']
LOCAL_INCLUDES += [
'/docshell/base', # for nsDocShell.h

View File

@ -561,6 +561,13 @@ private:
DECL_GFX_PREF(Live, "layers.low-precision-opacity", LowPrecisionOpacity, float, 1.0f);
DECL_GFX_PREF(Live, "layers.low-precision-resolution", LowPrecisionResolution, float, 0.25f);
DECL_GFX_PREF(Live, "layers.max-active", MaxActiveLayers, int32_t, -1);
DECL_GFX_PREF(Once, "layers.mlgpu.dev-enabled", AdvancedLayersEnabledDoNotUseDirectly, bool, false);
DECL_GFX_PREF(Once, "layers.mlgpu.enable-buffer-cache", AdvancedLayersEnableBufferCache, bool, true);
DECL_GFX_PREF(Once, "layers.mlgpu.enable-buffer-sharing", AdvancedLayersEnableBufferSharing, bool, true);
DECL_GFX_PREF(Once, "layers.mlgpu.enable-clear-view", AdvancedLayersEnableClearView, bool, true);
DECL_GFX_PREF(Once, "layers.mlgpu.enable-cpu-occlusion", AdvancedLayersEnableCPUOcclusion, bool, true);
DECL_GFX_PREF(Once, "layers.mlgpu.enable-depth-buffer", AdvancedLayersEnableDepthBuffer, bool, false);
DECL_GFX_PREF(Live, "layers.mlgpu.enable-invalidation", AdvancedLayersUseInvalidation, bool, true);
DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
DECL_GFX_PREF(Live, "layers.orientation.sync.timeout", OrientationSyncMillis, uint32_t, (uint32_t)0);

View File

@ -83,6 +83,14 @@ public:
return mArray[size_t(aIndex)];
}
EnumeratedArray& operator =(EnumeratedArray&& aOther)
{
for (size_t i = 0; i < kSize; i++) {
mArray[i] = Move(aOther.mArray[i]);
}
return *this;
}
typedef typename ArrayType::iterator iterator;
typedef typename ArrayType::const_iterator const_iterator;
typedef typename ArrayType::reverse_iterator reverse_iterator;

View File

@ -5716,6 +5716,14 @@ pref("dom.payments.request.enabled", false);
pref("fuzzing.enabled", false);
#endif
#if defined(XP_WIN)
pref("layers.mlgpu.dev-enabled", false);
// Both this and the master "enabled" pref must be on to use Advanced LAyers
// on Windows 7.
pref("layers.mlgpu.enable-on-windows7", false);
#endif
// Set advanced layers preferences here to have them show up in about:config or
// to be overridable in reftest.list files. They should pretty much all be set
// to a value of 2, and the conditional-pref code in gfxPrefs.h will convert