-
Notifications
You must be signed in to change notification settings - Fork 29.1k
Refactor text and runtime effect to separate skia and impeller implementations. #174219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor text and runtime effect to separate skia and impeller implementations. #174219
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some things I should have noted before, but most of them are style and better testing related...
.vscode/settings.json
Outdated
@@ -5,5 +5,57 @@ | |||
"githubPullRequests.ignoredPullRequestBranches": [ | |||
"master" | |||
], | |||
"files.trimTrailingWhitespace": true | |||
"files.trimTrailingWhitespace": true, | |||
"files.associations": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this part of the other PR? It doesn't make sense as part of this PR unless I've missed something...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gahhh this freaking file keeps changing itself and I accidentally added it. I will unstage this change.
receiver.drawTextFrame(text_frame, x, y); | ||
DisplayListCompare equals(const DrawTextOp* other) const { | ||
return text->getTextBlob() == other->text->getTextBlob() && | ||
text->getTextFrame() == other->text->getTextFrame() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps put this pointer comparison inside a DlText::==
operator to hide the details (and in case we eventually have some better way to compare them than by (pair of) subsumed pointer(s)?)
namespace flutter { | ||
class DlText { | ||
public: | ||
virtual DlRect getBounds() const = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know there is a mixed style in DL objects, but I think we are moving towards capitalized getters like GetBounds, GetTextFrame, etc. I wasn't doing that until I started integrating with Impeller, but I think it is better matched to the C++ style guidelines so I'd like to move in that direction.
Technically you can have a strict getter that is lower case, but I don't think either of getTextFoo() are "strict getters" since they are only defined for specific subclasses...
#define FLUTTER_DISPLAY_LIST_DL_TEXT_H_ | ||
|
||
#include "flutter/display_list/geometry/dl_geometry_types.h" | ||
#include "third_party/skia/include/core/SkRefCnt.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can change the getter to SkTextBlob* and we won't need SkRefCnt included here...?
public: | ||
virtual DlRect getBounds() const = 0; | ||
virtual std::shared_ptr<impeller::TextFrame> getTextFrame() const = 0; | ||
virtual sk_sp<SkTextBlob> getTextBlob() const = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the magic things about sk_sp is that you can just use a raw pointer in cases where you are just passing it along (because the ref count is built-in and there is free "shared from this"). So, this could return SkTextBlob*. Indeed, if you look at SkCanvas::drawTextBlob, the real method takes the ptr and the version that takes an sk_sp does a get() on it and passes it to the pointer version...
@@ -12,6 +12,8 @@ | |||
#include "flutter/flow/surface_frame.h" | |||
#include "flutter/fml/macros.h" | |||
|
|||
#include "third_party/skia/include/core/SkData.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this for the DlText changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SurfaceData
below uses an SkData
. I think we previously got away with it through some kind of transitive include.
} | ||
// 2x3 2D affine subset of a 4x4 transform in row major order | ||
void FirstPassDispatcher::transform2DAffine( | ||
DlScalar mxx, DlScalar mxy, DlScalar mxt, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your indent is off by 2 for these. Argument continuations are indented 4 spots, not 2. Same for FullPerspective below...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These still look off by 2. You got the ones inside the method body, but these argument methods should also be indented by 4 as well.
@@ -1224,7 +1230,8 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { | |||
static auto display_list = builder.Build(); | |||
RUN_TESTS2(canvas.DrawDisplayList(display_list);, false); | |||
} | |||
RUN_TESTS2(canvas.DrawTextBlob(GetTestTextBlob(1), 0, 0, paint);, false); | |||
RUN_TESTS2(canvas.DrawText(DlTextSkia::Make(GetTestTextBlob(1)), 0, 0, paint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no test for Impeller Text here (and the one test for it down below is testing a different condition). While the builder code is the same for both, it couldn't hurt to have both tested here so that when we eventually delete Skia we aren't left without any "text" case for this test.
@@ -3516,7 +3523,7 @@ TEST_F(DisplayListTest, NopOperationsOmittedFromRecords) { | |||
builder.DrawAtlas(kTestImage1, xforms, rects, nullptr, 2, | |||
DlBlendMode::kSrcOver, DlImageSampling::kLinear, | |||
nullptr, &paint); | |||
builder.DrawTextBlob(GetTestTextBlob(1), 10, 10, paint); | |||
builder.DrawText(DlTextSkia::Make(GetTestTextBlob(1)), 10, 10, paint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need a test to make sure that DrawText(Impeller) is also nop'd.
@@ -5505,13 +5514,16 @@ TEST_F(DisplayListTest, BoundedRenderOpsDoNotReportUnbounded) { | |||
ASSERT_LT(blob->bounds().width(), draw_rect.GetWidth()); | |||
ASSERT_LT(blob->bounds().height(), draw_rect.GetHeight()); | |||
|
|||
auto text = DlTextImpeller::Make(frame); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, makefromblob above instead of blob->frame->text.
@@ -120,8 +120,10 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { | |||
} | |||
std::shared_ptr<DlText> text; | |||
if (impeller_enabled_) { | |||
#if IMPELLER_SUPPORTS_RENDERING |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this have the if inside it as well? Otherwise we might have a mismatch? Or is that possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's possible for that to happen, but I can switch this up to cover the whole if statement.
return text->GetTextBlob() == other->text->GetTextBlob() && | ||
text->GetTextFrame() == other->text->GetTextFrame() && | ||
x == other->x && y == other->y | ||
return text == other->text && x == other->x && y == other->y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this only compares the shared pointers. You can use the Equals mechanism (see SetSharedImageFilterOp) which does pointer comparisons and then also *foo == *bar comparisons if needed. You could also do *text == *(other->text), but that gets dangerous if one of them is null. The Equals family of methods protects against that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Equals(text, other->text)
// clang-format off | ||
// full 4x4 transform in row major order | ||
void FirstPassDispatcher::transformFullPerspective( | ||
DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These argument methods are also indented off by 2.
@@ -1013,7 +1013,7 @@ struct DrawTextOp final : DrawOpBase { | |||
void dispatch(DlOpReceiver& receiver) const { receiver.drawText(text, x, y); } | |||
|
|||
DisplayListCompare equals(const DrawTextOp* other) const { | |||
return text == other->text && x == other->x && y == other->y | |||
return *text == *other->text && x == other->x && y == other->y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer Equals(text, other->text)
, but this is likely safe in practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I just reread your comment, I switched it to use Equals()
instead
This makes two refactors to the display list architecture:
DlRuntimeEffect
for skia and impeller are separated, and the impeller implementation put into theimpeller/display_list
target. This makes sure that a client can link against the maindisplay_list
library without actually pulling in all of impeller. (This is needed for [skwasm] Port toDisplayList
objects #172314)DrawTextBlob
andDrawTextFrame
methods are consolidated into oneDrawText
call, and that takes aDlText
object. TheDlText
object has two implementations, one for skia and one for impeller, and the impeller one is moved intoimpeller/display_list
for the same reason mentioned above.