Bug 1837068 - Support alpha strokes by rendering with AAStroke to a mask render target. r=aosmond

Since AAStroke can't deal with non-opaque stroked path, we first generate a normal opaque, anti-aliased
stroked path with AAStroke and render it to a cache texture bound to a render target. We can then later
just use that texture with alpha to support the initial alpha stroke request.

One caveat is that trying to both render to a texture bound to a framebuffer and also upload directly to
it with texSubImage2D can expose bugs in some OpenGL drivers that have different underlying representations
for data textures and for render target textures. To avoid this problem, we segregate the texture cache
pages based on whether they are used as render targets or for direct data uploads.

This ultimately all avoids the fallback of having to draw the alpha stroke in software with Skia and
then upload it to a texture. For stroked paths with large hollow areas, uploading a Skia surface whose
bounds contain the full stroke can cause a lot of uploading of unnecessary pixel data. This allows us
to only upload the triangle mesh for AAStroke and otherwise keep generation solely on the GPU.

Differential Revision: https://phabricator.services.mozilla.com/D180143
This commit is contained in:
Lee Salzman 2023-06-07 19:47:04 +00:00
Родитель 06417f4d0e
Коммит 5676a2eb44
3 изменённых файлов: 318 добавлений и 151 удалений

Просмотреть файл

@ -151,11 +151,14 @@ bool TexturePacker::Remove(const IntRect& aBounds) {
return removed;
}
BackingTexture::BackingTexture(const IntSize& aSize, SurfaceFormat aFormat,
const RefPtr<WebGLTextureJS>& aTexture)
: mSize(aSize), mFormat(aFormat), mTexture(aTexture) {}
SharedTexture::SharedTexture(const IntSize& aSize, SurfaceFormat aFormat,
const RefPtr<WebGLTextureJS>& aTexture)
: mPacker(IntRect(IntPoint(0, 0), aSize)),
mFormat(aFormat),
mTexture(aTexture) {}
: BackingTexture(aSize, aFormat, aTexture),
mPacker(IntRect(IntPoint(0, 0), aSize)) {}
SharedTextureHandle::SharedTextureHandle(const IntRect& aBounds,
SharedTexture* aTexture)
@ -185,7 +188,7 @@ bool SharedTexture::Free(const SharedTextureHandle& aHandle) {
StandaloneTexture::StandaloneTexture(const IntSize& aSize,
SurfaceFormat aFormat,
const RefPtr<WebGLTextureJS>& aTexture)
: mSize(aSize), mFormat(aFormat), mTexture(aTexture) {}
: BackingTexture(aSize, aFormat, aTexture) {}
DrawTargetWebgl::DrawTargetWebgl() = default;
@ -484,7 +487,7 @@ void DrawTargetWebgl::SharedContext::SetBlendState(
mDirtyAA = true;
// Map the composition op to a WebGL blend mode, if possible.
mWebgl->Enable(LOCAL_GL_BLEND);
bool enabled = true;
switch (aOp) {
case CompositionOp::OP_OVER:
if (aColor) {
@ -513,7 +516,7 @@ void DrawTargetWebgl::SharedContext::SetBlendState(
mWebgl->BlendFunc(LOCAL_GL_CONSTANT_COLOR,
LOCAL_GL_ONE_MINUS_SRC_COLOR);
} else {
mWebgl->Disable(LOCAL_GL_BLEND);
enabled = false;
}
break;
case CompositionOp::OP_CLEAR:
@ -525,9 +528,15 @@ void DrawTargetWebgl::SharedContext::SetBlendState(
LOCAL_GL_ONE_MINUS_SRC_ALPHA);
break;
default:
mWebgl->Disable(LOCAL_GL_BLEND);
enabled = false;
break;
}
if (enabled) {
mWebgl->Enable(LOCAL_GL_BLEND);
} else {
mWebgl->Disable(LOCAL_GL_BLEND);
}
}
// Ensure the WebGL framebuffer is set to the current target.
@ -860,7 +869,7 @@ void* DrawTargetWebgl::GetNativeSurface(NativeSurfaceType aType) {
already_AddRefed<TextureHandle> DrawTargetWebgl::SharedContext::WrapSnapshot(
const IntSize& aSize, SurfaceFormat aFormat, RefPtr<WebGLTextureJS> aTex) {
// Ensure there is enough space for the texture.
size_t usedBytes = TextureHandle::UsedBytes(aFormat, aSize);
size_t usedBytes = BackingTexture::UsedBytes(aFormat, aSize);
PruneTextureMemory(usedBytes, false);
// Allocate a handle for the texture
RefPtr<StandaloneTexture> handle =
@ -913,7 +922,7 @@ already_AddRefed<TextureHandle> DrawTargetWebgl::SharedContext::CopySnapshot(
mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, mScratchFramebuffer);
mWebgl->FramebufferTexture2D(
LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D,
aHandle->GetWebGLTexture(), 0);
aHandle->GetBackingTexture()->GetWebGLTexture(), 0);
}
// Create a texture to hold the copy
@ -1021,7 +1030,7 @@ bool DrawTargetWebgl::SharedContext::ReadInto(uint8_t* aDstData,
mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, mScratchFramebuffer);
mWebgl->FramebufferTexture2D(
LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D,
aHandle->GetWebGLTexture(), 0);
aHandle->GetBackingTexture()->GetWebGLTexture(), 0);
} else if (mCurrentTarget && mCurrentTarget->mIsClear) {
// If reading from a target that is still clear, then avoid the readback by
// just clearing the data.
@ -1781,7 +1790,8 @@ DrawTargetWebgl::SharedContext::GetCompatibleSnapshot(
if (this == webglSurf->mSharedContext) {
// If there is a snapshot copy in a texture handle, use that.
if (webglSurf->mHandle) {
return do_AddRef(webglSurf->mHandle->GetWebGLTexture());
return do_AddRef(
webglSurf->mHandle->GetBackingTexture()->GetWebGLTexture());
}
if (RefPtr<DrawTargetWebgl> webglDT = webglSurf->GetTarget()) {
// If there is a copy-on-write reference to a target, use its backing
@ -1888,6 +1898,84 @@ bool DrawTargetWebgl::SharedContext::UploadSurface(
return true;
}
// Allocate a new texture handle backed by either a standalone texture or as a
// sub-texture of a larger shared texture.
already_AddRefed<TextureHandle>
DrawTargetWebgl::SharedContext::AllocateTextureHandle(SurfaceFormat aFormat,
const IntSize& aSize,
bool aAllowShared,
bool aRenderable) {
RefPtr<TextureHandle> handle;
// Calculate the bytes that would be used by this texture handle, and prune
// enough other textures to ensure we have that much usable texture space
// available to allocate.
size_t usedBytes = BackingTexture::UsedBytes(aFormat, aSize);
PruneTextureMemory(usedBytes, false);
// The requested page size for shared textures.
int32_t pageSize = int32_t(std::min(
StaticPrefs::gfx_canvas_accelerated_shared_page_size(), mMaxTextureSize));
if (aAllowShared && std::max(aSize.width, aSize.height) <= pageSize / 2) {
// Ensure that the surface is no bigger than a quadrant of a shared texture
// page. If so, try to allocate it to a shared texture. Look for any
// existing shared texture page with a matching format and allocate
// from that if possible.
for (auto& shared : mSharedTextures) {
if (shared->GetFormat() == aFormat &&
shared->IsRenderable() == aRenderable) {
bool wasEmpty = !shared->HasAllocatedHandles();
handle = shared->Allocate(aSize);
if (handle) {
if (wasEmpty) {
// If the page was previously empty, then deduct it from the
// empty memory reserves.
mEmptyTextureMemory -= shared->UsedBytes();
}
break;
}
}
}
// If we couldn't find an existing shared texture page with matching
// format, then allocate a new page to put the request in.
if (!handle) {
if (RefPtr<WebGLTextureJS> tex = mWebgl->CreateTexture()) {
RefPtr<SharedTexture> shared =
new SharedTexture(IntSize(pageSize, pageSize), aFormat, tex);
if (aRenderable) {
shared->MarkRenderable();
}
mSharedTextures.push_back(shared);
mTotalTextureMemory += shared->UsedBytes();
handle = shared->Allocate(aSize);
}
}
} else {
// The surface wouldn't fit in a shared texture page, so we need to
// allocate a standalone texture for it instead.
if (RefPtr<WebGLTextureJS> tex = mWebgl->CreateTexture()) {
RefPtr<StandaloneTexture> standalone =
new StandaloneTexture(aSize, aFormat, tex);
if (aRenderable) {
standalone->MarkRenderable();
}
mStandaloneTextures.push_back(standalone);
mTotalTextureMemory += standalone->UsedBytes();
handle = standalone;
}
}
if (!handle) {
return nullptr;
}
// Insert the new texture handle into the front of the MRU list and
// update used space for it.
mTextureHandles.insertFront(handle);
++mNumTextureHandles;
mUsedTextureMemory += handle->UsedBytes();
return handle.forget();
}
static inline SamplingFilter GetSamplingFilter(const Pattern& aPattern) {
return aPattern.GetType() == PatternType::SURFACE
? static_cast<const SurfacePattern&>(aPattern).mSamplingFilter
@ -2150,7 +2238,6 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
IntRect bounds;
IntSize backingSize;
RefPtr<DataSourceSurface> data;
bool init = false;
if (handle) {
if (aForceUpdate) {
data = surfacePattern.mSurface->GetDataSurface();
@ -2177,76 +2264,14 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
if (!data) {
break;
}
// There is no existing handle. Calculate the bytes that would be used
// by this texture, and prune enough other textures to ensure we have
// that much usable texture space available to allocate.
size_t usedBytes = TextureHandle::UsedBytes(format, texSize);
PruneTextureMemory(usedBytes, false);
// The requested page size for shared textures.
int32_t pageSize = int32_t(
std::min(StaticPrefs::gfx_canvas_accelerated_shared_page_size(),
mMaxTextureSize));
if (!aForceUpdate &&
std::max(texSize.width, texSize.height) <= pageSize / 2) {
// Ensure that the surface size won't change via forced update and
// that the surface is no bigger than a quadrant of a shared texture
// page. If so, try to allocate it to a shared texture. Look for any
// existing shared texture page with a matching format and allocate
// from that if possible.
for (auto& shared : mSharedTextures) {
if (shared->GetFormat() == format) {
bool wasEmpty = !shared->HasAllocatedHandles();
handle = shared->Allocate(texSize);
if (handle) {
if (wasEmpty) {
// If the page was previously empty, then deduct it from the
// empty memory reserves.
mEmptyTextureMemory -= shared->UsedBytes();
}
break;
}
}
}
// If we couldn't find an existing shared texture page with matching
// format, then allocate a new page to put the request in.
if (!handle) {
tex = mWebgl->CreateTexture();
if (!tex) {
MOZ_ASSERT(false);
break;
}
RefPtr<SharedTexture> shared =
new SharedTexture(IntSize(pageSize, pageSize), format, tex);
mSharedTextures.push_back(shared);
mTotalTextureMemory += shared->UsedBytes();
handle = shared->Allocate(texSize);
if (!handle) {
MOZ_ASSERT(false);
break;
}
init = true;
}
} else {
// The surface wouldn't fit in a shared texture page, so we need to
// allocate a standalone texture for it instead.
tex = mWebgl->CreateTexture();
if (!tex) {
MOZ_ASSERT(false);
break;
}
RefPtr<StandaloneTexture> standalone =
new StandaloneTexture(texSize, format, tex);
mStandaloneTextures.push_back(standalone);
mTotalTextureMemory += standalone->UsedBytes();
handle = standalone;
init = true;
// There is no existing handle. Try to allocate a new one. If the
// surface size may change via a forced update, then don't allocate
// from a shared texture page.
handle = AllocateTextureHandle(format, texSize, !aForceUpdate);
if (!handle) {
MOZ_ASSERT(false);
break;
}
// Insert the new texture handle into the front of the MRU list and
// update used space for it.
mTextureHandles.insertFront(handle);
++mNumTextureHandles;
mUsedTextureMemory += handle->UsedBytes();
// Link the handle to the surface's user data.
handle->SetSamplingOffset(surfacePattern.mSamplingRect.TopLeft());
if (aHandle) {
@ -2327,21 +2352,24 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
{(const uint8_t*)&swizzleData, sizeof(swizzleData)});
// Start binding the WebGL state for the texture.
BackingTexture* backing = nullptr;
if (handle) {
backing = handle->GetBackingTexture();
if (!tex) {
tex = handle->GetWebGLTexture();
tex = backing->GetWebGLTexture();
}
bounds = handle->GetBounds();
backingSize = handle->GetBackingSize();
backingSize = backing->GetSize();
}
if (mLastTexture != tex) {
mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
mLastTexture = tex;
}
if (init) {
if (backing && !backing->IsInitialized()) {
// If this is the first time the texture is used, we need to initialize
// the clamping and filtering state.
backing->MarkInitialized();
InitTexParameters(tex);
if (texSize != backingSize) {
// If this is a shared texture handle whose actual backing texture is
@ -2968,27 +2996,124 @@ inline bool DrawTargetWebgl::ShouldAccelPath(
return mWebglValid && SupportsDrawOptions(aOptions) && PrepareContext();
}
// For now, we only support stroking solid color patterns to limit artifacts
// from blending of overlapping geometry generated by AAStroke.
static inline bool SupportsAAStroke(const Pattern& aPattern,
const DrawOptions& aOptions,
const StrokeOptions& aStrokeOptions) {
enum class AAStrokeMode {
Unsupported,
Geometry,
Mask,
};
// For now, we only directly support stroking solid color patterns to limit
// artifacts from blending of overlapping geometry generated by AAStroke. Other
// types of patterns may be partially supported by rendering to a temporary
// mask.
static inline AAStrokeMode SupportsAAStroke(
const Pattern& aPattern, const DrawOptions& aOptions,
const StrokeOptions& aStrokeOptions) {
if (aStrokeOptions.mDashPattern) {
return false;
return AAStrokeMode::Unsupported;
}
switch (aOptions.mCompositionOp) {
case CompositionOp::OP_SOURCE:
return true;
return AAStrokeMode::Geometry;
case CompositionOp::OP_OVER:
return aPattern.GetType() == PatternType::COLOR &&
static_cast<const ColorPattern&>(aPattern).mColor.a *
aOptions.mAlpha ==
1.0f;
if (aPattern.GetType() == PatternType::COLOR) {
return static_cast<const ColorPattern&>(aPattern).mColor.a *
aOptions.mAlpha <
1.0f
? AAStrokeMode::Mask
: AAStrokeMode::Geometry;
}
return AAStrokeMode::Unsupported;
default:
return false;
return AAStrokeMode::Unsupported;
}
}
// Render an AA-Stroke'd vertex range into an R8 mask texture for subsequent
// drawing.
already_AddRefed<TextureHandle> DrawTargetWebgl::SharedContext::DrawStrokeMask(
const PathVertexRange& aVertexRange, const IntSize& aSize) {
// Allocate a new texture handle to store the rendered mask.
RefPtr<TextureHandle> handle =
AllocateTextureHandle(SurfaceFormat::A8, aSize, true, true);
if (!handle) {
return nullptr;
}
IntRect texBounds = handle->GetBounds();
BackingTexture* backing = handle->GetBackingTexture();
if (!backing->IsInitialized()) {
// If the backing texture is uninitialized, it needs its sampling parameters
// set for later use.
mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, backing->GetWebGLTexture());
mWebgl->TexStorage2D(LOCAL_GL_TEXTURE_2D, 1, LOCAL_GL_R8,
backing->GetSize().width, backing->GetSize().height);
InitTexParameters(backing->GetWebGLTexture());
ClearLastTexture();
}
// Set up a scratch framebuffer to render to the appropriate sub-texture of
// the backing texture.
if (!mScratchFramebuffer) {
mScratchFramebuffer = mWebgl->CreateFramebuffer();
}
mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, mScratchFramebuffer);
mWebgl->FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
LOCAL_GL_TEXTURE_2D, backing->GetWebGLTexture(),
0);
mWebgl->Viewport(texBounds.x, texBounds.y, texBounds.width, texBounds.height);
if (!backing->IsInitialized()) {
backing->MarkInitialized();
// If the backing texture is uninitialized, then clear the entire backing
// texture to initialize it.
DisableScissor();
} else {
// Clear only the sub-texture.
EnableScissor(texBounds);
}
// Ensure the mask background is clear.
mWebgl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
mWebgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT);
// Reset any blending when drawing the mask.
SetBlendState(CompositionOp::OP_OVER);
// Set up the solid color shader to draw a simple opaque mask.
if (mLastProgram != mSolidProgram) {
mWebgl->UseProgram(mSolidProgram);
mLastProgram = mSolidProgram;
}
float viewportData[2] = {float(texBounds.width), float(texBounds.height)};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramViewport, false,
{(const uint8_t*)viewportData, sizeof(viewportData)});
float aaData = 0.0f;
mWebgl->UniformData(LOCAL_GL_FLOAT, mSolidProgramAA, false,
{(const uint8_t*)&aaData, sizeof(aaData)});
float clipData[4] = {-0.5f, -0.5f, float(texBounds.width) + 0.5f,
float(texBounds.height) + 0.5f};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramClipBounds, false,
{(const uint8_t*)clipData, sizeof(clipData)});
float colorData[4] = {1.0f, 1.0f, 1.0f, 1.0f};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramColor, false,
{(const uint8_t*)colorData, sizeof(colorData)});
float xformData[6] = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramTransform, false,
{(const uint8_t*)xformData, sizeof(xformData)});
// Draw the mask using the supplied path vertex range.
mWebgl->DrawArrays(LOCAL_GL_TRIANGLES, GLint(aVertexRange.mOffset),
GLsizei(aVertexRange.mLength));
// Restore the previous framebuffer state.
mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, mCurrentTarget->mFramebuffer);
mWebgl->Viewport(0, 0, mViewportSize.width, mViewportSize.height);
mDirtyViewport = true;
mDirtyAA = true;
mDirtyClip = true;
return handle.forget();
}
bool DrawTargetWebgl::SharedContext::DrawPathAccel(
const Path* aPath, const Pattern& aPattern, const DrawOptions& aOptions,
const StrokeOptions* aStrokeOptions, const ShadowOptions* aShadow,
@ -3117,7 +3242,8 @@ bool DrawTargetWebgl::SharedContext::DrawPathAccel(
mRasterizationTruncates, outputBuffer, outputBufferCapacity);
} else {
if (mPathAAStroke &&
SupportsAAStroke(aPattern, aOptions, *aStrokeOptions)) {
SupportsAAStroke(aPattern, aOptions, *aStrokeOptions) !=
AAStrokeMode::Unsupported) {
auto scaleFactors = currentTransform.ScaleFactors();
if (scaleFactors.AreScalesSame()) {
strokeVB = GenerateStrokeVertexBuffer(
@ -3182,9 +3308,6 @@ bool DrawTargetWebgl::SharedContext::DrawPathAccel(
PathVertexRange vertexRange(
uint32_t(mPathVertexOffset / sizeof(WGR::OutputVertex)),
uint32_t(vbLen));
if (entry) {
entry->SetVertexRange(vertexRange);
}
// printf_stderr(" ... offset %d\n", mPathVertexOffset);
// Normal glBufferSubData interleaved with draw calls causes performance
// issues on Mali, so use our special unsynchronized version. This is
@ -3192,22 +3315,48 @@ bool DrawTargetWebgl::SharedContext::DrawPathAccel(
mWebgl->RawBufferSubData(LOCAL_GL_ARRAY_BUFFER, mPathVertexOffset,
vbData, vertexBytes,
/* unsynchronized */ true);
mPathVertexOffset += vertexBytes;
if (wgrVB) {
WGR::wgr_vertex_buffer_release(wgrVB.ref());
} else {
AAStroke::aa_stroke_vertex_buffer_release(strokeVB.ref());
}
// Finally, draw the uploaded vertex data.
mCurrentTarget->mProfile.OnCacheMiss();
return DrawRectAccel(Rect(intBounds.TopLeft(), Size(1, 1)), aPattern,
aOptions, Nothing(), nullptr, false, true, true,
false, nullptr, &vertexRange);
}
if (wgrVB) {
WGR::wgr_vertex_buffer_release(wgrVB.ref());
if (strokeVB && SupportsAAStroke(aPattern, aOptions, *aStrokeOptions) ==
AAStrokeMode::Mask) {
// Attempt to generate a stroke mask for path.
if (RefPtr<TextureHandle> handle =
DrawStrokeMask(vertexRange, intBounds.Size())) {
// Finally, draw the rendered stroke mask.
if (entry) {
entry->Link(handle);
}
mCurrentTarget->mProfile.OnCacheMiss();
SurfacePattern maskPattern(
nullptr, ExtendMode::CLAMP,
Matrix::Translation(quantBounds.TopLeft()),
SamplingFilter::GOOD);
return DrawRectAccel(quantBounds, maskPattern, aOptions, color,
&handle, false, true, true);
}
} else {
// Remember the vertex range in the cache entry so that it can be
// reused later.
if (entry) {
entry->SetVertexRange(vertexRange);
}
mPathVertexOffset += vertexBytes;
// Finally, draw the uploaded vertex data.
mCurrentTarget->mProfile.OnCacheMiss();
return DrawRectAccel(Rect(intBounds.TopLeft(), Size(1, 1)), aPattern,
aOptions, Nothing(), nullptr, false, true, true,
false, nullptr, &vertexRange);
}
} else {
AAStroke::aa_stroke_vertex_buffer_release(strokeVB.ref());
if (wgrVB) {
WGR::wgr_vertex_buffer_release(wgrVB.ref());
} else {
AAStroke::aa_stroke_vertex_buffer_release(strokeVB.ref());
}
}
// If we failed to draw the vertex data for some reason, then fall through
// to the texture rasterization path.

Просмотреть файл

@ -329,6 +329,9 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
const IntRect& aSrcRect, const IntPoint& aDstOffset,
bool aInit, bool aZero = false,
const RefPtr<WebGLTextureJS>& aTex = nullptr);
already_AddRefed<TextureHandle> AllocateTextureHandle(
SurfaceFormat aFormat, const IntSize& aSize, bool aAllowShared = true,
bool aRenderable = false);
bool DrawRectAccel(const Rect& aRect, const Pattern& aPattern,
const DrawOptions& aOptions,
Maybe<DeviceColor> aMaskColor = Nothing(),
@ -338,6 +341,8 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
const StrokeOptions* aStrokeOptions = nullptr,
const PathVertexRange* aVertexRange = nullptr);
already_AddRefed<TextureHandle> DrawStrokeMask(
const PathVertexRange& aVertexRange, const IntSize& aSize);
bool DrawPathAccel(const Path* aPath, const Pattern& aPattern,
const DrawOptions& aOptions,
const StrokeOptions* aStrokeOptions = nullptr,

Просмотреть файл

@ -132,6 +132,45 @@ class CacheImpl {
ListType mChains[kNumChains];
};
// BackingTexture provides information about the shared or standalone texture
// that is backing a texture handle.
class BackingTexture {
public:
BackingTexture(const IntSize& aSize, SurfaceFormat aFormat,
const RefPtr<WebGLTextureJS>& aTexture);
SurfaceFormat GetFormat() const { return mFormat; }
IntSize GetSize() const { return mSize; }
static inline size_t UsedBytes(SurfaceFormat aFormat, const IntSize& aSize) {
return size_t(BytesPerPixel(aFormat)) * size_t(aSize.width) *
size_t(aSize.height);
}
size_t UsedBytes() const { return UsedBytes(GetFormat(), GetSize()); }
const RefPtr<WebGLTextureJS>& GetWebGLTexture() const { return mTexture; }
bool IsInitialized() const { return mFlags & INITIALIZED; }
void MarkInitialized() { mFlags |= INITIALIZED; }
bool IsRenderable() const { return mFlags & RENDERABLE; }
void MarkRenderable() { mFlags |= RENDERABLE; }
protected:
IntSize mSize;
SurfaceFormat mFormat;
RefPtr<WebGLTextureJS> mTexture;
private:
enum Flags : uint8_t {
INITIALIZED = 1 << 0,
RENDERABLE = 1 << 1,
};
uint8_t mFlags = 0;
};
// TextureHandle is an abstract base class for supplying textures to drawing
// commands that may be backed by different resource types (such as a shared
// or standalone texture). It may be further linked to use-specific metadata
@ -144,16 +183,14 @@ class TextureHandle : public RefCounted<TextureHandle>,
enum Type { SHARED, STANDALONE };
virtual Type GetType() const = 0;
virtual const RefPtr<WebGLTextureJS>& GetWebGLTexture() const = 0;
virtual IntRect GetBounds() const = 0;
IntSize GetSize() const { return GetBounds().Size(); }
virtual IntSize GetBackingSize() const = 0;
virtual SurfaceFormat GetFormat() const = 0;
virtual size_t UsedBytes() const = 0;
static inline size_t UsedBytes(SurfaceFormat aFormat, const IntSize& aSize) {
return size_t(BytesPerPixel(aFormat)) * size_t(aSize.width) *
size_t(aSize.height);
virtual BackingTexture* GetBackingTexture() = 0;
size_t UsedBytes() const {
return BackingTexture::UsedBytes(GetFormat(), GetSize());
}
virtual void UpdateSize(const IntSize& aSize) {}
@ -208,7 +245,7 @@ class SharedTextureHandle;
// SharedTexture is a large slab texture that is subdivided (by using a
// TexturePacker) to hold many small SharedTextureHandles. This avoids needing
// to allocate many WebGL textures for every single small Canvas 2D texture.
class SharedTexture : public RefCounted<SharedTexture> {
class SharedTexture : public RefCounted<SharedTexture>, public BackingTexture {
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(SharedTexture)
@ -218,21 +255,10 @@ class SharedTexture : public RefCounted<SharedTexture> {
already_AddRefed<SharedTextureHandle> Allocate(const IntSize& aSize);
bool Free(const SharedTextureHandle& aHandle);
SurfaceFormat GetFormat() const { return mFormat; }
IntSize GetSize() const { return mPacker.GetBounds().Size(); }
size_t UsedBytes() const {
return TextureHandle::UsedBytes(GetFormat(), GetSize());
}
bool HasAllocatedHandles() const { return mAllocatedHandles > 0; }
const RefPtr<WebGLTextureJS>& GetWebGLTexture() const { return mTexture; }
private:
TexturePacker mPacker;
SurfaceFormat mFormat;
RefPtr<WebGLTextureJS> mTexture;
size_t mAllocatedHandles = 0;
};
@ -248,18 +274,11 @@ class SharedTextureHandle : public TextureHandle {
Type GetType() const override { return Type::SHARED; }
const RefPtr<WebGLTextureJS>& GetWebGLTexture() const override {
return mTexture->GetWebGLTexture();
}
IntRect GetBounds() const override { return mBounds; }
IntSize GetBackingSize() const override { return mTexture->GetSize(); }
SurfaceFormat GetFormat() const override { return mTexture->GetFormat(); }
size_t UsedBytes() const override {
return TextureHandle::UsedBytes(GetFormat(), mBounds.Size());
}
BackingTexture* GetBackingTexture() override { return mTexture.get(); }
void Cleanup(DrawTargetWebgl::SharedContext& aContext) override;
@ -273,7 +292,7 @@ class SharedTextureHandle : public TextureHandle {
// StandaloneTexture is a texture that can not be effectively shared within
// a SharedTexture page, such that it is better to assign it its own WebGL
// texture.
class StandaloneTexture : public TextureHandle {
class StandaloneTexture : public TextureHandle, public BackingTexture {
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(StandaloneTexture, override)
@ -282,27 +301,21 @@ class StandaloneTexture : public TextureHandle {
Type GetType() const override { return Type::STANDALONE; }
SurfaceFormat GetFormat() const override { return mFormat; }
const RefPtr<WebGLTextureJS>& GetWebGLTexture() const override {
return mTexture;
IntRect GetBounds() const override {
return IntRect(IntPoint(0, 0), BackingTexture::GetSize());
}
IntRect GetBounds() const override { return IntRect(IntPoint(0, 0), mSize); }
IntSize GetBackingSize() const override { return mSize; }
size_t UsedBytes() const override {
return TextureHandle::UsedBytes(mFormat, mSize);
SurfaceFormat GetFormat() const override {
return BackingTexture::GetFormat();
}
using BackingTexture::UsedBytes;
BackingTexture* GetBackingTexture() override { return this; }
void UpdateSize(const IntSize& aSize) override { mSize = aSize; }
void Cleanup(DrawTargetWebgl::SharedContext& aContext) override;
private:
IntSize mSize;
SurfaceFormat mFormat;
RefPtr<WebGLTextureJS> mTexture;
};
// GlyphCacheEntry stores rendering metadata for a rendered text run, as well