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 "GrGLProgramBuilder.h"
9
10#include "GrAutoLocaleSetter.h"
11#include "GrContext.h"
12#include "GrCoordTransform.h"
13#include "GrGLProgramBuilder.h"
14#include "GrProgramDesc.h"
15#include "GrShaderCaps.h"
16#include "GrSwizzle.h"
17#include "SkAutoMalloc.h"
18#include "SkATrace.h"
19#include "SkTraceEvent.h"
20#include "gl/GrGLGpu.h"
21#include "gl/GrGLProgram.h"
22#include "gl/builders/GrGLShaderStringBuilder.h"
23#include "glsl/GrGLSLFragmentProcessor.h"
24#include "glsl/GrGLSLGeometryProcessor.h"
25#include "glsl/GrGLSLProgramDataManager.h"
26#include "glsl/GrGLSLXferProcessor.h"
27
28#define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X)
29#define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X)
30
31GrGLProgram* GrGLProgramBuilder::CreateProgram(const GrPipeline& pipeline,
32                                               const GrPrimitiveProcessor& primProc,
33                                               GrProgramDesc* desc,
34                                               GrGLGpu* gpu) {
35    SkASSERT(!pipeline.isBad() && primProc.instantiate(gpu->getContext()->resourceProvider()));
36
37    ATRACE_ANDROID_FRAMEWORK("Shader Compile");
38    GrAutoLocaleSetter als("C");
39
40    // create a builder.  This will be handed off to effects so they can use it to add
41    // uniforms, varyings, textures, etc
42    GrGLProgramBuilder builder(gpu, pipeline, primProc, desc);
43
44    if (!builder.emitAndInstallProcs()) {
45        builder.cleanupFragmentProcessors();
46        return nullptr;
47    }
48
49    return builder.finalize();
50}
51
52/////////////////////////////////////////////////////////////////////////////
53
54GrGLProgramBuilder::GrGLProgramBuilder(GrGLGpu* gpu,
55                                       const GrPipeline& pipeline,
56                                       const GrPrimitiveProcessor& primProc,
57                                       GrProgramDesc* desc)
58    : INHERITED(pipeline, primProc, desc)
59    , fGpu(gpu)
60    , fVaryingHandler(this)
61    , fUniformHandler(this) {
62}
63
64const GrCaps* GrGLProgramBuilder::caps() const {
65    return fGpu->caps();
66}
67
68bool GrGLProgramBuilder::compileAndAttachShaders(GrGLSLShaderBuilder& shader,
69                                                 GrGLuint programId,
70                                                 GrGLenum type,
71                                                 SkTDArray<GrGLuint>* shaderIds,
72                                                 const SkSL::Program::Settings& settings,
73                                                 SkSL::Program::Inputs* outInputs) {
74    GrGLGpu* gpu = this->gpu();
75    GrGLuint shaderId = GrGLCompileAndAttachShader(gpu->glContext(),
76                                                   programId,
77                                                   type,
78                                                   shader.fCompilerStrings.begin(),
79                                                   shader.fCompilerStringLengths.begin(),
80                                                   shader.fCompilerStrings.count(),
81                                                   gpu->stats(),
82                                                   settings,
83                                                   outInputs);
84
85    if (!shaderId) {
86        return false;
87    }
88
89    *shaderIds->append() = shaderId;
90    if (outInputs->fFlipY) {
91        GrProgramDesc* d = this->desc();
92        d->setSurfaceOriginKey(GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(
93                                                     this->pipeline().getRenderTarget()->origin()));
94        d->finalize();
95    }
96
97    return true;
98}
99
100GrGLProgram* GrGLProgramBuilder::finalize() {
101    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia"), "GrGLProgramBuilder::finalize()");
102
103    // verify we can get a program id
104    GrGLuint programID;
105    GL_CALL_RET(programID, CreateProgram());
106    if (0 == programID) {
107        this->cleanupFragmentProcessors();
108        return nullptr;
109    }
110
111    this->finalizeShaders();
112
113    // compile shaders and bind attributes / uniforms
114    SkSL::Program::Settings settings;
115    settings.fCaps = this->gpu()->glCaps().shaderCaps();
116    settings.fFlipY = this->pipeline().getRenderTarget()->origin() != kTopLeft_GrSurfaceOrigin;
117    SkSL::Program::Inputs inputs;
118    SkTDArray<GrGLuint> shadersToDelete;
119    if (!this->compileAndAttachShaders(fVS, programID, GR_GL_VERTEX_SHADER, &shadersToDelete,
120                                       settings, &inputs)) {
121        this->cleanupProgram(programID, shadersToDelete);
122        return nullptr;
123    }
124
125    // NVPR actually requires a vertex shader to compile
126    const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
127    bool useNvpr = primProc.isPathRendering();
128    if (!useNvpr) {
129        int vaCount = primProc.numAttribs();
130        for (int i = 0; i < vaCount; i++) {
131            GL_CALL(BindAttribLocation(programID, i, primProc.getAttrib(i).fName));
132        }
133    }
134
135    if (primProc.willUseGeoShader() &&
136        !this->compileAndAttachShaders(fGS, programID, GR_GL_GEOMETRY_SHADER, &shadersToDelete,
137                                       settings, &inputs)) {
138        this->cleanupProgram(programID, shadersToDelete);
139        return nullptr;
140    }
141
142    if (!this->compileAndAttachShaders(fFS, programID, GR_GL_FRAGMENT_SHADER, &shadersToDelete,
143                                       settings, &inputs)) {
144        this->cleanupProgram(programID, shadersToDelete);
145        return nullptr;
146    }
147
148    if (inputs.fRTHeight) {
149        this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
150    }
151
152    this->bindProgramResourceLocations(programID);
153
154    GL_CALL(LinkProgram(programID));
155
156    // Calling GetProgramiv is expensive in Chromium. Assume success in release builds.
157    bool checkLinked = kChromium_GrGLDriver != fGpu->ctxInfo().driver();
158#ifdef SK_DEBUG
159    checkLinked = true;
160#endif
161    if (checkLinked) {
162        if (!this->checkLinkStatus(programID)) {
163            SkDebugf("VS:\n");
164            GrGLPrintShader(fGpu->glContext(), GR_GL_VERTEX_SHADER, fVS.fCompilerStrings.begin(),
165                            fVS.fCompilerStringLengths.begin(), fVS.fCompilerStrings.count(),
166                            settings);
167            if (primProc.willUseGeoShader()) {
168                SkDebugf("\nGS:\n");
169                GrGLPrintShader(fGpu->glContext(), GR_GL_GEOMETRY_SHADER,
170                                fGS.fCompilerStrings.begin(), fGS.fCompilerStringLengths.begin(),
171                                fGS.fCompilerStrings.count(), settings);
172            }
173            SkDebugf("\nFS:\n");
174            GrGLPrintShader(fGpu->glContext(), GR_GL_FRAGMENT_SHADER, fFS.fCompilerStrings.begin(),
175                            fFS.fCompilerStringLengths.begin(), fFS.fCompilerStrings.count(),
176                            settings);
177            SkDEBUGFAIL("");
178            return nullptr;
179        }
180    }
181    this->resolveProgramResourceLocations(programID);
182
183    this->cleanupShaders(shadersToDelete);
184
185    return this->createProgram(programID);
186}
187
188void GrGLProgramBuilder::bindProgramResourceLocations(GrGLuint programID) {
189    fUniformHandler.bindUniformLocations(programID, fGpu->glCaps());
190
191    const GrGLCaps& caps = this->gpu()->glCaps();
192    if (fFS.hasCustomColorOutput() && caps.bindFragDataLocationSupport()) {
193        GL_CALL(BindFragDataLocation(programID, 0,
194                                     GrGLSLFragmentShaderBuilder::DeclaredColorOutputName()));
195    }
196    if (fFS.hasSecondaryOutput() && caps.shaderCaps()->mustDeclareFragmentShaderOutput()) {
197        GL_CALL(BindFragDataLocationIndexed(programID, 0, 1,
198                                  GrGLSLFragmentShaderBuilder::DeclaredSecondaryColorOutputName()));
199    }
200
201    // handle NVPR separable varyings
202    if (!fGpu->glCaps().shaderCaps()->pathRenderingSupport() ||
203        !fGpu->glPathRendering()->shouldBindFragmentInputs()) {
204        return;
205    }
206    int count = fVaryingHandler.fPathProcVaryingInfos.count();
207    for (int i = 0; i < count; ++i) {
208        GL_CALL(BindFragmentInputLocation(programID, i,
209                                       fVaryingHandler.fPathProcVaryingInfos[i].fVariable.c_str()));
210        fVaryingHandler.fPathProcVaryingInfos[i].fLocation = i;
211    }
212}
213
214bool GrGLProgramBuilder::checkLinkStatus(GrGLuint programID) {
215    GrGLint linked = GR_GL_INIT_ZERO;
216    GL_CALL(GetProgramiv(programID, GR_GL_LINK_STATUS, &linked));
217    if (!linked) {
218        SkDebugf("Program linking failed.\n");
219        GrGLint infoLen = GR_GL_INIT_ZERO;
220        GL_CALL(GetProgramiv(programID, GR_GL_INFO_LOG_LENGTH, &infoLen));
221        SkAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
222        if (infoLen > 0) {
223            // retrieve length even though we don't need it to workaround
224            // bug in chrome cmd buffer param validation.
225            GrGLsizei length = GR_GL_INIT_ZERO;
226            GL_CALL(GetProgramInfoLog(programID,
227                                      infoLen+1,
228                                      &length,
229                                      (char*)log.get()));
230            SkDebugf("%s", (char*)log.get());
231        }
232        GL_CALL(DeleteProgram(programID));
233        programID = 0;
234    }
235    return SkToBool(linked);
236}
237
238void GrGLProgramBuilder::resolveProgramResourceLocations(GrGLuint programID) {
239    fUniformHandler.getUniformLocations(programID, fGpu->glCaps());
240
241    // handle NVPR separable varyings
242    if (!fGpu->glCaps().shaderCaps()->pathRenderingSupport() ||
243        fGpu->glPathRendering()->shouldBindFragmentInputs()) {
244        return;
245    }
246    int count = fVaryingHandler.fPathProcVaryingInfos.count();
247    for (int i = 0; i < count; ++i) {
248        GrGLint location;
249        GL_CALL_RET(location, GetProgramResourceLocation(
250                                       programID,
251                                       GR_GL_FRAGMENT_INPUT,
252                                       fVaryingHandler.fPathProcVaryingInfos[i].fVariable.c_str()));
253        fVaryingHandler.fPathProcVaryingInfos[i].fLocation = location;
254    }
255}
256
257void GrGLProgramBuilder::cleanupProgram(GrGLuint programID, const SkTDArray<GrGLuint>& shaderIDs) {
258    GL_CALL(DeleteProgram(programID));
259    this->cleanupShaders(shaderIDs);
260    this->cleanupFragmentProcessors();
261}
262void GrGLProgramBuilder::cleanupShaders(const SkTDArray<GrGLuint>& shaderIDs) {
263    for (int i = 0; i < shaderIDs.count(); ++i) {
264      GL_CALL(DeleteShader(shaderIDs[i]));
265    }
266}
267
268GrGLProgram* GrGLProgramBuilder::createProgram(GrGLuint programID) {
269    return new GrGLProgram(fGpu,
270                           *this->desc(),
271                           fUniformHandles,
272                           programID,
273                           fUniformHandler.fUniforms,
274                           fUniformHandler.fSamplers,
275                           fUniformHandler.fTexelBuffers,
276                           fUniformHandler.fImageStorages,
277                           fVaryingHandler.fPathProcVaryingInfos,
278                           fGeometryProcessor,
279                           fXferProcessor,
280                           fFragmentProcessors);
281}
282