Chromium Code Reviews| Index: src/gpu/gl/GrGLGpu.cpp |
| diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp |
| index d56ccd147d52b64f2d4fbd67afa56d3d4422bd80..3dbc56db4ac9870eb6e0d0a72e252695674eec66 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" |
| @@ -187,6 +188,9 @@ GrGLGpu::GrGLGpu(const GrGLContext& ctx, GrContext* context) |
| SkDebugf("%s", this->glCaps().dump().c_str()); |
| } |
| + // Do this early before we start tracking GL state since this binds GL objects. |
| + this->createCopyProgram(); |
| + |
| fProgramCache = SkNEW_ARGS(ProgramCache, (this)); |
| SkASSERT(this->glCaps().maxVertexAttributes() >= GrGeometryProcessor::kMaxVertexAttribs); |
| @@ -197,6 +201,7 @@ GrGLGpu::GrGLGpu(const GrGLContext& ctx, GrContext* context) |
| fTempDstFBOID = 0; |
| fStencilClearFBOID = 0; |
| + this->createCopyProgram(); |
|
egdaniel
2015/05/13 19:04:42
you call this twice?
bsalomon
2015/05/15 17:56:31
Done.
|
| if (this->glCaps().shaderCaps()->pathRenderingSupport()) { |
| fPathRendering.reset(new GrGLPathRendering(this)); |
| } |
| @@ -220,6 +225,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 +243,8 @@ void GrGLGpu::contextAbandoned() { |
| fTempSrcFBOID = 0; |
| fTempDstFBOID = 0; |
| fStencilClearFBOID = 0; |
| + fCopyProgram.fArrayBuffer = 0; |
| + fCopyProgram.fProgram = 0; |
| if (this->glCaps().shaderCaps()->pathRenderingSupport()) { |
| this->glPathRendering()->abandonGpuResources(); |
| } |
| @@ -1490,7 +1505,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 +2633,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 +2688,275 @@ 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<GrGLTexture*>(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() { |
| + SkString vshaderTxt( |
| + "attribute vec2 a_vertex;" |
| + "uniform vec4 u_texCoordXform;" |
| + "uniform vec4 u_posXform;" |
| + "varying vec2 v_texCoord;" |
| + "void main() {" |
|
joshualitt
2015/05/13 19:23:17
Add comment to vshader and fshader so we know when
bsalomon
2015/05/15 17:56:31
Done.
|
| + " 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( |
| + "varying vec2 v_texCoord;" |
| + "uniform sampler2D u_texture;" |
| + "void main() {" |
| + " gl_FragColor = texture2D(u_texture, v_texCoord);" |
| + "}" |
| + ); |
| + |
| + const char* fsHeader; |
| + const char* vsHeader; |
| + if (kGLES_GrGLStandard == this->glStandard()) { |
| + vsHeader = "#version 100\n"; |
| + fsHeader = "#version 100\nprecision mediump float;\n"; |
| + } else { |
| + vsHeader = fsHeader = "#version 110\n"; |
| + } |
| + |
| + vshaderTxt.prepend(vsHeader); |
| + fshaderTxt.prepend(fsHeader); |
| + |
| + |
| + 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)); |
| + GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, 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<GrGLTexture*>(src->asTexture()); |
| + GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); |
| + this->bindTexture(0, params, srcTex); |
| + |
| + GrGLRenderTarget* dstRT = static_cast<GrGLRenderTarget*>(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); |
| + |
| + auto attribs = fHWGeometryState.bindArrayAndBufferToDraw(this, fCopyProgram.fArrayBuffer); |
|
egdaniel
2015/05/13 19:04:42
I think this is not the best use case for auto sin
bsalomon
2015/05/15 17:56:31
Done.
|
| + attribs->set(this, 0, fCopyProgram.fArrayBuffer, 2, GR_GL_FLOAT, false, |
| + 2 * sizeof(GrGLfloat), 0); |
| + |
| + |
| + // dst rect edges in NDC |
| + int dw = dst->width(); |
| + int dh = dst->height(); |
| + GrGLfloat dx0 = 2.f * dstPoint.fX / dw - 1.f; |
|
egdaniel
2015/05/13 19:04:42
little confused at what these equations are doing
bsalomon
2015/05/15 17:56:31
Done.
|
| + 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 |
| + 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<GrGLTexture*>(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(GrXferBarrierType type) { |
| switch (type) { |
| case kTexture_GrXferBarrierType: |
| @@ -2824,26 +2994,54 @@ 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 one buffer is a client-side array, so must the other. |
| + SkASSERT(!ibufferID || SkToBool(vbufferID) == SkToBool(*ibufferID)); |
| + 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); |
| } |