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