Bug 1857447 - Avoid redundant UniformData calls in DrawTargetWebgl. r=lsalzman

Calling ClientWebGLContext::UniformData() many times causes the
command buffer to fill up and we spend a fair amount of time flushing
the old buffer and allocating a new one, as well as serializing the
values.

The uniforms themselves are very small but they add up over a large
number of calls. We already have some code to track whether the
uniform values are dirty to avoid some redundancy, but a) this doesn't
cover every uniform, and b) we invalidate them all when switching
program.

This patch makes us track the value of every uniform that gets set
dynamically, and tracks the values separately for each program
used. It then uses these to avoid calling UniformData redundantly.

Differential Revision: https://phabricator.services.mozilla.com/D190269
This commit is contained in:
Jamie Nicol 2023-10-06 15:12:48 +00:00
parent 9b40b3e022
commit 836b0d935c
2 changed files with 126 additions and 116 deletions

View File

@ -490,7 +490,6 @@ void DrawTargetWebgl::SharedContext::SetBlendState(
// alpha that is blended separately from AA coverage. This would require two
// stage blending which can incur a substantial performance penalty, so to
// work around this currently we just disable AA for those ops.
mDirtyAA = true;
// Map the composition op to a WebGL blend mode, if possible.
bool enabled = true;
@ -556,8 +555,6 @@ bool DrawTargetWebgl::SharedContext::SetTarget(DrawTargetWebgl* aDT) {
mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER, aDT->mFramebuffer);
mViewportSize = aDT->GetSize();
mWebgl->Viewport(0, 0, mViewportSize.width, mViewportSize.height);
// Force the viewport to be reset.
mDirtyViewport = true;
}
}
return true;
@ -570,8 +567,6 @@ void DrawTargetWebgl::SharedContext::SetClipRect(const Rect& aClipRect) {
mClipAARect = aClipRect;
// Store the integer-aligned bounds.
mClipRect = RoundedOut(aClipRect);
// Notify the shader uniform it needs to update.
mDirtyClip = true;
}
}
@ -2018,6 +2013,19 @@ static inline Maybe<IntRect> IsAlignedRect(bool aTransformed,
return Nothing();
}
template <class T, size_t N>
void DrawTargetWebgl::SharedContext::MaybeUniformData(
GLenum aFuncElemType, const WebGLUniformLocationJS* const aLoc,
const Array<T, N>& aData, Maybe<Array<T, N>>& aCached) {
if (aCached.isNothing() || !(*aCached == aData)) {
aCached = Some(aData);
Span<const uint8_t> bytes = AsBytes(Span(aData));
// We currently always pass false for transpose. If in the future we need
// support for transpose then caching needs to take that in to account.
mWebgl->UniformData(aFuncElemType, aLoc, false, bytes);
}
}
// Common rectangle and pattern drawing function shared by many DrawTarget
// commands. If aMaskColor is specified, the provided surface pattern will be
// treated as a mask. If aHandle is specified, then the surface pattern's
@ -2145,47 +2153,38 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
if (mLastProgram != mSolidProgram) {
mWebgl->UseProgram(mSolidProgram);
mLastProgram = mSolidProgram;
// Ensure uniform state is current.
mDirtyViewport = true;
mDirtyAA = true;
mDirtyClip = true;
}
if (mDirtyViewport) {
float viewportData[2] = {float(mViewportSize.width),
float(mViewportSize.height)};
mWebgl->UniformData(
LOCAL_GL_FLOAT_VEC2, mSolidProgramViewport, false,
{(const uint8_t*)viewportData, sizeof(viewportData)});
mDirtyViewport = false;
}
if (mDirtyAA || aVertexRange) {
// Generated paths provide their own AA as vertex alpha.
float aaData = aVertexRange ? 0.0f : 1.0f;
mWebgl->UniformData(LOCAL_GL_FLOAT, mSolidProgramAA, false,
{(const uint8_t*)&aaData, sizeof(aaData)});
mDirtyAA = aaData == 0.0f;
}
if (mDirtyClip) {
// Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
// boundary.
float clipData[4] = {mClipAARect.x - 0.5f, mClipAARect.y - 0.5f,
mClipAARect.XMost() + 0.5f,
mClipAARect.YMost() + 0.5f};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramClipBounds, false,
{(const uint8_t*)clipData, sizeof(clipData)});
mDirtyClip = false;
}
float colorData[4] = {color.b, color.g, color.r, color.a};
Array<float, 2> viewportData = {float(mViewportSize.width),
float(mViewportSize.height)};
MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramViewport, viewportData,
mSolidProgramUniformState.mViewport);
// Generated paths provide their own AA as vertex alpha.
Array<float, 1> aaData = {aVertexRange ? 0.0f : 1.0f};
MaybeUniformData(LOCAL_GL_FLOAT, mSolidProgramAA, aaData,
mSolidProgramUniformState.mAA);
// Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
// boundary.
Array<float, 4> clipData = {mClipAARect.x - 0.5f, mClipAARect.y - 0.5f,
mClipAARect.XMost() + 0.5f,
mClipAARect.YMost() + 0.5f};
MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramClipBounds, clipData,
mSolidProgramUniformState.mClipBounds);
Array<float, 4> colorData = {color.b, color.g, color.r, color.a};
Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
if (aTransformed) {
xform *= currentTransform;
}
float xformData[6] = {xform._11, xform._12, xform._21,
xform._22, xform._31, xform._32};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramTransform, false,
{(const uint8_t*)xformData, sizeof(xformData)});
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramColor, false,
{(const uint8_t*)colorData, sizeof(colorData)});
Array<float, 6> xformData = {xform._11, xform._12, xform._21,
xform._22, xform._31, xform._32};
MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramTransform, xformData,
mSolidProgramUniformState.mTransform);
MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramColor, colorData,
mSolidProgramUniformState.mColor);
// Finally draw the colored rectangle.
if (aVertexRange) {
// If there's a vertex range, then we need to draw triangles within from
@ -2309,41 +2308,30 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
if (mLastProgram != mImageProgram) {
mWebgl->UseProgram(mImageProgram);
mLastProgram = mImageProgram;
// Ensure uniform state is current.
mDirtyViewport = true;
mDirtyAA = true;
mDirtyClip = true;
}
if (mDirtyViewport) {
float viewportData[2] = {float(mViewportSize.width),
float(mViewportSize.height)};
mWebgl->UniformData(
LOCAL_GL_FLOAT_VEC2, mImageProgramViewport, false,
{(const uint8_t*)viewportData, sizeof(viewportData)});
mDirtyViewport = false;
}
if (mDirtyAA || aVertexRange) {
// AA is not supported for OP_SOURCE. Generated paths provide their own
// AA as vertex alpha.
float aaData =
mLastCompositionOp == CompositionOp::OP_SOURCE || aVertexRange
? 0.0f
: 1.0f;
mWebgl->UniformData(LOCAL_GL_FLOAT, mImageProgramAA, false,
{(const uint8_t*)&aaData, sizeof(aaData)});
mDirtyAA = aaData == 0.0f;
}
if (mDirtyClip) {
// Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
// boundary.
float clipData[4] = {mClipAARect.x - 0.5f, mClipAARect.y - 0.5f,
mClipAARect.XMost() + 0.5f,
mClipAARect.YMost() + 0.5f};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramClipBounds, false,
{(const uint8_t*)clipData, sizeof(clipData)});
mDirtyClip = false;
}
Array<float, 2> viewportData = {float(mViewportSize.width),
float(mViewportSize.height)};
MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mImageProgramViewport, viewportData,
mImageProgramUniformState.mViewport);
// AA is not supported for OP_SOURCE. Generated paths provide their own
// AA as vertex alpha.
Array<float, 1> aaData = {
mLastCompositionOp == CompositionOp::OP_SOURCE || aVertexRange
? 0.0f
: 1.0f};
MaybeUniformData(LOCAL_GL_FLOAT, mImageProgramAA, aaData,
mImageProgramUniformState.mAA);
// Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
// boundary.
Array<float, 4> clipData = {mClipAARect.x - 0.5f, mClipAARect.y - 0.5f,
mClipAARect.XMost() + 0.5f,
mClipAARect.YMost() + 0.5f};
MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramClipBounds, clipData,
mImageProgramUniformState.mClipBounds);
DeviceColor color =
mLastCompositionOp == CompositionOp::OP_CLEAR
? DeviceColor(1, 1, 1, 1)
@ -2352,20 +2340,22 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
? DeviceColor::Mask(1.0f, aMaskColor->a)
: aMaskColor.valueOr(DeviceColor(1, 1, 1, 1)),
aOptions.mAlpha);
float colorData[4] = {color.b, color.g, color.r, color.a};
float swizzleData = format == SurfaceFormat::A8 ? 1.0f : 0.0f;
Array<float, 4> colorData = {color.b, color.g, color.r, color.a};
Array<float, 1> swizzleData = {format == SurfaceFormat::A8 ? 1.0f : 0.0f};
Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
if (aTransformed) {
xform *= currentTransform;
}
float xformData[6] = {xform._11, xform._12, xform._21,
xform._22, xform._31, xform._32};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC2, mImageProgramTransform, false,
{(const uint8_t*)xformData, sizeof(xformData)});
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramColor, false,
{(const uint8_t*)colorData, sizeof(colorData)});
mWebgl->UniformData(LOCAL_GL_FLOAT, mImageProgramSwizzle, false,
{(const uint8_t*)&swizzleData, sizeof(swizzleData)});
Array<float, 6> xformData = {xform._11, xform._12, xform._21,
xform._22, xform._31, xform._32};
MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mImageProgramTransform, xformData,
mImageProgramUniformState.mTransform);
MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramColor, colorData,
mImageProgramUniformState.mColor);
MaybeUniformData(LOCAL_GL_FLOAT, mImageProgramSwizzle, swizzleData,
mImageProgramUniformState.mSwizzle);
// Start binding the WebGL state for the texture.
BackingTexture* backing = nullptr;
@ -2415,20 +2405,20 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
1.0f / backingSizeF.height,
float(bounds.x - offset.x) / backingSizeF.width,
float(bounds.y - offset.y) / backingSizeF.height);
float uvData[6] = {uvMatrix._11, uvMatrix._12, uvMatrix._21,
uvMatrix._22, uvMatrix._31, uvMatrix._32};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC2, mImageProgramTexMatrix, false,
{(const uint8_t*)uvData, sizeof(uvData)});
Array<float, 6> uvData = {uvMatrix._11, uvMatrix._12, uvMatrix._21,
uvMatrix._22, uvMatrix._31, uvMatrix._32};
MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mImageProgramTexMatrix, uvData,
mImageProgramUniformState.mTexMatrix);
// Clamp sampling to within the bounds of the backing texture subrect.
float texBounds[4] = {
Array<float, 4> texBounds = {
(bounds.x + 0.5f) / backingSizeF.width,
(bounds.y + 0.5f) / backingSizeF.height,
(bounds.XMost() - 0.5f) / backingSizeF.width,
(bounds.YMost() - 0.5f) / backingSizeF.height,
};
mWebgl->UniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramTexBounds, false,
{(const uint8_t*)texBounds, sizeof(texBounds)});
MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mImageProgramTexBounds, texBounds,
mImageProgramUniformState.mTexBounds);
// Ensure we use nearest filtering when no antialiasing is requested.
if (UseNearestFilter(surfacePattern)) {
@ -3109,22 +3099,23 @@ already_AddRefed<TextureHandle> DrawTargetWebgl::SharedContext::DrawStrokeMask(
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)});
Array<float, 2> viewportData = {float(texBounds.width),
float(texBounds.height)};
MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramViewport, viewportData,
mSolidProgramUniformState.mViewport);
Array<float, 1> aaData = {0.0f};
MaybeUniformData(LOCAL_GL_FLOAT, mSolidProgramAA, aaData,
mSolidProgramUniformState.mAA);
Array<float, 4> clipData = {-0.5f, -0.5f, float(texBounds.width) + 0.5f,
float(texBounds.height) + 0.5f};
MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramClipBounds, clipData,
mSolidProgramUniformState.mClipBounds);
Array<float, 4> colorData = {1.0f, 1.0f, 1.0f, 1.0f};
MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mSolidProgramColor, colorData,
mSolidProgramUniformState.mColor);
Array<float, 6> xformData = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mSolidProgramTransform, xformData,
mSolidProgramUniformState.mTransform);
// Ensure the current clip mask is ignored.
RefPtr<WebGLTextureJS> prevClipMask = mLastClipMask;
@ -3137,9 +3128,6 @@ already_AddRefed<TextureHandle> DrawTargetWebgl::SharedContext::DrawStrokeMask(
// 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;
if (prevClipMask) {
SetClipMask(prevClipMask);
}

View File

@ -7,6 +7,8 @@
#ifndef _MOZILLA_GFX_DRAWTARGETWEBGL_H
#define _MOZILLA_GFX_DRAWTARGETWEBGL_H
#include "GLTypes.h"
#include "mozilla/Array.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/PathSkia.h"
#include "mozilla/LinkedList.h"
@ -180,12 +182,6 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
RefPtr<WebGLProgramJS> mLastProgram;
RefPtr<WebGLTextureJS> mLastTexture;
RefPtr<WebGLTextureJS> mLastClipMask;
// Whether the shader viewport state requires updating.
bool mDirtyViewport = true;
// Whether the shader anti-aliasing state requires updating.
bool mDirtyAA = true;
// Whether the shader clip AA bounds require updating.
bool mDirtyClip = true;
// WebGL shader resources
RefPtr<WebGLBufferJS> mPathVertexBuffer;
@ -221,6 +217,25 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
RefPtr<WebGLUniformLocationJS> mImageProgramClipMask;
RefPtr<WebGLUniformLocationJS> mImageProgramClipBounds;
struct SolidProgramUniformState {
Maybe<Array<float, 2>> mViewport;
Maybe<Array<float, 1>> mAA;
Maybe<Array<float, 6>> mTransform;
Maybe<Array<float, 4>> mColor;
Maybe<Array<float, 4>> mClipBounds;
} mSolidProgramUniformState;
struct ImageProgramUniformState {
Maybe<Array<float, 2>> mViewport;
Maybe<Array<float, 1>> mAA;
Maybe<Array<float, 6>> mTransform;
Maybe<Array<float, 6>> mTexMatrix;
Maybe<Array<float, 4>> mTexBounds;
Maybe<Array<float, 4>> mColor;
Maybe<Array<float, 1>> mSwizzle;
Maybe<Array<float, 4>> mClipBounds;
} mImageProgramUniformState;
// Scratch framebuffer used to wrap textures for miscellaneous utility ops.
RefPtr<WebGLFramebufferJS> mScratchFramebuffer;
// Buffer filled with zero data for initializing textures.
@ -293,6 +308,13 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
return mLastClipMask && mLastClipMask != mNoClipMask;
}
// Avoids redundant UniformData calls by caching the previously set value.
template <class T, size_t N>
void MaybeUniformData(GLenum aFuncElemType,
const WebGLUniformLocationJS* const aLoc,
const Array<T, N>& aData,
Maybe<Array<T, N>>& aCached);
bool IsCurrentTarget(DrawTargetWebgl* aDT) const {
return aDT == mCurrentTarget;
}