1/*
2 * Copyright 2014 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 "include/core/SkStream.h"
9#include "include/core/SkTypeface.h"
10#include "src/gpu/GrProgramInfo.h"
11#include "src/gpu/GrRenderTargetProxy.h"
12#include "src/gpu/gl/GrGLGpu.h"
13#include "src/gpu/gl/GrGLPath.h"
14#include "src/gpu/gl/GrGLPathRendering.h"
15#include "src/gpu/gl/GrGLUtil.h"
16
17#define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X)
18#define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->gpu()->glInterface(), RET, X)
19
20// Number of paths to allocate per glGenPaths call. The call can be overly slow on command buffer GL
21// implementation. The call has a result value, and thus waiting for the call completion is needed.
22static const GrGLsizei kPathIDPreallocationAmount = 65536;
23
24static_assert(0 == GrPathRendering::kNone_PathTransformType);
25static_assert(1 == GrPathRendering::kTranslateX_PathTransformType);
26static_assert(2 == GrPathRendering::kTranslateY_PathTransformType);
27static_assert(3 == GrPathRendering::kTranslate_PathTransformType);
28static_assert(4 == GrPathRendering::kAffine_PathTransformType);
29static_assert(GrPathRendering::kAffine_PathTransformType == GrPathRendering::kLast_PathTransformType);
30
31#ifdef SK_DEBUG
32
33static void verify_floats(const float* floats, int count) {
34 for (int i = 0; i < count; ++i) {
35 SkASSERT(!SkScalarIsNaN(SkFloatToScalar(floats[i])));
36 }
37}
38#endif
39
40static GrGLenum gr_stencil_op_to_gl_path_rendering_fill_mode(GrStencilOp op) {
41 switch (op) {
42 default:
43 SK_ABORT("Unexpected path fill.");
44 /* fallthrough */
45 case GrStencilOp::kIncWrap:
46 return GR_GL_COUNT_UP;
47 case GrStencilOp::kInvert:
48 return GR_GL_INVERT;
49 }
50}
51
52GrGLPathRendering::GrGLPathRendering(GrGLGpu* gpu)
53 : GrPathRendering(gpu)
54 , fPreallocatedPathCount(0) {
55 const GrGLInterface* glInterface = gpu->glInterface();
56 fCaps.bindFragmentInputSupport = (bool)glInterface->fFunctions.fBindFragmentInputLocation;
57}
58
59GrGLPathRendering::~GrGLPathRendering() {
60 if (fPreallocatedPathCount > 0) {
61 this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount);
62 }
63}
64
65void GrGLPathRendering::disconnect(GrGpu::DisconnectType type) {
66 if (GrGpu::DisconnectType::kCleanup == type) {
67 this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount);
68 }
69 fPreallocatedPathCount = 0;
70}
71
72void GrGLPathRendering::resetContext() {
73 fHWProjectionMatrixState.invalidate();
74 // we don't use the model view matrix.
75 GL_CALL(MatrixLoadIdentity(GR_GL_PATH_MODELVIEW));
76
77 fHWPathStencilSettings.invalidate();
78}
79
80sk_sp<GrPath> GrGLPathRendering::createPath(const SkPath& inPath, const GrStyle& style) {
81 return sk_make_sp<GrGLPath>(this->gpu(), inPath, style);
82}
83
84void GrGLPathRendering::onStencilPath(const StencilPathArgs& args, const GrPath* path) {
85 GrGLGpu* gpu = this->gpu();
86 SkASSERT(gpu->caps()->shaderCaps()->pathRenderingSupport());
87 gpu->flushColorWrite(false);
88
89 GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(args.fProxy->peekRenderTarget());
90 SkISize dimensions = rt->dimensions();
91 this->setProjectionMatrix(*args.fViewMatrix, dimensions, args.fOrigin);
92 gpu->flushScissor(*args.fScissor, rt->width(), rt->height(), args.fOrigin);
93 gpu->flushHWAAState(rt, args.fUseHWAA);
94 gpu->flushRenderTarget(rt);
95
96 const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
97
98 this->flushPathStencilSettings(*args.fStencil);
99
100 GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
101 fHWPathStencilSettings.singleSidedFace().fPassOp);
102 GrGLint writeMask = fHWPathStencilSettings.singleSidedFace().fWriteMask;
103
104 if (glPath->shouldFill()) {
105 GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
106 }
107 if (glPath->shouldStroke()) {
108 GL_CALL(StencilStrokePath(glPath->pathID(), 0xffff, writeMask));
109 }
110}
111
112void GrGLPathRendering::onDrawPath(const GrStencilSettings& stencilPassSettings,
113 const GrPath* path) {
114 const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
115
116 this->flushPathStencilSettings(stencilPassSettings);
117
118 GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
119 fHWPathStencilSettings.singleSidedFace().fPassOp);
120 GrGLint writeMask = fHWPathStencilSettings.singleSidedFace().fWriteMask;
121
122 if (glPath->shouldStroke()) {
123 if (glPath->shouldFill()) {
124 GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
125 }
126 GL_CALL(StencilThenCoverStrokePath(glPath->pathID(), 0xffff, writeMask,
127 GR_GL_BOUNDING_BOX));
128 } else {
129 GL_CALL(StencilThenCoverFillPath(glPath->pathID(), fillMode, writeMask,
130 GR_GL_BOUNDING_BOX));
131 }
132}
133
134void GrGLPathRendering::setProgramPathFragmentInputTransform(GrGLuint program, GrGLint location,
135 GrGLenum genMode, GrGLint components,
136 const SkMatrix& matrix) {
137 float coefficients[3 * 3];
138 SkASSERT(components >= 1 && components <= 3);
139
140 coefficients[0] = SkScalarToFloat(matrix[SkMatrix::kMScaleX]);
141 coefficients[1] = SkScalarToFloat(matrix[SkMatrix::kMSkewX]);
142 coefficients[2] = SkScalarToFloat(matrix[SkMatrix::kMTransX]);
143
144 if (components >= 2) {
145 coefficients[3] = SkScalarToFloat(matrix[SkMatrix::kMSkewY]);
146 coefficients[4] = SkScalarToFloat(matrix[SkMatrix::kMScaleY]);
147 coefficients[5] = SkScalarToFloat(matrix[SkMatrix::kMTransY]);
148 }
149
150 if (components >= 3) {
151 coefficients[6] = SkScalarToFloat(matrix[SkMatrix::kMPersp0]);
152 coefficients[7] = SkScalarToFloat(matrix[SkMatrix::kMPersp1]);
153 coefficients[8] = SkScalarToFloat(matrix[SkMatrix::kMPersp2]);
154 }
155 SkDEBUGCODE(verify_floats(coefficients, components * 3));
156
157 GL_CALL(ProgramPathFragmentInputGen(program, location, genMode, components, coefficients));
158}
159
160void GrGLPathRendering::setProjectionMatrix(const SkMatrix& matrix,
161 const SkISize& renderTargetSize,
162 GrSurfaceOrigin renderTargetOrigin) {
163
164 SkASSERT(this->gpu()->glCaps().shaderCaps()->pathRenderingSupport());
165
166 if (renderTargetOrigin == fHWProjectionMatrixState.fRenderTargetOrigin &&
167 renderTargetSize == fHWProjectionMatrixState.fRenderTargetSize &&
168 SkMatrixPriv::CheapEqual(matrix, fHWProjectionMatrixState.fViewMatrix)) {
169 return;
170 }
171
172 fHWProjectionMatrixState.fViewMatrix = matrix;
173 fHWProjectionMatrixState.fRenderTargetSize = renderTargetSize;
174 fHWProjectionMatrixState.fRenderTargetOrigin = renderTargetOrigin;
175
176 float glMatrix[4 * 4];
177 fHWProjectionMatrixState.getRTAdjustedGLMatrix(glMatrix);
178 SkDEBUGCODE(verify_floats(glMatrix, SK_ARRAY_COUNT(glMatrix)));
179 GL_CALL(MatrixLoadf(GR_GL_PATH_PROJECTION, glMatrix));
180}
181
182GrGLuint GrGLPathRendering::genPaths(GrGLsizei range) {
183 SkASSERT(range > 0);
184 GrGLuint firstID;
185 if (fPreallocatedPathCount >= range) {
186 firstID = fFirstPreallocatedPathID;
187 fPreallocatedPathCount -= range;
188 fFirstPreallocatedPathID += range;
189 return firstID;
190 }
191 // Allocate range + the amount to fill up preallocation amount. If succeed, either join with
192 // the existing preallocation range or delete the existing and use the new (potentially partial)
193 // preallocation range.
194 GrGLsizei allocAmount = range + (kPathIDPreallocationAmount - fPreallocatedPathCount);
195 if (allocAmount >= range) {
196 GL_CALL_RET(firstID, GenPaths(allocAmount));
197
198 if (firstID != 0) {
199 if (fPreallocatedPathCount > 0 &&
200 firstID == fFirstPreallocatedPathID + fPreallocatedPathCount) {
201 firstID = fFirstPreallocatedPathID;
202 fPreallocatedPathCount += allocAmount - range;
203 fFirstPreallocatedPathID += range;
204 return firstID;
205 }
206
207 if (allocAmount > range) {
208 if (fPreallocatedPathCount > 0) {
209 this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount);
210 }
211 fFirstPreallocatedPathID = firstID + range;
212 fPreallocatedPathCount = allocAmount - range;
213 }
214 // Special case: if allocAmount == range, we have full preallocated range.
215 return firstID;
216 }
217 }
218 // Failed to allocate with preallocation. Remove existing preallocation and try to allocate just
219 // the range.
220 if (fPreallocatedPathCount > 0) {
221 this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount);
222 fPreallocatedPathCount = 0;
223 }
224
225 GL_CALL_RET(firstID, GenPaths(range));
226 if (firstID == 0) {
227 SkDebugf("Warning: Failed to allocate path\n");
228 }
229 return firstID;
230}
231
232void GrGLPathRendering::deletePaths(GrGLuint path, GrGLsizei range) {
233 GL_CALL(DeletePaths(path, range));
234}
235
236void GrGLPathRendering::flushPathStencilSettings(const GrStencilSettings& stencilSettings) {
237 SkASSERT(!stencilSettings.isTwoSided());
238 if (fHWPathStencilSettings != stencilSettings) {
239 SkASSERT(stencilSettings.isValid());
240 // Just the func, ref, and mask is set here. The op and write mask are params to the call
241 // that draws the path to the SB (glStencilFillPath)
242 uint16_t ref = stencilSettings.singleSidedFace().fRef;
243 GrStencilTest test = stencilSettings.singleSidedFace().fTest;
244 uint16_t testMask = stencilSettings.singleSidedFace().fTestMask;
245
246 if (!fHWPathStencilSettings.isValid() ||
247 ref != fHWPathStencilSettings.singleSidedFace().fRef ||
248 test != fHWPathStencilSettings.singleSidedFace().fTest ||
249 testMask != fHWPathStencilSettings.singleSidedFace().fTestMask) {
250 GL_CALL(PathStencilFunc(GrToGLStencilFunc(test), ref, testMask));
251 }
252 fHWPathStencilSettings = stencilSettings;
253 }
254}
255
256inline GrGLGpu* GrGLPathRendering::gpu() {
257 return static_cast<GrGLGpu*>(fGpu);
258}
259