// Prints shaders one line at the time. This ensures they don't get truncated by the adb log. void PrintLineByLine(const char* header, const SkSL::String& text) { if (header) { SkDebugf("%s\n", header); } SkSL::String pretty = PrettyPrint(text); SkTArray<SkString> lines; SkStrSplit(pretty.c_str(), "\n", kStrict_SkStrSplitMode, &lines); for (int i = 0; i < lines.count(); ++i) { SkDebugf("%4i\t%s\n", i + 1, lines[i].c_str()); } }
// Given the path to a file (e.g. src/gpu/effects/GrFooFragmentProcessor.fp) and the expected // filename prefix and suffix (e.g. "Gr" and ".fp"), returns the "base name" of the // file (in this case, 'FooFragmentProcessor'). If no match, returns the empty string. static SkSL::String base_name(const char* fpPath, const char* prefix, const char* suffix) { SkSL::String result; const char* end = fpPath + strlen(fpPath); const char* fileName = end; // back up until we find a slash while (fileName != fpPath && '/' != *(fileName - 1) && '\\' != *(fileName - 1)) { --fileName; } if (!strncmp(fileName, prefix, strlen(prefix)) && !strncmp(end - strlen(suffix), suffix, strlen(suffix))) { result.append(fileName + strlen(prefix), end - fileName - strlen(prefix) - strlen(suffix)); } return result; }
bool GrCompileVkShaderModule(const GrVkGpu* gpu, const char* shaderString, VkShaderStageFlagBits stage, VkShaderModule* shaderModule, VkPipelineShaderStageCreateInfo* stageInfo, const SkSL::Program::Settings& settings, SkSL::Program::Inputs* outInputs) { std::unique_ptr<SkSL::Program> program = gpu->shaderCompiler()->convertProgram( vk_shader_stage_to_skiasl_kind(stage), SkSL::String(shaderString), settings); if (!program) { SkDebugf("SkSL error:\n%s\n", gpu->shaderCompiler()->errorText().c_str()); SkASSERT(false); } *outInputs = program->fInputs; SkSL::String code; if (!gpu->shaderCompiler()->toSPIRV(*program, &code)) { SkDebugf("%s\n", gpu->shaderCompiler()->errorText().c_str()); return false; } VkShaderModuleCreateInfo moduleCreateInfo; memset(&moduleCreateInfo, 0, sizeof(VkShaderModuleCreateInfo)); moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; moduleCreateInfo.pNext = nullptr; moduleCreateInfo.flags = 0; moduleCreateInfo.codeSize = code.size(); moduleCreateInfo.pCode = (const uint32_t*)code.c_str(); VkResult err = GR_VK_CALL(gpu->vkInterface(), CreateShaderModule(gpu->device(), &moduleCreateInfo, nullptr, shaderModule)); if (err) { return false; } memset(stageInfo, 0, sizeof(VkPipelineShaderStageCreateInfo)); stageInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stageInfo->pNext = nullptr; stageInfo->flags = 0; stageInfo->stage = skiasl_kind_to_vk_shader_stage(program->fKind); stageInfo->module = *shaderModule; stageInfo->pName = "main"; stageInfo->pSpecializationInfo = nullptr; return true; }
bool GrGLProgramBuilder::compileAndAttachShaders(GrGLSLShaderBuilder& shader, GrGLuint programId, GrGLenum type, SkTDArray<GrGLuint>* shaderIds, const SkSL::Program::Settings& settings, SkSL::Program::Inputs* outInputs) { SkSL::String glsl; std::unique_ptr<SkSL::Program> program = GrSkSLtoGLSL(gpu()->glContext(), type, shader.fCompilerStrings.begin(), shader.fCompilerStringLengths.begin(), shader.fCompilerStrings.count(), settings, &glsl); *outInputs = program->fInputs; return this->compileAndAttachShaders(glsl.c_str(), glsl.size(), programId, type, shaderIds, settings, *outInputs); }
GrGLProgram* GrGLProgramBuilder::finalize() { TRACE_EVENT0("skia", TRACE_FUNC); // verify we can get a program id GrGLuint programID; GL_CALL_RET(programID, CreateProgram()); if (0 == programID) { this->cleanupFragmentProcessors(); return nullptr; } if (this->gpu()->glCaps().programBinarySupport() && this->gpu()->getContext()->contextPriv().getPersistentCache()) { GL_CALL(ProgramParameteri(programID, GR_GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GR_GL_TRUE)); } this->finalizeShaders(); // compile shaders and bind attributes / uniforms const GrPrimitiveProcessor& primProc = this->primitiveProcessor(); SkSL::Program::Settings settings; settings.fCaps = this->gpu()->glCaps().shaderCaps(); settings.fFlipY = this->pipeline().proxy()->origin() != kTopLeft_GrSurfaceOrigin; settings.fSharpenTextures = this->gpu()->getContext()->contextPriv().sharpenMipmappedTextures(); SkSL::Program::Inputs inputs; SkTDArray<GrGLuint> shadersToDelete; bool cached = fGpu->glCaps().programBinarySupport() && nullptr != fCached.get(); if (cached) { this->bindProgramResourceLocations(programID); // cache hit, just hand the binary to GL const uint8_t* bytes = fCached->bytes(); size_t offset = 0; memcpy(&inputs, bytes + offset, sizeof(inputs)); offset += sizeof(inputs); int binaryFormat; memcpy(&binaryFormat, bytes + offset, sizeof(binaryFormat)); offset += sizeof(binaryFormat); GrGLClearErr(this->gpu()->glInterface()); GR_GL_CALL_NOERRCHECK(this->gpu()->glInterface(), ProgramBinary(programID, binaryFormat, (void*) (bytes + offset), fCached->size() - offset)); if (GR_GL_GET_ERROR(this->gpu()->glInterface()) == GR_GL_NO_ERROR) { if (inputs.fRTHeight) { this->addRTHeightUniform(SKSL_RTHEIGHT_NAME); } cached = this->checkLinkStatus(programID); } else { cached = false; } } if (!cached) { // cache miss, compile shaders if (fFS.fForceHighPrecision) { settings.fForceHighPrecision = true; } SkSL::String glsl; std::unique_ptr<SkSL::Program> fs = GrSkSLtoGLSL(gpu()->glContext(), GR_GL_FRAGMENT_SHADER, fFS.fCompilerStrings.begin(), fFS.fCompilerStringLengths.begin(), fFS.fCompilerStrings.count(), settings, &glsl); inputs = fs->fInputs; if (inputs.fRTHeight) { this->addRTHeightUniform(SKSL_RTHEIGHT_NAME); } if (!this->compileAndAttachShaders(glsl.c_str(), glsl.size(), programID, GR_GL_FRAGMENT_SHADER, &shadersToDelete, settings, inputs)) { this->cleanupProgram(programID, shadersToDelete); return nullptr; } std::unique_ptr<SkSL::Program> vs = GrSkSLtoGLSL(gpu()->glContext(), GR_GL_VERTEX_SHADER, fVS.fCompilerStrings.begin(), fVS.fCompilerStringLengths.begin(), fVS.fCompilerStrings.count(), settings, &glsl); if (!this->compileAndAttachShaders(glsl.c_str(), glsl.size(), programID, GR_GL_VERTEX_SHADER, &shadersToDelete, settings, inputs)) { this->cleanupProgram(programID, shadersToDelete); return nullptr; } // NVPR actually requires a vertex shader to compile bool useNvpr = primProc.isPathRendering(); if (!useNvpr) { int vaCount = primProc.numAttribs(); for (int i = 0; i < vaCount; i++) { GL_CALL(BindAttribLocation(programID, i, primProc.getAttrib(i).fName)); } } if (primProc.willUseGeoShader()) { std::unique_ptr<SkSL::Program> gs; gs = GrSkSLtoGLSL(gpu()->glContext(), GR_GL_GEOMETRY_SHADER, fGS.fCompilerStrings.begin(), fGS.fCompilerStringLengths.begin(), fGS.fCompilerStrings.count(), settings, &glsl); if (!this->compileAndAttachShaders(glsl.c_str(), glsl.size(), programID, GR_GL_GEOMETRY_SHADER, &shadersToDelete, settings, inputs)) { this->cleanupProgram(programID, shadersToDelete); return nullptr; } } this->bindProgramResourceLocations(programID); GL_CALL(LinkProgram(programID)); } // Calling GetProgramiv is expensive in Chromium. Assume success in release builds. bool checkLinked = kChromium_GrGLDriver != fGpu->ctxInfo().driver(); #ifdef SK_DEBUG checkLinked = true; #endif if (checkLinked) { if (!this->checkLinkStatus(programID)) { SkDebugf("VS:\n"); GrGLPrintShader(fGpu->glContext(), GR_GL_VERTEX_SHADER, fVS.fCompilerStrings.begin(), fVS.fCompilerStringLengths.begin(), fVS.fCompilerStrings.count(), settings); if (primProc.willUseGeoShader()) { SkDebugf("\nGS:\n"); GrGLPrintShader(fGpu->glContext(), GR_GL_GEOMETRY_SHADER, fGS.fCompilerStrings.begin(), fGS.fCompilerStringLengths.begin(), fGS.fCompilerStrings.count(), settings); } SkDebugf("\nFS:\n"); GrGLPrintShader(fGpu->glContext(), GR_GL_FRAGMENT_SHADER, fFS.fCompilerStrings.begin(), fFS.fCompilerStringLengths.begin(), fFS.fCompilerStrings.count(), settings); SkDEBUGFAIL(""); return nullptr; } } this->resolveProgramResourceLocations(programID); this->cleanupShaders(shadersToDelete); if (!cached && this->gpu()->getContext()->contextPriv().getPersistentCache() && fGpu->glCaps().programBinarySupport()) { GrGLsizei length = 0; GL_CALL(GetProgramiv(programID, GL_PROGRAM_BINARY_LENGTH, &length)); if (length > 0) { // store shader in cache sk_sp<SkData> key = SkData::MakeWithoutCopy(desc()->asKey(), desc()->keyLength()); GrGLenum binaryFormat; std::unique_ptr<char[]> binary(new char[length]); GL_CALL(GetProgramBinary(programID, length, &length, &binaryFormat, binary.get())); size_t dataLength = sizeof(inputs) + sizeof(binaryFormat) + length; std::unique_ptr<uint8_t[]> data(new uint8_t[dataLength]); size_t offset = 0; memcpy(data.get() + offset, &inputs, sizeof(inputs)); offset += sizeof(inputs); memcpy(data.get() + offset, &binaryFormat, sizeof(binaryFormat)); offset += sizeof(binaryFormat); memcpy(data.get() + offset, binary.get(), length); this->gpu()->getContext()->contextPriv().getPersistentCache()->store( *key, *SkData::MakeWithoutCopy(data.get(), dataLength)); } } return this->createProgram(programID); }
SkSL::String prettify(const SkSL::String& string) { fTabs = 0; fFreshline = true; // If a string breaks while in the middle 'parse until' we need to continue parsing on the // next string fInParseUntilNewline = false; fInParseUntil = false; int parensDepth = 0; // setup pretty state fIndex = 0; fLength = string.length(); fInput = string.c_str(); while (fLength > fIndex) { /* the heart and soul of our prettification algorithm. The rules should hopefully * be self explanatory. For '#' and '//' tokens we parse until we reach a newline. * * For long style comments like this one, we search for the ending token. We also * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines * ourselves. This allows us to remain in control of line numbers, and matching * tabs Existing tabs in the input string are copied over too, but this will look * funny * * '{' and '}' are handled in basically the same way. We add a newline if we aren't * on a fresh line, dirty the line, then add a second newline, ie braces are always * on their own lines indented properly. The one funkiness here is structs print * with the semicolon on its own line. Its not a problem for a glsl compiler though * * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala * in for loops. * * ';' means add a new line * * '\t' and '\n' are ignored in general parsing for backwards compatability with * existing shader code and we also have a special case for handling whitespace * at the beginning of fresh lines. * * Otherwise just add the new character to the pretty string, indenting if * necessary. */ if (fInParseUntilNewline) { this->parseUntilNewline(); } else if (fInParseUntil) { this->parseUntil(fInParseUntilToken); } else if (this->hasToken("#") || this->hasToken("//")) { this->parseUntilNewline(); } else if (this->hasToken("/*")) { this->parseUntil("*/"); } else if ('{' == fInput[fIndex]) { this->newline(); this->appendChar('{'); fTabs++; this->newline(); } else if ('}' == fInput[fIndex]) { fTabs--; this->newline(); this->appendChar('}'); this->newline(); } else if (this->hasToken(")")) { parensDepth--; } else if (this->hasToken("(")) { parensDepth++; } else if (!parensDepth && this->hasToken(";")) { this->newline(); } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] || (fFreshline && ' ' == fInput[fIndex])) { fIndex++; } else { this->appendChar(fInput[fIndex]); } } return fPretty; }