From 5df6fee929823f50c55cc50f7c882a309c1b1de9 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Mon, 18 May 2015 06:26:15 -0700 Subject: [PATCH] Move copy-surface-as-draw fallback to GrGLGpu. Review URL: https://codereview.chromium.org/1144433002 --- src/gpu/GrDrawTarget.cpp | 63 +-- src/gpu/GrDrawTarget.h | 16 +- src/gpu/GrGpu.h | 10 - src/gpu/GrInOrderDrawBuffer.cpp | 1 - src/gpu/GrTest.cpp | 5 - src/gpu/gl/GrGLCaps.cpp | 3 + src/gpu/gl/GrGLCaps.h | 9 + src/gpu/gl/GrGLGpu.cpp | 471 ++++++++++++++++------ src/gpu/gl/GrGLGpu.h | 42 +- src/gpu/gl/GrGLGpuProgramCache.cpp | 4 +- src/gpu/gl/GrGLSL.cpp | 19 + src/gpu/gl/GrGLSL.h | 18 + src/gpu/gl/GrGLVertexArray.cpp | 23 +- src/gpu/gl/GrGLVertexArray.h | 8 +- src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp | 26 +- src/gpu/gl/builders/GrGLProgramBuilder.cpp | 3 +- src/gpu/gl/builders/GrGLShaderBuilder.cpp | 10 +- 17 files changed, 458 insertions(+), 273 deletions(-) diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index 549ab37..7565cd1 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -100,13 +100,10 @@ bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuil return false; } SkIPoint dstPoint = {0, 0}; - if (this->copySurface(copy, rt, copyRect, dstPoint)) { - dstCopy->setTexture(copy); - dstCopy->setOffset(copyRect.fLeft, copyRect.fTop); - return true; - } else { - return false; - } + this->copySurface(copy, rt, copyRect, dstPoint); + dstCopy->setTexture(copy); + dstCopy->setOffset(copyRect.fLeft, copyRect.fTop); + return true; } void GrDrawTarget::flush() { @@ -421,7 +418,7 @@ bool clip_srcrect_and_dstpoint(const GrSurface* dst, } } -bool GrDrawTarget::copySurface(GrSurface* dst, +void GrDrawTarget::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { @@ -437,56 +434,10 @@ bool GrDrawTarget::copySurface(GrSurface* dst, dstPoint, &clippedSrcRect, &clippedDstPoint)) { - return true; - } - - if (this->getGpu()->canCopySurface(dst, src, clippedSrcRect, clippedDstPoint)) { - this->onCopySurface(dst, src, clippedSrcRect, clippedDstPoint); - return true; - } - - GrRenderTarget* rt = dst->asRenderTarget(); - GrTexture* tex = src->asTexture(); - - if ((dst == src) || !rt || !tex) { - return false; + return; } - GrPipelineBuilder pipelineBuilder; - pipelineBuilder.setRenderTarget(rt); - SkMatrix matrix; - matrix.setTranslate(SkIntToScalar(clippedSrcRect.fLeft - clippedDstPoint.fX), - SkIntToScalar(clippedSrcRect.fTop - clippedDstPoint.fY)); - matrix.postIDiv(tex->width(), tex->height()); - pipelineBuilder.addColorTextureProcessor(tex, matrix); - SkIRect dstRect = SkIRect::MakeXYWH(clippedDstPoint.fX, - clippedDstPoint.fY, - clippedSrcRect.width(), - clippedSrcRect.height()); - this->drawSimpleRect(&pipelineBuilder, GrColor_WHITE, SkMatrix::I(), dstRect); - return true; -} - -bool GrDrawTarget::canCopySurface(const GrSurface* dst, - const GrSurface* src, - const SkIRect& srcRect, - const SkIPoint& dstPoint) { - SkASSERT(dst); - SkASSERT(src); - - SkIRect clippedSrcRect; - SkIPoint clippedDstPoint; - // If the rect is outside the src or dst then we're guaranteed success - if (!clip_srcrect_and_dstpoint(dst, - src, - srcRect, - dstPoint, - &clippedSrcRect, - &clippedDstPoint)) { - return true; - } - return ((dst != src) && dst->asRenderTarget() && src->asTexture()) || - this->getGpu()->canCopySurface(dst, src, clippedSrcRect, clippedDstPoint); + this->onCopySurface(dst, src, clippedSrcRect, clippedDstPoint); } void GrDrawTarget::setupPipeline(const PipelineInfo& pipelineInfo, diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index 84f7eb2..0157354 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -178,25 +178,15 @@ public: * copied are specified by srcRect. They are copied to a rect of the same * size in dst with top left at dstPoint. If the src rect is clipped by the * src bounds then pixel values in the dst rect corresponding to area clipped - * by the src rect are not overwritten. This method can fail and return false + * by the src rect are not overwritten. This method is not guaranteed to succeed * depending on the type of surface, configs, etc, and the backend-specific - * limitations. If rect is clipped out entirely by the src or dst bounds then - * true is returned since there is no actual copy necessary to succeed. + * limitations. */ - bool copySurface(GrSurface* dst, + void copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint); /** - * Function that determines whether a copySurface call would succeed without actually - * performing the copy. - */ - bool canCopySurface(const GrSurface* dst, - const GrSurface* src, - const SkIRect& srcRect, - const SkIPoint& dstPoint); - - /** * Release any resources that are cached but not currently in use. This * is intended to give an application some recourse when resources are low. */ diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index f2ad10f..e8d4939 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -257,16 +257,6 @@ public: const GrPipeline&, const GrBatchTracker&) const = 0; - // Called to determine whether a copySurface call would succeed or not. Derived - // classes must keep this consistent with their implementation of onCopySurface(). Fallbacks - // to issuing a draw from the src to dst take place at the GrDrawTarget level and this function - // should only return true if a faster copy path exists. The rect and point are pre-clipped. The - // src rect and implied dst rect are guaranteed to be within the src/dst bounds and non-empty. - virtual bool canCopySurface(const GrSurface* dst, - const GrSurface* src, - const SkIRect& srcRect, - const SkIPoint& dstPoint) = 0; - // Called to perform a surface to surface copy. Fallbacks to issuing a draw from the src to dst // take place at the GrDrawTarget level and this function implement faster copy paths. The rect // and point are pre-clipped. The src rect and implied dst rect are guaranteed to be within the diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp index 6b6490c..8cc3658 100644 --- a/src/gpu/GrInOrderDrawBuffer.cpp +++ b/src/gpu/GrInOrderDrawBuffer.cpp @@ -121,7 +121,6 @@ void GrInOrderDrawBuffer::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { - SkASSERT(this->getGpu()->canCopySurface(dst, src, srcRect, dstPoint)); GrTargetCommands::Cmd* cmd = fCommands->recordCopySurface(dst, src, srcRect, dstPoint); this->recordTraceMarkersIfNecessary(cmd); } diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp index 0b3d030..6504acf 100644 --- a/src/gpu/GrTest.cpp +++ b/src/gpu/GrTest.cpp @@ -155,11 +155,6 @@ public: void discard(GrRenderTarget*) override {} - bool canCopySurface(const GrSurface* dst, - const GrSurface* src, - const SkIRect& srcRect, - const SkIPoint& dstPoint) override { return false; }; - bool copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index 06ebe3d..5ee7546 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -942,6 +942,7 @@ GrGLSLCaps::GrGLSLCaps(const GrGLSLCaps& caps) : GrShaderCaps() { GrGLSLCaps& GrGLSLCaps::operator= (const GrGLSLCaps& caps) { INHERITED::operator=(caps); + fGLSLGeneration = caps.fGLSLGeneration; fDropsTileOnZeroDivide = caps.fDropsTileOnZeroDivide; fFBFetchSupport = caps.fFBFetchSupport; fFBFetchNeedsCustomOutput = caps.fFBFetchNeedsCustomOutput; @@ -960,6 +961,8 @@ bool GrGLSLCaps::init(const GrGLContextInfo& ctxInfo, return false; } + fGLSLGeneration = ctxInfo.glslGeneration(); + GrGLStandard standard = ctxInfo.standard(); GrGLVersion version = ctxInfo.version(); diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h index 25c7889..dfc6233 100644 --- a/src/gpu/gl/GrGLCaps.h +++ b/src/gpu/gl/GrGLCaps.h @@ -374,6 +374,7 @@ private: typedef GrDrawTargetCaps INHERITED; }; +#include "GrGLSL.h" class GrGLSLCaps : public GrShaderCaps { public: @@ -438,6 +439,12 @@ public: bool mustEnableSpecificAdvBlendEqs() const { return fAdvBlendEqInteraction == kSpecificEnables_AdvBlendEqInteraction; } + + bool mustDeclareFragmentShaderOutput() const { + return fGLSLGeneration > k110_GrGLSLGeneration; + } + + GrGLSLGeneration generation() const { return fGLSLGeneration; } /** * Returns a string containing the caps info. @@ -448,6 +455,8 @@ private: // Must be called after fGeometryShaderSupport is initialized. void initShaderPrecisionTable(const GrGLContextInfo&, const GrGLInterface*); + GrGLSLGeneration fGLSLGeneration; + bool fDropsTileOnZeroDivide : 1; bool fFBFetchSupport : 1; bool fFBFetchNeedsCustomOutput : 1; diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index 6d97804..56b3d42 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -17,6 +17,7 @@ #include "GrTexturePriv.h" #include "GrTypes.h" #include "GrVertices.h" +#include "builders/GrGLShaderStringBuilder.h" #include "SkStrokeRec.h" #include "SkTemplates.h" @@ -200,13 +201,14 @@ GrGLGpu::GrGLGpu(const GrGLContext& ctx, GrContext* context) if (this->glCaps().shaderCaps()->pathRenderingSupport()) { fPathRendering.reset(new GrGLPathRendering(this)); } + + this->createCopyProgram(); } GrGLGpu::~GrGLGpu() { if (0 != fHWProgramID) { // detach the current program so there is no confusion on OpenGL's part // that we want it to be deleted - SkASSERT(fHWProgramID == fCurrentProgram->programID()); GL_CALL(UseProgram(0)); } @@ -220,6 +222,14 @@ GrGLGpu::~GrGLGpu() { GL_CALL(DeleteFramebuffers(1, &fStencilClearFBOID)); } + if (0 != fCopyProgram.fArrayBuffer) { + GL_CALL(DeleteBuffers(1, &fCopyProgram.fArrayBuffer)); + } + + if (0 != fCopyProgram.fProgram) { + GL_CALL(DeleteProgram(fCopyProgram.fProgram)); + } + delete fProgramCache; } @@ -230,6 +240,8 @@ void GrGLGpu::contextAbandoned() { fTempSrcFBOID = 0; fTempDstFBOID = 0; fStencilClearFBOID = 0; + fCopyProgram.fArrayBuffer = 0; + fCopyProgram.fProgram = 0; if (this->glCaps().shaderCaps()->pathRenderingSupport()) { this->glPathRendering()->abandonGpuResources(); } @@ -1417,15 +1429,13 @@ bool GrGLGpu::flushGLState(const DrawArgs& args) { this->flushColorWrite(blendInfo.fWriteColor); this->flushDrawFace(pipeline.getDrawFace()); - fCurrentProgram.reset(fProgramCache->getProgram(args)); - if (NULL == fCurrentProgram.get()) { + SkAutoTUnref program(fProgramCache->refProgram(args)); + if (!program) { GrContextDebugf(this->getContext(), "Failed to create program!\n"); return false; } - fCurrentProgram.get()->ref(); - - GrGLuint programID = fCurrentProgram->programID(); + GrGLuint programID = program->programID(); if (fHWProgramID != programID) { GL_CALL(UseProgram(programID)); fHWProgramID = programID; @@ -1435,7 +1445,7 @@ bool GrGLGpu::flushGLState(const DrawArgs& args) { this->flushBlend(blendInfo); } - fCurrentProgram->setData(*args.fPrimitiveProcessor, pipeline, *args.fBatchTracker); + program->setData(*args.fPrimitiveProcessor, pipeline, *args.fBatchTracker); GrGLRenderTarget* glRT = static_cast(pipeline.getRenderTarget()); this->flushStencil(pipeline.getStencil()); @@ -1490,7 +1500,7 @@ void GrGLGpu::setupGeometry(const GrPrimitiveProcessor& primProc, GrVertexAttribType attribType = attrib.fType; attribState->set(this, attribIndex, - vbuf, + vbuf->bufferID(), GrGLAttribTypeToLayout(attribType).fCount, GrGLAttribTypeToLayout(attribType).fType, GrGLAttribTypeToLayout(attribType).fNormalized, @@ -2618,7 +2628,16 @@ void GrGLGpu::unbindTextureFromFBO(GrGLenum fboTarget) { } bool GrGLGpu::initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) { - // In here we look for opportunities to use CopyTexSubImage, or fbo blit. If neither are + // If the src is a texture, we can implement the blit as a draw assuming the config is + // renderable. + if (src->asTexture() && this->caps()->isConfigRenderable(src->config(), false)) { + desc->fOrigin = kDefault_GrSurfaceOrigin; + desc->fFlags = kRenderTarget_GrSurfaceFlag; + desc->fConfig = src->config(); + return true; + } + + // We look for opportunities to use CopyTexSubImage, or fbo blit. If neither are // possible and we return false to fallback to creating a render target dst for render-to- // texture. This code prefers CopyTexSubImage to fbo blit and avoids triggering temporary fbo // creation. It isn't clear that avoiding temporary fbo creation is actually optimal. @@ -2664,129 +2683,297 @@ bool GrGLGpu::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { - bool copied = false; - if (can_copy_texsubimage(dst, src, this)) { - GrGLuint srcFBO; - GrGLIRect srcVP; - srcFBO = this->bindSurfaceAsFBO(src, GR_GL_FRAMEBUFFER, &srcVP, kSrc_TempFBOTarget); - GrGLTexture* dstTex = static_cast(dst->asTexture()); - SkASSERT(dstTex); - // We modified the bound FBO - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; - GrGLIRect srcGLRect; - srcGLRect.setRelativeTo(srcVP, - srcRect.fLeft, - srcRect.fTop, - srcRect.width(), - srcRect.height(), - src->origin()); - - this->setScratchTextureUnit(); - GL_CALL(BindTexture(GR_GL_TEXTURE_2D, dstTex->textureID())); - GrGLint dstY; - if (kBottomLeft_GrSurfaceOrigin == dst->origin()) { - dstY = dst->height() - (dstPoint.fY + srcGLRect.fHeight); - } else { - dstY = dstPoint.fY; - } - GL_CALL(CopyTexSubImage2D(GR_GL_TEXTURE_2D, 0, - dstPoint.fX, dstY, - srcGLRect.fLeft, srcGLRect.fBottom, - srcGLRect.fWidth, srcGLRect.fHeight)); - copied = true; - if (srcFBO) { - this->unbindTextureFromFBO(GR_GL_FRAMEBUFFER); - } - } else if (can_blit_framebuffer(dst, src, this)) { - SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, - srcRect.width(), srcRect.height()); - bool selfOverlap = false; - if (dst == src) { - selfOverlap = SkIRect::IntersectsNoEmptyCheck(dstRect, srcRect); - } - - if (!selfOverlap) { - GrGLuint dstFBO; - GrGLuint srcFBO; - GrGLIRect dstVP; - GrGLIRect srcVP; - dstFBO = this->bindSurfaceAsFBO(dst, GR_GL_DRAW_FRAMEBUFFER, &dstVP, - kDst_TempFBOTarget); - srcFBO = this->bindSurfaceAsFBO(src, GR_GL_READ_FRAMEBUFFER, &srcVP, - kSrc_TempFBOTarget); - // We modified the bound FBO - fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; - GrGLIRect srcGLRect; - GrGLIRect dstGLRect; - srcGLRect.setRelativeTo(srcVP, - srcRect.fLeft, - srcRect.fTop, - srcRect.width(), - srcRect.height(), - src->origin()); - dstGLRect.setRelativeTo(dstVP, - dstRect.fLeft, - dstRect.fTop, - dstRect.width(), - dstRect.height(), - dst->origin()); - - // BlitFrameBuffer respects the scissor, so disable it. - this->disableScissor(); - - GrGLint srcY0; - GrGLint srcY1; - // Does the blit need to y-mirror or not? - if (src->origin() == dst->origin()) { - srcY0 = srcGLRect.fBottom; - srcY1 = srcGLRect.fBottom + srcGLRect.fHeight; - } else { - srcY0 = srcGLRect.fBottom + srcGLRect.fHeight; - srcY1 = srcGLRect.fBottom; - } - GL_CALL(BlitFramebuffer(srcGLRect.fLeft, - srcY0, - srcGLRect.fLeft + srcGLRect.fWidth, - srcY1, - dstGLRect.fLeft, - dstGLRect.fBottom, - dstGLRect.fLeft + dstGLRect.fWidth, - dstGLRect.fBottom + dstGLRect.fHeight, - GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST)); - if (dstFBO) { - this->unbindTextureFromFBO(GR_GL_DRAW_FRAMEBUFFER); - } - if (srcFBO) { - this->unbindTextureFromFBO(GR_GL_READ_FRAMEBUFFER); - } - copied = true; - } + if (src->asTexture() && dst->asRenderTarget()) { + this->copySurfaceAsDraw(dst, src, srcRect, dstPoint); + return true; } - return copied; -} - -bool GrGLGpu::canCopySurface(const GrSurface* dst, - const GrSurface* src, - const SkIRect& srcRect, - const SkIPoint& dstPoint) { - // This mirrors the logic in onCopySurface. + if (can_copy_texsubimage(dst, src, this)) { + this->copySurfaceAsCopyTexSubImage(dst, src, srcRect, dstPoint); return true; } + if (can_blit_framebuffer(dst, src, this)) { - if (dst == src) { - SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, - srcRect.width(), srcRect.height()); - if(!SkIRect::IntersectsNoEmptyCheck(dstRect, srcRect)) { - return true; - } - } else { - return true; - } + return this->copySurfaceAsBlitFramebuffer(dst, src, srcRect, dstPoint); } + return false; } + +void GrGLGpu::createCopyProgram() { + const char* version = GrGetGLSLVersionDecl(this->ctxInfo()); + + GrGLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier); + GrGLShaderVar uTexCoordXform("u_texCoordXform", kVec4f_GrSLType, + GrShaderVar::kUniform_TypeModifier); + GrGLShaderVar uPosXform("u_posXform", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier); + GrGLShaderVar uTexture("u_texture", kSampler2D_GrSLType, GrShaderVar::kUniform_TypeModifier); + GrGLShaderVar vTexCoord("v_texCoord", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier); + GrGLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); + + SkString vshaderTxt(version); + aVertex.appendDecl(this->ctxInfo(), &vshaderTxt); + vshaderTxt.append(";"); + uTexCoordXform.appendDecl(this->ctxInfo(), &vshaderTxt); + vshaderTxt.append(";"); + uPosXform.appendDecl(this->ctxInfo(), &vshaderTxt); + vshaderTxt.append(";"); + vTexCoord.appendDecl(this->ctxInfo(), &vshaderTxt); + vshaderTxt.append(";"); + + vshaderTxt.append( + "// Copy Program VS\n" + "void main() {" + " v_texCoord = a_vertex.xy * u_texCoordXform.xy + u_texCoordXform.zw;" + " gl_Position.xy = a_vertex * u_posXform.xy + u_posXform.zw;" + " gl_Position.zw = vec2(0, 1);" + "}" + ); + + SkString fshaderTxt(version); + GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, this->glStandard(), + &fshaderTxt); + vTexCoord.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier); + vTexCoord.appendDecl(this->ctxInfo(), &fshaderTxt); + fshaderTxt.append(";"); + uTexture.appendDecl(this->ctxInfo(), &fshaderTxt); + fshaderTxt.append(";"); + const char* fsOutName; + if (this->glCaps().glslCaps()->mustDeclareFragmentShaderOutput()) { + oFragColor.appendDecl(this->ctxInfo(), &fshaderTxt); + fshaderTxt.append(";"); + fsOutName = oFragColor.c_str(); + } else { + fsOutName = "gl_FragColor"; + } + fshaderTxt.appendf( + "// Copy Program FS\n" + "void main() {" + " %s = %s(u_texture, v_texCoord);" + "}", + fsOutName, + GrGLSLTexture2DFunctionName(kVec2f_GrSLType, this->glslGeneration()) + ); + + GL_CALL_RET(fCopyProgram.fProgram, CreateProgram()); + const char* str; + GrGLint length; + + str = vshaderTxt.c_str(); + length = SkToInt(vshaderTxt.size()); + GrGLuint vshader = GrGLCompileAndAttachShader(fGLContext, fCopyProgram.fProgram, + GR_GL_VERTEX_SHADER, &str, &length, 1, &fStats); + + str = fshaderTxt.c_str(); + length = SkToInt(fshaderTxt.size()); + GrGLuint fshader = GrGLCompileAndAttachShader(fGLContext, fCopyProgram.fProgram, + GR_GL_FRAGMENT_SHADER, &str, &length, 1, &fStats); + + GL_CALL(LinkProgram(fCopyProgram.fProgram)); + + GL_CALL_RET(fCopyProgram.fTextureUniform, GetUniformLocation(fCopyProgram.fProgram, + "u_texture")); + GL_CALL_RET(fCopyProgram.fPosXformUniform, GetUniformLocation(fCopyProgram.fProgram, + "u_posXform")); + GL_CALL_RET(fCopyProgram.fTexCoordXformUniform, GetUniformLocation(fCopyProgram.fProgram, + "u_texCoordXform")); + + GL_CALL(BindAttribLocation(fCopyProgram.fProgram, 0, "a_vertex")); + + GL_CALL(DeleteShader(vshader)); + GL_CALL(DeleteShader(fshader)); + + GL_CALL(GenBuffers(1, &fCopyProgram.fArrayBuffer)); + fHWGeometryState.setVertexBufferID(this, fCopyProgram.fArrayBuffer); + static const GrGLfloat vdata[] = { + 0, 0, + 0, 1, + 1, 0, + 1, 1 + }; + GL_ALLOC_CALL(this->glInterface(), + BufferData(GR_GL_ARRAY_BUFFER, + (GrGLsizeiptr) sizeof(vdata), + vdata, // data ptr + GR_GL_STATIC_DRAW)); +} + +void GrGLGpu::copySurfaceAsDraw(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint) { + int w = srcRect.width(); + int h = srcRect.height(); + + GrGLTexture* srcTex = static_cast(src->asTexture()); + GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); + this->bindTexture(0, params, srcTex); + + GrGLRenderTarget* dstRT = static_cast(dst->asRenderTarget()); + SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, w, h); + this->flushRenderTarget(dstRT, &dstRect); + + GL_CALL(UseProgram(fCopyProgram.fProgram)); + fHWProgramID = fCopyProgram.fProgram; + + fHWGeometryState.setVertexArrayID(this, 0); + + GrGLAttribArrayState* attribs = + fHWGeometryState.bindArrayAndBufferToDraw(this, fCopyProgram.fArrayBuffer); + attribs->set(this, 0, fCopyProgram.fArrayBuffer, 2, GR_GL_FLOAT, false, + 2 * sizeof(GrGLfloat), 0); + + + // dst rect edges in NDC (-1 to 1) + int dw = dst->width(); + int dh = dst->height(); + GrGLfloat dx0 = 2.f * dstPoint.fX / dw - 1.f; + GrGLfloat dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f; + GrGLfloat dy0 = 2.f * dstPoint.fY / dh - 1.f; + GrGLfloat dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f; + if (kBottomLeft_GrSurfaceOrigin == dst->origin()) { + dy0 = -dy0; + dy1 = -dy1; + } + + // src rect edges in normalized texture space (0 to 1) + int sw = src->width(); + int sh = src->height(); + GrGLfloat sx0 = (GrGLfloat)srcRect.fLeft / sw; + GrGLfloat sx1 = (GrGLfloat)(srcRect.fLeft + w) / sw; + GrGLfloat sy0 = (GrGLfloat)srcRect.fTop / sh; + GrGLfloat sy1 = (GrGLfloat)(srcRect.fTop + h) / sh; + if (kBottomLeft_GrSurfaceOrigin == src->origin()) { + sy0 = 1.f - sy0; + sy1 = 1.f - sy1; + } + + GL_CALL(Uniform4f(fCopyProgram.fPosXformUniform, dx1 - dx0, dy1 - dy0, dx0, dy0)); + GL_CALL(Uniform4f(fCopyProgram.fTexCoordXformUniform, sx1 - sx0, sy1 - sy0, sx0, sy0)); + GL_CALL(Uniform1i(fCopyProgram.fTextureUniform, 0)); + + GrXferProcessor::BlendInfo blendInfo; + blendInfo.reset(); + this->flushBlend(blendInfo); + this->flushColorWrite(true); + this->flushDither(false); + this->flushDrawFace(GrPipelineBuilder::kBoth_DrawFace); + this->flushHWAAState(dstRT, false); + this->disableScissor(); + GrStencilSettings stencil; + stencil.setDisabled(); + this->flushStencil(stencil); + + GL_CALL(DrawArrays(GR_GL_TRIANGLE_STRIP, 0, 4)); +} + +void GrGLGpu::copySurfaceAsCopyTexSubImage(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint) { + SkASSERT(can_copy_texsubimage(dst, src, this)); + GrGLuint srcFBO; + GrGLIRect srcVP; + srcFBO = this->bindSurfaceAsFBO(src, GR_GL_FRAMEBUFFER, &srcVP, kSrc_TempFBOTarget); + GrGLTexture* dstTex = static_cast(dst->asTexture()); + SkASSERT(dstTex); + // We modified the bound FBO + fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; + GrGLIRect srcGLRect; + srcGLRect.setRelativeTo(srcVP, + srcRect.fLeft, + srcRect.fTop, + srcRect.width(), + srcRect.height(), + src->origin()); + + this->setScratchTextureUnit(); + GL_CALL(BindTexture(GR_GL_TEXTURE_2D, dstTex->textureID())); + GrGLint dstY; + if (kBottomLeft_GrSurfaceOrigin == dst->origin()) { + dstY = dst->height() - (dstPoint.fY + srcGLRect.fHeight); + } else { + dstY = dstPoint.fY; + } + GL_CALL(CopyTexSubImage2D(GR_GL_TEXTURE_2D, 0, + dstPoint.fX, dstY, + srcGLRect.fLeft, srcGLRect.fBottom, + srcGLRect.fWidth, srcGLRect.fHeight)); + if (srcFBO) { + this->unbindTextureFromFBO(GR_GL_FRAMEBUFFER); + } +} + +bool GrGLGpu::copySurfaceAsBlitFramebuffer(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint) { + SkASSERT(can_blit_framebuffer(dst, src, this)); + SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, + srcRect.width(), srcRect.height()); + if (dst == src) { + if (SkIRect::IntersectsNoEmptyCheck(dstRect, srcRect)) { + return false; + } + } + + GrGLuint dstFBO; + GrGLuint srcFBO; + GrGLIRect dstVP; + GrGLIRect srcVP; + dstFBO = this->bindSurfaceAsFBO(dst, GR_GL_DRAW_FRAMEBUFFER, &dstVP, + kDst_TempFBOTarget); + srcFBO = this->bindSurfaceAsFBO(src, GR_GL_READ_FRAMEBUFFER, &srcVP, + kSrc_TempFBOTarget); + // We modified the bound FBO + fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID; + GrGLIRect srcGLRect; + GrGLIRect dstGLRect; + srcGLRect.setRelativeTo(srcVP, + srcRect.fLeft, + srcRect.fTop, + srcRect.width(), + srcRect.height(), + src->origin()); + dstGLRect.setRelativeTo(dstVP, + dstRect.fLeft, + dstRect.fTop, + dstRect.width(), + dstRect.height(), + dst->origin()); + + // BlitFrameBuffer respects the scissor, so disable it. + this->disableScissor(); + + GrGLint srcY0; + GrGLint srcY1; + // Does the blit need to y-mirror or not? + if (src->origin() == dst->origin()) { + srcY0 = srcGLRect.fBottom; + srcY1 = srcGLRect.fBottom + srcGLRect.fHeight; + } else { + srcY0 = srcGLRect.fBottom + srcGLRect.fHeight; + srcY1 = srcGLRect.fBottom; + } + GL_CALL(BlitFramebuffer(srcGLRect.fLeft, + srcY0, + srcGLRect.fLeft + srcGLRect.fWidth, + srcY1, + dstGLRect.fLeft, + dstGLRect.fBottom, + dstGLRect.fLeft + dstGLRect.fWidth, + dstGLRect.fBottom + dstGLRect.fHeight, + GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST)); + if (dstFBO) { + this->unbindTextureFromFBO(GR_GL_DRAW_FRAMEBUFFER); + } + if (srcFBO) { + this->unbindTextureFromFBO(GR_GL_READ_FRAMEBUFFER); + } + return true; +} + void GrGLGpu::xferBarrier(GrRenderTarget* rt, GrXferBarrierType type) { switch (type) { case kTexture_GrXferBarrierType: { @@ -2832,26 +3019,52 @@ void GrGLGpu::didRemoveGpuTraceMarker() { } /////////////////////////////////////////////////////////////////////////////// - GrGLAttribArrayState* GrGLGpu::HWGeometryState::bindArrayAndBuffersToDraw( GrGLGpu* gpu, const GrGLVertexBuffer* vbuffer, const GrGLIndexBuffer* ibuffer) { SkASSERT(vbuffer); + GrGLuint vbufferID = vbuffer->bufferID(); + GrGLuint* ibufferIDPtr = NULL; + GrGLuint ibufferID; + if (ibuffer) { + ibufferID = ibuffer->bufferID(); + ibufferIDPtr = &ibufferID; + } + return this->internalBind(gpu, vbufferID, ibufferIDPtr); +} + +GrGLAttribArrayState* GrGLGpu::HWGeometryState::bindArrayAndBufferToDraw(GrGLGpu* gpu, + GrGLuint vbufferID) { + return this->internalBind(gpu, vbufferID, NULL); +} + +GrGLAttribArrayState* GrGLGpu::HWGeometryState::bindArrayAndBuffersToDraw(GrGLGpu* gpu, + GrGLuint vbufferID, + GrGLuint ibufferID) { + return this->internalBind(gpu, vbufferID, &ibufferID); +} + +GrGLAttribArrayState* GrGLGpu::HWGeometryState::internalBind(GrGLGpu* gpu, + GrGLuint vbufferID, + GrGLuint* ibufferID) { GrGLAttribArrayState* attribState; - // We use a vertex array if we're on a core profile and the verts are in a VBO. - if (gpu->glCaps().isCoreProfile() && !vbuffer->isCPUBacked()) { + if (gpu->glCaps().isCoreProfile() && 0 != vbufferID) { if (!fVBOVertexArray) { GrGLuint arrayID; GR_GL_CALL(gpu->glInterface(), GenVertexArrays(1, &arrayID)); int attrCount = gpu->glCaps().maxVertexAttributes(); fVBOVertexArray = SkNEW_ARGS(GrGLVertexArray, (arrayID, attrCount)); } - attribState = fVBOVertexArray->bindWithIndexBuffer(gpu, ibuffer); + if (ibufferID) { + attribState = fVBOVertexArray->bindWithIndexBuffer(gpu, *ibufferID); + } else { + attribState = fVBOVertexArray->bind(gpu); + } } else { - if (ibuffer) { - this->setIndexBufferIDOnDefaultVertexArray(gpu, ibuffer->bufferID()); + if (ibufferID) { + this->setIndexBufferIDOnDefaultVertexArray(gpu, *ibufferID); } else { this->setVertexArrayID(gpu, 0); } diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index 3323ece..986045a 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -101,11 +101,6 @@ public: const SkIRect& srcRect, const SkIPoint& dstPoint) override; - bool canCopySurface(const GrSurface* dst, - const GrSurface* src, - const SkIRect& srcRect, - const SkIPoint& dstPoint) override; - void xferBarrier(GrRenderTarget*, GrXferBarrierType) override; void buildProgramDesc(GrProgramDesc*, @@ -183,6 +178,19 @@ private: // Subclasses should call this to flush the blend state. void flushBlend(const GrXferProcessor::BlendInfo& blendInfo); + void copySurfaceAsDraw(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint); + void copySurfaceAsCopyTexSubImage(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint); + bool copySurfaceAsBlitFramebuffer(GrSurface* dst, + GrSurface* src, + const SkIRect& srcRect, + const SkIPoint& dstPoint); + bool hasExtension(const char* ext) const { return fGLContext.hasExtension(ext); } static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff); @@ -193,7 +201,7 @@ private: ~ProgramCache(); void abandon(); - GrGLProgram* getProgram(const DrawArgs&); + GrGLProgram* refProgram(const DrawArgs&); private: enum { @@ -294,11 +302,12 @@ private: void unbindTextureFromFBO(GrGLenum fboTarget); + void createCopyProgram(); + GrGLContext fGLContext; // GL program-related state ProgramCache* fProgramCache; - SkAutoTUnref fCurrentProgram; /////////////////////////////////////////////////////////////////////////// ///@name Caching of GL State @@ -420,7 +429,17 @@ private: const GrGLVertexBuffer* vbuffer, const GrGLIndexBuffer* ibuffer); + /** Variants of the above that takes GL buffer IDs. Note that 0 does not imply that a + buffer won't be bound. The "default buffer" will be bound, which is used for client-side + array rendering. */ + GrGLAttribArrayState* bindArrayAndBufferToDraw(GrGLGpu* gpu, GrGLuint vbufferID); + GrGLAttribArrayState* bindArrayAndBuffersToDraw(GrGLGpu* gpu, + GrGLuint vbufferID, + GrGLuint ibufferID); + private: + GrGLAttribArrayState* internalBind(GrGLGpu* gpu, GrGLuint vbufferID, GrGLuint* ibufferID); + GrGLuint fBoundVertexArrayID; GrGLuint fBoundVertexBufferID; bool fBoundVertexArrayIDIsValid; @@ -454,6 +473,15 @@ private: } } fHWBlendState; + /** IDs for copy surface program. */ + struct { + GrGLuint fProgram; + GrGLint fTextureUniform; + GrGLint fTexCoordXformUniform; + GrGLint fPosXformUniform; + GrGLuint fArrayBuffer; + } fCopyProgram; + TriState fMSAAEnabled; GrStencilSettings fHWStencilSettings; diff --git a/src/gpu/gl/GrGLGpuProgramCache.cpp b/src/gpu/gl/GrGLGpuProgramCache.cpp index 911fd93..06efe29 100644 --- a/src/gpu/gl/GrGLGpuProgramCache.cpp +++ b/src/gpu/gl/GrGLGpuProgramCache.cpp @@ -90,7 +90,7 @@ int GrGLGpu::ProgramCache::search(const GrProgramDesc& desc) const { return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); } -GrGLProgram* GrGLGpu::ProgramCache::getProgram(const DrawArgs& args) { +GrGLProgram* GrGLGpu::ProgramCache::refProgram(const DrawArgs& args) { #ifdef PROGRAM_CACHE_STATS ++fTotalRequests; #endif @@ -193,5 +193,5 @@ GrGLProgram* GrGLGpu::ProgramCache::getProgram(const DrawArgs& args) { } } ++fCurrLRUStamp; - return entry->fProgram; + return SkRef(entry->fProgram.get()); } diff --git a/src/gpu/gl/GrGLSL.cpp b/src/gpu/gl/GrGLSL.cpp index d388d91..f4caf52 100644 --- a/src/gpu/gl/GrGLSL.cpp +++ b/src/gpu/gl/GrGLSL.cpp @@ -91,6 +91,25 @@ const char* GrGetGLSLVersionDecl(const GrGLContextInfo& info) { } } +void GrGLSLAppendDefaultFloatPrecisionDeclaration(GrSLPrecision p, GrGLStandard s, SkString* out) { + // Desktop GLSL has added precision qualifiers but they don't do anything. + if (kGLES_GrGLStandard == s) { + switch (p) { + case kHigh_GrSLPrecision: + out->append("precision highp float;\n"); + break; + case kMedium_GrSLPrecision: + out->append("precision mediump float;\n"); + break; + case kLow_GrSLPrecision: + out->append("precision lowp float;\n"); + break; + default: + SkFAIL("Unknown precision value."); + } + } +} + void GrGLSLMulVarBy4f(SkString* outAppend, const char* vec4VarName, const GrGLSLExpr4& mulFactor) { if (mulFactor.isOnes()) { *outAppend = SkString(); diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h index 99671c1..db8e1bd 100644 --- a/src/gpu/gl/GrGLSL.h +++ b/src/gpu/gl/GrGLSL.h @@ -57,6 +57,24 @@ bool GrGetGLSLGeneration(const GrGLInterface* gl, GrGLSLGeneration* generation); const char* GrGetGLSLVersionDecl(const GrGLContextInfo&); /** + * Adds a line of GLSL code to declare the default precision for float types. + */ +void GrGLSLAppendDefaultFloatPrecisionDeclaration(GrSLPrecision, GrGLStandard, SkString* out); + +/** + * Gets the name of the function that should be used to sample a 2D texture. Coord type is used + * to indicate whether the texture is sampled using projective textured (kVec3f) or not (kVec2f). + */ +inline const char* GrGLSLTexture2DFunctionName(GrSLType coordType, GrGLSLGeneration glslGen) { + if (kVec2f_GrSLType == coordType) { + return glslGen >= k130_GrGLSLGeneration ? "texture" : "texture2D"; + } else { + SkASSERT(kVec3f_GrSLType == coordType); + return glslGen >= k130_GrGLSLGeneration ? "textureProj" : "texture2DProj"; + } +} + +/** * Converts a GrSLType to a string containing the name of the equivalent GLSL type. */ static inline const char* GrGLSLTypeString(GrSLType t) { diff --git a/src/gpu/gl/GrGLVertexArray.cpp b/src/gpu/gl/GrGLVertexArray.cpp index e20dbb5..265b5b3 100644 --- a/src/gpu/gl/GrGLVertexArray.cpp +++ b/src/gpu/gl/GrGLVertexArray.cpp @@ -9,9 +9,10 @@ #include "GrGLGpu.h" -void GrGLAttribArrayState::set(const GrGLGpu* gpu, + +void GrGLAttribArrayState::set(GrGLGpu* gpu, int index, - GrGLVertexBuffer* buffer, + GrGLuint vertexBufferID, GrGLint size, GrGLenum type, GrGLboolean normalized, @@ -25,13 +26,13 @@ void GrGLAttribArrayState::set(const GrGLGpu* gpu, array->fEnabled = true; } if (!array->fAttribPointerIsValid || - array->fVertexBufferID != buffer->bufferID() || + array->fVertexBufferID != vertexBufferID || array->fSize != size || array->fNormalized != normalized || array->fStride != stride || array->fOffset != offset) { - buffer->bind(); + gpu->bindVertexBuffer(vertexBufferID); GR_GL_CALL(gpu->glInterface(), VertexAttribPointer(index, size, type, @@ -39,7 +40,7 @@ void GrGLAttribArrayState::set(const GrGLGpu* gpu, stride, offset)); array->fAttribPointerIsValid = true; - array->fVertexBufferID = buffer->bufferID(); + array->fVertexBufferID = vertexBufferID; array->fSize = size; array->fNormalized = normalized; array->fStride = stride; @@ -80,15 +81,13 @@ GrGLAttribArrayState* GrGLVertexArray::bind(GrGLGpu* gpu) { return &fAttribArrays; } -GrGLAttribArrayState* GrGLVertexArray::bindWithIndexBuffer(GrGLGpu* gpu, - const GrGLIndexBuffer* buffer) { +GrGLAttribArrayState* GrGLVertexArray::bindWithIndexBuffer(GrGLGpu* gpu, GrGLuint ibufferID) { GrGLAttribArrayState* state = this->bind(gpu); - if (state && buffer) { - GrGLuint bufferID = buffer->bufferID(); - if (!fIndexBufferIDIsValid || bufferID != fIndexBufferID) { - GR_GL_CALL(gpu->glInterface(), BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, bufferID)); + if (state) { + if (!fIndexBufferIDIsValid || ibufferID != fIndexBufferID) { + GR_GL_CALL(gpu->glInterface(), BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, ibufferID)); fIndexBufferIDIsValid = true; - fIndexBufferID = bufferID; + fIndexBufferID = ibufferID; } } return state; diff --git a/src/gpu/gl/GrGLVertexArray.h b/src/gpu/gl/GrGLVertexArray.h index a8feb73..afb23c5 100644 --- a/src/gpu/gl/GrGLVertexArray.h +++ b/src/gpu/gl/GrGLVertexArray.h @@ -67,9 +67,9 @@ public: * assumed that the GrGLAttribArrayState is tracking the state of the currently bound vertex * array object. */ - void set(const GrGLGpu*, - int index, - GrGLVertexBuffer*, + void set(GrGLGpu*, + int attribIndex, + GrGLuint vertexBufferID, GrGLint size, GrGLenum type, GrGLboolean normalized, @@ -147,7 +147,7 @@ public: * This is a version of the above function that also binds an index buffer to the vertex * array object. */ - GrGLAttribArrayState* bindWithIndexBuffer(GrGLGpu* gpu, const GrGLIndexBuffer*); + GrGLAttribArrayState* bindWithIndexBuffer(GrGLGpu* gpu, GrGLuint indexBufferID); void notifyIndexBufferDelete(GrGLuint bufferID); diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp index 044752f..afb0718 100644 --- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp +++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp @@ -15,26 +15,6 @@ const char* GrGLFragmentShaderBuilder::kDstCopyColorName = "_dstColor"; static const char* declared_color_output_name() { return "fsColorOut"; } static const char* dual_source_output_name() { return "dualSourceOut"; } -static void append_default_precision_qualifier(GrSLPrecision p, - GrGLStandard standard, - SkString* str) { - // Desktop GLSL has added precision qualifiers but they don't do anything. - if (kGLES_GrGLStandard == standard) { - switch (p) { - case kHigh_GrSLPrecision: - str->append("precision highp float;\n"); - break; - case kMedium_GrSLPrecision: - str->append("precision mediump float;\n"); - break; - case kLow_GrSLPrecision: - str->append("precision lowp float;\n"); - break; - default: - SkFAIL("Unknown precision value."); - } - } -} static const char* specific_layout_qualifier_name(GrBlendEquation equation) { SkASSERT(GrBlendEquationIsAdvanced(equation)); @@ -270,9 +250,9 @@ bool GrGLFragmentShaderBuilder::compileAndAttachShaders(GrGLuint programId, SkTDArray* shaderIds) { GrGLGpu* gpu = fProgramBuilder->gpu(); this->versionDecl() = GrGetGLSLVersionDecl(gpu->ctxInfo()); - append_default_precision_qualifier(kDefault_GrSLPrecision, - gpu->glStandard(), - &this->precisionQualifier()); + GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, + gpu->glStandard(), + &this->precisionQualifier()); this->compileAndAppendLayoutQualifiers(); fProgramBuilder->appendUniformDecls(GrGLProgramBuilder::kFragment_Visibility, &this->uniforms()); diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp index 28e1fba..95f75ff 100644 --- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp +++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp @@ -343,8 +343,7 @@ void GrGLProgramBuilder::emitAndInstallXferProc(const GrXferProcessor& xp, fFS.enableSecondaryOutput(); } - // On any post 1.10 GLSL supporting GPU, we declare custom output - if (k110_GrGLSLGeneration != fFS.fProgramBuilder->gpu()->glslGeneration()) { + if (this->ctxInfo().caps()->glslCaps()->mustDeclareFragmentShaderOutput()) { fFS.enableCustomOutput(); } diff --git a/src/gpu/gl/builders/GrGLShaderBuilder.cpp b/src/gpu/gl/builders/GrGLShaderBuilder.cpp index 67fbe24..984233d 100644 --- a/src/gpu/gl/builders/GrGLShaderBuilder.cpp +++ b/src/gpu/gl/builders/GrGLShaderBuilder.cpp @@ -12,14 +12,6 @@ #include "../GrGLShaderVar.h" namespace { -inline const char* sample_function_name(GrSLType type, GrGLSLGeneration glslGen) { - if (kVec2f_GrSLType == type) { - return glslGen >= k130_GrGLSLGeneration ? "texture" : "texture2D"; - } else { - SkASSERT(kVec3f_GrSLType == type); - return glslGen >= k130_GrGLSLGeneration ? "textureProj" : "texture2DProj"; - } -} void append_texture_lookup(SkString* out, GrGLGpu* gpu, const char* samplerName, @@ -30,7 +22,7 @@ void append_texture_lookup(SkString* out, SkASSERT(coordName); out->appendf("%s(%s, %s)", - sample_function_name(varyingType, gpu->glslGeneration()), + GrGLSLTexture2DFunctionName(varyingType, gpu->glslGeneration()), samplerName, coordName); -- 2.7.4