/* * 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 "GrSKSLPrettyPrint.h" #include "SkAutoMalloc.h" #include "SkSLCompiler.h" #include "SkSLGLSLCodeGenerator.h" #include "SkTraceEvent.h" #include "gl/GrGLGpu.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_source_lines_with_numbers(const char* source, std::function println) { SkTArray lines; SkStrSplit(source, "\n", kStrict_SkStrSplitMode, &lines); for (int i = 0; i < lines.count(); ++i) { SkString& line = lines[i]; line.prependf("%4i\t", i + 1); println(line.c_str()); } } // Prints shaders one line at the time. This ensures they don't get truncated by the adb log. static void print_shaders_line_by_line(const char** skslStrings, int* lengths, int count, const SkSL::String& glsl, std::function println = [](const char* ln) { SkDebugf("%s\n", ln); }) { SkString sksl = GrSKSLPrettyPrint::PrettyPrint(skslStrings, lengths, count, false); println("SKSL:"); print_source_lines_with_numbers(sksl.c_str(), println); if (!glsl.isEmpty()) { println("GLSL:"); print_source_lines_with_numbers(glsl.c_str(), println); } } std::unique_ptr translate_to_glsl(const GrGLContext& context, GrGLenum type, const char** skslStrings, int* lengths, int count, const SkSL::Program::Settings& settings, SkSL::String* glsl) { SkString sksl; #ifdef SK_DEBUG sksl = GrSKSLPrettyPrint::PrettyPrint(skslStrings, lengths, count, false); #else for (int i = 0; i < count; i++) { sksl.append(skslStrings[i], lengths[i]); } #endif SkSL::Compiler* compiler = context.compiler(); std::unique_ptr program; SkSL::Program::Kind programKind; switch (type) { case GR_GL_VERTEX_SHADER: programKind = SkSL::Program::kVertex_Kind; break; case GR_GL_FRAGMENT_SHADER: programKind = SkSL::Program::kFragment_Kind; break; case GR_GL_GEOMETRY_SHADER: programKind = SkSL::Program::kGeometry_Kind; break; } program = compiler->convertProgram(programKind, sksl, settings); if (!program || !compiler->toGLSL(*program, glsl)) { SkDebugf("SKSL compilation error\n----------------------\n"); print_shaders_line_by_line(skslStrings, lengths, count, *glsl); SkDebugf("\nErrors:\n%s\n", compiler->errorText().c_str()); SkDEBUGFAIL("SKSL compilation failed!\n"); return nullptr; } return program; } GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx, GrGLuint programId, GrGLenum type, const char** skslStrings, int* lengths, int count, GrGpu::Stats* stats, const SkSL::Program::Settings& settings, SkSL::Program::Inputs* outInputs) { const GrGLInterface* gli = glCtx.interface(); SkSL::String glsl; auto program = translate_to_glsl(glCtx, type, skslStrings, lengths, count, settings, &glsl); if (!program) { return 0; } // Specify GLSL source to the driver. GrGLuint shaderId; GR_GL_CALL_RET(gli, shaderId, CreateShader(type)); if (0 == shaderId) { return 0; } const char* glslChars = glsl.c_str(); GrGLint glslLength = (GrGLint) glsl.size(); GR_GL_CALL(gli, ShaderSource(shaderId, 1, &glslChars, &glslLength)); // Trace event for shader preceding driver compilation bool traceShader; TRACE_EVENT_CATEGORY_GROUP_ENABLED("skia.gpu", &traceShader); if (traceShader) { SkString shaderDebugString; print_shaders_line_by_line(skslStrings, lengths, count, glsl, [&](const char* ln) { shaderDebugString.append(ln); shaderDebugString.append("\n"); }); TRACE_EVENT_INSTANT1("skia.gpu", "skia_gpu::GLShader", TRACE_EVENT_SCOPE_THREAD, "shader", TRACE_STR_COPY(shaderDebugString.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) { SkDebugf("GLSL compilation error\n----------------------\n"); print_shaders_line_by_line(skslStrings, lengths, count, glsl); 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("Errors:\n%s\n", (const char*) log.get()); } SkDEBUGFAIL("GLSL compilation failed!"); GR_GL_CALL(gli, DeleteShader(shaderId)); return 0; } } if (c_PrintShaders) { const char* typeName = "Unknown"; switch (type) { case GR_GL_VERTEX_SHADER: typeName = "Vertex"; break; case GR_GL_GEOMETRY_SHADER: typeName = "Geometry"; break; case GR_GL_FRAGMENT_SHADER: typeName = "Fragment"; break; } SkDebugf("---- %s shader ----------------------------------------------------\n", typeName); print_shaders_line_by_line(skslStrings, lengths, count, glsl); } // 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)); *outInputs = program->fInputs; return shaderId; } void GrGLPrintShader(const GrGLContext& context, GrGLenum type, const char** skslStrings, int* lengths, int count, const SkSL::Program::Settings& settings) { SkSL::String glsl; if (translate_to_glsl(context, type, skslStrings, lengths, count, settings, &glsl)) { print_shaders_line_by_line(skslStrings, lengths, count, glsl); } }