diff options
author | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2010-12-22 21:39:39 +0000 |
---|---|---|
committer | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2010-12-22 21:39:39 +0000 |
commit | ac10a2d039c5d52eed66e27cbbc503ab523c1cd5 (patch) | |
tree | c5be0c3dd15052016e7d32f376507cb1ea7101dd /gpu/src/GrGpuGL.cpp | |
parent | ea8509cd3b1771b36054313d3ccd56679df56044 (diff) |
add gpu backend (not hooked up yet)
git-svn-id: http://skia.googlecode.com/svn/trunk@649 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'gpu/src/GrGpuGL.cpp')
-rw-r--r-- | gpu/src/GrGpuGL.cpp | 1824 |
1 files changed, 1824 insertions, 0 deletions
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp new file mode 100644 index 0000000000..8a1169e4c4 --- /dev/null +++ b/gpu/src/GrGpuGL.cpp @@ -0,0 +1,1824 @@ +/* + Copyright 2010 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "GrGpuGL.h" +#include "GrMemory.h" +#include <stdio.h> + +static const GLuint GR_MAX_GLUINT = ~0; +static const GLint GR_INVAL_GLINT = ~0; + +#define SKIP_CACHE_CHECK true + +static const GLenum gXfermodeCoeff2Blend[] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, +}; + +bool has_gl_extension(const char* ext) { + const char* glstr = (const char*) glGetString(GL_EXTENSIONS); + + int extLength = strlen(ext); + + while (true) { + int n = strcspn(glstr, " "); + if (n == extLength && 0 == strncmp(ext, glstr, n)) { + return true; + } + if (0 == glstr[n]) { + return false; + } + glstr += n+1; + } +} + +void gl_version(int* major, int* minor) { + const char* v = (const char*) glGetString(GL_VERSION); + if (NULL == v) { + GrAssert(0); + *major = 0; + *minor = 0; + return; + } +#if GR_GL_DESKTOP + int n = sscanf(v, "%d.%d", major, minor); + if (n != 2) { + GrAssert(0); + *major = 0; + *minor = 0; + return; + } +#else + char profile[2]; + int n = sscanf(v, "OpenGL ES-%c%c %d.%d", profile, profile+1, major, minor); + bool ok = 4 == n; + if (!ok) { + int n = sscanf(v, "OpenGL ES %d.%d", major, minor); + ok = 2 == n; + } + if (!ok) { + GrAssert(0); + *major = 0; + *minor = 0; + return; + } +#endif +} +/////////////////////////////////////////////////////////////////////////////// + +bool fbo_test(GrGLExts exts, int w, int h) { + GLuint testFBO; + GR_GLEXT(exts, GenFramebuffers(1, &testFBO)); + GR_GLEXT(exts, BindFramebuffer(GR_FRAMEBUFFER, testFBO)); + GLuint testRTTex; + GR_GL(GenTextures(1, &testRTTex)); + GR_GL(BindTexture(GL_TEXTURE_2D, testRTTex)); + + GR_GL(TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); + GR_GL(BindTexture(GL_TEXTURE_2D, 0)); + GR_GLEXT(exts, FramebufferTexture2D(GR_FRAMEBUFFER, GR_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, testRTTex, 0)); + GLenum status = GR_GLEXT(exts, CheckFramebufferStatus(GR_FRAMEBUFFER)); + GR_GLEXT(exts, DeleteFramebuffers(1, &testFBO)); + GR_GL(DeleteTextures(1, &testRTTex)); + return status == GR_FRAMEBUFFER_COMPLETE; +} + +/////////////////////////////////////////////////////////////////////////////// + +GrGpuGL::GrGpuGL() { + GrPrintf("------------------------- create GrGpuGL %p --------------\n", + this); + GrPrintf("------ VENDOR %s\n", glGetString(GL_VENDOR)); + GrPrintf("------ RENDERER %s\n", glGetString(GL_RENDERER)); + GrPrintf("------ VERSION %s\n", glGetString(GL_VERSION)); + GrPrintf("------ EXTENSIONS\n %s \n", glGetString(GL_EXTENSIONS)); + + GrGLClearErr(); + + GrGLInitExtensions(&fExts); + + resetContextHelper(); + + GrGLRenderTarget::GLRenderTargetIDs defaultRTIDs; + GR_GL(GetIntegerv(GR_FRAMEBUFFER_BINDING, (GLint*)&defaultRTIDs.fRTFBOID)); + defaultRTIDs.fTexFBOID = defaultRTIDs.fRTFBOID; + defaultRTIDs.fMSColorRenderbufferID = 0; + defaultRTIDs.fStencilRenderbufferID = 0; + GLint vp[4]; + GR_GL(GetIntegerv(GL_VIEWPORT, vp)); + fHWBounds.fViewportRect.setLTRB(vp[0], + vp[1] + vp[3], + vp[0] + vp[2], + vp[1]); + defaultRTIDs.fOwnIDs = false; + + fDefaultRenderTarget = new GrGLRenderTarget(defaultRTIDs, + fHWBounds.fViewportRect, + NULL, + this); + fHWDrawState.fRenderTarget = fDefaultRenderTarget; + fRenderTargetChanged = true; + + fCurrDrawState = fHWDrawState; + + //////////////////////////////////////////////////////////////////////////// + // Check for supported features. + + int major, minor; + gl_version(&major, &minor); + + GLint numFormats; + GR_GL(GetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats)); + GrAutoSTMalloc<10, GLint> formats(numFormats); + GR_GL(GetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats)); + for (int i = 0; i < numFormats; ++i) { + if (formats[i] == GR_PALETTE8_RGBA8) { + f8bitPaletteSupport = true; + break; + } + } + GrPrintf("Palette8 support: %s\n", (f8bitPaletteSupport ? "YES" : "NO")); + + GR_STATIC_ASSERT(0 == kNone_AALevel); + GR_STATIC_ASSERT(1 == kLow_AALevel); + GR_STATIC_ASSERT(2 == kMed_AALevel); + GR_STATIC_ASSERT(3 == kHigh_AALevel); + + memset(fAASamples, 0, sizeof(fAASamples)); + fMSFBOType = kNone_MSFBO; + if (has_gl_extension("GL_IMG_multisampled_render_to_texture")) { + fMSFBOType = kIMG_MSFBO; + GrPrintf("MSAA Support: IMG ES EXT.\n"); + } + else if (has_gl_extension("GL_APPLE_framebuffer_multisample")) { + fMSFBOType = kApple_MSFBO; + GrPrintf("MSAA Support: APPLE ES EXT.\n"); + } +#if GR_GL_DESKTOP + else if ((major >= 3) || + has_gl_extension("GL_ARB_framebuffer_object") || + (has_gl_extension("GL_EXT_framebuffer_multisample") && + has_gl_extension("GL_EXT_framebuffer_blit"))) { + fMSFBOType = kDesktop_MSFBO; + GrPrintf("MSAA Support: DESKTOP\n"); + } +#endif + else { + GrPrintf("MSAA Support: NONE\n"); + } + + if (kNone_MSFBO != fMSFBOType) { + GLint maxSamples; + GLenum maxSampleGetter = (kIMG_MSFBO == fMSFBOType) ? + GR_MAX_SAMPLES_IMG : + GR_MAX_SAMPLES; + GR_GL(GetIntegerv(maxSampleGetter, &maxSamples)); + if (maxSamples > 1 ) { + fAASamples[kNone_AALevel] = 0; + fAASamples[kLow_AALevel] = GrMax(2, + GrFixedFloorToInt((GR_FixedHalf) * + maxSamples)); + fAASamples[kMed_AALevel] = GrMax(2, + GrFixedFloorToInt(((GR_Fixed1*3)/4) * + maxSamples)); + fAASamples[kHigh_AALevel] = maxSamples; + } + GrPrintf("\tMax Samples: %d\n", maxSamples); + } + +#if GR_GL_DESKTOP + fHasStencilWrap = (major >= 2 || (major == 1 && minor >= 4)) || + has_gl_extension("GL_EXT_stencil_wrap"); +#else + fHasStencilWrap = (major >= 2) || has_gl_extension("GL_OES_stencil_wrap"); +#endif + GrPrintf("Stencil Wrap: %s\n", (fHasStencilWrap ? "YES" : "NO")); + +#if GR_GL_DESKTOP + // we could also look for GL_ATI_separate_stencil extension or + // GL_EXT_stencil_two_side but they use different function signatures + // than GL2.0+ (and than each other). + fSingleStencilPassForWinding = (major >= 2); +#else + // ES 2 has two sided stencil but 1.1 doesn't. There doesn't seem to be + // an ES1 extension. + fSingleStencilPassForWinding = (major >= 2); +#endif + GrPrintf("Single Stencil Pass For Winding: %s\n", (fSingleStencilPassForWinding ? "YES" : "NO")); + + +#if GR_GL_DESKTOP + fRGBA8Renderbuffer = true; +#else + fRGBA8Renderbuffer = has_gl_extension("GL_OES_rgb8_rgba8"); +#endif + GrPrintf("RGBA Renderbuffer: %s\n", (fRGBA8Renderbuffer ? "YES" : "NO")); + + +#if GR_GL_DESKTOP + fBufferLockSupport = true; // we require VBO support and the desktop VBO + // extension includes glMapBuffer. +#else + fBufferLockSupport = has_gl_extension("GL_OES_mapbuffer"); +#endif + GrPrintf("Map Buffer: %s\n", (fBufferLockSupport ? "YES" : "NO")); + +#if GR_GL_DESKTOP + fNPOTTextureSupport = + (major >= 2 || has_gl_extension("GL_ARB_texture_non_power_of_two")) ? + kFull_NPOTTextureType : + kNone_NPOTTextureType; +#else + if (has_gl_extension("GL_OES_texture_npot")) { + fNPOTTextureSupport = kFull_NPOTTextureType; + } else if (major >= 2 || + has_gl_extension("GL_APPLE_texture_2D_limited_npot")) { + fNPOTTextureSupport = kNoRepeat_NPOTTextureType; + } else { + fNPOTTextureSupport = kNone_NPOTTextureType; + } +#endif + //////////////////////////////////////////////////////////////////////////// + // Experiments to determine limitations that can't be queried. TODO: Make + // these a preprocess that generate some compile time constants. + + /* Experimentation has found that some GLs that support NPOT textures + do not support FBOs with a NPOT texture. They report "unsupported" FBO + status. I don't know how to explicitly query for this. Do an + experiment. Note they may support NPOT with a renderbuffer but not a + texture. Presumably, the implementation bloats the renderbuffer + internally to the next POT. + */ + if (fNPOTTextureSupport == kFull_NPOTTextureType) { + bool npotFBOSuccess = fbo_test(fExts, 200, 200); + if (!npotFBOSuccess) { + fNPOTTextureSupport = kNonRendertarget_NPOTTextureType; + GrPrintf("NPOT Renderbuffer Test: FAILED\n"); + } else { + GrPrintf("NPOT Renderbuffer Test: PASSED\n"); + } + } + switch (fNPOTTextureSupport) { + case kNone_NPOTTextureType: + GrPrintf("NPOT Support: NONE\n"); + break; + case kNoRepeat_NPOTTextureType: + GrPrintf("NPOT Support: NO REPEAT\n"); + break; + case kNonRendertarget_NPOTTextureType: + GrPrintf("NPOT Support: NO FBOTEX\n"); + break; + case kFull_NPOTTextureType: + GrPrintf("NPOT Support: FULL\n"); + break; + } + + // sanity check to make sure we can at least create an FBO from a POT texture + if (fNPOTTextureSupport < kFull_NPOTTextureType) { + bool npotFBOSuccess = fbo_test(fExts, 128, 128); + if (!npotFBOSuccess) { + GrPrintf("FBO Sanity Test: FAILED\n"); + } else { + GrPrintf("FBO Sanity Test: PASSED\n"); + } + } + + /* The iPhone 4 has a restriction that for an FBO with texture color + attachment with height <= 8 then the width must be <= height. Here + we look for such a limitation. + */ + fMinRenderTargetHeight = GR_INVAL_GLINT; + GLint maxRenderSize; + glGetIntegerv(GR_MAX_RENDERBUFFER_SIZE, &maxRenderSize); + + GrPrintf("Small height FBO texture experiments\n"); + for (GLuint i = 1; i <= 256; + (kFull_NPOTTextureType != fNPOTTextureSupport) ? i *= 2 : ++i) { + GLuint w = maxRenderSize; + GLuint h = i; + if (fbo_test(fExts, w, h)) { + GrPrintf("\t[%d, %d]: PASSED\n", w, h); + fMinRenderTargetHeight = i; + break; + } else { + GrPrintf("\t[%d, %d]: FAILED\n", w, h); + } + } + GrAssert(GR_INVAL_GLINT != fMinRenderTargetHeight); + + GrPrintf("Small width FBO texture experiments\n"); + fMinRenderTargetWidth = GR_MAX_GLUINT; + for (GLuint i = 1; i <= 256; + (kFull_NPOTTextureType != fNPOTTextureSupport) ? i *= 2 : ++i) { + GLuint w = i; + GLuint h = maxRenderSize; + if (fbo_test(fExts, w, h)) { + GrPrintf("\t[%d, %d]: PASSED\n", w, h); + fMinRenderTargetWidth = i; + break; + } else { + GrPrintf("\t[%d, %d]: FAILED\n", w, h); + } + } + GrAssert(GR_INVAL_GLINT != fMinRenderTargetWidth); + +#if GR_IOS_BUILD + /* + The iPad seems to fail, at least sometimes, if the height is < 16, + so we pin the values here for now. A better fix might be to + conditionalize this based on known that its an iPad (or some other + check). + */ + fMinRenderTargetWidth = GrMax<GLuint>(fMinRenderTargetWidth, 16); + fMinRenderTargetHeight = GrMax<GLuint>(fMinRenderTargetHeight, 16); +#endif + // bind back to original FBO + GR_GLEXT(fExts, BindFramebuffer(GR_FRAMEBUFFER, defaultRTIDs.fRTFBOID)); +#if GR_COLLECT_STATS + ++fStats.fRenderTargetChngCnt; +#endif + eraseStencil(0, ~0); +} + +GrGpuGL::~GrGpuGL() { + fDefaultRenderTarget->abandon(); + fDefaultRenderTarget->unref(); +} + +void GrGpuGL::resetContextHelper() { +// We detect cases when blending is effectively off + fHWBlendDisabled = false; + GR_GL(Enable(GL_BLEND)); + + // this is always disabled + GR_GL(Disable(GL_CULL_FACE)); + + GR_GL(Disable(GL_DITHER)); +#if GR_GL_DESKTOP + GR_GL(Disable(GL_LINE_SMOOTH)); + GR_GL(Disable(GL_POINT_SMOOTH)); + GR_GL(Disable(GL_MULTISAMPLE)); +#endif + + // we only ever use lines in hairline mode + GR_GL(LineWidth(1)); + + GR_GL(ActiveTexture(GL_TEXTURE0)); + + fHWDrawState.fFlagBits = 0; + + // illegal values + fHWDrawState.fSrcBlend = (BlendCoeff)-1; + fHWDrawState.fDstBlend = (BlendCoeff)-1; + fHWDrawState.fColor = GrColor_ILLEGAL; + fHWDrawState.fPointSize = -1; + fHWDrawState.fTexture = NULL; + + GR_GL(Scissor(0,0,0,0)); + fHWBounds.fScissorRect.setLTRB(0,0,0,0); + fHWBounds.fScissorEnabled = false; + GR_GL(Disable(GL_SCISSOR_TEST)); + + fHWDrawState.fSamplerState.setRadial2Params(-GR_ScalarMax, + -GR_ScalarMax, + true); + + for (int i = 0; i < kMatrixModeCount; i++) { + fHWDrawState.fMatrixModeCache[i].setScale(GR_ScalarMax, GR_ScalarMax); // illegal + } + + // disabling the stencil test also disables + // stencil buffer writes + GR_GL(Disable(GL_STENCIL_TEST)); + GR_GL(StencilMask(0xffffffff)); + GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + fHWDrawState.fReverseFill = false; + fHWDrawState.fStencilPass = kNone_StencilPass; + fHWStencilClip = false; + + fHWGeometryState.fIndexBuffer = NULL; + fHWGeometryState.fVertexBuffer = NULL; + GR_GL(BindBuffer(GL_ARRAY_BUFFER, 0)); + GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + fHWDrawState.fRenderTarget = NULL; +} + +void GrGpuGL::resetContext() { + INHERITED::resetContext(); + resetContextHelper(); +} + + +// defines stencil formats from more to less preferred +#if GR_GL_ES + GLenum GR_GL_STENCIL_FORMAT_ARRAY[] = { + GR_STENCIL_INDEX8, + }; +#else + GLenum GR_GL_STENCIL_FORMAT_ARRAY[] = { + GR_STENCIL_INDEX8, + GR_STENCIL_INDEX16, + GR_UNSIGNED_INT_24_8, + GR_DEPTH_STENCIL, + }; +#endif + +// good to set a break-point here to know when createTexture fails +static GrTexture* return_null_texture() { +// GrAssert(!"null texture"); + return NULL; +} + +#if GR_DEBUG +static size_t as_size_t(int x) { + return x; +} +#endif + +GrRenderTarget* GrGpuGL::createPlatformRenderTarget( + intptr_t platformRenderTarget, + int width, int height) { + GrGLRenderTarget::GLRenderTargetIDs rtIDs; + rtIDs.fStencilRenderbufferID = 0; + rtIDs.fMSColorRenderbufferID = 0; + rtIDs.fTexFBOID = 0; + rtIDs.fOwnIDs = false; + + GrIRect viewport; + + // viewport is in GL coords (top >= bottom) + viewport.setLTRB(0, height, width, 0); + + rtIDs.fRTFBOID = (GLuint)platformRenderTarget; + rtIDs.fTexFBOID = (GLuint)platformRenderTarget; + + GrGLRenderTarget* rt = new GrGLRenderTarget(rtIDs, viewport, NULL, this); + + return rt; +} + +GrTexture* GrGpuGL::createTexture(const TextureDesc& desc, + const void* srcData, size_t rowBytes) { + +#if GR_COLLECT_STATS + ++fStats.fTextureCreateCnt; +#endif + GrGLTexture::GLTextureDesc glDesc; + GLenum internalFormat; + + glDesc.fContentWidth = desc.fWidth; + glDesc.fContentHeight = desc.fHeight; + glDesc.fAllocWidth = desc.fWidth; + glDesc.fAllocHeight = desc.fHeight; + glDesc.fFormat = desc.fFormat; + + bool renderTarget = 0 != (desc.fFlags & kRenderTarget_TextureFlag); + if (!canBeTexture(desc.fFormat, + &internalFormat, + &glDesc.fUploadFormat, + &glDesc.fUploadType)) { + return return_null_texture(); + } + + GrAssert(as_size_t(desc.fAALevel) < GR_ARRAY_COUNT(fAASamples)); + GLint samples = fAASamples[desc.fAALevel]; + if (kNone_MSFBO == fMSFBOType && desc.fAALevel != kNone_AALevel) { + GrPrintf("AA RT requested but not supported on this platform."); + } + + GR_GL(GenTextures(1, &glDesc.fTextureID)); + if (!glDesc.fTextureID) { + return return_null_texture(); + } + + glDesc.fUploadByteCount = GrTexture::BytesPerPixel(desc.fFormat); + + /* + * check if our srcData has extra bytes past each row. If so, we need + * to trim those off here, since GL doesn't let us pass the rowBytes as + * a parameter to glTexImage2D + */ +#if GR_GL_DESKTOP + if (srcData) { + GR_GL(PixelStorei(GL_UNPACK_ROW_LENGTH, + rowBytes / glDesc.fUploadByteCount)); + } +#else + GrAutoSMalloc<128 * 128> trimStorage; + size_t trimRowBytes = desc.fWidth * glDesc.fUploadByteCount; + if (srcData && (trimRowBytes < rowBytes)) { + size_t trimSize = desc.fHeight * trimRowBytes; + trimStorage.realloc(trimSize); + // now copy the data into our new storage, skipping the trailing bytes + const char* src = (const char*)srcData; + char* dst = (char*)trimStorage.get(); + for (uint32_t y = 0; y < desc.fHeight; y++) { + memcpy(dst, src, trimRowBytes); + src += rowBytes; + dst += trimRowBytes; + } + // now point srcData to our trimmed version + srcData = trimStorage.get(); + } +#endif + + if (fNPOTTextureSupport < kNonRendertarget_NPOTTextureType || + (fNPOTTextureSupport == kNonRendertarget_NPOTTextureType && + renderTarget)) { + glDesc.fAllocWidth = GrNextPow2(desc.fWidth); + glDesc.fAllocHeight = GrNextPow2(desc.fHeight); + } + + if (renderTarget) { + glDesc.fAllocWidth = GrMax<int>(fMinRenderTargetWidth, + glDesc.fAllocWidth); + glDesc.fAllocHeight = GrMax<int>(fMinRenderTargetHeight, + glDesc.fAllocHeight); + } + + GR_GL(BindTexture(GL_TEXTURE_2D, glDesc.fTextureID)); +#if GR_COLLECT_STATS + ++fStats.fTextureChngCnt; +#endif + + GR_GL(PixelStorei(GL_UNPACK_ALIGNMENT, glDesc.fUploadByteCount)); + if (GrTexture::kIndex_8_PixelConfig == desc.fFormat && + supports8BitPalette()) { + // ES only supports CompressedTexImage2D, not CompressedTexSubimage2D + GrAssert(desc.fWidth == glDesc.fAllocWidth); + GrAssert(desc.fHeight == glDesc.fAllocHeight); + GLsizei imageSize = glDesc.fAllocWidth * glDesc.fAllocHeight + + kColorTableSize; + GR_GL(CompressedTexImage2D(GL_TEXTURE_2D, 0, glDesc.fUploadFormat, + glDesc.fAllocWidth, glDesc.fAllocHeight, + 0, imageSize, srcData)); + GrGL_RestoreResetRowLength(); + } else { + if (NULL != srcData && (glDesc.fAllocWidth != desc.fWidth || + glDesc.fAllocHeight != desc.fHeight)) { + GR_GL(TexImage2D(GL_TEXTURE_2D, 0, internalFormat, + glDesc.fAllocWidth, glDesc.fAllocHeight, + 0, glDesc.fUploadFormat, glDesc.fUploadType, NULL)); + GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, desc.fWidth, + desc.fHeight, glDesc.fUploadFormat, + glDesc.fUploadType, srcData)); + GrGL_RestoreResetRowLength(); + + uint32_t extraW = glDesc.fAllocWidth - desc.fWidth; + uint32_t extraH = glDesc.fAllocHeight - desc.fHeight; + uint32_t maxTexels = extraW * extraH; + maxTexels = GrMax(extraW * desc.fHeight, maxTexels); + maxTexels = GrMax(desc.fWidth * extraH, maxTexels); + + GrAutoSMalloc<128*128> texels(glDesc.fUploadByteCount * maxTexels); + + uint32_t rowSize = desc.fWidth * glDesc.fUploadByteCount; + if (extraH) { + uint8_t* lastRowStart = (uint8_t*) srcData + + (desc.fHeight - 1) * rowSize; + uint8_t* extraRowStart = (uint8_t*)texels.get(); + + for (uint32_t i = 0; i < extraH; ++i) { + memcpy(extraRowStart, lastRowStart, rowSize); + extraRowStart += rowSize; + } + GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, 0, desc.fHeight, desc.fWidth, + extraH, glDesc.fUploadFormat, glDesc.fUploadType, + texels.get())); + } + if (extraW) { + uint8_t* edgeTexel = (uint8_t*)srcData + rowSize - glDesc.fUploadByteCount; + uint8_t* extraTexel = (uint8_t*)texels.get(); + for (uint32_t j = 0; j < desc.fHeight; ++j) { + for (uint32_t i = 0; i < extraW; ++i) { + memcpy(extraTexel, edgeTexel, glDesc.fUploadByteCount); + extraTexel += glDesc.fUploadByteCount; + } + edgeTexel += rowSize; + } + GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, desc.fWidth, 0, extraW, + desc.fHeight, glDesc.fUploadFormat, + glDesc.fUploadType, texels.get())); + } + if (extraW && extraH) { + uint8_t* cornerTexel = (uint8_t*)srcData + desc.fHeight * rowSize + - glDesc.fUploadByteCount; + uint8_t* extraTexel = (uint8_t*)texels.get(); + for (uint32_t i = 0; i < extraW*extraH; ++i) { + memcpy(extraTexel, cornerTexel, glDesc.fUploadByteCount); + extraTexel += glDesc.fUploadByteCount; + } + GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, desc.fWidth, desc.fHeight, + extraW, extraH, glDesc.fUploadFormat, + glDesc.fUploadType, texels.get())); + } + + } else { + GR_GL(TexImage2D(GL_TEXTURE_2D, 0, internalFormat, glDesc.fAllocWidth, + glDesc.fAllocHeight, 0, glDesc.fUploadFormat, + glDesc.fUploadType, srcData)); + GrGL_RestoreResetRowLength(); + } + } + + glDesc.fOrientation = GrGLTexture::kTopDown_Orientation; + + GrGLRenderTarget::GLRenderTargetIDs rtIDs; + rtIDs.fStencilRenderbufferID = 0; + rtIDs.fMSColorRenderbufferID = 0; + rtIDs.fRTFBOID = 0; + rtIDs.fTexFBOID = 0; + rtIDs.fOwnIDs = true; + GLenum msColorRenderbufferFormat = -1; + + if (renderTarget) { +#if GR_COLLECT_STATS + ++fStats.fRenderTargetCreateCnt; +#endif + bool failed = true; + GLenum status; + GLint err; + + // If need have both RT flag and srcData we have + // to invert the data before uploading because FBO + // will be rendered bottom up + GrAssert(NULL == srcData); + glDesc.fOrientation = GrGLTexture::kBottomUp_Orientation; + + GR_GLEXT(fExts, GenFramebuffers(1, &rtIDs.fTexFBOID)); + GrAssert(rtIDs.fTexFBOID); + + // If we are using multisampling and any extension other than the IMG + // one we will create two FBOs. We render to one and then resolve to + // the texture bound to the other. The IMG extension does an implicit + // resolve. + if (samples > 1 && kIMG_MSFBO != fMSFBOType && kNone_MSFBO != fMSFBOType) { + GR_GLEXT(fExts, GenFramebuffers(1, &rtIDs.fRTFBOID)); + GrAssert(0 != rtIDs.fRTFBOID); + GR_GLEXT(fExts, GenRenderbuffers(1, &rtIDs.fMSColorRenderbufferID)); + GrAssert(0 != rtIDs.fMSColorRenderbufferID); + if (!fboInternalFormat(desc.fFormat, &msColorRenderbufferFormat)) { + GR_GLEXT(fExts, + DeleteRenderbuffers(1, &rtIDs.fMSColorRenderbufferID)); + GR_GL(DeleteTextures(1, &glDesc.fTextureID)); + GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fTexFBOID)); + GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fRTFBOID)); + fHWDrawState.fTexture = NULL; + return return_null_texture(); + } + } else { + rtIDs.fRTFBOID = rtIDs.fTexFBOID; + } + int attempts = 1; + if (!(kNoPathRendering_TextureFlag & desc.fFlags)) { + GR_GLEXT(fExts, GenRenderbuffers(1, &rtIDs.fStencilRenderbufferID)); + GrAssert(0 != rtIDs.fStencilRenderbufferID); + attempts = GR_ARRAY_COUNT(GR_GL_STENCIL_FORMAT_ARRAY); + } + + // need to unbind the texture before we call FramebufferTexture2D + GR_GL(BindTexture(GL_TEXTURE_2D, 0)); +#if GR_COLLECT_STATS + ++fStats.fTextureChngCnt; +#endif + + fHWDrawState.fTexture = NULL; + + err = ~GL_NO_ERROR; + for (int i = 0; i < attempts; ++i) { + if (rtIDs.fStencilRenderbufferID) { + GR_GLEXT(fExts, BindRenderbuffer(GR_RENDERBUFFER, + rtIDs.fStencilRenderbufferID)); + if (samples > 1) { + GR_GLEXT_NO_ERR(fExts, RenderbufferStorageMultisample( + GR_RENDERBUFFER, + samples, + GR_GL_STENCIL_FORMAT_ARRAY[i], + glDesc.fAllocWidth, + glDesc.fAllocHeight)); + } else { + GR_GLEXT_NO_ERR(fExts, RenderbufferStorage( + GR_RENDERBUFFER, + GR_GL_STENCIL_FORMAT_ARRAY[i], + glDesc.fAllocWidth, + glDesc.fAllocHeight)); + } + err = glGetError(); + if (err != GL_NO_ERROR) { + continue; + } + } + if (rtIDs.fRTFBOID != rtIDs.fTexFBOID) { + GrAssert(samples > 1); + GR_GLEXT(fExts, BindRenderbuffer(GR_RENDERBUFFER, + rtIDs.fMSColorRenderbufferID)); + GR_GLEXT_NO_ERR(fExts, RenderbufferStorageMultisample( + GR_RENDERBUFFER, + samples, + msColorRenderbufferFormat, + glDesc.fAllocWidth, + glDesc.fAllocHeight)); + err = glGetError(); + if (err != GL_NO_ERROR) { + continue; + } + } + GR_GLEXT(fExts, BindFramebuffer(GR_FRAMEBUFFER, rtIDs.fTexFBOID)); + +#if GR_COLLECT_STATS + ++fStats.fRenderTargetChngCnt; +#endif + if (kIMG_MSFBO == fMSFBOType && samples > 1) { + GR_GLEXT(fExts, FramebufferTexture2DMultisample( + GR_FRAMEBUFFER, + GR_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + glDesc.fTextureID, + 0, + samples)); + + } else { + GR_GLEXT(fExts, FramebufferTexture2D(GR_FRAMEBUFFER, + GR_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + glDesc.fTextureID, 0)); + } + if (rtIDs.fRTFBOID != rtIDs.fTexFBOID) { + GLenum status = GR_GLEXT(fExts, + CheckFramebufferStatus(GR_FRAMEBUFFER)); + if (status != GR_FRAMEBUFFER_COMPLETE) { + GrPrintf("-- glCheckFramebufferStatus %x %d %d\n", + status, desc.fWidth, desc.fHeight); + continue; + } + GR_GLEXT(fExts, BindFramebuffer(GR_FRAMEBUFFER, rtIDs.fRTFBOID)); + #if GR_COLLECT_STATS + ++fStats.fRenderTargetChngCnt; + #endif + GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER, + GR_COLOR_ATTACHMENT0, + GR_RENDERBUFFER, + rtIDs.fMSColorRenderbufferID)); + + } + if (rtIDs.fStencilRenderbufferID) { + // bind the stencil to rt fbo if present, othewise the tex fbo + GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER, + GR_STENCIL_ATTACHMENT, + GR_RENDERBUFFER, + rtIDs.fStencilRenderbufferID)); + } + status = GR_GLEXT(fExts, CheckFramebufferStatus(GR_FRAMEBUFFER)); + +#if GR_GL_DESKTOP + // On some implementations you have to be bound as DEPTH_STENCIL. + // (Even binding to DEPTH and STENCIL separately with the same + // buffer doesn't work.) + if (rtIDs.fStencilRenderbufferID && + status != GR_FRAMEBUFFER_COMPLETE) { + GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER, + GR_STENCIL_ATTACHMENT, + GR_RENDERBUFFER, + 0)); + GR_GLEXT(fExts, + FramebufferRenderbuffer(GR_FRAMEBUFFER, + GR_DEPTH_STENCIL_ATTACHMENT, + GR_RENDERBUFFER, + rtIDs.fStencilRenderbufferID)); + status = GR_GLEXT(fExts, CheckFramebufferStatus(GR_FRAMEBUFFER)); + } +#endif + if (status != GR_FRAMEBUFFER_COMPLETE) { + GrPrintf("-- glCheckFramebufferStatus %x %d %d\n", + status, desc.fWidth, desc.fHeight); +#if GR_GL_DESKTOP + if (rtIDs.fStencilRenderbufferID) { + GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER, + GR_DEPTH_STENCIL_ATTACHMENT, + GR_RENDERBUFFER, + 0)); + } +#endif + continue; + } + // we're successful! + failed = false; + break; + } + if (failed) { + if (rtIDs.fStencilRenderbufferID) { + GR_GLEXT(fExts, + DeleteRenderbuffers(1, &rtIDs.fStencilRenderbufferID)); + } + if (rtIDs.fMSColorRenderbufferID) { + GR_GLEXT(fExts, + DeleteRenderbuffers(1, &rtIDs.fMSColorRenderbufferID)); + } + if (rtIDs.fRTFBOID != rtIDs.fTexFBOID) { + GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fRTFBOID)); + } + if (rtIDs.fTexFBOID) { + GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fTexFBOID)); + } + GR_GL(DeleteTextures(1, &glDesc.fTextureID)); + return return_null_texture(); + } + } +#ifdef TRACE_TEXTURE_CREATION + GrPrintf("--- new texture [%d] size=(%d %d) bpp=%d\n", + tex->fTextureID, width, height, tex->fUploadByteCount); +#endif + GrGLTexture* tex = new GrGLTexture(glDesc, rtIDs, this); + + if (0 != rtIDs.fTexFBOID) { + GrRenderTarget* rt = tex->asRenderTarget(); + // We've messed with FBO state but may not have set the correct viewport + // so just dirty the rendertarget state to force a resend. + fHWDrawState.fRenderTarget = NULL; + + // clear the new stencil buffer if we have one + if (!(desc.fFlags & kNoPathRendering_TextureFlag)) { + GrRenderTarget* rtSave = fCurrDrawState.fRenderTarget; + fCurrDrawState.fRenderTarget = rt; + eraseStencil(0, ~0); + fCurrDrawState.fRenderTarget = rtSave; + } + } + return tex; +} + +GrRenderTarget* GrGpuGL::defaultRenderTarget() { + return fDefaultRenderTarget; +} + +GrVertexBuffer* GrGpuGL::createVertexBuffer(uint32_t size, bool dynamic) { + GLuint id; + GR_GL(GenBuffers(1, &id)); + if (id) { + GR_GL(BindBuffer(GL_ARRAY_BUFFER, id)); + GrGLClearErr(); + // make sure driver can allocate memory for this buffer + GR_GL_NO_ERR(BufferData(GL_ARRAY_BUFFER, size, NULL, + dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW)); + if (glGetError() != GL_NO_ERROR) { + GR_GL(DeleteBuffers(1, &id)); + // deleting bound buffer does implicit bind to 0 + fHWGeometryState.fVertexBuffer = NULL; + return NULL; + } + GrGLVertexBuffer* vertexBuffer = new GrGLVertexBuffer(id, this, + size, dynamic); + fHWGeometryState.fVertexBuffer = vertexBuffer; + return vertexBuffer; + } + return NULL; +} + +GrIndexBuffer* GrGpuGL::createIndexBuffer(uint32_t size, bool dynamic) { + GLuint id; + GR_GL(GenBuffers(1, &id)); + if (id) { + GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, id)); + GrGLClearErr(); + // make sure driver can allocate memory for this buffer + GR_GL_NO_ERR(BufferData(GL_ELEMENT_ARRAY_BUFFER, size, NULL, + dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW)); + if (glGetError() != GL_NO_ERROR) { + GR_GL(DeleteBuffers(1, &id)); + // deleting bound buffer does implicit bind to 0 + fHWGeometryState.fIndexBuffer = NULL; + return NULL; + } + GrIndexBuffer* indexBuffer = new GrGLIndexBuffer(id, this, + size, dynamic); + fHWGeometryState.fIndexBuffer = indexBuffer; + return indexBuffer; + } + return NULL; +} + +void GrGpuGL::setDefaultRenderTargetSize(uint32_t width, uint32_t height) { + GrIRect viewport(0, height, width, 0); + if (viewport != fDefaultRenderTarget->viewport()) { + fDefaultRenderTarget->setViewport(viewport); + if (fHWDrawState.fRenderTarget == fDefaultRenderTarget) { + fHWDrawState.fRenderTarget = NULL; + } + } +} + +void GrGpuGL::flushScissor(const GrIRect* rect) { + GrAssert(NULL != fCurrDrawState.fRenderTarget); + const GrIRect& vp = + ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport(); + + if (NULL != rect && + rect->contains(vp)) { + rect = NULL; + } + + if (NULL != rect) { + GrIRect scissor; + // viewport is already in GL coords + // create a scissor in GL coords (top > bottom) + scissor.setLTRB(vp.fLeft + rect->fLeft, + vp.fTop - rect->fTop, + vp.fLeft + rect->fRight, + vp.fTop - rect->fBottom); + + if (fHWBounds.fScissorRect != scissor) { + GR_GL(Scissor(scissor.fLeft, scissor.fBottom, + scissor.width(), -scissor.height())); + fHWBounds.fScissorRect = scissor; + } + + if (!fHWBounds.fScissorEnabled) { + GR_GL(Enable(GL_SCISSOR_TEST)); + fHWBounds.fScissorEnabled = true; + } + } else { + if (fHWBounds.fScissorEnabled) { + GR_GL(Disable(GL_SCISSOR_TEST)); + fHWBounds.fScissorEnabled = false; + } + } +} + +void GrGpuGL::setSamplerStateImm(const GrSamplerState& state) { + + GLenum filter = state.isFilter() ? GL_LINEAR : GL_NEAREST; + GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter)); + GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter)); + GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GrGLTexture::gWrapMode2GLWrap[state.getWrapX()])); + GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GrGLTexture::gWrapMode2GLWrap[state.getWrapY()])); + +} + +void GrGpuGL::eraseColor(GrColor color) { + flushRenderTarget(); + if (fHWBounds.fScissorEnabled) { + GR_GL(Disable(GL_SCISSOR_TEST)); + } + GR_GL(ColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE)); + GR_GL(ClearColor(GrColorUnpackR(color)/255.f, + GrColorUnpackG(color)/255.f, + GrColorUnpackB(color)/255.f, + GrColorUnpackA(color)/255.f)); + GR_GL(Clear(GL_COLOR_BUFFER_BIT)); + fHWBounds.fScissorEnabled = false; + fWriteMaskChanged = true; +} + +void GrGpuGL::eraseStencil(uint32_t value, uint32_t mask) { + flushRenderTarget(); + if (fHWBounds.fScissorEnabled) { + GR_GL(Disable(GL_SCISSOR_TEST)); + } + GR_GL(StencilMask(mask)); + GR_GL(ClearStencil(value)); + GR_GL(Clear(GL_STENCIL_BUFFER_BIT)); + fHWBounds.fScissorEnabled = false; + fWriteMaskChanged = true; +} + +void GrGpuGL::eraseStencilClip() { + GLint stencilBitCount; + GR_GL(GetIntegerv(GL_STENCIL_BITS, &stencilBitCount)); + GrAssert(stencilBitCount > 0); + GLint clipStencilMask = (1 << (stencilBitCount - 1)); + eraseStencil(0, clipStencilMask); +} + +void GrGpuGL::forceRenderTargetFlush() { + flushRenderTarget(); +} + +bool GrGpuGL::readPixels(int left, int top, int width, int height, + GrTexture::PixelConfig config, void* buffer) { + GLenum internalFormat; // we don't use this for glReadPixels + GLenum format; + GLenum type; + if (!this->canBeTexture(config, &internalFormat, &format, &type)) { + return false; + } + + GrAssert(NULL != fCurrDrawState.fRenderTarget); + const GrIRect& vp = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport(); + + // Brian says that viewport rects are already upside down (grrrrr) + glReadPixels(left, -vp.height() - top - height, width, height, + format, type, buffer); + + // now reverse the order of the rows, since GL's are bottom-to-top, but our + // API presents top-to-bottom + { + size_t stride = width * GrTexture::BytesPerPixel(config); + GrAutoMalloc rowStorage(stride); + void* tmp = rowStorage.get(); + + const int halfY = height >> 1; + char* top = reinterpret_cast<char*>(buffer); + char* bottom = top + (height - 1) * stride; + for (int y = 0; y < halfY; y++) { + memcpy(tmp, top, stride); + memcpy(top, bottom, stride); + memcpy(bottom, tmp, stride); + top += stride; + bottom -= stride; + } + } + return true; +} + +void GrGpuGL::flushRenderTarget() { + if (fHWDrawState.fRenderTarget != fCurrDrawState.fRenderTarget) { + GrGLRenderTarget* rt = (GrGLRenderTarget*)fCurrDrawState.fRenderTarget; + GR_GLEXT(fExts, BindFramebuffer(GR_FRAMEBUFFER, rt->renderFBOID())); + #if GR_COLLECT_STATS + ++fStats.fRenderTargetChngCnt; + #endif + rt->setDirty(true); + #if GR_DEBUG + GLenum status = GR_GLEXT(fExts, CheckFramebufferStatus(GR_FRAMEBUFFER)); + if (status != GR_FRAMEBUFFER_COMPLETE) { + GrPrintf("-- glCheckFramebufferStatus %x\n", status); + } + #endif + fHWDrawState.fRenderTarget = fCurrDrawState.fRenderTarget; + const GrIRect& vp = rt->viewport(); + fRenderTargetChanged = true; + if (fHWBounds.fViewportRect != vp) { + GR_GL(Viewport(vp.fLeft, + vp.fBottom, + vp.width(), + -vp.height())); + fHWBounds.fViewportRect = vp; + } + } +} + +GLenum gPrimitiveType2GLMode[] = { + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN, + GL_POINTS, + GL_LINES, + GL_LINE_STRIP +}; + +void GrGpuGL::drawIndexedHelper(PrimitiveType type, + uint32_t startVertex, + uint32_t startIndex, + uint32_t vertexCount, + uint32_t indexCount) { + GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode)); + + GLvoid* indices = (GLvoid*)(sizeof(uint16_t) * startIndex); + if (kReserved_GeometrySrcType == fGeometrySrc.fIndexSrc) { + indices = (GLvoid*)((intptr_t)indices + (intptr_t)fIndices.get()); + } else if (kArray_GeometrySrcType == fGeometrySrc.fIndexSrc) { + indices = (GLvoid*)((intptr_t)indices + + (intptr_t)fGeometrySrc.fIndexArray); + } + + GR_GL(DrawElements(gPrimitiveType2GLMode[type], indexCount, + GL_UNSIGNED_SHORT, indices)); +} + +void GrGpuGL::drawNonIndexedHelper(PrimitiveType type, + uint32_t startVertex, + uint32_t vertexCount) { + GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode)); + + GR_GL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount)); +} + +#if !defined(SK_GL_HAS_COLOR4UB) +static inline GrFixed byte2fixed(unsigned value) { + return (value + (value >> 7)) << 8; +} +#endif + +void GrGpuGL::resolveTextureRenderTarget(GrGLTexture* texture) { + GrGLRenderTarget* rt = (GrGLRenderTarget*) texture->asRenderTarget(); + + if (NULL != rt && rt->needsResolve()) { + GrAssert(kNone_MSFBO != fMSFBOType); + GrAssert(rt->textureFBOID() != rt->renderFBOID()); + GR_GLEXT(fExts, BindFramebuffer(GR_READ_FRAMEBUFFER, + rt->renderFBOID())); + GR_GLEXT(fExts, BindFramebuffer(GR_DRAW_FRAMEBUFFER, + rt->textureFBOID())); + #if GR_COLLECT_STATS + ++fStats.fRenderTargetChngCnt; + #endif + // make sure we go through set render target + fHWDrawState.fRenderTarget = NULL; + + GLint left = 0; + GLint right = texture->contentWidth(); + // we will have rendered to the top of the FBO. + GLint top = texture->allocHeight(); + GLint bottom = texture->allocHeight() - texture->contentHeight(); + if (kApple_MSFBO == fMSFBOType) { + GR_GL(Enable(GL_SCISSOR_TEST)); + GR_GL(Scissor(left, bottom, right-left, top-bottom)); + GR_GLEXT(fExts, ResolveMultisampleFramebuffer()); + fHWBounds.fScissorRect.setEmpty(); + fHWBounds.fScissorEnabled = true; + } else { + GR_GLEXT(fExts, BlitFramebuffer(left, bottom, right, top, + left, bottom, right, top, + GL_COLOR_BUFFER_BIT, GL_NEAREST)); + } + rt->setDirty(false); + + } +} + +void GrGpuGL::flushStencil() { + + // use stencil for clipping if clipping is enabled and the clip + // has been written into the stencil. + bool stencilClip = fClipState.fClipInStencil && + (kClip_StateBit & fCurrDrawState.fFlagBits); + bool stencilChange = + fWriteMaskChanged || + fHWStencilClip != stencilClip || + fHWDrawState.fStencilPass != fCurrDrawState.fStencilPass || + (kNone_StencilPass != fCurrDrawState.fStencilPass && + (StencilPass)kSetClip_StencilPass != fCurrDrawState.fStencilPass && + fHWDrawState.fReverseFill != fCurrDrawState.fReverseFill); + + if (stencilChange) { + GLint stencilBitCount; + GLint clipStencilMask; + GLint pathStencilMask; + GR_GL(GetIntegerv(GL_STENCIL_BITS, &stencilBitCount)); + GrAssert(stencilBitCount > 0 || + kNone_StencilPass == fCurrDrawState.fStencilPass); + clipStencilMask = (1 << (stencilBitCount - 1)); + pathStencilMask = clipStencilMask - 1; + switch (fCurrDrawState.fStencilPass) { + case kNone_StencilPass: + if (stencilClip) { + GR_GL(Enable(GL_STENCIL_TEST)); + GR_GL(StencilFunc(GL_EQUAL, + clipStencilMask, + clipStencilMask)); + GR_GL(StencilOp(GL_KEEP, GL_KEEP, GL_KEEP)); + } else { + GR_GL(Disable(GL_STENCIL_TEST)); + } + GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + if (!fSingleStencilPassForWinding) { + GR_GL(Disable(GL_CULL_FACE)); + } + break; + case kEvenOddStencil_StencilPass: + GR_GL(Enable(GL_STENCIL_TEST)); + if (stencilClip) { + GR_GL(StencilFunc(GL_EQUAL, clipStencilMask, clipStencilMask)); + } else { + GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0)); + } + GR_GL(StencilMask(pathStencilMask)); + GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + GR_GL(StencilOp(GL_KEEP, GL_INVERT, GL_INVERT)); + GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); + if (!fSingleStencilPassForWinding) { + GR_GL(Disable(GL_CULL_FACE)); + } + break; + case kEvenOddColor_StencilPass: { + GR_GL(Enable(GL_STENCIL_TEST)); + GLint funcRef = 0; + GLuint funcMask = pathStencilMask; + if (stencilClip) { + funcRef |= clipStencilMask; + funcMask |= clipStencilMask; + } + if (!fCurrDrawState.fReverseFill) { + funcRef |= pathStencilMask; + } + glStencilFunc(GL_EQUAL, funcRef, funcMask); + glStencilMask(pathStencilMask); + GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO)); + GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + if (!fSingleStencilPassForWinding) { + GR_GL(Disable(GL_CULL_FACE)); + } + } break; + case kWindingStencil1_StencilPass: + GR_GL(Enable(GL_STENCIL_TEST)); + if (fHasStencilWrap) { + if (stencilClip) { + GR_GL(StencilFunc(GL_EQUAL, + clipStencilMask, + clipStencilMask)); + } else { + GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0)); + } + if (fSingleStencilPassForWinding) { + GR_GL(StencilOpSeparate(GL_FRONT, GL_KEEP, + GL_INCR_WRAP, GL_INCR_WRAP)); + GR_GL(StencilOpSeparate(GL_BACK, GL_KEEP, + GL_DECR_WRAP, GL_DECR_WRAP)); + } else { + GR_GL(StencilOp(GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP)); + GR_GL(Enable(GL_CULL_FACE)); + GR_GL(CullFace(GL_BACK)); + } + } else { + // If we don't have wrap then we use the Func to detect + // values that would wrap (0 on decr and mask on incr). We + // make the func fail on these values and use the sfail op + // to effectively wrap by inverting. + // This applies whether we are doing a two-pass (front faces + // followed by back faces) or a single pass (separate func/op) + + // Note that in the case where we are also using stencil to + // clip this means we will write into the path bits in clipped + // out pixels. We still apply the clip bit in the color pass + // stencil func so we don't draw color outside the clip. + // We also will clear the stencil bits in clipped pixels by + // using zero in the sfail op with write mask set to the + // path mask. + GR_GL(Enable(GL_STENCIL_TEST)); + if (fSingleStencilPassForWinding) { + GR_GL(StencilFuncSeparate(GL_FRONT, + GL_NOTEQUAL, + pathStencilMask, + pathStencilMask)); + GR_GL(StencilFuncSeparate(GL_BACK, + GL_NOTEQUAL, + 0x0, + pathStencilMask)); + GR_GL(StencilOpSeparate(GL_FRONT, GL_INVERT, + GL_INCR, GL_INCR)); + GR_GL(StencilOpSeparate(GL_BACK, GL_INVERT, + GL_DECR, GL_DECR)); + } else { + GR_GL(StencilFunc(GL_NOTEQUAL, + pathStencilMask, + pathStencilMask)); + GR_GL(StencilOp(GL_INVERT, GL_INCR, GL_INCR)); + GR_GL(Enable(GL_CULL_FACE)); + GR_GL(CullFace(GL_BACK)); + } + } + GR_GL(StencilMask(pathStencilMask)); + GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + case kWindingStencil2_StencilPass: + GrAssert(!fSingleStencilPassForWinding); + GR_GL(Enable(GL_STENCIL_TEST)); + if (fHasStencilWrap) { + if (stencilClip) { + GR_GL(StencilFunc(GL_EQUAL, + clipStencilMask, + clipStencilMask)); + } else { + GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0)); + } + GR_GL(StencilOp(GL_DECR_WRAP, GL_DECR_WRAP, GL_DECR_WRAP)); + } else { + GR_GL(StencilFunc(GL_NOTEQUAL, 0x0, pathStencilMask)); + GR_GL(StencilOp(GL_INVERT, GL_DECR, GL_DECR)); + } + GR_GL(StencilMask(pathStencilMask)); + GR_GL(Enable(GL_CULL_FACE)); + GR_GL(CullFace(GL_FRONT)); + GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); + break; + case kWindingColor_StencilPass: { + GR_GL(Enable(GL_STENCIL_TEST)); + GLint funcRef = 0; + GLuint funcMask = pathStencilMask; + GLenum funcFunc; + if (stencilClip) { + funcRef |= clipStencilMask; + funcMask |= clipStencilMask; + } + if (fCurrDrawState.fReverseFill) { + funcFunc = GL_EQUAL; + } else { + funcFunc = GL_LESS; + } + GR_GL(StencilFunc(funcFunc, funcRef, funcMask)); + GR_GL(StencilMask(pathStencilMask)); + // must zero in sfail because winding w/o wrap will write + // path stencil bits in clipped out pixels + GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO)); + GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + if (!fSingleStencilPassForWinding) { + GR_GL(Disable(GL_CULL_FACE)); + } + } break; + case kSetClip_StencilPass: + GR_GL(Enable(GL_STENCIL_TEST)); + GR_GL(StencilFunc(GL_ALWAYS, clipStencilMask, clipStencilMask)); + GR_GL(StencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE)); + GR_GL(StencilMask(clipStencilMask)); + GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); + if (!fSingleStencilPassForWinding) { + GR_GL(Disable(GL_CULL_FACE)); + } + break; + default: + GrAssert(!"Unexpected stencil pass."); + break; + + } + fHWDrawState.fStencilPass = fCurrDrawState.fStencilPass; + fHWDrawState.fReverseFill = fCurrDrawState.fReverseFill; + fWriteMaskChanged = false; + fHWStencilClip = stencilClip; + } +} + +void GrGpuGL::flushGLStateCommon(PrimitiveType type) { + + bool usingTexture = VertexHasTexCoords(fGeometrySrc.fVertexLayout); + + // bind texture and set sampler state + if (usingTexture) { + GrGLTexture* nextTexture = (GrGLTexture*)fCurrDrawState.fTexture; + + if (NULL != nextTexture) { + // if we created a rt/tex and rendered to it without using a texture + // and now we're texuring from the rt it will still be the last bound + // texture, but it needs resolving. So keep this out of the last + // != next check. + resolveTextureRenderTarget(nextTexture); + + if (fHWDrawState.fTexture != nextTexture) { + + GR_GL(BindTexture(GL_TEXTURE_2D, nextTexture->textureID())); + #if GR_COLLECT_STATS + ++fStats.fTextureChngCnt; + #endif + //GrPrintf("---- bindtexture %d\n", nextTexture->textureID()); + fHWDrawState.fTexture = nextTexture; + } + const GrSamplerState& lastSampler = nextTexture->samplerState(); + if (lastSampler.isFilter() != fCurrDrawState.fSamplerState.isFilter()) { + GLenum filter = fCurrDrawState.fSamplerState.isFilter() ? + GL_LINEAR : + GL_NEAREST; + GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + filter)); + GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + filter)); + } + if (lastSampler.getWrapX() != fCurrDrawState.fSamplerState.getWrapX()) { + GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GrGLTexture::gWrapMode2GLWrap[ + fCurrDrawState.fSamplerState.getWrapX()])); + } + if (lastSampler.getWrapY() != fCurrDrawState.fSamplerState.getWrapY()) { + GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GrGLTexture::gWrapMode2GLWrap[ + fCurrDrawState.fSamplerState.getWrapY()])); + } + nextTexture->setSamplerState(fCurrDrawState.fSamplerState); + } else { + GrAssert(!"Rendering with texture vert flag set but no texture"); + if (NULL != fHWDrawState.fTexture) { + GR_GL(BindTexture(GL_TEXTURE_2D, 0)); + // GrPrintf("---- bindtexture 0\n"); + #if GR_COLLECT_STATS + ++fStats.fTextureChngCnt; + #endif + fHWDrawState.fTexture = NULL; + } + } + } + + flushRenderTarget(); + + if ((fCurrDrawState.fFlagBits & kDither_StateBit) != + (fHWDrawState.fFlagBits & kDither_StateBit)) { + if (fCurrDrawState.fFlagBits & kDither_StateBit) { + GR_GL(Enable(GL_DITHER)); + } else { + GR_GL(Disable(GL_DITHER)); + } + } + +#if GR_GL_DESKTOP + // ES doesn't support toggling GL_MULTISAMPLE and doesn't have + // smooth lines. + if (fRenderTargetChanged || + (fCurrDrawState.fFlagBits & kAntialias_StateBit) != + (fHWDrawState.fFlagBits & kAntialias_StateBit)) { + GLint msaa = 0; + // only perform query if we know MSAA is supported. + // calling on non-MSAA target caused a crash in one environment, + // though I don't think it should. + if (!fAASamples[kHigh_AALevel]) { + GR_GL(GetIntegerv(GL_SAMPLE_BUFFERS, &msaa)); + } + if (fCurrDrawState.fFlagBits & kAntialias_StateBit) { + if (msaa) { + GR_GL(Enable(GL_MULTISAMPLE)); + } else { + GR_GL(Enable(GL_LINE_SMOOTH)); + } + } else { + if (msaa) { + GR_GL(Disable(GL_MULTISAMPLE)); + } + GR_GL(Disable(GL_LINE_SMOOTH)); + } + } +#endif + + bool blendOff = canDisableBlend(); + if (fHWBlendDisabled != blendOff) { + if (blendOff) { + GR_GL(Disable(GL_BLEND)); + } else { + GR_GL(Enable(GL_BLEND)); + } + fHWBlendDisabled = blendOff; + } + + if (!blendOff) { + if (fHWDrawState.fSrcBlend != fCurrDrawState.fSrcBlend || + fHWDrawState.fDstBlend != fCurrDrawState.fDstBlend) { + GR_GL(BlendFunc(gXfermodeCoeff2Blend[fCurrDrawState.fSrcBlend], + gXfermodeCoeff2Blend[fCurrDrawState.fDstBlend])); + fHWDrawState.fSrcBlend = fCurrDrawState.fSrcBlend; + fHWDrawState.fDstBlend = fCurrDrawState.fDstBlend; + } + } + + // check for circular rendering + GrAssert(!usingTexture || + NULL == fCurrDrawState.fRenderTarget || + NULL == fCurrDrawState.fTexture || + fCurrDrawState.fTexture->asRenderTarget() != fCurrDrawState.fRenderTarget); + + flushStencil(); + + fHWDrawState.fFlagBits = fCurrDrawState.fFlagBits; +} + +void GrGpuGL::notifyVertexBufferBind(const GrGLVertexBuffer* buffer) { + fHWGeometryState.fVertexBuffer = buffer; +} + +void GrGpuGL::notifyVertexBufferDelete(const GrGLVertexBuffer* buffer) { + GrAssert(!(kBuffer_GeometrySrcType == fGeometrySrc.fVertexSrc && + buffer == fGeometrySrc.fVertexBuffer)); + + if (fHWGeometryState.fVertexBuffer == buffer) { + // deleting bound buffer does implied bind to 0 + fHWGeometryState.fVertexBuffer = NULL; + } +} + +void GrGpuGL::notifyIndexBufferBind(const GrGLIndexBuffer* buffer) { + fGeometrySrc.fIndexBuffer = buffer; +} + +void GrGpuGL::notifyIndexBufferDelete(const GrGLIndexBuffer* buffer) { + GrAssert(!(kBuffer_GeometrySrcType == fGeometrySrc.fIndexSrc && + buffer == fGeometrySrc.fIndexBuffer)); + + if (fHWGeometryState.fIndexBuffer == buffer) { + // deleting bound buffer does implied bind to 0 + fHWGeometryState.fIndexBuffer = NULL; + } +} + +void GrGpuGL::notifyTextureBind(GrGLTexture* texture) { + fHWDrawState.fTexture = texture; +#if GR_COLLECT_STATS + ++fStats.fTextureChngCnt; +#endif +} + +void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) { + GrAssert(NULL != renderTarget); + + // if the bound FBO is destroyed we can't rely on the implicit bind to 0 + // a) we want the default RT which may not be FBO 0 + // b) we set more state than just FBO based on the RT + // So trash the HW state to force an RT flush next time + if (fCurrDrawState.fRenderTarget == renderTarget) { + fCurrDrawState.fRenderTarget = (GrRenderTarget*)&fDefaultRenderTarget; + } + if (fHWDrawState.fRenderTarget == renderTarget) { + fHWDrawState.fRenderTarget = NULL; + } + if (fClipState.fStencilClipTarget == renderTarget) { + fClipState.fStencilClipTarget = NULL; + } +} + +void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) { + if (fCurrDrawState.fTexture == texture) { + fCurrDrawState.fTexture = NULL; + } + if (fHWDrawState.fTexture == texture) { + // deleting bound texture does implied bind to 0 + fHWDrawState.fTexture = NULL; + } +} + +void GrGpuGL::notifyTextureRemoveRenderTarget(GrGLTexture* texture) { + GrAssert(NULL != texture->asRenderTarget()); + + // if there is a pending resolve, perform it. + resolveTextureRenderTarget(texture); +} + +bool GrGpuGL::canBeTexture(GrTexture::PixelConfig config, + GLenum* internalFormat, + GLenum* format, + GLenum* type) { + switch (config) { + case GrTexture::kRGBA_8888_PixelConfig: + case GrTexture::kRGBX_8888_PixelConfig: // todo: can we tell it our X? + *format = SK_GL_32BPP_COLOR_FORMAT; + *internalFormat = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + break; + case GrTexture::kRGB_565_PixelConfig: + *format = GL_RGB; + *internalFormat = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + break; + case GrTexture::kRGBA_4444_PixelConfig: + *format = GL_RGBA; + *internalFormat = GL_RGBA; + *type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case GrTexture::kIndex_8_PixelConfig: + if (this->supports8BitPalette()) { + *format = GR_PALETTE8_RGBA8; + *internalFormat = GR_PALETTE8_RGBA8; + *type = GL_UNSIGNED_BYTE; // unused I think + } else { + return false; + } + break; + case GrTexture::kAlpha_8_PixelConfig: + *format = GL_ALPHA; + *internalFormat = GL_ALPHA; + *type = GL_UNSIGNED_BYTE; + break; + default: + return false; + } + return true; +} + +/* On ES the internalFormat and format must match for TexImage and we use + GL_RGB, GL_RGBA for color formats. We also generally like having the driver + decide the internalFormat. However, on ES internalFormat for + RenderBufferStorage* has to be a specific format (not a base format like + GL_RGBA). + */ +bool GrGpuGL::fboInternalFormat(GrTexture::PixelConfig config, GLenum* format) { + switch (config) { + case GrTexture::kRGBA_8888_PixelConfig: + case GrTexture::kRGBX_8888_PixelConfig: + if (fRGBA8Renderbuffer) { + *format = GR_RGBA8; + return true; + } else { + return false; + } +#if GR_GL_ES // ES2 supports 565. ES1 supports it with FBO extension + // desktop GL has no such internal format + case GrTexture::kRGB_565_PixelConfig: + *format = GR_RGB565; + return true; +#endif + case GrTexture::kRGBA_4444_PixelConfig: + *format = GL_RGBA4; + return true; + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLCheckErr(const char* location, const char* call) { + uint32_t err = glGetError(); + if (GL_NO_ERROR != err) { + GrPrintf("---- glGetError %x", err); + if (NULL != location) { + GrPrintf(" at\n\t%s", location); + } + if (NULL != call) { + GrPrintf("\n\t\t%s", call); + } + GrPrintf("\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef void (*glProc)(void); + +void get_gl_proc(const char procName[], glProc *address) { +#if GR_WIN32_BUILD + *address = wglGetProcAddress(procName); + GrAssert(NULL != *address); +#elif GR_MAC_BUILD || GR_IOS_BUILD + GrAssert(!"Extensions don't need to be initialized!"); +#elif GR_ANDROID_BUILD + *address = eglGetProcAddress(procName); + GrAssert(NULL != *address); +#elif GR_LINUX_BUILD + GR_STATIC_ASSERT(!"Add environment-dependent implementation here"); + //*address = glXGetProcAddressARB(procName); + //*address = eglGetProcAddress(procName); +#elif GR_QNX_BUILD + *address = eglGetProcAddress(procName); + GrAssert(NULL != *address); +#else + // hopefully we're on a system with EGL + *address = eglGetProcAddress(procName); + GrAssert(NULL != *address); +#endif +} + +#define GET_PROC(EXT_STRUCT, PROC_NAME, EXT_TAG) \ + get_gl_proc("gl" #PROC_NAME #EXT_TAG, (glProc*)&EXT_STRUCT-> PROC_NAME); + +extern void GrGLInitExtensions(GrGLExts* exts) { + exts->GenFramebuffers = NULL; + exts->BindFramebuffer = NULL; + exts->FramebufferTexture2D = NULL; + exts->CheckFramebufferStatus = NULL; + exts->DeleteFramebuffers = NULL; + exts->RenderbufferStorage = NULL; + exts->GenRenderbuffers = NULL; + exts->DeleteRenderbuffers = NULL; + exts->FramebufferRenderbuffer = NULL; + exts->BindRenderbuffer = NULL; + exts->RenderbufferStorageMultisample = NULL; + exts->BlitFramebuffer = NULL; + exts->ResolveMultisampleFramebuffer = NULL; + exts->FramebufferTexture2DMultisample = NULL; + exts->MapBuffer = NULL; + exts->UnmapBuffer = NULL; + +#if GR_MAC_BUILD + exts->GenFramebuffers = glGenFramebuffers; + exts->BindFramebuffer = glBindFramebuffer; + exts->FramebufferTexture2D = glFramebufferTexture2D; + exts->CheckFramebufferStatus = glCheckFramebufferStatus; + exts->DeleteFramebuffers = glDeleteFramebuffers; + exts->RenderbufferStorage = glRenderbufferStorage; + exts->GenRenderbuffers = glGenRenderbuffers; + exts->DeleteRenderbuffers = glDeleteRenderbuffers; + exts->FramebufferRenderbuffer = glFramebufferRenderbuffer; + exts->BindRenderbuffer = glBindRenderbuffer; + exts->RenderbufferStorageMultisample = glRenderbufferStorageMultisample; + exts->BlitFramebuffer = glBlitFramebuffer; + exts->MapBuffer = glMapBuffer; + exts->UnmapBuffer = glUnmapBuffer; +#elif GR_IOS_BUILD + exts->GenFramebuffers = glGenFramebuffers; + exts->BindFramebuffer = glBindFramebuffer; + exts->FramebufferTexture2D = glFramebufferTexture2D; + exts->CheckFramebufferStatus = glCheckFramebufferStatus; + exts->DeleteFramebuffers = glDeleteFramebuffers; + exts->RenderbufferStorage = glRenderbufferStorage; + exts->GenRenderbuffers = glGenRenderbuffers; + exts->DeleteRenderbuffers = glDeleteRenderbuffers; + exts->FramebufferRenderbuffer = glFramebufferRenderbuffer; + exts->BindRenderbuffer = glBindRenderbuffer; + exts->RenderbufferStorageMultisample = glRenderbufferStorageMultisampleAPPLE; + exts->ResolveMultisampleFramebuffer = glResolveMultisampleFramebufferAPPLE; + exts->MapBuffer = glMapBufferOES; + exts->UnmapBuffer = glUnmapBufferOES; +#else + GLint major, minor; + gl_version(&major, &minor); + #if GR_GL_DESKTOP + if (major >= 3) {// FBO, FBOMS, and FBOBLIT part of 3.0 + exts->GenFramebuffers = glGenFramebuffers; + exts->BindFramebuffer = glBindFramebuffer; + exts->FramebufferTexture2D = glFramebufferTexture2D; + exts->CheckFramebufferStatus = glCheckFramebufferStatus; + exts->DeleteFramebuffers = glDeleteFramebuffers; + exts->RenderbufferStorage = glRenderbufferStorage; + exts->GenRenderbuffers = glGenRenderbuffers; + exts->DeleteRenderbuffers = glDeleteRenderbuffers; + exts->FramebufferRenderbuffer = glFramebufferRenderbuffer; + exts->BindRenderbuffer = glBindRenderbuffer; + exts->RenderbufferStorageMultisample = glRenderbufferStorageMultisample; + exts->BlitFramebuffer = glBlitFramebuffer; + } else if (has_gl_extension("GL_ARB_framebuffer_object")) { + GET_PROC(exts, GenFramebuffers, ARB); + GET_PROC(exts, BindFramebuffer, ARB); + GET_PROC(exts, FramebufferTexture2D, ARB); + GET_PROC(exts, CheckFramebufferStatus, ARB); + GET_PROC(exts, DeleteFramebuffers, ARB); + GET_PROC(exts, RenderbufferStorage, ARB); + GET_PROC(exts, GenRenderbuffers, ARB); + GET_PROC(exts, DeleteRenderbuffers, ARB); + GET_PROC(exts, FramebufferRenderbuffer, ARB); + GET_PROC(exts, BindRenderbuffer, ARB); + GET_PROC(exts, RenderbufferStorageMultisample, ARB); + GET_PROC(exts, BlitFramebuffer, ARB); + } else { + // we require some form of FBO + GrAssert(has_gl_extension("GL_EXT_framebuffer_object")); + GET_PROC(exts, GenFramebuffers, EXT); + GET_PROC(exts, BindFramebuffer, EXT); + GET_PROC(exts, FramebufferTexture2D, EXT); + GET_PROC(exts, CheckFramebufferStatus, EXT); + GET_PROC(exts, DeleteFramebuffers, EXT); + GET_PROC(exts, RenderbufferStorage, EXT); + GET_PROC(exts, GenRenderbuffers, EXT); + GET_PROC(exts, DeleteRenderbuffers, EXT); + GET_PROC(exts, FramebufferRenderbuffer, EXT); + GET_PROC(exts, BindRenderbuffer, EXT); + if (has_gl_extension("GL_EXT_framebuffer_multisample")) { + GET_PROC(exts, RenderbufferStorageMultisample, EXT); + } + if (has_gl_extension("GL_EXT_framebuffer_blit")) { + GET_PROC(exts, BlitFramebuffer, EXT); + } + } + // we assume we have at least GL 1.5 or higher (VBOs introduced in 1.5) + exts->MapBuffer = glMapBuffer; + exts->UnmapBuffer = glUnmapBuffer; + #else // !GR_GL_DESKTOP + if (major >= 2) {// ES 2.0 supports FBO + exts->GenFramebuffers = glGenFramebuffers; + exts->BindFramebuffer = glBindFramebuffer; + exts->FramebufferTexture2D = glFramebufferTexture2D; + exts->CheckFramebufferStatus = glCheckFramebufferStatus; + exts->DeleteFramebuffers = glDeleteFramebuffers; + exts->RenderbufferStorage = glRenderbufferStorage; + exts->GenRenderbuffers = glGenRenderbuffers; + exts->DeleteRenderbuffers = glDeleteRenderbuffers; + exts->FramebufferRenderbuffer = glFramebufferRenderbuffer; + exts->BindRenderbuffer = glBindRenderbuffer; + } else { + // we require some form of FBO + GrAssert(has_gl_extension("GL_OES_framebuffer_object")); + + GET_PROC(exts, GenFramebuffers, OES); + GET_PROC(exts, BindFramebuffer, OES); + GET_PROC(exts, FramebufferTexture2D, OES); + GET_PROC(exts, CheckFramebufferStatus, OES); + GET_PROC(exts, DeleteFramebuffers, OES); + GET_PROC(exts, RenderbufferStorage, OES); + GET_PROC(exts, GenRenderbuffers, OES); + GET_PROC(exts, DeleteRenderbuffers, OES); + GET_PROC(exts, FramebufferRenderbuffer, OES); + GET_PROC(exts, BindRenderbuffer, OES); + } + if (has_gl_extension("GL_APPLE_framebuffer_multisample")) { + GET_PROC(exts, ResolveMultisampleFramebuffer, APPLE); + } + if (has_gl_extension("GL_IMG_multisampled_render_to_texture")) { + GET_PROC(exts, FramebufferTexture2DMultisample, IMG); + } + if (has_gl_extension("GL_OES_mapbuffer")) { + GET_PROC(exts, MapBuffer, OES); + GET_PROC(exts, UnmapBuffer, OES); + } + #endif // !GR_GL_DESKTOP +#endif // BUILD +} + +bool gPrintGL = true; + |