Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@dnfield
Copy link
Contributor

@dnfield dnfield commented Sep 15, 2022

No description provided.

//------------------------------------------------------------------------------
/// @brief Whether the glyph is a path or a bitmap.
///
Type type = Type::kPath;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be incorporated into equality/hash right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I... guess? This is specific to the glyph ID though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah....I dunno. As long as we never have glyph collisions its fine

/// @brief Describes how the glyphs are represented in the texture.
enum class Type {
//--------------------------------------------------------------------------
/// The glyphs are represented at a fixed size in an 8-bit grayscale texture
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW there are SDF algorithms that use all of the color channels for fewer artifacts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but in talking with Chinmay about this he's concerned they're slow and more meant for offline processing.

Maybe we could find a compute based algorithm that would be fast... in which case we'd update this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd probably add another enumeration for that too, something like kMultiChannelSignedDistanceField


bool TextRun::AddGlyph(Glyph glyph, Point position) {
glyphs_.emplace_back(GlyphPosition{glyph, position});
has_color_ |= glyph.type == Glyph::Type::kBitmap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not that they're always bitmaps though. The COLR fonts can be drawn as a path too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I rename this to something like is_bitmap or something then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the underlying is_color is being used to detect both bitmaps and COLR fonts right now though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there was a skia parameter that returned whether or not something was a bitmap but I couldn't figure out how it was initialized, and seemed to always be false even for emoji

@dnfield
Copy link
Contributor Author

dnfield commented Sep 20, 2022

This definitely has some issues still, but I think it's good enough to review and land to star tmaking incremental improvements.

Current issues:

  • We're not picking a strategy for text rendering at this point. [Impeller] Come up with a heuristic/strategy for when to use different types of text rendering strategies. flutter#111640.
  • The SDF atlas is currently being rendered at the full point size/scale of the glyphs without SDF. Need to figure out a point size to actually use and how to pick that point size back out when we want to use it.
  • The SDF atlas might not be spacing things out correctly - I don't know that this is causing problems yet but it seems like it might.
  • SDF text is not getting anti-aliasing. @bdero seems to have ideas about how to make this work.
  • The SDF algorithm would be really nice to do as a compute algorithm, and in its current state there's enough for me to try to pick that back up and see what I can do with it.

@dnfield dnfield marked this pull request as ready for review September 20, 2022 20:53
in vec2 v_unit_vertex;
in vec2 v_atlas_position;
in vec2 v_atlas_glyph_size;
in float v_color_glyph;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one isn't used. I think we can't render full color glyphs or bitmaps with SDF anyway so I'd drop it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd rather just leave this to avoid replicating a bunch of code on the C++ side for now.

dnfield and others added 2 commits September 20, 2022 13:58
Co-authored-by: Jonah Williams <jonahwilliams@google.com>
# Unit tests targets are only enabled for host machines and Fuchsia right now
declare_args() {
enable_unittests = current_toolchain == host_toolchain || is_fuchsia
enable_unittests = current_toolchain == host_toolchain || is_fuchsia || true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to stage this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No


if (auto atlas = std::get_if<std::shared_ptr<GlyphAtlas>>(&atlas_)) {
return *atlas;
FML_DCHECK(lazy_atlas_);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the assertion as well as the conditional is redundant. I suggest removing the assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional is defensive - the assert is so that we don't get here in a test and return null, which would fail to render text.

mat4 mvp;
} frame_info;

in vec2 unit_vertex;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be shared with the non-SDF glyph atlas implementation. You can put the common stuff in its own header and #include that here and in the other spot too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, I am surprised so much is shared between the two strategies.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very similar. We might be able to simplify it if we move SDF to using a fixed glyph size, but it's probably not worth it. The differences are really in the fragment shader.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this to transforms.glsl

while (auto frame = frame_iterator()) {
for (const auto& run : frame->GetRuns()) {
auto font = run.GetFont();
// switch (type) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't a consistent font size desirable for an SDF based strategy?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I saw your clarifying remark. Let's cross reference a bug here instead of leaving the code commented out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flutter/flutter#112016, adding TODO

/// Compute signed-distance field for an 8-bpp grayscale image (values greater
/// than 127 are considered "on") For details of this algorithm, see "The 'dead
/// reckoning' signed distance transform" [Grevera 2004]
static void ConvertBitmapToSignedDistanceField(uint8_t* pixels,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its going to take me a bit to walk through this (and we should totally not block landing this because of it). But, can we fixup the style to match the style guide?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the comment bit? I tried to fix up the variable names.

Perhaps you mean the in-out pointer parameter? Doing this in-place avoids an extra allocation the size of the texture though. Maybe there's a more style-guide friendly way of doing that?

for (SkTextBlobRunIterator run(blob.get()); !run.done(); run.next()) {
TextRun text_run(ToFont(run.font(), scale));

// TODO(jonahwilliams): ask Skia for a public API to look this up.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we file a bug for this and cross reference it here? We can followup.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added bug to comment.

Copy link
Member

@chinmaygarde chinmaygarde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nits. Let's file issues for the remaining action items for followup. This is a great start and let's land iterate on it ASAP.

cmd.pipeline =
renderer.GetGlyphAtlasPipeline(OptionsFromPassAndEntity(pass, entity));
cmd.stencil_reference = entity.GetStencilDepth();
template <class VS_, class FS_>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template type param can just be the pipeline type itself rather than its shaders (see the advanced blends in BlendFilter for an example of this).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, doen

float sample_distance = texture(glyph_atlas_sampler, v_unit_vertex * scale_perspective + offset).a;
// // Use local automatic gradients to find anti-aliased anisotropic edge width, cf. Gustavson 2012
float edge_width = length(vec2(dFdx(sample_distance), dFdy(sample_distance)));
// // Smooth the glyph edge by interpolating across the boundary in a band with the width determined above
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Double double slashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

// // Smooth the glyph edge by interpolating across the boundary in a band with the width determined above
float insideness = smoothstep(edge_distance - edge_width, edge_distance + edge_width, sample_distance);
frag_color = frag_info.text_color * insideness;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: IIRC, I think you're using the same VScode plugins as me for shaders. Mind running this through the formatter with Cmd + Alt + L?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't seem to get the formatter working :\

// Sample the signed-distance field to find distance from this fragment to the glyph outline
float sample_distance = texture(glyph_atlas_sampler, v_unit_vertex * scale_perspective + offset).a;
// // Use local automatic gradients to find anti-aliased anisotropic edge width, cf. Gustavson 2012
float edge_width = length(vec2(dFdx(sample_distance), dFdy(sample_distance)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice trick -- derivatives work the same whether or not the render pass does multisample resolve (considering MSAA changes nothing about how the fragment stage runs), right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I.. think so. I heavily borrowed from the Metal by Example shader for SDF text rendering.

// // Use local automatic gradients to find anti-aliased anisotropic edge width, cf. Gustavson 2012
float edge_width = length(vec2(dFdx(sample_distance), dFdy(sample_distance)));
// // Smooth the glyph edge by interpolating across the boundary in a band with the width determined above
float insideness = smoothstep(edge_distance - edge_width, edge_distance + edge_width, sample_distance);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are you planning to address non-antialiased drawing? Given the template solution, It seems like binding an extra float uniform for all atlas implementations wouldn't be so bad even if unused in some of them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I'm not married to the template solution, but it saved me from copy/pasting a bunch of code that was nearly identical. There's already a branch in there based on the type of glyph atlas, we could add another branch for SDF if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh except that branch doesn't work lol.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, fixed the branching using std::is_same, and we should be able to do that for other parameters we need here ... although at some point it may make sense to stop using templates and just replicate some code or create additional inline helper functions or something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual metal by example drawing seemed to handling some AA here in a way that didn't quite work, it'd probably be worth revisiting.

@dnfield dnfield added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 20, 2022
@dnfield
Copy link
Contributor Author

dnfield commented Sep 20, 2022

Marking this for submission, realizing it's not quite complete and will need some further work.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

autosubmit Merge PR when tree becomes green via auto submit App e: impeller

Projects

No open projects
Archived in project

Development

Successfully merging this pull request may close these issues.

4 participants