xref: /aosp_15_r20/external/skia/src/gpu/graphite/render/GraphiteVertexFiller.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7 #include "include/core/SkM44.h"
8 #include "include/core/SkMatrix.h"
9 #include "include/core/SkPoint.h"
10 #include "include/core/SkScalar.h"
11 #include "include/core/SkSpan.h"
12 #include "src/base/SkVx.h"
13 #include "src/base/SkZip.h"
14 #include "src/gpu/AtlasTypes.h"
15 #include "src/gpu/BufferWriter.h"
16 #include "src/gpu/graphite/DrawWriter.h"
17 #include "src/gpu/graphite/geom/Rect.h"
18 #include "src/gpu/graphite/geom/Transform_graphite.h"
19 #include "src/text/gpu/Glyph.h"
20 #include "src/text/gpu/VertexFiller.h"
21 
22 #include <cstdint>
23 #include <tuple>
24 
25 namespace sktext::gpu {
26 
27 struct AtlasPt {
28     uint16_t u;
29     uint16_t v;
30 };
31 
fillInstanceData(skgpu::graphite::DrawWriter * dw,int offset,int count,unsigned short flags,skvx::uint2 ssboIndex,SkSpan<const Glyph * > glyphs,SkScalar depth) const32 void VertexFiller::fillInstanceData(skgpu::graphite::DrawWriter* dw,
33                                     int offset, int count,
34                                     unsigned short flags,
35                                     skvx::uint2 ssboIndex,
36                                     SkSpan<const Glyph*> glyphs,
37                                     SkScalar depth) const {
38     auto quadData = [&]() {
39         return SkMakeZip(glyphs.subspan(offset, count),
40                          fLeftTop.subspan(offset, count));
41     };
42 
43     skgpu::graphite::DrawWriter::Instances instances{*dw, {}, {}, 4};
44     instances.reserve(count);
45     // Need to send width, height, uvPos, xyPos, and strikeToSourceScale
46     // pre-transform coords = (s*w*b_x + t_x, s*h*b_y + t_y)
47     // where (b_x, b_y) are the vertexID coords
48     for (auto [glyph, leftTop]: quadData()) {
49         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
50         instances.append(1) << AtlasPt{uint16_t(ar-al), uint16_t(ab-at)}
51                             << AtlasPt{uint16_t(al & 0x1fff), at}
52                             << leftTop << /*index=*/uint16_t(al >> 13) << flags
53                             << 1.0f
54                             << depth << ssboIndex;
55     }
56 }
57 
58 using Rect = skgpu::graphite::Rect;
59 using Transform = skgpu::graphite::Transform;
60 
boundsAndDeviceMatrix(const Transform & localToDevice,SkPoint drawOrigin) const61 std::tuple<Rect, Transform> VertexFiller::boundsAndDeviceMatrix(const Transform& localToDevice,
62                                                                 SkPoint drawOrigin) const {
63     // The baked-in matrix differs from the current localToDevice by a translation if the
64     // upper 2x2 remains the same, and there's no perspective. Since there's no projection,
65     // Z is irrelevant, so it's okay that fCreationMatrix is an SkMatrix and has
66     // discarded the 3rd row/col, and can ignore those values in localToDevice.
67     const SkM44& positionMatrix = localToDevice.matrix();
68     const bool compatibleMatrix = positionMatrix.rc(0,0) == fCreationMatrix.rc(0, 0) &&
69                                   positionMatrix.rc(0,1) == fCreationMatrix.rc(0, 1) &&
70                                   positionMatrix.rc(1,0) == fCreationMatrix.rc(1, 0) &&
71                                   positionMatrix.rc(1,1) == fCreationMatrix.rc(1, 1) &&
72                                   localToDevice.type() != Transform::Type::kPerspective &&
73                                   !fCreationMatrix.hasPerspective();
74 
75     if (compatibleMatrix) {
76         const SkV4 mappedOrigin = positionMatrix.map(drawOrigin.x(), drawOrigin.y(), 0.f, 1.f);
77         const SkV2 offset = {mappedOrigin.x - fCreationMatrix.getTranslateX(),
78                              mappedOrigin.y - fCreationMatrix.getTranslateY()};
79         if (SkScalarIsInt(offset.x) && SkScalarIsInt(offset.y)) {
80             // The offset is an integer (but make sure), which means the generated mask can be
81             // accessed without changing how texels would be sampled.
82             return {Rect(fCreationBounds),
83                     Transform(SkM44::Translate(SkScalarRoundToInt(offset.x),
84                                                SkScalarRoundToInt(offset.y)))};
85         }
86     }
87 
88     // Otherwise compute the relative transformation from fCreationMatrix to
89     // localToDevice, with the drawOrigin applied. If fCreationMatrix or the
90     // concatenation is not invertible the returned Transform is marked invalid and the draw
91     // will be automatically dropped.
92     const SkMatrix viewDifference = this->viewDifference(
93             localToDevice.preTranslate(drawOrigin.x(), drawOrigin.y()));
94     return {Rect(fCreationBounds), Transform(SkM44(viewDifference))};
95 }
96 
97 }  // namespace sktext::gpu
98