Bug 1826420 - Fast-path non-aligned clip rects in DrawTargetWebgl. r=gfx-reviewers,aosmond

This adds interpolants to the AA distance calculation to handle the AA'ing of
the clip rect.

Differential Revision: https://phabricator.services.mozilla.com/D174650
This commit is contained in:
Lee Salzman 2023-04-05 15:52:36 +00:00
Родитель aebcad9703
Коммит 805dbc2586
2 изменённых файлов: 109 добавлений и 31 удалений

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

@ -547,6 +547,18 @@ bool DrawTargetWebgl::SharedContext::SetTarget(DrawTargetWebgl* aDT) {
return true;
}
// Replace the current clip rect with a new potentially-AA'd clip rect.
void DrawTargetWebgl::SharedContext::SetClipRect(const Rect& aClipRect) {
// Only invalidate the clip rect if it actually changes.
if (!mClipAARect.IsEqualEdges(aClipRect)) {
mClipAARect = aClipRect;
// Store the integer-aligned bounds.
mClipRect = RoundedOut(aClipRect);
// Notify the shader uniform it needs to update.
mDirtyClip = true;
}
}
bool DrawTargetWebgl::SharedContext::SetClipMask(
const RefPtr<WebGLTextureJS>& aTex) {
if (mLastClipMask != aTex) {
@ -645,6 +657,7 @@ bool DrawTargetWebgl::GenerateComplexClipMask() {
// If we can't get bounds, then just use the entire viewport.
mClipBounds = GetRect();
}
mClipAARect = Rect(mClipBounds);
// If initializing the clip mask, then allocate the entire texture to ensure
// all pixels get filled with an empty mask regardless. Otherwise, restrict
// uploading to only the clip region.
@ -703,18 +716,34 @@ bool DrawTargetWebgl::SetSimpleClipRect() {
// Determine whether the clipping rectangle is simple enough to accelerate.
// Check if there is a device space clip rectangle available from the Skia
// target.
Maybe<IntRect> clip = mSkia->GetDeviceClipRect(false);
if (!clip) {
return false;
if (Maybe<IntRect> clip = mSkia->GetDeviceClipRect(false)) {
// If the clip is empty, leave the final integer clip rectangle empty to
// trivially discard the draw request.
// If the clip rect is larger than the viewport, just set it to the
// viewport.
if (!clip->IsEmpty() && clip->Contains(GetRect())) {
clip = Some(GetRect());
}
mSharedContext->SetClipRect(*clip);
mSharedContext->SetNoClipMask();
return true;
}
// If the clip is empty, leave the final integer clip rectangle empty to
// trivially discard the draw request.
// If the clip rect is larger than the viewport, just set it to the
// viewport.
if (!clip->IsEmpty() && clip->Contains(GetRect())) {
clip = Some(GetRect());
// There was no pixel-aligned clip rect available, so check the clip stack to
// see if there is an AA'd axis-aligned rectangle clip.
Rect rect(GetRect());
for (auto& clipStack : mClipStack) {
// If clip is a path or it has a non-axis-aligned transform, then it is
// complex.
if (clipStack.mPath ||
!clipStack.mTransform.PreservesAxisAlignedRectangles()) {
return false;
}
// Transform the rect and intersect it with the current clip.
rect =
clipStack.mTransform.TransformBounds(clipStack.mRect).Intersect(rect);
}
mSharedContext->SetClipRect(*clip);
mSharedContext->SetClipRect(rect);
mSharedContext->SetNoClipMask();
return true;
}
@ -912,11 +941,11 @@ already_AddRefed<TextureHandle> DrawTargetWebgl::SharedContext::CopySnapshot(
inline DrawTargetWebgl::AutoRestoreContext::AutoRestoreContext(
DrawTargetWebgl* aTarget)
: mTarget(aTarget),
mClipRect(aTarget->mSharedContext->mClipRect),
mClipAARect(aTarget->mSharedContext->mClipAARect),
mLastClipMask(aTarget->mSharedContext->mLastClipMask) {}
inline DrawTargetWebgl::AutoRestoreContext::~AutoRestoreContext() {
mTarget->mSharedContext->SetClipRect(mClipRect);
mTarget->mSharedContext->SetClipRect(mClipAARect);
if (mLastClipMask) {
mTarget->mSharedContext->SetClipMask(mLastClipMask);
}
@ -1163,7 +1192,7 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
"uniform vec2 u_transform[3];\n"
"uniform vec2 u_viewport;\n"
"uniform float u_aa;\n"
"varying vec2 v_cliptc;\n"
"varying vec4 v_cliptc;\n"
"varying vec4 v_dist;\n"
"varying float v_alpha;\n"
"void main() {\n"
@ -1177,7 +1206,7 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
" u_transform[1] * extrude.y +\n"
" u_transform[2];\n"
" gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
" v_cliptc = vertex / u_viewport;\n"
" v_cliptc = vec4(vertex / u_viewport, vertex);\n"
" v_dist = vec4(extrude, 1.0 - extrude) * scale.xyxy + 1.5 - u_aa;\n"
" v_alpha = a_vertex.z;\n"
"}\n"_ns;
@ -1185,12 +1214,16 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
u"precision mediump float;\n"
"uniform vec4 u_color;\n"
"uniform sampler2D u_clipmask;\n"
"varying vec2 v_cliptc;\n"
"uniform vec4 u_clipbounds;\n"
"varying vec4 v_cliptc;\n"
"varying vec4 v_dist;\n"
"varying float v_alpha;\n"
"void main() {\n"
" float clip = texture2D(u_clipmask, v_cliptc).r;\n"
" vec2 dist = min(v_dist.xy, v_dist.zw);\n"
" float clip = texture2D(u_clipmask, v_cliptc.xy).r;\n"
" vec4 clipdist = vec4(v_cliptc.zw - u_clipbounds.xy,\n"
" u_clipbounds.zw - v_cliptc.zw);\n"
" vec4 dist = min(v_dist, clipdist);\n"
" dist.xy = min(dist.xy, dist.zw);\n"
" float aa = v_alpha * clamp(min(dist.x, dist.y), 0.0, 1.0);\n"
" gl_FragColor = clip * aa * u_color;\n"
"}\n"_ns;
@ -1223,8 +1256,11 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
mWebgl->GetUniformLocation(*mSolidProgram, u"u_color"_ns);
mSolidProgramClipMask =
mWebgl->GetUniformLocation(*mSolidProgram, u"u_clipmask"_ns);
mSolidProgramClipBounds =
mWebgl->GetUniformLocation(*mSolidProgram, u"u_clipbounds"_ns);
if (!mSolidProgramViewport || !mSolidProgramAA || !mSolidProgramTransform ||
!mSolidProgramColor || !mSolidProgramClipMask) {
!mSolidProgramColor || !mSolidProgramClipMask ||
!mSolidProgramClipBounds) {
return false;
}
mWebgl->UseProgram(mSolidProgram);
@ -1240,7 +1276,7 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
"uniform float u_aa;\n"
"uniform vec2 u_transform[3];\n"
"uniform vec2 u_texmatrix[3];\n"
"varying vec2 v_cliptc;\n"
"varying vec4 v_cliptc;\n"
"varying vec2 v_texcoord;\n"
"varying vec4 v_dist;\n"
"varying float v_alpha;\n"
@ -1255,7 +1291,7 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
" u_transform[1] * extrude.y +\n"
" u_transform[2];\n"
" gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
" v_cliptc = vertex / u_viewport;\n"
" v_cliptc = vec4(vertex / u_viewport, vertex);\n"
" v_texcoord = u_texmatrix[0] * extrude.x +\n"
" u_texmatrix[1] * extrude.y +\n"
" u_texmatrix[2];\n"
@ -1269,15 +1305,19 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
"uniform float u_swizzle;\n"
"uniform sampler2D u_sampler;\n"
"uniform sampler2D u_clipmask;\n"
"varying vec2 v_cliptc;\n"
"uniform vec4 u_clipbounds;\n"
"varying vec4 v_cliptc;\n"
"varying vec2 v_texcoord;\n"
"varying vec4 v_dist;\n"
"varying float v_alpha;\n"
"void main() {\n"
" vec2 tc = clamp(v_texcoord, u_texbounds.xy, u_texbounds.zw);\n"
" vec4 image = texture2D(u_sampler, tc);\n"
" float clip = texture2D(u_clipmask, v_cliptc).r;\n"
" vec2 dist = min(v_dist.xy, v_dist.zw);\n"
" float clip = texture2D(u_clipmask, v_cliptc.xy).r;\n"
" vec4 clipdist = vec4(v_cliptc.zw - u_clipbounds.xy,\n"
" u_clipbounds.zw - v_cliptc.zw);\n"
" vec4 dist = min(v_dist, clipdist);\n"
" dist.xy = min(dist.xy, dist.zw);\n"
" float aa = v_alpha * clamp(min(dist.x, dist.y), 0.0, 1.0);\n"
" gl_FragColor = clip * aa * u_color *\n"
" mix(image, image.rrrr, u_swizzle);\n"
@ -1319,10 +1359,12 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
mWebgl->GetUniformLocation(*mImageProgram, u"u_sampler"_ns);
mImageProgramClipMask =
mWebgl->GetUniformLocation(*mImageProgram, u"u_clipmask"_ns);
mImageProgramClipBounds =
mWebgl->GetUniformLocation(*mImageProgram, u"u_clipbounds"_ns);
if (!mImageProgramViewport || !mImageProgramAA || !mImageProgramTransform ||
!mImageProgramTexMatrix || !mImageProgramTexBounds ||
!mImageProgramSwizzle || !mImageProgramColor || !mImageProgramSampler ||
!mImageProgramClipMask) {
!mImageProgramClipMask || !mImageProgramClipBounds) {
return false;
}
mWebgl->UseProgram(mImageProgram);
@ -1355,7 +1397,7 @@ void DrawTargetWebgl::ClearRect(const Rect& aRect) {
if (mTransform.PreservesAxisAlignedRectangles() &&
mTransform.TransformBounds(aRect).Contains(Rect(GetRect())) &&
mSharedContext->IsCurrentTarget(this) && !mSharedContext->HasClipMask() &&
mSharedContext->mClipRect.Contains(GetRect())) {
mSharedContext->mClipAARect.Contains(Rect(GetRect()))) {
mIsClear = true;
}
}
@ -1858,7 +1900,7 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
if (aOptions.mCompositionOp == CompositionOp::OP_SOURCE && aTransformed &&
aClipped &&
(HasClipMask() || !currentTransform.PreservesAxisAlignedRectangles() ||
!currentTransform.TransformBounds(aRect).Contains(Rect(mClipRect)) ||
!currentTransform.TransformBounds(aRect).Contains(Rect(mClipAARect)) ||
(aPattern.GetType() == PatternType::SURFACE &&
!IsAlignedRect(aTransformed, currentTransform, aRect)))) {
// Clear outside the mask region for masks that are not bounded by clip.
@ -1906,7 +1948,8 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
aOptions.mCompositionOp == CompositionOp::OP_OVER) ||
aOptions.mCompositionOp == CompositionOp::OP_SOURCE ||
aOptions.mCompositionOp == CompositionOp::OP_CLEAR) &&
!aStrokeOptions && !aVertexRange && !HasClipMask()) {
!aStrokeOptions && !aVertexRange && !HasClipMask() &&
mClipAARect.IsEqualEdges(Rect(mClipRect))) {
// Certain color patterns can be mapped to scissored clears. The
// composition op must effectively overwrite the destination, and the
// transform must map to an axis-aligned integer rectangle.
@ -1945,9 +1988,10 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
if (mLastProgram != mSolidProgram) {
mWebgl->UseProgram(mSolidProgram);
mLastProgram = mSolidProgram;
// Ensure viewport and AA state is current.
// Ensure uniform state is current.
mDirtyViewport = true;
mDirtyAA = true;
mDirtyClip = true;
}
if (mDirtyViewport) {
float viewportData[2] = {float(mViewportSize.width),
@ -1964,6 +2008,16 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
{(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};
Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
if (aTransformed) {
@ -2156,9 +2210,10 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
if (mLastProgram != mImageProgram) {
mWebgl->UseProgram(mImageProgram);
mLastProgram = mImageProgram;
// Ensure viewport and AA state is current.
// Ensure uniform state is current.
mDirtyViewport = true;
mDirtyAA = true;
mDirtyClip = true;
}
if (mDirtyViewport) {
float viewportData[2] = {float(mViewportSize.width),
@ -2180,6 +2235,16 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
{(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;
}
DeviceColor color =
mLastCompositionOp == CompositionOp::OP_CLEAR
? DeviceColor(1, 1, 1, 1)

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

@ -75,7 +75,10 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
RefPtr<WebGLFramebufferJS> mFramebuffer;
RefPtr<WebGLTextureJS> mTex;
RefPtr<WebGLTextureJS> mClipMask;
// The integer-aligned, scissor-compatible conservative bounds of the clip.
IntRect mClipBounds;
// The fractional, AA'd bounds of the clip rect, if applicable.
Rect mClipAARect;
RefPtr<DrawTargetSkia> mSkia;
// Skia DT pointing to the same pixel data, but without any applied clips.
RefPtr<DrawTargetSkia> mSkiaNoClip;
@ -166,7 +169,10 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
WeakPtr<DrawTargetWebgl> mCurrentTarget;
IntSize mViewportSize;
// The current integer-aligned scissor rect.
IntRect mClipRect;
// The current fractional AA'd clip rect bounds.
Rect mClipAARect;
RefPtr<ClientWebGLContext> mWebgl;
@ -174,8 +180,12 @@ 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;
@ -198,6 +208,7 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
RefPtr<WebGLUniformLocationJS> mSolidProgramTransform;
RefPtr<WebGLUniformLocationJS> mSolidProgramColor;
RefPtr<WebGLUniformLocationJS> mSolidProgramClipMask;
RefPtr<WebGLUniformLocationJS> mSolidProgramClipBounds;
RefPtr<WebGLProgramJS> mImageProgram;
RefPtr<WebGLUniformLocationJS> mImageProgramViewport;
RefPtr<WebGLUniformLocationJS> mImageProgramAA;
@ -208,6 +219,7 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
RefPtr<WebGLUniformLocationJS> mImageProgramSwizzle;
RefPtr<WebGLUniformLocationJS> mImageProgramSampler;
RefPtr<WebGLUniformLocationJS> mImageProgramClipMask;
RefPtr<WebGLUniformLocationJS> mImageProgramClipBounds;
// Scratch framebuffer used to wrap textures for miscellaneous utility ops.
RefPtr<WebGLFramebufferJS> mScratchFramebuffer;
@ -268,7 +280,8 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
void SetBlendState(CompositionOp aOp,
const Maybe<DeviceColor>& aBlendColor = Nothing());
void SetClipRect(const IntRect& aClipRect) { mClipRect = aClipRect; }
void SetClipRect(const Rect& aClipRect);
void SetClipRect(const IntRect& aClipRect) { SetClipRect(Rect(aClipRect)); }
bool SetClipMask(const RefPtr<WebGLTextureJS>& aTex);
bool SetNoClipMask();
bool HasClipMask() const {
@ -563,7 +576,7 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
// that clip state is restored after the context is used.
struct AutoRestoreContext {
DrawTargetWebgl* mTarget;
IntRect mClipRect;
Rect mClipAARect;
RefPtr<WebGLTextureJS> mLastClipMask;
explicit AutoRestoreContext(DrawTargetWebgl* aTarget);