void GrGLUniformHandler::getUniformLocations(GrGLuint programID, const GrGLCaps& caps) { if (!caps.bindUniformLocationSupport()) { int count = fUniforms.count(); for (int i = 0; i < count; ++i) { GrGLint location; GL_CALL_RET(location, GetUniformLocation(programID, fUniforms[i].fVariable.c_str())); fUniforms[i].fLocation = location; } for (int i = 0; i < fSamplers.count(); ++i) { GrGLint location; GL_CALL_RET(location, GetUniformLocation(programID, fSamplers[i].fVariable.c_str())); fSamplers[i].fLocation = location; } for (int i = 0; i < fTexelBuffers.count(); ++i) { GrGLint location; GL_CALL_RET(location, GetUniformLocation(programID, fTexelBuffers[i].fVariable.c_str())); fTexelBuffers[i].fLocation = location; } for (int i = 0; i < fImageStorages.count(); ++i) { GrGLint location; GL_CALL_RET(location, GetUniformLocation(programID, fImageStorages[i].fVariable.c_str())); fImageStorages[i].fLocation = location; } } }
GrGLuint GrGLPathRendering::genPaths(GrGLsizei range) { if (range > 1) { GrGLuint name; GL_CALL_RET(name, GenPaths(range)); return name; } if (NULL == fPathNameAllocator.get()) { static const int range = 65536; GrGLuint firstName; GL_CALL_RET(firstName, GenPaths(range)); fPathNameAllocator.reset(SkNEW_ARGS(GrGLNameAllocator, (firstName, firstName + range))); } // When allocating names one at a time, pull from a client-side pool of // available names in order to save a round trip to the GL server. GrGLuint name = fPathNameAllocator->allocateName(); if (0 == name) { // Our reserved path names are all in use. Fall back on GenPaths. GL_CALL_RET(name, GenPaths(1)); } return name; }
void GrGLBuffer::onMap() { if (this->wasDestroyed()) { return; } VALIDATE(); SkASSERT(!this->isMapped()); if (0 == fBufferID) { fMapPtr = fCPUData; VALIDATE(); return; } // TODO: Make this a function parameter. bool readOnly = (kXferGpuToCpu_GrBufferType == fIntendedType); // Handling dirty context is done in the bindBuffer call switch (this->glCaps().mapBufferType()) { case GrGLCaps::kNone_MapBufferType: break; case GrGLCaps::kMapBuffer_MapBufferType: { GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); // Let driver know it can discard the old data if (GR_GL_USE_BUFFER_DATA_NULL_HINT || fGLSizeInBytes != fSizeInBytes) { GL_CALL(BufferData(target, fSizeInBytes, nullptr, fUsage)); } GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY)); break; } case GrGLCaps::kMapBufferRange_MapBufferType: { GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); // Make sure the GL buffer size agrees with fDesc before mapping. if (fGLSizeInBytes != fSizeInBytes) { GL_CALL(BufferData(target, fSizeInBytes, nullptr, fUsage)); } GrGLbitfield writeAccess = GR_GL_MAP_WRITE_BIT; if (kXferCpuToGpu_GrBufferType != fIntendedType) { // TODO: Make this a function parameter. writeAccess |= GR_GL_MAP_INVALIDATE_BUFFER_BIT; } GL_CALL_RET(fMapPtr, MapBufferRange(target, 0, fSizeInBytes, readOnly ? GR_GL_MAP_READ_BIT : writeAccess)); break; } case GrGLCaps::kChromium_MapBufferType: { GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); // Make sure the GL buffer size agrees with fDesc before mapping. if (fGLSizeInBytes != fSizeInBytes) { GL_CALL(BufferData(target, fSizeInBytes, nullptr, fUsage)); } GL_CALL_RET(fMapPtr, MapBufferSubData(target, 0, fSizeInBytes, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY)); break; } } fGLSizeInBytes = fSizeInBytes; VALIDATE(); }
void GrGLBuffer::onMap() { SkASSERT(fBufferID); SkASSERT(!this->wasDestroyed()); VALIDATE(); SkASSERT(!this->isMapped()); // TODO: Make this a function parameter. bool readOnly = (GrGpuBufferType::kXferGpuToCpu == fIntendedType); // Handling dirty context is done in the bindBuffer call switch (this->glCaps().mapBufferType()) { case GrGLCaps::kNone_MapBufferType: return; case GrGLCaps::kMapBuffer_MapBufferType: { GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); if (!readOnly) { // Let driver know it can discard the old data if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->size()) { GL_CALL(BufferData(target, this->size(), nullptr, fUsage)); } } GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY)); break; } case GrGLCaps::kMapBufferRange_MapBufferType: { GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); // Make sure the GL buffer size agrees with fDesc before mapping. if (fGLSizeInBytes != this->size()) { GL_CALL(BufferData(target, this->size(), nullptr, fUsage)); } GrGLbitfield access; if (readOnly) { access = GR_GL_MAP_READ_BIT; } else { access = GR_GL_MAP_WRITE_BIT; if (GrGpuBufferType::kXferCpuToGpu != fIntendedType) { // TODO: Make this a function parameter. access |= GR_GL_MAP_INVALIDATE_BUFFER_BIT; } } GL_CALL_RET(fMapPtr, MapBufferRange(target, 0, this->size(), access)); break; } case GrGLCaps::kChromium_MapBufferType: { GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this); // Make sure the GL buffer size agrees with fDesc before mapping. if (fGLSizeInBytes != this->size()) { GL_CALL(BufferData(target, this->size(), nullptr, fUsage)); } GL_CALL_RET(fMapPtr, MapBufferSubData(target, 0, this->size(), readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY)); break; } } fGLSizeInBytes = this->size(); VALIDATE(); }
GrGLuint GrGLPathRendering::genPaths(GrGLsizei range) { SkASSERT(range > 0); GrGLuint firstID; if (fPreallocatedPathCount >= range) { firstID = fFirstPreallocatedPathID; fPreallocatedPathCount -= range; fFirstPreallocatedPathID += range; return firstID; } // Allocate range + the amount to fill up preallocation amount. If succeed, either join with // the existing preallocation range or delete the existing and use the new (potentially partial) // preallocation range. GrGLsizei allocAmount = range + (kPathIDPreallocationAmount - fPreallocatedPathCount); if (allocAmount >= range) { GL_CALL_RET(firstID, GenPaths(allocAmount)); if (firstID != 0) { if (fPreallocatedPathCount > 0 && firstID == fFirstPreallocatedPathID + fPreallocatedPathCount) { firstID = fFirstPreallocatedPathID; fPreallocatedPathCount += allocAmount - range; fFirstPreallocatedPathID += range; return firstID; } if (allocAmount > range) { if (fPreallocatedPathCount > 0) { this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount); } fFirstPreallocatedPathID = firstID + range; fPreallocatedPathCount = allocAmount - range; } // Special case: if allocAmount == range, we have full preallocated range. return firstID; } } // Failed to allocate with preallocation. Remove existing preallocation and try to allocate just // the range. if (fPreallocatedPathCount > 0) { this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount); fPreallocatedPathCount = 0; } GL_CALL_RET(firstID, GenPaths(range)); if (firstID == 0) { SkDebugf("Warning: Failed to allocate path\n"); } return firstID; }
GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu, kIsWrapped) { #ifndef SK_SCALAR_IS_FLOAT GrCrash("Assumes scalar is float."); #endif SkASSERT(!path.isEmpty()); GL_CALL_RET(fPathID, GenPaths(1)); SkSTArray<16, GrGLubyte, true> pathCommands; SkSTArray<16, SkPoint, true> pathPoints; int verbCnt = path.countVerbs(); int pointCnt = path.countPoints(); pathCommands.resize_back(verbCnt); pathPoints.resize_back(pointCnt); // TODO: Direct access to path points since we could pass them on directly. path.getPoints(&pathPoints[0], pointCnt); path.getVerbs(&pathCommands[0], verbCnt); GR_DEBUGCODE(int numPts = 0); for (int i = 0; i < verbCnt; ++i) { SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]); pathCommands[i] = verb_to_gl_path_cmd(v); GR_DEBUGCODE(numPts += num_pts(v)); } GrAssert(pathPoints.count() == numPts); GL_CALL(PathCommands(fPathID, verbCnt, &pathCommands[0], 2 * pointCnt, GR_GL_FLOAT, &pathPoints[0])); fBounds = path.getBounds(); }
bool GrGLShaderBuilder::finish() { SkASSERT(0 == fOutput.fProgramID); GL_CALL_RET(fOutput.fProgramID, CreateProgram()); if (!fOutput.fProgramID) { return false; } SkTDArray<GrGLuint> shadersToDelete; if (!this->compileAndAttachShaders(fOutput.fProgramID, &shadersToDelete)) { GL_CALL(DeleteProgram(fOutput.fProgramID)); return false; } this->bindProgramLocations(fOutput.fProgramID); if (fUniformManager->isUsingBindUniform()) { fUniformManager->getUniformLocations(fOutput.fProgramID, fUniforms); } GL_CALL(LinkProgram(fOutput.fProgramID)); // Calling GetProgramiv is expensive in Chromium. Assume success in release builds. bool checkLinked = !fGpu->ctxInfo().isChromium(); #ifdef SK_DEBUG checkLinked = true; #endif if (checkLinked) { GrGLint linked = GR_GL_INIT_ZERO; GL_CALL(GetProgramiv(fOutput.fProgramID, GR_GL_LINK_STATUS, &linked)); if (!linked) { GrGLint infoLen = GR_GL_INIT_ZERO; GL_CALL(GetProgramiv(fOutput.fProgramID, 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 chrome cmd buffer param validation. GrGLsizei length = GR_GL_INIT_ZERO; GL_CALL(GetProgramInfoLog(fOutput.fProgramID, infoLen+1, &length, (char*)log.get())); GrPrintf((char*)log.get()); } SkDEBUGFAIL("Error linking program"); GL_CALL(DeleteProgram(fOutput.fProgramID)); fOutput.fProgramID = 0; return false; } } if (!fUniformManager->isUsingBindUniform()) { fUniformManager->getUniformLocations(fOutput.fProgramID, fUniforms); } for (int i = 0; i < shadersToDelete.count(); ++i) { GL_CALL(DeleteShader(shadersToDelete[i])); } return true; }
void GrGLProgramBuilder::resolveUniformLocations(GrGLuint programID) { int count = fUniforms.count(); for (int i = 0; i < count; ++i) { GrGLint location; GL_CALL_RET(location, GetUniformLocation(programID, fUniforms[i].fVariable.c_str())); fUniforms[i].fLocation = location; } }
void GrGLNvprProgramBuilder::resolveSeparableVaryings(GrGLuint programId) { int count = fSeparableVaryingInfos.count(); for (int i = 0; i < count; ++i) { GrGLint location; GL_CALL_RET(location, GetProgramResourceLocation(programId, GR_GL_FRAGMENT_INPUT, fSeparableVaryingInfos[i].fVariable.c_str())); fSeparableVaryingInfos[i].fLocation = location; } }
void GrGLShaderBuilder::resolveProgramLocations(GrGLuint programId) { bool usingBindUniform = fGpu->glInterface()->fFunctions.fBindUniformLocation != NULL; if (!usingBindUniform) { int count = fUniforms.count(); for (int i = 0; i < count; ++i) { GrGLint location; GL_CALL_RET(location, GetUniformLocation(programId, fUniforms[i].fVariable.c_str())); fUniforms[i].fLocation = location; } } }
GrGLProgram* GrGLProgramBuilder::finalize() { // verify we can get a program id GrGLuint programID; GL_CALL_RET(programID, CreateProgram()); if (0 == programID) { this->cleanupFragmentProcessors(); return nullptr; } // compile shaders and bind attributes / uniforms SkTDArray<GrGLuint> shadersToDelete; fVS.finalize(GrGLSLUniformHandler::kVertex_Visibility); if (!this->compileAndAttachShaders(fVS, programID, GR_GL_VERTEX_SHADER, &shadersToDelete)) { this->cleanupProgram(programID, shadersToDelete); return nullptr; } // NVPR actually requires a vertex shader to compile bool useNvpr = primitiveProcessor().isPathRendering(); if (!useNvpr) { const GrPrimitiveProcessor& primProc = this->primitiveProcessor(); int vaCount = primProc.numAttribs(); for (int i = 0; i < vaCount; i++) { GL_CALL(BindAttribLocation(programID, i, primProc.getAttrib(i).fName)); } } fFS.finalize(GrGLSLUniformHandler::kFragment_Visibility); if (!this->compileAndAttachShaders(fFS, programID, GR_GL_FRAGMENT_SHADER, &shadersToDelete)) { 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) { checkLinkStatus(programID); } this->resolveProgramResourceLocations(programID); this->cleanupShaders(shadersToDelete); return this->createProgram(programID); }
bool GrGLShaderBuilder::finish(GrGLuint* outProgramId) { SK_TRACE_EVENT0("GrGLShaderBuilder::finish"); GrGLuint programId = 0; GL_CALL_RET(programId, CreateProgram()); if (!programId) { return false; } if (!this->compileAndAttachShaders(programId)) { GL_CALL(DeleteProgram(programId)); return false; } this->bindProgramLocations(programId); GL_CALL(LinkProgram(programId)); // Calling GetProgramiv is expensive in Chromium. Assume success in release builds. bool checkLinked = !fGpu->ctxInfo().isChromium(); #ifdef SK_DEBUG checkLinked = true; #endif if (checkLinked) { GrGLint linked = GR_GL_INIT_ZERO; GL_CALL(GetProgramiv(programId, GR_GL_LINK_STATUS, &linked)); if (!linked) { GrGLint infoLen = GR_GL_INIT_ZERO; GL_CALL(GetProgramiv(programId, 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 chrome cmd buffer param validation. GrGLsizei length = GR_GL_INIT_ZERO; GL_CALL(GetProgramInfoLog(programId, infoLen+1, &length, (char*)log.get())); GrPrintf((char*)log.get()); } SkDEBUGFAIL("Error linking program"); GL_CALL(DeleteProgram(programId)); return false; } } fUniformManager.getUniformLocations(programId, fUniforms); *outProgramId = programId; return true; }
GrGLProgram* GrGLProgramBuilder::finalize() { // verify we can get a program id GrGLuint programID; GL_CALL_RET(programID, CreateProgram()); if (0 == programID) { return NULL; } // compile shaders and bind attributes / uniforms SkTDArray<GrGLuint> shadersToDelete; if (!fVS.compileAndAttachShaders(programID, &shadersToDelete)) { this->cleanupProgram(programID, shadersToDelete); return NULL; } // NVPR actually requires a vertex shader to compile bool useNvpr = primitiveProcessor().isPathRendering(); if (!useNvpr) { fVS.bindVertexAttributes(programID); } if (!fFS.compileAndAttachShaders(programID, &shadersToDelete)) { this->cleanupProgram(programID, shadersToDelete); return NULL; } bool usingBindUniform = fGpu->glInterface()->fFunctions.fBindUniformLocation != NULL; if (usingBindUniform) { this->bindUniformLocations(programID); } fFS.bindFragmentShaderLocations(programID); GL_CALL(LinkProgram(programID)); // Calling GetProgramiv is expensive in Chromium. Assume success in release builds. bool checkLinked = !fGpu->ctxInfo().isChromium(); #ifdef SK_DEBUG checkLinked = true; #endif if (checkLinked) { checkLinkStatus(programID); } if (!usingBindUniform) { this->resolveUniformLocations(programID); } this->cleanupShaders(shadersToDelete); return this->createProgram(programID); }
GrPathRange* GrGLPathRendering::createGlyphs(const SkTypeface* typeface, const SkDescriptor* desc, const SkStrokeRec& stroke) { if (NULL != desc || !caps().glyphLoadingSupport) { return GrPathRendering::createGlyphs(typeface, desc, stroke); } if (NULL == typeface) { typeface = SkTypeface::GetDefaultTypeface(); SkASSERT(NULL != typeface); } int faceIndex; SkAutoTDelete<SkStream> fontStream(typeface->openStream(&faceIndex)); const size_t fontDataLength = fontStream->getLength(); if (0 == fontDataLength) { return GrPathRendering::createGlyphs(typeface, NULL, stroke); } SkTArray<uint8_t> fontTempBuffer; const void* fontData = fontStream->getMemoryBase(); if (NULL == fontData) { // TODO: Find a more efficient way to pass the font data (e.g. open file descriptor). fontTempBuffer.reset(SkToInt(fontDataLength)); fontStream->read(&fontTempBuffer.front(), fontDataLength); fontData = &fontTempBuffer.front(); } const int numPaths = typeface->countGlyphs(); const GrGLuint basePathID = this->genPaths(numPaths); SkAutoTUnref<GrGLPath> templatePath(SkNEW_ARGS(GrGLPath, (fGpu, SkPath(), stroke))); GrGLenum status; GL_CALL_RET(status, PathMemoryGlyphIndexArray(basePathID, GR_GL_STANDARD_FONT_FORMAT, fontDataLength, fontData, faceIndex, 0, numPaths, templatePath->pathID(), SkPaint::kCanonicalTextSizeForPaths)); if (GR_GL_FONT_GLYPHS_AVAILABLE != status) { this->deletePaths(basePathID, numPaths); return GrPathRendering::createGlyphs(typeface, NULL, stroke); } // This is a crude approximation. We may want to consider giving this class // a pseudo PathGenerator whose sole purpose is to track the approximate gpu // memory size. const size_t gpuMemorySize = fontDataLength / 4; return SkNEW_ARGS(GrGLPathRange, (fGpu, basePathID, numPaths, gpuMemorySize, stroke)); }
void GrGLPathProgramBuilder::resolveProgramResourceLocations(GrGLuint programID) { this->INHERITED::resolveProgramResourceLocations(programID); if (fGpu->glPathRendering()->shouldBindFragmentInputs()) { return; } int count = fSeparableVaryingInfos.count(); for (int i = 0; i < count; ++i) { GrGLint location; GL_CALL_RET(location, GetProgramResourceLocation(programID, GR_GL_FRAGMENT_INPUT, fSeparableVaryingInfos[i].fVariable.c_str())); fSeparableVaryingInfos[i].fLocation = location; } }
void GrGLProgramBuilder::resolveProgramResourceLocations(GrGLuint programID) { fUniformHandler.getUniformLocations(programID, fGpu->glCaps()); // handle NVPR separable varyings if (!fGpu->glCaps().shaderCaps()->pathRenderingSupport() || fGpu->glPathRendering()->shouldBindFragmentInputs()) { return; } int count = fVaryingHandler.fPathProcVaryingInfos.count(); for (int i = 0; i < count; ++i) { GrGLint location; GL_CALL_RET(location, GetProgramResourceLocation( programID, GR_GL_FRAGMENT_INPUT, fVaryingHandler.fPathProcVaryingInfos[i].fVariable.c_str())); fVaryingHandler.fPathProcVaryingInfos[i].fLocation = location; } }
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); }