aboutsummaryrefslogtreecommitdiffhomepage
path: root/gpu/src/GrGpuGL.cpp
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-12-22 21:39:39 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-12-22 21:39:39 +0000
commitac10a2d039c5d52eed66e27cbbc503ab523c1cd5 (patch)
treec5be0c3dd15052016e7d32f376507cb1ea7101dd /gpu/src/GrGpuGL.cpp
parentea8509cd3b1771b36054313d3ccd56679df56044 (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.cpp1824
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;
+