/src/skia/src/gpu/ganesh/ops/AtlasTextOp.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2015 Google Inc. |
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 | | |
8 | | #include "src/gpu/ganesh/ops/AtlasTextOp.h" |
9 | | |
10 | | #include "include/core/SkSamplingOptions.h" |
11 | | #include "include/core/SkTypes.h" |
12 | | #include "include/private/base/SkDebug.h" |
13 | | #include "include/private/base/SkTArray.h" |
14 | | #include "src/base/SkArenaAlloc.h" |
15 | | #include "src/core/SkMatrixPriv.h" |
16 | | #include "src/core/SkTraceEvent.h" |
17 | | #include "src/gpu/ganesh/GrBufferAllocPool.h" |
18 | | #include "src/gpu/ganesh/GrCaps.h" |
19 | | #include "src/gpu/ganesh/GrColorSpaceXform.h" |
20 | | #include "src/gpu/ganesh/GrGeometryProcessor.h" |
21 | | #include "src/gpu/ganesh/GrMeshDrawTarget.h" |
22 | | #include "src/gpu/ganesh/GrOpFlushState.h" |
23 | | #include "src/gpu/ganesh/GrPaint.h" |
24 | | #include "src/gpu/ganesh/GrPipeline.h" |
25 | | #include "src/gpu/ganesh/GrProcessorAnalysis.h" |
26 | | #include "src/gpu/ganesh/GrResourceProvider.h" |
27 | | #include "src/gpu/ganesh/GrSamplerState.h" |
28 | | #include "src/gpu/ganesh/GrSimpleMesh.h" |
29 | | #include "src/gpu/ganesh/GrSurfaceProxy.h" |
30 | | #include "src/gpu/ganesh/GrSurfaceProxyView.h" |
31 | | #include "src/gpu/ganesh/GrUserStencilSettings.h" |
32 | | #include "src/gpu/ganesh/effects/GrBitmapTextGeoProc.h" |
33 | | #include "src/gpu/ganesh/effects/GrDistanceFieldGeoProc.h" |
34 | | #include "src/gpu/ganesh/ops/GrDrawOp.h" |
35 | | #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h" |
36 | | #include "src/gpu/ganesh/text/GrAtlasManager.h" |
37 | | #include "src/text/gpu/DistanceFieldAdjustTable.h" |
38 | | #include "src/text/gpu/GlyphVector.h" |
39 | | #include "src/text/gpu/SubRunContainer.h" |
40 | | |
41 | | #if defined(SK_GAMMA_APPLY_TO_A8) |
42 | | #include "include/private/base/SkCPUTypes.h" |
43 | | #include "src/core/SkMaskGamma.h" |
44 | | #endif |
45 | | |
46 | | #include <algorithm> |
47 | | #include <functional> |
48 | | #include <new> |
49 | | #include <tuple> |
50 | | #include <utility> |
51 | | |
52 | | struct GrShaderCaps; |
53 | | |
54 | | using MaskFormat = skgpu::MaskFormat; |
55 | | |
56 | | namespace skgpu::ganesh { |
57 | | |
58 | | inline static constexpr int kVerticesPerGlyph = 4; |
59 | | inline static constexpr int kIndicesPerGlyph = 6; |
60 | | |
61 | | // If we have thread local, then cache memory for a single AtlasTextOp. |
62 | | static thread_local void* gCache = nullptr; |
63 | 1.65k | void* AtlasTextOp::operator new(size_t s) { |
64 | 1.65k | if (gCache != nullptr) { |
65 | 513 | return std::exchange(gCache, nullptr); |
66 | 513 | } |
67 | | |
68 | 1.13k | return ::operator new(s); |
69 | 1.65k | } |
70 | | |
71 | 1.65k | void AtlasTextOp::operator delete(void* bytes) noexcept { |
72 | 1.65k | if (gCache == nullptr) { |
73 | 766 | gCache = bytes; |
74 | 766 | return; |
75 | 766 | } |
76 | 884 | ::operator delete(bytes); |
77 | 884 | } |
78 | | |
79 | 4.32k | void AtlasTextOp::ClearCache() { |
80 | 4.32k | ::operator delete(gCache); |
81 | 4.32k | gCache = nullptr; |
82 | 4.32k | } |
83 | | |
84 | | AtlasTextOp::AtlasTextOp(MaskType maskType, |
85 | | bool needsTransform, |
86 | | int glyphCount, |
87 | | SkRect deviceRect, |
88 | | Geometry* geo, |
89 | | const GrColorInfo& dstColorInfo, |
90 | | GrPaint&& paint) |
91 | | : INHERITED{ClassID()} |
92 | | , fProcessors(std::move(paint)) |
93 | | , fNumGlyphs(glyphCount) |
94 | | , fDFGPFlags(0) |
95 | | , fMaskType(static_cast<uint32_t>(maskType)) |
96 | | , fUsesLocalCoords(false) |
97 | | , fNeedsGlyphTransform(needsTransform) |
98 | | , fHasPerspective(needsTransform && geo->fDrawMatrix.hasPerspective()) |
99 | | , fUseGammaCorrectDistanceTable(false) |
100 | | , fHead{geo} |
101 | 1.19k | , fTail{&fHead->fNext} { |
102 | | // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds |
103 | | // we treat this as a set of non-AA rects rendered with a texture. |
104 | 1.19k | this->setBounds(deviceRect, HasAABloat::kNo, IsHairline::kNo); |
105 | 1.19k | if (maskType == MaskType::kColorBitmap) { |
106 | | // We assume that color emoji use the sRGB colorspace |
107 | 7 | fColorSpaceXform = dstColorInfo.refColorSpaceXformFromSRGB(); |
108 | 7 | } |
109 | 1.19k | } |
110 | | |
111 | | AtlasTextOp::AtlasTextOp(MaskType maskType, |
112 | | bool needsTransform, |
113 | | int glyphCount, |
114 | | SkRect deviceRect, |
115 | | SkColor luminanceColor, |
116 | | bool useGammaCorrectDistanceTable, |
117 | | uint32_t DFGPFlags, |
118 | | Geometry* geo, |
119 | | GrPaint&& paint) |
120 | | : INHERITED{ClassID()} |
121 | | , fProcessors(std::move(paint)) |
122 | | , fNumGlyphs(glyphCount) |
123 | | , fDFGPFlags(DFGPFlags) |
124 | | , fMaskType(static_cast<uint32_t>(maskType)) |
125 | | , fUsesLocalCoords(false) |
126 | | , fNeedsGlyphTransform(needsTransform) |
127 | | , fHasPerspective(needsTransform && geo->fDrawMatrix.hasPerspective()) |
128 | | , fUseGammaCorrectDistanceTable(useGammaCorrectDistanceTable) |
129 | | , fLuminanceColor(luminanceColor) |
130 | | , fHead{geo} |
131 | 459 | , fTail{&fHead->fNext} { |
132 | | // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds |
133 | | // we treat this as a set of non-AA rects rendered with a texture. |
134 | 459 | this->setBounds(deviceRect, HasAABloat::kNo, IsHairline::kNo); |
135 | 459 | } |
136 | | |
137 | | auto AtlasTextOp::Geometry::Make(const sktext::gpu::AtlasSubRun& subRun, |
138 | | const SkMatrix& drawMatrix, |
139 | | SkPoint drawOrigin, |
140 | | SkIRect clipRect, |
141 | | sk_sp<SkRefCnt>&& supportData, |
142 | | const SkPMColor4f& color, |
143 | 1.65k | SkArenaAlloc* alloc) -> Geometry* { |
144 | | // Bypass the automatic dtor behavior in SkArenaAlloc. I'm leaving this up to the Op to run |
145 | | // all geometry dtors for now. |
146 | 1.65k | void* geo = alloc->makeBytesAlignedTo(sizeof(Geometry), alignof(Geometry)); |
147 | 1.65k | return new(geo) Geometry{subRun, |
148 | 1.65k | drawMatrix, |
149 | 1.65k | drawOrigin, |
150 | 1.65k | clipRect, |
151 | 1.65k | std::move(supportData), |
152 | 1.65k | color}; |
153 | 1.65k | } |
154 | | |
155 | 1.53k | void AtlasTextOp::Geometry::fillVertexData(void *dst, int offset, int count) const { |
156 | 1.53k | fSubRun.fillVertexData( |
157 | 1.53k | dst, offset, count, fColor.toBytes_RGBA(), fDrawMatrix, fDrawOrigin, fClipRect); |
158 | 1.53k | } |
159 | | |
160 | 2.60k | void AtlasTextOp::visitProxies(const GrVisitProxyFunc& func) const { |
161 | 2.60k | fProcessors.visitProxies(func); |
162 | 2.60k | } |
163 | | |
164 | | #if defined(GPU_TEST_UTILS) |
165 | 0 | SkString AtlasTextOp::onDumpInfo() const { |
166 | 0 | SkString str; |
167 | 0 | int i = 0; |
168 | 0 | for(Geometry* geom = fHead; geom != nullptr; geom = geom->fNext) { |
169 | 0 | str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f\n", |
170 | 0 | i++, |
171 | 0 | geom->fColor.toBytes_RGBA(), |
172 | 0 | geom->fDrawOrigin.x(), |
173 | 0 | geom->fDrawOrigin.y()); |
174 | 0 | } |
175 | |
|
176 | 0 | str += fProcessors.dumpProcessors(); |
177 | 0 | return str; |
178 | 0 | } |
179 | | #endif |
180 | | |
181 | 3.19k | GrDrawOp::FixedFunctionFlags AtlasTextOp::fixedFunctionFlags() const { |
182 | 3.19k | return FixedFunctionFlags::kNone; |
183 | 3.19k | } |
184 | | |
185 | | GrProcessorSet::Analysis AtlasTextOp::finalize(const GrCaps& caps, |
186 | | const GrAppliedClip* clip, |
187 | 1.54k | GrClampType clampType) { |
188 | 1.54k | GrProcessorAnalysisCoverage coverage; |
189 | 1.54k | GrProcessorAnalysisColor color; |
190 | 1.54k | if (this->maskType() == MaskType::kColorBitmap) { |
191 | 7 | color.setToUnknown(); |
192 | 1.53k | } else { |
193 | | // finalize() is called before any merging is done, so at this point there's at most one |
194 | | // Geometry with a color. Later, for non-bitmap ops, we may have mixed colors. |
195 | 1.53k | color.setToConstant(fHead->fColor); |
196 | 1.53k | } |
197 | | |
198 | 1.54k | switch (this->maskType()) { |
199 | 1.18k | case MaskType::kGrayscaleCoverage: |
200 | 1.18k | #if !defined(SK_DISABLE_SDF_TEXT) |
201 | 1.29k | case MaskType::kAliasedDistanceField: |
202 | 1.53k | case MaskType::kGrayscaleDistanceField: |
203 | 1.53k | #endif |
204 | 1.53k | coverage = GrProcessorAnalysisCoverage::kSingleChannel; |
205 | 1.53k | break; |
206 | 0 | case MaskType::kLCDCoverage: |
207 | 0 | #if !defined(SK_DISABLE_SDF_TEXT) |
208 | 0 | case MaskType::kLCDDistanceField: |
209 | 0 | #endif |
210 | 0 | coverage = GrProcessorAnalysisCoverage::kLCD; |
211 | 0 | break; |
212 | 7 | case MaskType::kColorBitmap: |
213 | 7 | coverage = GrProcessorAnalysisCoverage::kNone; |
214 | 7 | break; |
215 | 1.54k | } |
216 | | |
217 | 1.54k | auto analysis = fProcessors.finalize(color, coverage, clip, &GrUserStencilSettings::kUnused, |
218 | 1.54k | caps, clampType, &fHead->fColor); |
219 | | // TODO(michaelludwig): Once processor analysis can be done external to op creation/finalization |
220 | | // the atlas op metadata can be fully const. This is okay for now since finalize() happens |
221 | | // before the op is merged, so during combineIfPossible, metadata is effectively const. |
222 | 1.54k | fUsesLocalCoords = analysis.usesLocalCoords(); |
223 | 1.54k | return analysis; |
224 | 1.54k | } |
225 | | |
226 | 1.06k | void AtlasTextOp::onPrepareDraws(GrMeshDrawTarget* target) { |
227 | 1.06k | auto resourceProvider = target->resourceProvider(); |
228 | | |
229 | | // If we need local coordinates, compute an inverse view matrix. If this is solid color, the |
230 | | // processor analysis will not require local coords and the GPs will skip local coords when |
231 | | // the matrix is identity. When the shaders require local coords, combineIfPossible requires all |
232 | | // all geometries to have same draw matrix. |
233 | 1.06k | SkMatrix localMatrix = SkMatrix::I(); |
234 | 1.06k | if (fUsesLocalCoords && !fHead->fDrawMatrix.invert(&localMatrix)) { |
235 | 0 | return; |
236 | 0 | } |
237 | | |
238 | 1.06k | GrAtlasManager* atlasManager = target->atlasManager(); |
239 | | |
240 | 1.06k | MaskFormat maskFormat = this->maskFormat(); |
241 | | |
242 | 1.06k | unsigned int numActiveViews; |
243 | 1.06k | const GrSurfaceProxyView* views = atlasManager->getViews(maskFormat, &numActiveViews); |
244 | 1.06k | if (!views) { |
245 | 0 | SkDebugf("Could not allocate backing texture for atlas\n"); |
246 | 0 | return; |
247 | 0 | } |
248 | 1.06k | SkASSERT(views[0].proxy()); |
249 | | |
250 | 1.06k | static constexpr int kMaxTextures = GrBitmapTextGeoProc::kMaxTextures; |
251 | 1.06k | #if !defined(SK_DISABLE_SDF_TEXT) |
252 | 1.06k | static_assert(GrDistanceFieldA8TextGeoProc::kMaxTextures == kMaxTextures); |
253 | 1.06k | static_assert(GrDistanceFieldLCDTextGeoProc::kMaxTextures == kMaxTextures); |
254 | 1.06k | #endif |
255 | | |
256 | 1.06k | auto primProcProxies = target->allocPrimProcProxyPtrs(kMaxTextures); |
257 | 1.88k | for (unsigned i = 0; i < numActiveViews; ++i) { |
258 | 821 | primProcProxies[i] = views[i].proxy(); |
259 | | // This op does not know its atlas proxies when it is added to a OpsTasks, so the proxies |
260 | | // don't get added during the visitProxies call. Thus we add them here. |
261 | 821 | target->sampledProxyArray()->push_back(views[i].proxy()); |
262 | 821 | } |
263 | | |
264 | 1.06k | FlushInfo flushInfo; |
265 | 1.06k | flushInfo.fPrimProcProxies = primProcProxies; |
266 | 1.06k | flushInfo.fIndexBuffer = resourceProvider->refNonAAQuadIndexBuffer(); |
267 | | |
268 | 1.06k | #if !defined(SK_DISABLE_SDF_TEXT) |
269 | 1.06k | if (this->usesDistanceFields()) { |
270 | 218 | flushInfo.fGeometryProcessor = this->setupDfProcessor(target->allocator(), |
271 | 218 | *target->caps().shaderCaps(), |
272 | 218 | localMatrix, views, numActiveViews); |
273 | 218 | } else |
274 | 848 | #endif |
275 | 848 | { |
276 | 848 | auto filter = fNeedsGlyphTransform ? GrSamplerState::Filter::kLinear |
277 | 848 | : GrSamplerState::Filter::kNearest; |
278 | | // Bitmap text uses a single color, combineIfPossible ensures all geometries have the same |
279 | | // color, so we can use the first's without worry. |
280 | 848 | flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make( |
281 | 848 | target->allocator(), *target->caps().shaderCaps(), fHead->fColor, |
282 | 848 | /*wideColor=*/false, fColorSpaceXform, views, numActiveViews, filter, |
283 | 848 | maskFormat, localMatrix, fHasPerspective); |
284 | 848 | } |
285 | | |
286 | 1.06k | const int vertexStride = (int)flushInfo.fGeometryProcessor->vertexStride(); |
287 | | |
288 | | // Ensure we don't request an insanely large contiguous vertex allocation. |
289 | 1.06k | static const int kMaxVertexBytes = GrBufferAllocPool::kDefaultBufferSize; |
290 | 1.06k | const int quadSize = vertexStride * kVerticesPerGlyph; |
291 | 1.06k | const int maxQuadsPerBuffer = kMaxVertexBytes / quadSize; |
292 | | |
293 | 1.06k | int allGlyphsCursor = 0; |
294 | 1.06k | const int allGlyphsEnd = fNumGlyphs; |
295 | 1.06k | int quadCursor; |
296 | 1.06k | int quadEnd; |
297 | 1.06k | char* vertices; |
298 | | |
299 | 1.06k | auto resetVertexBuffer = [&] { |
300 | 1.06k | quadCursor = 0; |
301 | 1.06k | quadEnd = std::min(maxQuadsPerBuffer, allGlyphsEnd - allGlyphsCursor); |
302 | | |
303 | 1.06k | vertices = (char*)target->makeVertexSpace( |
304 | 1.06k | vertexStride, |
305 | 1.06k | kVerticesPerGlyph * quadEnd, |
306 | 1.06k | &flushInfo.fVertexBuffer, |
307 | 1.06k | &flushInfo.fVertexOffset); |
308 | | |
309 | 1.06k | if (!vertices || !flushInfo.fVertexBuffer) { |
310 | 0 | SkDebugf("Could not allocate vertices\n"); |
311 | 0 | return false; |
312 | 0 | } |
313 | 1.06k | return true; |
314 | 1.06k | }; AtlasTextOp.cpp:skgpu::ganesh::AtlasTextOp::onPrepareDraws(GrMeshDrawTarget*)::$_0::operator()() const Line | Count | Source | 299 | 1.06k | auto resetVertexBuffer = [&] { | 300 | 1.06k | quadCursor = 0; | 301 | 1.06k | quadEnd = std::min(maxQuadsPerBuffer, allGlyphsEnd - allGlyphsCursor); | 302 | | | 303 | 1.06k | vertices = (char*)target->makeVertexSpace( | 304 | 1.06k | vertexStride, | 305 | 1.06k | kVerticesPerGlyph * quadEnd, | 306 | 1.06k | &flushInfo.fVertexBuffer, | 307 | 1.06k | &flushInfo.fVertexOffset); | 308 | | | 309 | 1.06k | if (!vertices || !flushInfo.fVertexBuffer) { | 310 | 0 | SkDebugf("Could not allocate vertices\n"); | 311 | 0 | return false; | 312 | 0 | } | 313 | 1.06k | return true; | 314 | 1.06k | }; |
Unexecuted instantiation: AtlasTextOp.cpp:skgpu::ganesh::AtlasTextOp::onPrepareDraws(GrMeshDrawTarget*)::$_1::operator()() const |
315 | | |
316 | 1.06k | if (!resetVertexBuffer()) { |
317 | 0 | return; |
318 | 0 | } |
319 | | |
320 | 2.60k | for (const Geometry* geo = fHead; geo != nullptr; geo = geo->fNext) { |
321 | 1.53k | const sktext::gpu::AtlasSubRun& subRun = geo->fSubRun; |
322 | 1.53k | SkASSERTF((int) subRun.vertexStride(geo->fDrawMatrix) == vertexStride, |
323 | 1.53k | "subRun stride: %d vertex buffer stride: %d\n", |
324 | 1.53k | (int)subRun.vertexStride(geo->fDrawMatrix), vertexStride); |
325 | | |
326 | 1.53k | const int subRunEnd = subRun.glyphCount(); |
327 | 1.53k | auto regenerateDelegate = [&](sktext::gpu::GlyphVector* glyphs, |
328 | 1.53k | int begin, |
329 | 1.53k | int end, |
330 | 1.53k | skgpu::MaskFormat maskFormat, |
331 | 1.53k | int padding) { |
332 | 1.53k | return glyphs->regenerateAtlasForGanesh(begin, end, maskFormat, padding, target); |
333 | 1.53k | }; AtlasTextOp.cpp:skgpu::ganesh::AtlasTextOp::onPrepareDraws(GrMeshDrawTarget*)::$_1::operator()(sktext::gpu::GlyphVector*, int, int, skgpu::MaskFormat, int) const Line | Count | Source | 331 | 1.53k | int padding) { | 332 | 1.53k | return glyphs->regenerateAtlasForGanesh(begin, end, maskFormat, padding, target); | 333 | 1.53k | }; |
Unexecuted instantiation: AtlasTextOp.cpp:skgpu::ganesh::AtlasTextOp::onPrepareDraws(GrMeshDrawTarget*)::$_3::operator()(sktext::gpu::GlyphVector*, int, int, skgpu::MaskFormat, int) const |
334 | 3.06k | for (int subRunCursor = 0; subRunCursor < subRunEnd;) { |
335 | | // Regenerate the atlas for the remainder of the glyphs in the run, or the remainder |
336 | | // of the glyphs to fill the vertex buffer. |
337 | 1.53k | int regenEnd = subRunCursor + std::min(subRunEnd - subRunCursor, quadEnd - quadCursor); |
338 | 1.53k | auto[ok, glyphsRegenerated] = subRun.regenerateAtlas(subRunCursor, regenEnd, |
339 | 1.53k | regenerateDelegate); |
340 | | // There was a problem allocating the glyph in the atlas. Bail. |
341 | 1.53k | if (!ok) { |
342 | 0 | return; |
343 | 0 | } |
344 | | |
345 | 1.53k | geo->fillVertexData(vertices + quadCursor * quadSize, subRunCursor, glyphsRegenerated); |
346 | | |
347 | 1.53k | subRunCursor += glyphsRegenerated; |
348 | 1.53k | quadCursor += glyphsRegenerated; |
349 | 1.53k | allGlyphsCursor += glyphsRegenerated; |
350 | 1.53k | flushInfo.fGlyphsToFlush += glyphsRegenerated; |
351 | | |
352 | 1.53k | if (quadCursor == quadEnd || subRunCursor < subRunEnd) { |
353 | | // Flush if not all the glyphs are drawn because either the quad buffer is full or |
354 | | // the atlas is out of space. |
355 | 1.06k | if (subRunCursor < subRunEnd) { |
356 | 0 | ATRACE_ANDROID_FRAMEWORK_ALWAYS("Atlas full"); |
357 | 0 | } |
358 | 1.06k | this->createDrawForGeneratedGlyphs(target, &flushInfo); |
359 | 1.06k | if (quadCursor == quadEnd && allGlyphsCursor < allGlyphsEnd) { |
360 | | // If the vertex buffer is full and there are still glyphs to draw then |
361 | | // get a new buffer. |
362 | 0 | if(!resetVertexBuffer()) { |
363 | 0 | return; |
364 | 0 | } |
365 | 0 | } |
366 | 1.06k | } |
367 | 1.53k | } |
368 | 1.53k | } |
369 | 1.06k | } |
370 | | |
371 | 1.06k | void AtlasTextOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) { |
372 | 1.06k | auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, |
373 | 1.06k | std::move(fProcessors), |
374 | 1.06k | GrPipeline::InputFlags::kNone); |
375 | | |
376 | 1.06k | flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline, |
377 | 1.06k | &GrUserStencilSettings::kUnused); |
378 | 1.06k | } |
379 | | |
380 | | void AtlasTextOp::createDrawForGeneratedGlyphs(GrMeshDrawTarget* target, |
381 | 1.06k | FlushInfo* flushInfo) const { |
382 | 1.06k | if (!flushInfo->fGlyphsToFlush) { |
383 | 0 | return; |
384 | 0 | } |
385 | | |
386 | 1.06k | auto atlasManager = target->atlasManager(); |
387 | | |
388 | 1.06k | GrGeometryProcessor* gp = flushInfo->fGeometryProcessor; |
389 | 1.06k | MaskFormat maskFormat = this->maskFormat(); |
390 | | |
391 | 1.06k | unsigned int numActiveViews; |
392 | 1.06k | const GrSurfaceProxyView* views = atlasManager->getViews(maskFormat, &numActiveViews); |
393 | 1.06k | SkASSERT(views); |
394 | | // Something has gone terribly wrong, bail |
395 | 1.06k | if (!views || 0 == numActiveViews) { |
396 | 0 | return; |
397 | 0 | } |
398 | 1.06k | if (gp->numTextureSamplers() != (int) numActiveViews) { |
399 | | // During preparation the number of atlas pages has increased. |
400 | | // Update the proxies used in the GP to match. |
401 | 490 | for (unsigned i = gp->numTextureSamplers(); i < numActiveViews; ++i) { |
402 | 245 | flushInfo->fPrimProcProxies[i] = views[i].proxy(); |
403 | | // This op does not know its atlas proxies when it is added to a OpsTasks, so the |
404 | | // proxies don't get added during the visitProxies call. Thus we add them here. |
405 | 245 | target->sampledProxyArray()->push_back(views[i].proxy()); |
406 | | // These will get unreffed when the previously recorded draws destruct. |
407 | 245 | for (int d = 0; d < flushInfo->fNumDraws; ++d) { |
408 | 0 | flushInfo->fPrimProcProxies[i]->ref(); |
409 | 0 | } |
410 | 245 | } |
411 | 245 | #if !defined(SK_DISABLE_SDF_TEXT) |
412 | 245 | if (this->usesDistanceFields()) { |
413 | 45 | if (this->isLCD()) { |
414 | 0 | reinterpret_cast<GrDistanceFieldLCDTextGeoProc*>(gp)->addNewViews( |
415 | 0 | views, numActiveViews, GrSamplerState::Filter::kLinear); |
416 | 45 | } else { |
417 | 45 | reinterpret_cast<GrDistanceFieldA8TextGeoProc*>(gp)->addNewViews( |
418 | 45 | views, numActiveViews, GrSamplerState::Filter::kLinear); |
419 | 45 | } |
420 | 45 | } else |
421 | 200 | #endif |
422 | 200 | { |
423 | 200 | auto filter = fNeedsGlyphTransform ? GrSamplerState::Filter::kLinear |
424 | 200 | : GrSamplerState::Filter::kNearest; |
425 | 200 | reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewViews(views, numActiveViews, filter); |
426 | 200 | } |
427 | 245 | } |
428 | 1.06k | int maxGlyphsPerDraw = static_cast<int>(flushInfo->fIndexBuffer->size() / sizeof(uint16_t) / 6); |
429 | 1.06k | GrSimpleMesh* mesh = target->allocMesh(); |
430 | 1.06k | mesh->setIndexedPatterned(flushInfo->fIndexBuffer, kIndicesPerGlyph, flushInfo->fGlyphsToFlush, |
431 | 1.06k | maxGlyphsPerDraw, flushInfo->fVertexBuffer, kVerticesPerGlyph, |
432 | 1.06k | flushInfo->fVertexOffset); |
433 | 1.06k | target->recordDraw(flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fPrimProcProxies, |
434 | 1.06k | GrPrimitiveType::kTriangles); |
435 | 1.06k | flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush; |
436 | 1.06k | flushInfo->fGlyphsToFlush = 0; |
437 | 1.06k | ++flushInfo->fNumDraws; |
438 | 1.06k | } skgpu::ganesh::AtlasTextOp::createDrawForGeneratedGlyphs(GrMeshDrawTarget*, skgpu::ganesh::AtlasTextOp::FlushInfo*) const Line | Count | Source | 381 | 1.06k | FlushInfo* flushInfo) const { | 382 | 1.06k | if (!flushInfo->fGlyphsToFlush) { | 383 | 0 | return; | 384 | 0 | } | 385 | | | 386 | 1.06k | auto atlasManager = target->atlasManager(); | 387 | | | 388 | 1.06k | GrGeometryProcessor* gp = flushInfo->fGeometryProcessor; | 389 | 1.06k | MaskFormat maskFormat = this->maskFormat(); | 390 | | | 391 | 1.06k | unsigned int numActiveViews; | 392 | 1.06k | const GrSurfaceProxyView* views = atlasManager->getViews(maskFormat, &numActiveViews); | 393 | 1.06k | SkASSERT(views); | 394 | | // Something has gone terribly wrong, bail | 395 | 1.06k | if (!views || 0 == numActiveViews) { | 396 | 0 | return; | 397 | 0 | } | 398 | 1.06k | if (gp->numTextureSamplers() != (int) numActiveViews) { | 399 | | // During preparation the number of atlas pages has increased. | 400 | | // Update the proxies used in the GP to match. | 401 | 490 | for (unsigned i = gp->numTextureSamplers(); i < numActiveViews; ++i) { | 402 | 245 | flushInfo->fPrimProcProxies[i] = views[i].proxy(); | 403 | | // This op does not know its atlas proxies when it is added to a OpsTasks, so the | 404 | | // proxies don't get added during the visitProxies call. Thus we add them here. | 405 | 245 | target->sampledProxyArray()->push_back(views[i].proxy()); | 406 | | // These will get unreffed when the previously recorded draws destruct. | 407 | 245 | for (int d = 0; d < flushInfo->fNumDraws; ++d) { | 408 | 0 | flushInfo->fPrimProcProxies[i]->ref(); | 409 | 0 | } | 410 | 245 | } | 411 | 245 | #if !defined(SK_DISABLE_SDF_TEXT) | 412 | 245 | if (this->usesDistanceFields()) { | 413 | 45 | if (this->isLCD()) { | 414 | 0 | reinterpret_cast<GrDistanceFieldLCDTextGeoProc*>(gp)->addNewViews( | 415 | 0 | views, numActiveViews, GrSamplerState::Filter::kLinear); | 416 | 45 | } else { | 417 | 45 | reinterpret_cast<GrDistanceFieldA8TextGeoProc*>(gp)->addNewViews( | 418 | 45 | views, numActiveViews, GrSamplerState::Filter::kLinear); | 419 | 45 | } | 420 | 45 | } else | 421 | 200 | #endif | 422 | 200 | { | 423 | 200 | auto filter = fNeedsGlyphTransform ? GrSamplerState::Filter::kLinear | 424 | 200 | : GrSamplerState::Filter::kNearest; | 425 | 200 | reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewViews(views, numActiveViews, filter); | 426 | 200 | } | 427 | 245 | } | 428 | 1.06k | int maxGlyphsPerDraw = static_cast<int>(flushInfo->fIndexBuffer->size() / sizeof(uint16_t) / 6); | 429 | 1.06k | GrSimpleMesh* mesh = target->allocMesh(); | 430 | 1.06k | mesh->setIndexedPatterned(flushInfo->fIndexBuffer, kIndicesPerGlyph, flushInfo->fGlyphsToFlush, | 431 | 1.06k | maxGlyphsPerDraw, flushInfo->fVertexBuffer, kVerticesPerGlyph, | 432 | 1.06k | flushInfo->fVertexOffset); | 433 | 1.06k | target->recordDraw(flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fPrimProcProxies, | 434 | 1.06k | GrPrimitiveType::kTriangles); | 435 | 1.06k | flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush; | 436 | 1.06k | flushInfo->fGlyphsToFlush = 0; | 437 | 1.06k | ++flushInfo->fNumDraws; | 438 | 1.06k | } |
Unexecuted instantiation: skgpu::ganesh::AtlasTextOp::createDrawForGeneratedGlyphs(GrMeshDrawTarget*, skgpu::ganesh::AtlasTextOp::FlushInfo*) const |
439 | | |
440 | 923 | GrOp::CombineResult AtlasTextOp::onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) { |
441 | 923 | auto that = t->cast<AtlasTextOp>(); |
442 | | |
443 | 923 | if (fDFGPFlags != that->fDFGPFlags || |
444 | 923 | fMaskType != that->fMaskType || |
445 | 923 | fUsesLocalCoords != that->fUsesLocalCoords || |
446 | 923 | fNeedsGlyphTransform != that->fNeedsGlyphTransform || |
447 | 923 | fHasPerspective != that->fHasPerspective || |
448 | 923 | fUseGammaCorrectDistanceTable != that->fUseGammaCorrectDistanceTable) { |
449 | | // All flags must match for an op to be combined |
450 | 210 | return CombineResult::kCannotCombine; |
451 | 210 | } |
452 | | |
453 | 713 | if (fProcessors != that->fProcessors) { |
454 | 243 | return CombineResult::kCannotCombine; |
455 | 243 | } |
456 | | |
457 | 470 | if (fUsesLocalCoords) { |
458 | | // If the fragment processors use local coordinates, the GPs compute them using the inverse |
459 | | // of the view matrix stored in a uniform, so all geometries must have the same matrix. |
460 | 14 | const SkMatrix& thisFirstMatrix = fHead->fDrawMatrix; |
461 | 14 | const SkMatrix& thatFirstMatrix = that->fHead->fDrawMatrix; |
462 | 14 | if (!SkMatrixPriv::CheapEqual(thisFirstMatrix, thatFirstMatrix)) { |
463 | 0 | return CombineResult::kCannotCombine; |
464 | 0 | } |
465 | 14 | } |
466 | | |
467 | 470 | #if !defined(SK_DISABLE_SDF_TEXT) |
468 | 470 | if (this->usesDistanceFields()) { |
469 | 129 | SkASSERT(that->usesDistanceFields()); |
470 | 129 | if (fLuminanceColor != that->fLuminanceColor) { |
471 | 0 | return CombineResult::kCannotCombine; |
472 | 0 | } |
473 | 129 | } else |
474 | 341 | #endif |
475 | 341 | { |
476 | 341 | if (this->maskType() == MaskType::kColorBitmap && |
477 | 341 | fHead->fColor != that->fHead->fColor) { |
478 | | // This ensures all merged bitmap color text ops have a constant color |
479 | 0 | return CombineResult::kCannotCombine; |
480 | 0 | } |
481 | 341 | } |
482 | | |
483 | 470 | fNumGlyphs += that->fNumGlyphs; |
484 | | |
485 | | // After concat, that's geometry list is emptied so it will not unref the blobs when destructed |
486 | 470 | this->addGeometry(that->fHead); |
487 | 470 | that->fHead = nullptr; |
488 | 470 | return CombineResult::kMerged; |
489 | 470 | } skgpu::ganesh::AtlasTextOp::onCombineIfPossible(GrOp*, SkArenaAlloc*, GrCaps const&) Line | Count | Source | 440 | 923 | GrOp::CombineResult AtlasTextOp::onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) { | 441 | 923 | auto that = t->cast<AtlasTextOp>(); | 442 | | | 443 | 923 | if (fDFGPFlags != that->fDFGPFlags || | 444 | 923 | fMaskType != that->fMaskType || | 445 | 923 | fUsesLocalCoords != that->fUsesLocalCoords || | 446 | 923 | fNeedsGlyphTransform != that->fNeedsGlyphTransform || | 447 | 923 | fHasPerspective != that->fHasPerspective || | 448 | 923 | fUseGammaCorrectDistanceTable != that->fUseGammaCorrectDistanceTable) { | 449 | | // All flags must match for an op to be combined | 450 | 210 | return CombineResult::kCannotCombine; | 451 | 210 | } | 452 | | | 453 | 713 | if (fProcessors != that->fProcessors) { | 454 | 243 | return CombineResult::kCannotCombine; | 455 | 243 | } | 456 | | | 457 | 470 | if (fUsesLocalCoords) { | 458 | | // If the fragment processors use local coordinates, the GPs compute them using the inverse | 459 | | // of the view matrix stored in a uniform, so all geometries must have the same matrix. | 460 | 14 | const SkMatrix& thisFirstMatrix = fHead->fDrawMatrix; | 461 | 14 | const SkMatrix& thatFirstMatrix = that->fHead->fDrawMatrix; | 462 | 14 | if (!SkMatrixPriv::CheapEqual(thisFirstMatrix, thatFirstMatrix)) { | 463 | 0 | return CombineResult::kCannotCombine; | 464 | 0 | } | 465 | 14 | } | 466 | | | 467 | 470 | #if !defined(SK_DISABLE_SDF_TEXT) | 468 | 470 | if (this->usesDistanceFields()) { | 469 | 129 | SkASSERT(that->usesDistanceFields()); | 470 | 129 | if (fLuminanceColor != that->fLuminanceColor) { | 471 | 0 | return CombineResult::kCannotCombine; | 472 | 0 | } | 473 | 129 | } else | 474 | 341 | #endif | 475 | 341 | { | 476 | 341 | if (this->maskType() == MaskType::kColorBitmap && | 477 | 341 | fHead->fColor != that->fHead->fColor) { | 478 | | // This ensures all merged bitmap color text ops have a constant color | 479 | 0 | return CombineResult::kCannotCombine; | 480 | 0 | } | 481 | 341 | } | 482 | | | 483 | 470 | fNumGlyphs += that->fNumGlyphs; | 484 | | | 485 | | // After concat, that's geometry list is emptied so it will not unref the blobs when destructed | 486 | 470 | this->addGeometry(that->fHead); | 487 | 470 | that->fHead = nullptr; | 488 | 470 | return CombineResult::kMerged; | 489 | 470 | } |
Unexecuted instantiation: skgpu::ganesh::AtlasTextOp::onCombineIfPossible(GrOp*, SkArenaAlloc*, GrCaps const&) |
490 | | |
491 | | #if !defined(SK_DISABLE_SDF_TEXT) |
492 | | GrGeometryProcessor* AtlasTextOp::setupDfProcessor(SkArenaAlloc* arena, |
493 | | const GrShaderCaps& caps, |
494 | | const SkMatrix& localMatrix, |
495 | | const GrSurfaceProxyView* views, |
496 | 218 | unsigned int numActiveViews) const { |
497 | 218 | auto dfAdjustTable = sktext::gpu::DistanceFieldAdjustTable::Get(); |
498 | | |
499 | | // see if we need to create a new effect |
500 | 218 | if (this->isLCD()) { |
501 | 0 | float redCorrection = dfAdjustTable->getAdjustment(SkColorGetR(fLuminanceColor), |
502 | 0 | fUseGammaCorrectDistanceTable); |
503 | 0 | float greenCorrection = dfAdjustTable->getAdjustment(SkColorGetG(fLuminanceColor), |
504 | 0 | fUseGammaCorrectDistanceTable); |
505 | 0 | float blueCorrection = dfAdjustTable->getAdjustment(SkColorGetB(fLuminanceColor), |
506 | 0 | fUseGammaCorrectDistanceTable); |
507 | 0 | GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust = |
508 | 0 | GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make( |
509 | 0 | redCorrection, greenCorrection, blueCorrection); |
510 | 0 | return GrDistanceFieldLCDTextGeoProc::Make(arena, caps, views, numActiveViews, |
511 | 0 | GrSamplerState::Filter::kLinear, widthAdjust, |
512 | 0 | fDFGPFlags, localMatrix); |
513 | 218 | } else { |
514 | 218 | #if defined(SK_GAMMA_APPLY_TO_A8) |
515 | 218 | float correction = 0; |
516 | 218 | if (this->maskType() != MaskType::kAliasedDistanceField) { |
517 | 131 | U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, fLuminanceColor); |
518 | 131 | correction = dfAdjustTable->getAdjustment(lum, fUseGammaCorrectDistanceTable); |
519 | 131 | } |
520 | 218 | return GrDistanceFieldA8TextGeoProc::Make(arena, caps, views, numActiveViews, |
521 | 218 | GrSamplerState::Filter::kLinear, correction, |
522 | 218 | fDFGPFlags, localMatrix); |
523 | | #else |
524 | | return GrDistanceFieldA8TextGeoProc::Make(arena, caps, views, numActiveViews, |
525 | | GrSamplerState::Filter::kLinear, fDFGPFlags, |
526 | | localMatrix); |
527 | | #endif |
528 | 218 | } |
529 | 218 | } |
530 | | #endif // !defined(SK_DISABLE_SDF_TEXT) |
531 | | |
532 | | } // namespace skgpu::ganesh |
533 | | |
534 | | |