1/*
2 * Copyright 2020 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/ops/AtlasInstancedHelper.h"
9
10#include "src/gpu/BufferWriter.h"
11#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
12#include "src/gpu/glsl/GrGLSLVarying.h"
13#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
14
15namespace skgpu::v1 {
16
17void AtlasInstancedHelper::appendInstanceAttribs(
18        SkTArray<GrGeometryProcessor::Attribute>* instanceAttribs) const {
19    instanceAttribs->emplace_back("locations", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
20    if (fShaderFlags & ShaderFlags::kCheckBounds) {
21        instanceAttribs->emplace_back("sizeInAtlas", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
22    }
23}
24
25void AtlasInstancedHelper::writeInstanceData(VertexWriter* instanceWriter,
26                                             const Instance* i) const {
27    SkASSERT(i->fLocationInAtlas.x() >= 0);
28    SkASSERT(i->fLocationInAtlas.y() >= 0);
29    *instanceWriter <<
30            // A negative x coordinate in the atlas indicates that the path is transposed.
31            // Also add 1 since we can't negate zero.
32            ((float)(i->fTransposedInAtlas ? -i->fLocationInAtlas.x() - 1
33                     : i->fLocationInAtlas.x() + 1)) <<
34            (float)i->fLocationInAtlas.y() <<
35            (float)i->fPathDevIBounds.left() <<
36            (float)i->fPathDevIBounds.top() <<
37            VertexWriter::If(fShaderFlags & ShaderFlags::kCheckBounds,
38                             SkSize::Make(i->fPathDevIBounds.size()));
39}
40
41void AtlasInstancedHelper::injectShaderCode(
42        const GrGeometryProcessor::ProgramImpl::EmitArgs& args,
43        const GrShaderVar& devCoord,
44        GrGLSLUniformHandler::UniformHandle* atlasAdjustUniformHandle) const {
45    GrGLSLVarying atlasCoord(kFloat2_GrSLType);
46    args.fVaryingHandler->addVarying("atlasCoord", &atlasCoord);
47
48    const char* atlasAdjustName;
49    *atlasAdjustUniformHandle = args.fUniformHandler->addUniform(
50            nullptr, kVertex_GrShaderFlag, kFloat2_GrSLType, "atlas_adjust", &atlasAdjustName);
51
52    args.fVertBuilder->codeAppendf(R"(
53    // A negative x coordinate in the atlas indicates that the path is transposed.
54    // We also added 1 since we can't negate zero.
55    float2 atlasTopLeft = float2(abs(locations.x) - 1, locations.y);
56    float2 devTopLeft = locations.zw;
57    bool transposed = locations.x < 0;
58    float2 atlasCoord = %s - devTopLeft;
59    if (transposed) {
60        atlasCoord = atlasCoord.yx;
61    }
62    atlasCoord += atlasTopLeft;
63    %s = atlasCoord * %s;)", devCoord.c_str(), atlasCoord.vsOut(), atlasAdjustName);
64
65    if (fShaderFlags & ShaderFlags::kCheckBounds) {
66        GrGLSLVarying atlasBounds(kFloat4_GrSLType);
67        args.fVaryingHandler->addVarying("atlasbounds", &atlasBounds,
68                                         GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
69        args.fVertBuilder->codeAppendf(R"(
70        float4 atlasBounds = atlasTopLeft.xyxy + (transposed ? sizeInAtlas.00yx
71                                                             : sizeInAtlas.00xy);
72        %s = atlasBounds * %s.xyxy;)", atlasBounds.vsOut(), atlasAdjustName);
73
74        args.fFragBuilder->codeAppendf(R"(
75        half atlasCoverage = 0;
76        float2 atlasCoord = %s;
77        float4 atlasBounds = %s;
78        if (all(greaterThan(atlasCoord, atlasBounds.xy)) &&
79            all(lessThan(atlasCoord, atlasBounds.zw))) {
80            atlasCoverage = )", atlasCoord.fsIn(), atlasBounds.fsIn());
81        args.fFragBuilder->appendTextureLookup(args.fTexSamplers[0], "atlasCoord");
82        args.fFragBuilder->codeAppendf(R"(.a;
83        })");
84    } else {
85        args.fFragBuilder->codeAppendf("half atlasCoverage = ");
86        args.fFragBuilder->appendTextureLookup(args.fTexSamplers[0], atlasCoord.fsIn());
87        args.fFragBuilder->codeAppendf(".a;");
88    }
89
90    if (fShaderFlags & ShaderFlags::kInvertCoverage) {
91        args.fFragBuilder->codeAppendf("%s *= (1 - atlasCoverage);", args.fOutputCoverage);
92    } else {
93        args.fFragBuilder->codeAppendf("%s *= atlasCoverage;", args.fOutputCoverage);
94    }
95}
96
97void AtlasInstancedHelper::setUniformData(
98        const GrGLSLProgramDataManager& pdman,
99        const GrGLSLUniformHandler::UniformHandle& atlasAdjustUniformHandle) const {
100    SkASSERT(fAtlasProxy->isInstantiated());
101    SkISize dimensions = fAtlasProxy->backingStoreDimensions();
102    pdman.set2f(atlasAdjustUniformHandle, 1.f / dimensions.width(), 1.f / dimensions.height());
103}
104
105} // namespace skgpu::v1
106