1/* 2 * Copyright 2021 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 8#include "src/gpu/tessellate/PatchWriter.h" 9 10#include "src/gpu/tessellate/PathTessellator.h" 11 12namespace skgpu { 13 14SK_ALWAYS_INLINE SkPoint to_skpoint(float2 p) { return skvx::bit_pun<SkPoint>(p); } 15 16#if SK_GPU_V1 17PatchWriter::PatchWriter(GrMeshDrawTarget* target, 18 PathTessellator* tessellator, 19 int initialPatchAllocCount) 20 : PatchWriter(target, 21 &tessellator->fVertexChunkArray, 22 tessellator->fAttribs, 23 sizeof(SkPoint) * 4 + PatchAttribsStride(tessellator->fAttribs), 24 initialPatchAllocCount) { 25} 26#endif 27 28void PatchWriter::chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatches) { 29 // If we aren't fanning, we need to fill the space between chops with triangles. 30 bool needsInnerTriangles = !(fPatchAttribs & PatchAttribs::kFanPoint); 31 MiddleOutPolygonTriangulator innerTriangulator(numPatches, to_skpoint(p0)); 32 for (; numPatches >= 3; numPatches -= 2) { 33 // Chop into 3 quads. 34 float4 T = float4(1,1,2,2) / numPatches; 35 float4 ab = mix(p0.xyxy(), p1.xyxy(), T); 36 float4 bc = mix(p1.xyxy(), p2.xyxy(), T); 37 float4 abc = mix(ab, bc, T); 38 // p1 & p2 of the cubic representation of the middle quad. 39 float4 middle = mix(ab, bc, mix(T, T.zwxy(), 2/3.f)); 40 41 CubicPatch(*this) << QuadToCubic{p0, ab.lo, abc.lo}; // Write the 1st quad. 42 if (needsInnerTriangles) { 43 TrianglePatch(*this) << p0 << abc.lo << abc.hi; 44 } 45 CubicPatch(*this) << abc.lo << middle << abc.hi; // Write the 2nd quad. 46 if (needsInnerTriangles) { 47 *this << innerTriangulator.pushVertex(to_skpoint(abc.hi)); 48 } 49 std::tie(p0, p1) = {abc.hi, bc.hi}; // Save the 3rd quad. 50 } 51 if (numPatches == 2) { 52 // Chop into 2 quads. 53 float2 ab = (p0 + p1) * .5f; 54 float2 bc = (p1 + p2) * .5f; 55 float2 abc = (ab + bc) * .5f; 56 57 CubicPatch(*this) << QuadToCubic{p0, ab, abc}; // Write the 1st quad. 58 if (needsInnerTriangles) { 59 TrianglePatch(*this) << p0 << abc << p2; 60 } 61 CubicPatch(*this) << QuadToCubic{abc, bc, p2}; // Write the 2nd quad. 62 } else { 63 SkASSERT(numPatches == 1); 64 CubicPatch(*this) << QuadToCubic{p0, p1, p2}; // Write the single quad. 65 } 66 if (needsInnerTriangles) { 67 *this << innerTriangulator.pushVertex(to_skpoint(p2)); 68 *this << innerTriangulator.close(); 69 } 70} 71 72void PatchWriter::chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, int numPatches) { 73 // If we aren't fanning, we need to fill the space between chops with triangles. 74 bool needsInnerTriangles = !(fPatchAttribs & PatchAttribs::kFanPoint); 75 MiddleOutPolygonTriangulator innerTriangulator(numPatches, to_skpoint(p0)); 76 // Load the conic in 3d homogeneous (unprojected) space. 77 float4 h0 = float4(p0,1,1); 78 float4 h1 = float4(p1,1,1) * w; 79 float4 h2 = float4(p2,1,1); 80 for (; numPatches >= 2; --numPatches) { 81 // Chop in homogeneous space. 82 float T = 1.f/numPatches; 83 float4 ab = mix(h0, h1, T); 84 float4 bc = mix(h1, h2, T); 85 float4 abc = mix(ab, bc, T); 86 87 // Project and write the 1st conic. 88 float2 midpoint = abc.xy() / abc.w(); 89 ConicPatch(*this) << (h0.xy() / h0.w()) 90 << (ab.xy() / ab.w()) 91 << midpoint 92 << (ab.w() / sqrtf(h0.w() * abc.w())); 93 if (needsInnerTriangles) { 94 *this << innerTriangulator.pushVertex(to_skpoint(midpoint)); 95 } 96 std::tie(h0, h1) = {abc, bc}; // Save the 2nd conic (in homogeneous space). 97 } 98 // Project and write the remaining conic. 99 SkASSERT(numPatches == 1); 100 ConicPatch(*this) << (h0.xy() / h0.w()) 101 << (h1.xy() / h1.w()) 102 << h2.xy() // h2.w == 1 103 << (h1.w() / sqrtf(h0.w())); 104 if (needsInnerTriangles) { 105 *this << innerTriangulator.pushVertex(to_skpoint(h2.xy())); 106 *this << innerTriangulator.close(); 107 } 108} 109 110void PatchWriter::chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3, int numPatches) { 111 // If we aren't fanning, we need to fill the space between chops with triangles. 112 bool needsInnerTriangles = !(fPatchAttribs & PatchAttribs::kFanPoint); 113 MiddleOutPolygonTriangulator innerTriangulator(numPatches, to_skpoint(p0)); 114 for (; numPatches >= 3; numPatches -= 2) { 115 // Chop into 3 cubics. 116 float4 T = float4(1,1,2,2) / numPatches; 117 float4 ab = mix(p0.xyxy(), p1.xyxy(), T); 118 float4 bc = mix(p1.xyxy(), p2.xyxy(), T); 119 float4 cd = mix(p2.xyxy(), p3.xyxy(), T); 120 float4 abc = mix(ab, bc, T); 121 float4 bcd = mix(bc, cd, T); 122 float4 abcd = mix(abc, bcd, T); 123 float4 middle = mix(abc, bcd, T.zwxy()); // p1 & p2 of the middle cubic. 124 125 CubicPatch(*this) << p0 << ab.lo << abc.lo << abcd.lo; // Write the 1st cubic. 126 if (needsInnerTriangles) { 127 TrianglePatch(*this) << p0 << abcd.lo << abcd.hi; 128 } 129 CubicPatch(*this) << abcd.lo << middle << abcd.hi; // Write the 2nd cubic. 130 if (needsInnerTriangles) { 131 *this << innerTriangulator.pushVertex(to_skpoint(abcd.hi)); 132 } 133 std::tie(p0, p1, p2) = {abcd.hi, bcd.hi, cd.hi}; // Save the 3rd cubic. 134 } 135 if (numPatches == 2) { 136 // Chop into 2 cubics. 137 float2 ab = (p0 + p1) * .5f; 138 float2 bc = (p1 + p2) * .5f; 139 float2 cd = (p2 + p3) * .5f; 140 float2 abc = (ab + bc) * .5f; 141 float2 bcd = (bc + cd) * .5f; 142 float2 abcd = (abc + bcd) * .5f; 143 144 CubicPatch(*this) << p0 << ab << abc << abcd; // Write the 1st cubic. 145 if (needsInnerTriangles) { 146 TrianglePatch(*this) << p0 << abcd << p3; 147 } 148 CubicPatch(*this) << abcd << bcd << cd << p3; // Write the 2nd cubic. 149 } else { 150 SkASSERT(numPatches == 1); 151 CubicPatch(*this) << p0 << p1 << p2 << p3; // Write the single cubic. 152 } 153 if (needsInnerTriangles) { 154 *this << innerTriangulator.pushVertex(to_skpoint(p3)); 155 *this << innerTriangulator.close(); 156 } 157} 158 159} // namespace skgpu 160