blob: b26ce894f0246b35dbf6a0ecfcb266a89eedda43 [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrGLShaderStringBuilder.h"
#include "gl/GrGLGpu.h"
#include "gl/GrGLSLPrettyPrint.h"
#include "SkTraceEvent.h"
#include "SkSLCompiler.h"
#include "SkSLGLSLCodeGenerator.h"
#include "ir/SkSLProgram.h"
#define GL_CALL(X) GR_GL_CALL(gpu->glInterface(), X)
#define GL_CALL_RET(R, X) GR_GL_CALL_RET(gpu->glInterface(), R, X)
// Print the source code for all shaders generated.
static const bool c_PrintShaders{false};
static void print_shader_source(const char** strings, int* lengths, int count);
SkSL::GLCaps GrGLSkSLCapsForContext(const GrGLContext& context) {
GrGLStandard standard = context.standard();
const GrGLCaps* caps = context.caps();
const GrGLSLCaps* glslCaps = caps->glslCaps();
SkSL::GLCaps result;
switch (standard) {
case kGL_GrGLStandard:
result.fStandard = SkSL::GLCaps::kGL_Standard;
break;
case kGLES_GrGLStandard:
result.fStandard = SkSL::GLCaps::kGLES_Standard;
break;
default:
SkASSERT(false);
result.fStandard = SkSL::GLCaps::kGL_Standard;
}
switch (glslCaps->generation()) {
case k110_GrGLSLGeneration:
if (kGLES_GrGLStandard == standard) {
// ES2's shader language is based on GLSL 1.20 but is version 1.00 of the ES
// language
result.fVersion = 100;
} else {
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 110;
}
break;
case k130_GrGLSLGeneration:
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 130;
break;
case k140_GrGLSLGeneration:
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 140;
break;
case k150_GrGLSLGeneration:
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 150;
break;
case k330_GrGLSLGeneration:
if (kGLES_GrGLStandard == standard) {
result.fVersion = 300;
} else {
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 330;
}
break;
case k400_GrGLSLGeneration:
SkASSERT(kGL_GrGLStandard == standard);
result.fVersion = 400;
break;
case k310es_GrGLSLGeneration:
SkASSERT(kGLES_GrGLStandard == standard);
result.fVersion = 310;
break;
case k320es_GrGLSLGeneration:
SkASSERT(kGLES_GrGLStandard == standard);
result.fVersion = 320;
break;
}
result.fIsCoreProfile = caps->isCoreProfile();
result.fUsesPrecisionModifiers = glslCaps->usesPrecisionModifiers();
result.fMustDeclareFragmentShaderOutput = glslCaps->mustDeclareFragmentShaderOutput();
result.fCanUseMinAndAbsTogether = glslCaps->canUseMinAndAbsTogether();
return result;
}
static void dump_string(std::string s) {
// on Android, SkDebugf only displays the first 1K characters of output, which results in
// incomplete shader source code. Print each line individually to avoid this problem.
size_t index = 0;
for (;;) {
size_t next = s.find("\n", index);
if (next == std::string::npos) {
SkDebugf("%s", s.substr(index).c_str());
break;
} else {
SkDebugf("%s", s.substr(index, next - index + 1).c_str());
index = next + 1;
}
}
}
GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
GrGLuint programId,
GrGLenum type,
const char** strings,
int* lengths,
int count,
GrGpu::Stats* stats) {
const GrGLInterface* gli = glCtx.interface();
GrGLuint shaderId;
GR_GL_CALL_RET(gli, shaderId, CreateShader(type));
if (0 == shaderId) {
return 0;
}
std::string sksl;
#ifdef SK_DEBUG
SkString prettySource = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
sksl = std::string(prettySource.c_str());
#else
for (int i = 0; i < count; i++) {
sksl.append(strings[i], lengths[i]);
}
#endif
std::string glsl;
SkSL::Compiler& compiler = *glCtx.compiler();
SkSL::GLCaps caps = GrGLSkSLCapsForContext(glCtx);
SkASSERT(type == GR_GL_VERTEX_SHADER || type == GR_GL_FRAGMENT_SHADER);
SkDEBUGCODE(bool result = )compiler.toGLSL(type == GR_GL_VERTEX_SHADER
? SkSL::Program::kVertex_Kind
: SkSL::Program::kFragment_Kind,
std::string(sksl.c_str()),
caps,
&glsl);
#ifdef SK_DEBUG
if (!result) {
SkDebugf("SKSL compilation error\n----------------------\n");
SkDebugf("SKSL:\n");
dump_string(sksl);
SkDebugf("\nErrors:\n%s\n", compiler.errorText().c_str());
SkDEBUGFAIL("SKSL compilation failed!\n");
}
#endif
const char* glslChars = glsl.c_str();
GrGLint glslLength = (GrGLint) glsl.length();
GR_GL_CALL(gli, ShaderSource(shaderId, 1, &glslChars, &glslLength));
// If tracing is enabled in chrome then we pretty print
bool traceShader;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), &traceShader);
if (traceShader) {
SkString shader = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "skia_gpu::GLShader",
TRACE_EVENT_SCOPE_THREAD, "shader", TRACE_STR_COPY(shader.c_str()));
}
stats->incShaderCompilations();
GR_GL_CALL(gli, CompileShader(shaderId));
// Calling GetShaderiv in Chromium is quite expensive. Assume success in release builds.
bool checkCompiled = kChromium_GrGLDriver != glCtx.driver();
#ifdef SK_DEBUG
checkCompiled = true;
#endif
if (checkCompiled) {
GrGLint compiled = GR_GL_INIT_ZERO;
GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_COMPILE_STATUS, &compiled));
if (!compiled) {
GrGLint infoLen = GR_GL_INIT_ZERO;
GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_INFO_LOG_LENGTH, &infoLen));
SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
if (infoLen > 0) {
// retrieve length even though we don't need it to workaround bug in Chromium cmd
// buffer param validation.
GrGLsizei length = GR_GL_INIT_ZERO;
GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1, &length, (char*)log.get()));
SkDebugf("GLSL compilation error\n----------------------\n");
SkDebugf("SKSL:\n");
dump_string(sksl);
SkDebugf("GLSL:\n");
dump_string(glsl);
SkDebugf("Errors:\n%s\n", (const char*) log.get());
}
SkDEBUGFAIL("GLSL compilation failed!");
GR_GL_CALL(gli, DeleteShader(shaderId));
return 0;
}
}
if (c_PrintShaders) {
print_shader_source(strings, lengths, count);
}
// Attach the shader, but defer deletion until after we have linked the program.
// This works around a bug in the Android emulator's GLES2 wrapper which
// will immediately delete the shader object and free its memory even though it's
// attached to a program, which then causes glLinkProgram to fail.
GR_GL_CALL(gli, AttachShader(programId, shaderId));
return shaderId;
}
static void print_shader_source(const char** strings, int* lengths, int count) {
const SkString& pretty = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, true);
SkTArray<SkString> lines;
SkStrSplit(pretty.c_str(), "\n", &lines);
for (const SkString& line : lines) {
// Print the shader one line at the time so it doesn't get truncated by the adb log.
SkDebugf("%s\n", line.c_str());
}
}