1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef _MOZILLA_GFX_DRAWTARGETWEBGL_INTERNAL_H
8 #define _MOZILLA_GFX_DRAWTARGETWEBGL_INTERNAL_H
10 #include "DrawTargetWebgl.h"
12 #include "mozilla/HashFunctions.h"
13 #include "mozilla/gfx/PathSkia.h"
14 #include "mozilla/gfx/WPFGpuRaster.h"
16 namespace mozilla::gfx
{
18 // TexturePacker implements a bin-packing algorithm for 2D rectangles. It uses
19 // a binary tree that partitions the space of a node at a given split. This
20 // produces two children, one on either side of the split. This subdivision
21 // proceeds recursively as necessary.
24 explicit TexturePacker(const IntRect
& aBounds
, bool aAvailable
= true)
26 mAvailable(aAvailable
? std::min(aBounds
.width
, aBounds
.height
) : 0) {}
28 Maybe
<IntPoint
> Insert(const IntSize
& aSize
);
30 bool Remove(const IntRect
& aBounds
);
32 const IntRect
& GetBounds() const { return mBounds
; }
35 bool IsLeaf() const { return !mChildren
; }
36 bool IsFullyAvailable() const { return IsLeaf() && mAvailable
> 0; }
38 void DiscardChildren() { mChildren
.reset(); }
40 // If applicable, the two children produced by picking a single axis split
41 // within the node's bounds and subdividing the bounds there.
42 UniquePtr
<TexturePacker
[]> mChildren
;
43 // The bounds enclosing this node and any children within it.
45 // For a leaf node, specifies the size of the smallest dimension available to
46 // allocate. For a branch node, specifies largest potential available size of
47 // all children. This can be used during the allocation process to rapidly
48 // reject certain sub-trees without having to search all the way to a leaf
49 // node if we know that largest available size within the sub-tree wouldn't
50 // fit the requested size.
54 // CacheEnty is a generic interface for various items that need to be cached to
56 class CacheEntry
: public RefCounted
<CacheEntry
> {
58 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(CacheEntry
)
60 CacheEntry(const Matrix
& aTransform
, const IntRect
& aBounds
, HashNumber aHash
)
61 : mTransform(aTransform
), mBounds(aBounds
), mHash(aHash
) {}
62 virtual ~CacheEntry() = default;
64 void Link(const RefPtr
<TextureHandle
>& aHandle
);
67 const RefPtr
<TextureHandle
>& GetHandle() const { return mHandle
; }
69 const Matrix
& GetTransform() const { return mTransform
; }
70 const IntRect
& GetBounds() const { return mBounds
; }
71 HashNumber
GetHash() const { return mHash
; }
73 virtual bool IsValid() const { return true; }
76 virtual void RemoveFromList() = 0;
78 // The handle of the rendered cache item.
79 RefPtr
<TextureHandle
> mHandle
;
80 // The transform that was used to render the entry. This is necessary as
81 // the geometry might only be correctly rendered in device space after
82 // the transform is applied, so in general we can't cache untransformed
85 // The device space bounds of the rendered geometry.
87 // A hash of the geometry that may be used for quickly rejecting entries.
91 // CacheEntryImpl provides type-dependent boilerplate code for implementations
94 class CacheEntryImpl
: public CacheEntry
, public LinkedListElement
<RefPtr
<T
>> {
95 typedef LinkedListElement
<RefPtr
<T
>> ListType
;
98 CacheEntryImpl(const Matrix
& aTransform
, const IntRect
& aBounds
,
100 : CacheEntry(aTransform
, aBounds
, aHash
) {}
102 void RemoveFromList() override
{
103 if (ListType::isInList()) {
109 // CacheImpl manages a list of CacheEntry.
110 template <typename T
, bool BIG
>
113 typedef LinkedList
<RefPtr
<T
>> ListType
;
115 // Whether the cache should be small and space-efficient or prioritize speed.
116 static constexpr size_t kNumChains
= BIG
? 499 : 17;
120 for (auto& chain
: mChains
) {
121 while (RefPtr
<T
> entry
= chain
.popLast()) {
128 ListType
& GetChain(HashNumber aHash
) { return mChains
[aHash
% kNumChains
]; }
130 void Insert(T
* aEntry
) { GetChain(aEntry
->GetHash()).insertFront(aEntry
); }
132 ListType mChains
[kNumChains
];
135 // BackingTexture provides information about the shared or standalone texture
136 // that is backing a texture handle.
137 class BackingTexture
{
139 BackingTexture(const IntSize
& aSize
, SurfaceFormat aFormat
,
140 const RefPtr
<WebGLTextureJS
>& aTexture
);
142 SurfaceFormat
GetFormat() const { return mFormat
; }
143 IntSize
GetSize() const { return mSize
; }
145 static inline size_t UsedBytes(SurfaceFormat aFormat
, const IntSize
& aSize
) {
146 return size_t(BytesPerPixel(aFormat
)) * size_t(aSize
.width
) *
147 size_t(aSize
.height
);
150 size_t UsedBytes() const { return UsedBytes(GetFormat(), GetSize()); }
152 const RefPtr
<WebGLTextureJS
>& GetWebGLTexture() const { return mTexture
; }
154 bool IsInitialized() const { return mFlags
& INITIALIZED
; }
155 void MarkInitialized() { mFlags
|= INITIALIZED
; }
157 bool IsRenderable() const { return mFlags
& RENDERABLE
; }
158 void MarkRenderable() { mFlags
|= RENDERABLE
; }
162 SurfaceFormat mFormat
;
163 RefPtr
<WebGLTextureJS
> mTexture
;
166 enum Flags
: uint8_t {
167 INITIALIZED
= 1 << 0,
174 // TextureHandle is an abstract base class for supplying textures to drawing
175 // commands that may be backed by different resource types (such as a shared
176 // or standalone texture). It may be further linked to use-specific metadata
177 // such as for shadow drawing or for cached entries in the glyph cache.
178 class TextureHandle
: public RefCounted
<TextureHandle
>,
179 public LinkedListElement
<RefPtr
<TextureHandle
>> {
181 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(TextureHandle
)
183 enum Type
{ SHARED
, STANDALONE
};
185 virtual Type
GetType() const = 0;
186 virtual IntRect
GetBounds() const = 0;
187 IntSize
GetSize() const { return GetBounds().Size(); }
188 virtual SurfaceFormat
GetFormat() const = 0;
190 virtual BackingTexture
* GetBackingTexture() = 0;
192 size_t UsedBytes() const {
193 return BackingTexture::UsedBytes(GetFormat(), GetSize());
196 virtual void UpdateSize(const IntSize
& aSize
) {}
198 virtual void Cleanup(DrawTargetWebgl::SharedContext
& aContext
) {}
200 virtual ~TextureHandle() {}
202 bool IsValid() const { return mValid
; }
203 void Invalidate() { mValid
= false; }
205 void SetSurface(SourceSurface
* aSurface
) { mSurface
= aSurface
; }
206 SourceSurface
* GetSurface() const { return mSurface
; }
208 float GetSigma() const { return mSigma
; }
209 void SetSigma(float aSigma
) { mSigma
= aSigma
; }
210 bool IsShadow() const { return mSigma
>= 0.0f
; }
212 void SetSamplingOffset(const IntPoint
& aSamplingOffset
) {
213 mSamplingOffset
= aSamplingOffset
;
215 const IntPoint
& GetSamplingOffset() const { return mSamplingOffset
; }
216 IntRect
GetSamplingRect() const {
217 return IntRect(GetSamplingOffset(), GetSize());
220 const RefPtr
<CacheEntry
>& GetCacheEntry() const { return mCacheEntry
; }
221 void SetCacheEntry(const RefPtr
<CacheEntry
>& aEntry
) { mCacheEntry
= aEntry
; }
223 // Note as used if there is corresponding surface or cache entry.
224 bool IsUsed() const {
225 return mSurface
|| (mCacheEntry
&& mCacheEntry
->IsValid());
230 // If applicable, weak pointer to the SourceSurface that is linked to this
232 SourceSurface
* mSurface
= nullptr;
233 // If this TextureHandle stores a cached shadow, then we need to remember the
234 // blur sigma used to produce the shadow.
235 float mSigma
= -1.0f
;
236 // If the originating surface requested a sampling rect, then we need to know
237 // the offset of the subrect within the surface for texture coordinates.
238 IntPoint mSamplingOffset
;
239 // If applicable, the CacheEntry that is linked to this TextureHandle.
240 RefPtr
<CacheEntry
> mCacheEntry
;
243 class SharedTextureHandle
;
245 // SharedTexture is a large slab texture that is subdivided (by using a
246 // TexturePacker) to hold many small SharedTextureHandles. This avoids needing
247 // to allocate many WebGL textures for every single small Canvas 2D texture.
248 class SharedTexture
: public RefCounted
<SharedTexture
>, public BackingTexture
{
250 MOZ_DECLARE_REFCOUNTED_TYPENAME(SharedTexture
)
252 SharedTexture(const IntSize
& aSize
, SurfaceFormat aFormat
,
253 const RefPtr
<WebGLTextureJS
>& aTexture
);
255 already_AddRefed
<SharedTextureHandle
> Allocate(const IntSize
& aSize
);
256 bool Free(const SharedTextureHandle
& aHandle
);
258 bool HasAllocatedHandles() const { return mAllocatedHandles
> 0; }
261 TexturePacker mPacker
;
262 size_t mAllocatedHandles
= 0;
265 // SharedTextureHandle is an allocated region within a large SharedTexture page
267 class SharedTextureHandle
: public TextureHandle
{
268 friend class SharedTexture
;
271 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SharedTextureHandle
, override
)
273 SharedTextureHandle(const IntRect
& aBounds
, SharedTexture
* aTexture
);
275 Type
GetType() const override
{ return Type::SHARED
; }
277 IntRect
GetBounds() const override
{ return mBounds
; }
279 SurfaceFormat
GetFormat() const override
{ return mTexture
->GetFormat(); }
281 BackingTexture
* GetBackingTexture() override
{ return mTexture
.get(); }
283 void Cleanup(DrawTargetWebgl::SharedContext
& aContext
) override
;
285 const RefPtr
<SharedTexture
>& GetOwner() const { return mTexture
; }
289 RefPtr
<SharedTexture
> mTexture
;
292 // StandaloneTexture is a texture that can not be effectively shared within
293 // a SharedTexture page, such that it is better to assign it its own WebGL
295 class StandaloneTexture
: public TextureHandle
, public BackingTexture
{
297 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(StandaloneTexture
, override
)
299 StandaloneTexture(const IntSize
& aSize
, SurfaceFormat aFormat
,
300 const RefPtr
<WebGLTextureJS
>& aTexture
);
302 Type
GetType() const override
{ return Type::STANDALONE
; }
304 IntRect
GetBounds() const override
{
305 return IntRect(IntPoint(0, 0), BackingTexture::GetSize());
308 SurfaceFormat
GetFormat() const override
{
309 return BackingTexture::GetFormat();
312 using BackingTexture::UsedBytes
;
314 BackingTexture
* GetBackingTexture() override
{ return this; }
316 void UpdateSize(const IntSize
& aSize
) override
{ mSize
= aSize
; }
318 void Cleanup(DrawTargetWebgl::SharedContext
& aContext
) override
;
321 // GlyphCacheEntry stores rendering metadata for a rendered text run, as well
322 // the handle to the texture it was rendered into, so that it can be located
323 // for reuse under similar rendering circumstances.
324 class GlyphCacheEntry
: public CacheEntryImpl
<GlyphCacheEntry
> {
326 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphCacheEntry
, override
)
328 GlyphCacheEntry(const GlyphBuffer
& aBuffer
, const DeviceColor
& aColor
,
329 const Matrix
& aTransform
, const IntPoint
& aQuantizeScale
,
330 const IntRect
& aBounds
, const IntRect
& aFullBounds
,
332 StoredStrokeOptions
* aStrokeOptions
= nullptr);
335 const GlyphBuffer
& GetGlyphBuffer() const { return mBuffer
; }
337 bool MatchesGlyphs(const GlyphBuffer
& aBuffer
, const DeviceColor
& aColor
,
338 const Matrix
& aTransform
, const IntPoint
& aQuantizeOffset
,
339 const IntPoint
& aBoundsOffset
, const IntRect
& aClipRect
,
340 HashNumber aHash
, const StrokeOptions
* aStrokeOptions
);
342 static HashNumber
HashGlyphs(const GlyphBuffer
& aBuffer
,
343 const Matrix
& aTransform
,
344 const IntPoint
& aQuantizeScale
);
347 // The glyph keys used to render the text run.
348 GlyphBuffer mBuffer
= {nullptr, 0};
349 // The color of the text run.
351 // The full bounds of the text run without any clipping applied.
353 // Stroke options for the text run.
354 UniquePtr
<StoredStrokeOptions
> mStrokeOptions
;
357 // GlyphCache maintains a list of GlyphCacheEntry's representing previously
358 // rendered text runs. The cache is searched to see if a given incoming text
359 // run has already been rendered to a texture, and if so, just reuses it.
360 // Otherwise, the text run will be rendered to a new texture handle and
361 // inserted into a new GlyphCacheEntry to represent it.
362 class GlyphCache
: public LinkedListElement
<GlyphCache
>,
363 public CacheImpl
<GlyphCacheEntry
, false> {
365 explicit GlyphCache(ScaledFont
* aFont
);
367 ScaledFont
* GetFont() const { return mFont
; }
369 already_AddRefed
<GlyphCacheEntry
> FindEntry(const GlyphBuffer
& aBuffer
,
370 const DeviceColor
& aColor
,
371 const Matrix
& aTransform
,
372 const IntPoint
& aQuantizeScale
,
373 const IntRect
& aClipRect
,
375 const StrokeOptions
* aOptions
);
377 already_AddRefed
<GlyphCacheEntry
> InsertEntry(
378 const GlyphBuffer
& aBuffer
, const DeviceColor
& aColor
,
379 const Matrix
& aTransform
, const IntPoint
& aQuantizeScale
,
380 const IntRect
& aBounds
, const IntRect
& aFullBounds
, HashNumber aHash
,
381 const StrokeOptions
* aOptions
);
384 // Weak pointer to the owning font
388 struct QuantizedPath
{
389 explicit QuantizedPath(const WGR::Path
& aPath
);
390 // Ensure the path can only be moved, but not copied.
391 QuantizedPath(QuantizedPath
&&) noexcept
;
392 QuantizedPath(const QuantizedPath
&) = delete;
395 bool operator==(const QuantizedPath
&) const;
400 struct PathVertexRange
{
404 PathVertexRange() : mOffset(0), mLength(0) {}
405 PathVertexRange(uint32_t aOffset
, uint32_t aLength
)
406 : mOffset(aOffset
), mLength(aLength
) {}
408 bool IsValid() const { return mLength
> 0; }
411 // PathCacheEntry stores a rasterized version of a supplied path with a given
413 class PathCacheEntry
: public CacheEntryImpl
<PathCacheEntry
> {
415 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCacheEntry
, override
)
417 PathCacheEntry(QuantizedPath
&& aPath
, Pattern
* aPattern
,
418 StoredStrokeOptions
* aStrokeOptions
, const Matrix
& aTransform
,
419 const IntRect
& aBounds
, const Point
& aOrigin
, HashNumber aHash
,
420 float aSigma
= -1.0f
);
422 bool MatchesPath(const QuantizedPath
& aPath
, const Pattern
* aPattern
,
423 const StrokeOptions
* aStrokeOptions
,
424 const Matrix
& aTransform
, const IntRect
& aBounds
,
425 const Point
& aOrigin
, HashNumber aHash
, float aSigma
);
427 static HashNumber
HashPath(const QuantizedPath
& aPath
,
428 const Pattern
* aPattern
, const Matrix
& aTransform
,
429 const IntRect
& aBounds
, const Point
& aOrigin
);
431 const QuantizedPath
& GetPath() const { return mPath
; }
433 const Point
& GetOrigin() const { return mOrigin
; }
435 // Valid if either a mask (no pattern) or there is valid pattern.
436 bool IsValid() const override
{ return !mPattern
|| mPattern
->IsValid(); }
438 const PathVertexRange
& GetVertexRange() const { return mVertexRange
; }
439 void SetVertexRange(const PathVertexRange
& aRange
) { mVertexRange
= aRange
; }
442 // The actual path geometry supplied
444 // The transformed origin of the path
446 // The pattern used to rasterize the path, if not a mask
447 UniquePtr
<Pattern
> mPattern
;
448 // The StrokeOptions used for stroked paths, if applicable
449 UniquePtr
<StoredStrokeOptions
> mStrokeOptions
;
450 // The shadow blur sigma
452 // If the path has cached geometry in the vertex buffer.
453 PathVertexRange mVertexRange
;
456 class PathCache
: public CacheImpl
<PathCacheEntry
, true> {
458 PathCache() = default;
460 already_AddRefed
<PathCacheEntry
> FindOrInsertEntry(
461 QuantizedPath aPath
, const Pattern
* aPattern
,
462 const StrokeOptions
* aStrokeOptions
, const Matrix
& aTransform
,
463 const IntRect
& aBounds
, const Point
& aOrigin
, float aSigma
= -1.0f
);
465 void ClearVertexRanges();
468 } // namespace mozilla::gfx
470 #endif // _MOZILLA_GFX_DRAWTARGETWEBGL_INTERNAL_H