aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/gl
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu/gl')
-rw-r--r--src/gpu/gl/GrGLContextInfo.cpp74
-rw-r--r--src/gpu/gl/GrGLContextInfo.h68
-rw-r--r--src/gpu/gl/GrGLCreateNativeInterface_none.cpp13
-rw-r--r--src/gpu/gl/GrGLCreateNullInterface.cpp506
-rw-r--r--src/gpu/gl/GrGLDefaultInterface_native.cpp13
-rw-r--r--src/gpu/gl/GrGLDefaultInterface_none.cpp13
-rw-r--r--src/gpu/gl/GrGLIRect.h74
-rw-r--r--src/gpu/gl/GrGLIndexBuffer.cpp131
-rw-r--r--src/gpu/gl/GrGLIndexBuffer.h55
-rw-r--r--src/gpu/gl/GrGLInterface.cpp527
-rw-r--r--src/gpu/gl/GrGLProgram.cpp1889
-rw-r--r--src/gpu/gl/GrGLProgram.h390
-rw-r--r--src/gpu/gl/GrGLRenderTarget.cpp97
-rw-r--r--src/gpu/gl/GrGLRenderTarget.h110
-rw-r--r--src/gpu/gl/GrGLSL.cpp82
-rw-r--r--src/gpu/gl/GrGLSL.h76
-rw-r--r--src/gpu/gl/GrGLShaderVar.h304
-rw-r--r--src/gpu/gl/GrGLStencilBuffer.cpp40
-rw-r--r--src/gpu/gl/GrGLStencilBuffer.h60
-rw-r--r--src/gpu/gl/GrGLTexture.cpp90
-rw-r--r--src/gpu/gl/GrGLTexture.h128
-rw-r--r--src/gpu/gl/GrGLUtil.cpp42
-rw-r--r--src/gpu/gl/GrGLVertexBuffer.cpp126
-rw-r--r--src/gpu/gl/GrGLVertexBuffer.h52
-rw-r--r--src/gpu/gl/GrGpuGL.cpp2544
-rw-r--r--src/gpu/gl/GrGpuGL.h365
-rw-r--r--src/gpu/gl/GrGpuGLShaders.cpp1188
-rw-r--r--src/gpu/gl/GrGpuGLShaders.h103
-rw-r--r--src/gpu/gl/SkGLContext.cpp128
-rw-r--r--src/gpu/gl/SkNullGLContext.cpp13
30 files changed, 9301 insertions, 0 deletions
diff --git a/src/gpu/gl/GrGLContextInfo.cpp b/src/gpu/gl/GrGLContextInfo.cpp
new file mode 100644
index 0000000000..e6257e7b89
--- /dev/null
+++ b/src/gpu/gl/GrGLContextInfo.cpp
@@ -0,0 +1,74 @@
+#include "GrGLContextInfo.h"
+
+GrGLContextInfo::~GrGLContextInfo() {
+ GrSafeUnref(fInterface);
+}
+
+GrGLContextInfo::GrGLContextInfo() {
+ this->reset();
+}
+
+GrGLContextInfo::GrGLContextInfo(const GrGLInterface* interface) {
+ fInterface = NULL;
+ this->initialize(interface);
+}
+
+GrGLContextInfo::GrGLContextInfo(const GrGLContextInfo& ctx) {
+ fInterface = NULL;
+ *this = ctx;
+}
+
+GrGLContextInfo& GrGLContextInfo::operator = (const GrGLContextInfo& ctx) {
+ GrSafeAssign(fInterface, ctx.fInterface);
+ fBindingInUse = ctx.fBindingInUse;
+ fGLVersion = ctx.fGLVersion;
+ fGLSLGeneration = ctx.fGLSLGeneration;
+ fExtensionString = ctx.fExtensionString;
+ return *this;
+}
+
+void GrGLContextInfo::reset() {
+ GrSafeSetNull(fInterface);
+ fBindingInUse = kNone_GrGLBinding;
+ fGLVersion = GR_GL_VER(0, 0);
+ fGLSLGeneration = static_cast<GrGLSLGeneration>(0);
+ fExtensionString = "";
+}
+
+bool GrGLContextInfo::initialize(const GrGLInterface* interface) {
+ this->reset();
+ // We haven't validated the GrGLInterface yet, so check for GetString
+ // function pointer
+ if (NULL != interface->fGetString) {
+
+ const GrGLubyte* verUByte;
+ GR_GL_CALL_RET(interface, verUByte, GetString(GR_GL_VERSION));
+ const char* ver = reinterpret_cast<const char*>(verUByte);
+ GrGLBinding binding = GrGLGetBindingInUseFromString(ver);
+
+ if (!interface->validate(fBindingInUse)) {
+
+ fInterface = interface;
+ interface->ref();
+
+ fBindingInUse = binding;
+
+ fGLVersion = GrGLGetVersionFromString(ver);
+
+ fGLSLGeneration = GrGetGLSLGeneration(fBindingInUse,
+ this->interface());
+
+ const GrGLubyte* ext;
+ GR_GL_CALL_RET(interface, ext, GetString(GR_GL_EXTENSIONS));
+ fExtensionString = reinterpret_cast<const char*>(ext);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GrGLContextInfo::isInitialized() const {
+ return kNone_GrGLBinding != fBindingInUse;
+}
+
diff --git a/src/gpu/gl/GrGLContextInfo.h b/src/gpu/gl/GrGLContextInfo.h
new file mode 100644
index 0000000000..cac8959861
--- /dev/null
+++ b/src/gpu/gl/GrGLContextInfo.h
@@ -0,0 +1,68 @@
+#include "GrGLInterface.h"
+#include "GrGLSL.h"
+
+#include "SkString.h"
+
+/**
+ * Encapsulates information about an OpenGL context including the GrGLInterface
+ * used to make GL calls, the OpenGL version, the GrGLBinding type of the
+ * context, and GLSL version.
+ */
+class GrGLContextInfo {
+public:
+
+ /**
+ * Default constructor, creates an uninitialized GrGLContextInfo
+ */
+ GrGLContextInfo();
+
+ /**
+ * Creates a GrGLContextInfo from a GrGLInterface and the currently
+ * bound OpenGL context accesible by the GrGLInterface.
+ */
+ GrGLContextInfo(const GrGLInterface* interface);
+
+ /**
+ * Copies a GrGLContextInfo
+ */
+ GrGLContextInfo(const GrGLContextInfo& ctx);
+
+ ~GrGLContextInfo();
+
+ /**
+ * Copies a GrGLContextInfo
+ */
+ GrGLContextInfo& operator = (const GrGLContextInfo& ctx);
+
+ /**
+ * Initializes a GrGLContextInfo from a GrGLInterface and the currently
+ * bound OpenGL context accessible by the GrGLInterface.
+ */
+ bool initialize(const GrGLInterface* interface);
+ bool isInitialized() const;
+
+ const GrGLInterface* interface() const { return fInterface; }
+ GrGLBinding binding() const { return fBindingInUse; }
+ GrGLVersion version() const { return fGLVersion; }
+ GrGLSLGeneration glslGeneration() const { return fGLSLGeneration; }
+
+ /**
+ * Checks for extension support using a cached copy of the GL_EXTENSIONS
+ * string.
+ */
+ bool hasExtension(const char* ext) const {
+ if (!this->isInitialized()) {
+ return false;
+ }
+ return GrGLHasExtensionFromString(ext, fExtensionString.c_str());
+ }
+
+private:
+ void reset();
+
+ const GrGLInterface* fInterface;
+ GrGLBinding fBindingInUse;
+ GrGLVersion fGLVersion;
+ GrGLSLGeneration fGLSLGeneration;
+ SkString fExtensionString;
+};
diff --git a/src/gpu/gl/GrGLCreateNativeInterface_none.cpp b/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
new file mode 100644
index 0000000000..7de59126ce
--- /dev/null
+++ b/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLInterface.h"
+
+const GrGLInterface* GrGLCreateNativeInterface() {
+ return NULL;
+}
diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
new file mode 100644
index 0000000000..cdac75f553
--- /dev/null
+++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
@@ -0,0 +1,506 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLInterface.h"
+#include "../GrTDArray.h"
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLActiveTexture(GrGLenum texture) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLAttachShader(GrGLuint program, GrGLuint shader) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBeginQuery(GrGLenum target, GrGLuint id) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindAttribLocation(GrGLuint program, GrGLuint index, const char* name) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindTexture(GrGLenum target, GrGLuint texture) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlendColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFragDataLocation(GrGLuint program, GrGLuint colorNumber, const GrGLchar* name) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlendFunc(GrGLenum sfactor, GrGLenum dfactor) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferSubData(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid* data) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClear(GrGLbitfield mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClearColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClearStencil(GrGLint s) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLColorMask(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCompileShader(GrGLuint shader) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCompressedTexImage2D(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCullFace(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDepthMask(GrGLboolean flag) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDisable(GrGLenum cap) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDisableVertexAttribArray(GrGLuint index) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawArrays(GrGLenum mode, GrGLint first, GrGLsizei count) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawBuffer(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawBuffers(GrGLsizei n, const GrGLenum* bufs) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawElements(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEnable(GrGLenum cap) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEnableVertexAttribArray(GrGLuint index) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEndQuery(GrGLenum target) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFinish() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFlush() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFrontFace(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLLineWidth(GrGLfloat width) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLLinkProgram(GrGLuint program) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLPixelStorei(GrGLenum pname, GrGLint param) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLQueryCounter(GrGLuint id, GrGLenum target) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadBuffer(GrGLenum src) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadPixels(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLScissor(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLShaderSource(GrGLuint shader, GrGLsizei count, const char** str, const GrGLint* length) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilFunc(GrGLenum func, GrGLint ref, GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilFuncSeparate(GrGLenum face, GrGLenum func, GrGLint ref, GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilMask(GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilMaskSeparate(GrGLenum face, GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilOp(GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilOpSeparate(GrGLenum face, GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexImage2D(GrGLenum target, GrGLint level, GrGLint internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexParameteri(GrGLenum target, GrGLenum pname, GrGLint param) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexStorage2D(GrGLenum target, GrGLsizei levels, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1f(GrGLint location, GrGLfloat v0) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1i(GrGLint location, GrGLint v0) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2f(GrGLint location, GrGLfloat v0, GrGLfloat v1) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2i(GrGLint location, GrGLint v0, GrGLint v1) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2, GrGLfloat v3) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix2fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix3fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix4fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUseProgram(GrGLuint program) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLVertexAttrib4fv(GrGLuint indx, const GrGLfloat* values) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLVertexAttribPointer(GrGLuint indx, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const GrGLvoid* ptr) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLViewport(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFramebuffer(GrGLenum target, GrGLuint framebuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteFramebuffers(GrGLsizei n, const GrGLuint *framebuffers) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteRenderbuffers(GrGLsizei n, const GrGLuint *renderbuffers) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferRenderbuffer(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetFramebufferAttachmentParameteriv(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetRenderbufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLRenderbufferStorage(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLRenderbufferStorageMultisample(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlitFramebuffer(GrGLint srcX0, GrGLint srcY0, GrGLint srcX1, GrGLint srcY1, GrGLint dstX0, GrGLint dstY0, GrGLint dstX1, GrGLint dstY1, GrGLbitfield mask, GrGLenum filter) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLResolveMultisampleFramebuffer() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFragDataLocationIndexed(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name) {}
+
+GrGLenum GR_GL_FUNCTION_TYPE nullGLCheckFramebufferStatus(GrGLenum target) {
+ return GR_GL_FRAMEBUFFER_COMPLETE;
+}
+
+GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateProgram() {
+ static int gCurrID = 0;
+ return ++gCurrID;
+}
+
+GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateShader(GrGLenum type) {
+ static int gCurrID = 0;
+ return ++gCurrID;
+}
+
+// same delete used for shaders and programs
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDelete(GrGLuint program) {
+}
+
+// same function used for all glGen*(GLsize i, GLuint*) functions
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGenIds(GrGLsizei n, GrGLuint* ids) {
+ static int gCurrID = 0;
+ for (int i = 0; i < n; ++i) {
+ ids[i] = ++gCurrID;
+ }
+}
+// same delete function for all glDelete*(GLsize i, const GLuint*) except buffers
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteIds(GrGLsizei n, const GrGLuint* ids) {}
+
+// In debug builds we do asserts that ensure we agree with GL about when a buffer
+// is mapped.
+static GrTDArray<GrGLuint> gMappedBuffers;
+static GrGLuint gCurrArrayBuffer;
+static GrGLuint gCurrElementArrayBuffer;
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindBuffer(GrGLenum target, GrGLuint buffer) {
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ gCurrArrayBuffer = buffer;
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ gCurrElementArrayBuffer = buffer;
+ break;
+ }
+}
+
+// deleting a bound buffer has the side effect of binding 0
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteBuffers(GrGLsizei n, const GrGLuint* ids) {
+ for (int i = 0; i < n; ++i) {
+ if (ids[i] == gCurrArrayBuffer) {
+ gCurrArrayBuffer = 0;
+ }
+ if (ids[i] == gCurrElementArrayBuffer) {
+ gCurrElementArrayBuffer = 0;
+ }
+ for (int j = 0; j < gMappedBuffers.count(); ++j) {
+ if (gMappedBuffers[j] == ids[i]) {
+ gMappedBuffers.remove(j);
+ // don't break b/c we didn't check for dupes on insert
+ --j;
+ }
+ }
+ }
+}
+
+GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) {
+ // We just reserve 32MB of RAM for all locks and hope its big enough
+ static SkAutoMalloc gBufferData(32 * (1 << 20));
+ GrGLuint buf = 0;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buf = gCurrArrayBuffer;
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buf = gCurrElementArrayBuffer;
+ break;
+ }
+ if (buf) {
+ *gMappedBuffers.append() = buf;
+ }
+ return gBufferData.get();
+}
+
+GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) {
+ GrGLuint buf = 0;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buf = gCurrArrayBuffer;
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buf = gCurrElementArrayBuffer;
+ break;
+ }
+ if (buf) {
+ for (int i = 0; i < gMappedBuffers.count(); ++i) {
+ if (gMappedBuffers[i] == buf) {
+ gMappedBuffers.remove(i);
+ // don't break b/c we didn't check for dupes on insert
+ --i;
+ }
+ }
+ }
+ return GR_GL_TRUE;
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {
+ switch (pname) {
+ case GR_GL_BUFFER_MAPPED: {
+ *params = GR_GL_FALSE;
+ GrGLuint buf = 0;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buf = gCurrArrayBuffer;
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buf = gCurrElementArrayBuffer;
+ break;
+ }
+ if (buf) {
+ for (int i = 0; i < gMappedBuffers.count(); ++i) {
+ if (gMappedBuffers[i] == buf) {
+ *params = GR_GL_TRUE;
+ break;
+ }
+ }
+ }
+ break; }
+ default:
+ GrCrash("Unexpected pname to GetBufferParamateriv");
+ break;
+ }
+};
+
+GrGLenum GR_GL_FUNCTION_TYPE nullGLGetError() {
+ return GR_GL_NO_ERROR;
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetIntegerv(GrGLenum pname, GrGLint* params) {
+ switch (pname) {
+ case GR_GL_STENCIL_BITS:
+ *params = 8;
+ break;
+ case GR_GL_SAMPLES:
+ *params = 1;
+ break;
+ case GR_GL_FRAMEBUFFER_BINDING:
+ *params = 0;
+ break;
+ case GR_GL_VIEWPORT:
+ params[0] = 0;
+ params[1] = 0;
+ params[2] = 800;
+ params[3] = 600;
+ break;
+ case GR_GL_MAX_TEXTURE_IMAGE_UNITS:
+ *params = 8;
+ break;
+ case GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
+ *params = 16;
+ break;
+ case GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS:
+ *params = 16 * 4;
+ break;
+ case GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS:
+ *params = 0;
+ break;
+ case GR_GL_COMPRESSED_TEXTURE_FORMATS:
+ break;
+ case GR_GL_MAX_TEXTURE_SIZE:
+ *params = 8192;
+ break;
+ case GR_GL_MAX_RENDERBUFFER_SIZE:
+ *params = 8192;
+ break;
+ case GR_GL_MAX_SAMPLES:
+ *params = 32;
+ break;
+ case GR_GL_MAX_VERTEX_ATTRIBS:
+ *params = 16;
+ break;
+ case GR_GL_MAX_TEXTURE_UNITS:
+ *params = 8;
+ break;
+ default:
+ GrCrash("Unexpected pname to GetIntegerv");
+ }
+}
+// used for both the program and shader info logs
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, char* infolog) {
+ if (length) {
+ *length = 0;
+ }
+ if (bufsize > 0) {
+ *infolog = 0;
+ }
+}
+
+// used for both the program and shader params
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetShaderOrProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) {
+ switch (pname) {
+ case GR_GL_LINK_STATUS: // fallthru
+ case GR_GL_COMPILE_STATUS:
+ *params = GR_GL_TRUE;
+ break;
+ case GR_GL_INFO_LOG_LENGTH:
+ *params = 0;
+ break;
+ // we don't expect any other pnames
+ default:
+ GrCrash("Unexpected pname to GetProgramiv");
+ break;
+ }
+}
+
+namespace {
+template <typename T>
+void query_result(GrGLenum GLtarget, GrGLenum pname, T *params) {
+ switch (pname) {
+ case GR_GL_QUERY_RESULT_AVAILABLE:
+ *params = GR_GL_TRUE;
+ break;
+ case GR_GL_QUERY_RESULT:
+ *params = 0;
+ break;
+ default:
+ GrCrash("Unexpected pname passed to GetQueryObject.");
+ break;
+ }
+}
+}
+
+// Queries on the null GL just don't do anything at all. We could potentially make
+// the timers work.
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) {
+ switch (pname) {
+ case GR_GL_CURRENT_QUERY:
+ *params = 0;
+ break;
+ case GR_GL_QUERY_COUNTER_BITS:
+ *params = 32;
+ break;
+ default:
+ GrCrash("Unexpected pname passed GetQueryiv.");
+ }
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) {
+ query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) {
+ query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) {
+ query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) {
+ query_result(id, pname, params);
+}
+
+const GrGLubyte* GR_GL_FUNCTION_TYPE nullGLGetString(GrGLenum name) {
+ switch (name) {
+ case GR_GL_EXTENSIONS:
+ return (const GrGLubyte*)"GL_ARB_framebuffer_object GL_ARB_blend_func_extended GL_ARB_timer_query GL_ARB_draw_buffers GL_ARB_occlusion_query GL_EXT_blend_color GL_EXT_stencil_wrap";
+ case GR_GL_VERSION:
+ return (const GrGLubyte*)"4.0 Null GL";
+ case GR_GL_SHADING_LANGUAGE_VERSION:
+ return (const GrGLubyte*)"4.20.8 Null GLSL";
+ default:
+ GrCrash("Unexpected name to GetString");
+ return NULL;
+ }
+}
+
+// we used to use this to query stuff about externally created textures, now we just
+// require clients to tell us everything about the texture.
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetTexLevelParameteriv(GrGLenum target, GrGLint level, GrGLenum pname, GrGLint* params) {
+ GrCrash("Should never query texture parameters.");
+}
+
+GrGLint GR_GL_FUNCTION_TYPE nullGLGetUniformLocation(GrGLuint program, const char* name) {
+ static int gUniLocation = 0;
+ return ++gUniLocation;
+}
+
+const GrGLInterface* GrGLCreateNullInterface() {
+ // The gl functions are not context-specific so we create one global
+ // interface
+ static SkAutoTUnref<GrGLInterface> glInterface;
+ if (!glInterface.get()) {
+ GrGLInterface* interface = new GrGLInterface;
+ glInterface.reset(interface);
+ interface->fBindingsExported = kDesktop_GrGLBinding;
+ interface->fActiveTexture = nullGLActiveTexture;
+ interface->fAttachShader = nullGLAttachShader;
+ interface->fBeginQuery = nullGLBeginQuery;
+ interface->fBindAttribLocation = nullGLBindAttribLocation;
+ interface->fBindBuffer = nullGLBindBuffer;
+ interface->fBindFragDataLocation = nullGLBindFragDataLocation;
+ interface->fBindTexture = nullGLBindTexture;
+ interface->fBlendColor = nullGLBlendColor;
+ interface->fBlendFunc = nullGLBlendFunc;
+ interface->fBufferData = nullGLBufferData;
+ interface->fBufferSubData = nullGLBufferSubData;
+ interface->fClear = nullGLClear;
+ interface->fClearColor = nullGLClearColor;
+ interface->fClearStencil = nullGLClearStencil;
+ interface->fColorMask = nullGLColorMask;
+ interface->fCompileShader = nullGLCompileShader;
+ interface->fCompressedTexImage2D = nullGLCompressedTexImage2D;
+ interface->fCreateProgram = nullGLCreateProgram;
+ interface->fCreateShader = nullGLCreateShader;
+ interface->fCullFace = nullGLCullFace;
+ interface->fDeleteBuffers = nullGLDeleteBuffers;
+ interface->fDeleteProgram = nullGLDelete;
+ interface->fDeleteQueries = nullGLDeleteIds;
+ interface->fDeleteShader = nullGLDelete;
+ interface->fDeleteTextures = nullGLDeleteIds;
+ interface->fDepthMask = nullGLDepthMask;
+ interface->fDisable = nullGLDisable;
+ interface->fDisableVertexAttribArray = nullGLDisableVertexAttribArray;
+ interface->fDrawArrays = nullGLDrawArrays;
+ interface->fDrawBuffer = nullGLDrawBuffer;
+ interface->fDrawBuffers = nullGLDrawBuffers;
+ interface->fDrawElements = nullGLDrawElements;
+ interface->fEnable = nullGLEnable;
+ interface->fEnableVertexAttribArray = nullGLEnableVertexAttribArray;
+ interface->fEndQuery = nullGLEndQuery;
+ interface->fFinish = nullGLFinish;
+ interface->fFlush = nullGLFlush;
+ interface->fFrontFace = nullGLFrontFace;
+ interface->fGenBuffers = nullGLGenIds;
+ interface->fGenQueries = nullGLGenIds;
+ interface->fGenTextures = nullGLGenIds;
+ interface->fGetBufferParameteriv = nullGLGetBufferParameteriv;
+ interface->fGetError = nullGLGetError;
+ interface->fGetIntegerv = nullGLGetIntegerv;
+ interface->fGetQueryObjecti64v = nullGLGetQueryObjecti64v;
+ interface->fGetQueryObjectiv = nullGLGetQueryObjectiv;
+ interface->fGetQueryObjectui64v = nullGLGetQueryObjectui64v;
+ interface->fGetQueryObjectuiv = nullGLGetQueryObjectuiv;
+ interface->fGetQueryiv = nullGLGetQueryiv;
+ interface->fGetProgramInfoLog = nullGLGetInfoLog;
+ interface->fGetProgramiv = nullGLGetShaderOrProgramiv;
+ interface->fGetShaderInfoLog = nullGLGetInfoLog;
+ interface->fGetShaderiv = nullGLGetShaderOrProgramiv;
+ interface->fGetString = nullGLGetString;
+ interface->fGetTexLevelParameteriv = nullGLGetTexLevelParameteriv;
+ interface->fGetUniformLocation = nullGLGetUniformLocation;
+ interface->fLineWidth = nullGLLineWidth;
+ interface->fLinkProgram = nullGLLinkProgram;
+ interface->fPixelStorei = nullGLPixelStorei;
+ interface->fQueryCounter = nullGLQueryCounter;
+ interface->fReadBuffer = nullGLReadBuffer;
+ interface->fReadPixels = nullGLReadPixels;
+ interface->fScissor = nullGLScissor;
+ interface->fShaderSource = nullGLShaderSource;
+ interface->fStencilFunc = nullGLStencilFunc;
+ interface->fStencilFuncSeparate = nullGLStencilFuncSeparate;
+ interface->fStencilMask = nullGLStencilMask;
+ interface->fStencilMaskSeparate = nullGLStencilMaskSeparate;
+ interface->fStencilOp = nullGLStencilOp;
+ interface->fStencilOpSeparate = nullGLStencilOpSeparate;
+ interface->fTexImage2D = nullGLTexImage2D;
+ interface->fTexParameteri = nullGLTexParameteri;
+ interface->fTexSubImage2D = nullGLTexSubImage2D;
+ interface->fTexStorage2D = nullGLTexStorage2D;
+ interface->fUniform1f = nullGLUniform1f;
+ interface->fUniform1i = nullGLUniform1i;
+ interface->fUniform1fv = nullGLUniform1fv;
+ interface->fUniform1iv = nullGLUniform1iv;
+ interface->fUniform2f = nullGLUniform2f;
+ interface->fUniform2i = nullGLUniform2i;
+ interface->fUniform2fv = nullGLUniform2fv;
+ interface->fUniform2iv = nullGLUniform2iv;
+ interface->fUniform3f = nullGLUniform3f;
+ interface->fUniform3i = nullGLUniform3i;
+ interface->fUniform3fv = nullGLUniform3fv;
+ interface->fUniform3iv = nullGLUniform3iv;
+ interface->fUniform4f = nullGLUniform4f;
+ interface->fUniform4i = nullGLUniform4i;
+ interface->fUniform4fv = nullGLUniform4fv;
+ interface->fUniform4iv = nullGLUniform4iv;
+ interface->fUniformMatrix2fv = nullGLUniformMatrix2fv;
+ interface->fUniformMatrix3fv = nullGLUniformMatrix3fv;
+ interface->fUniformMatrix4fv = nullGLUniformMatrix4fv;
+ interface->fUseProgram = nullGLUseProgram;
+ interface->fVertexAttrib4fv = nullGLVertexAttrib4fv;
+ interface->fVertexAttribPointer = nullGLVertexAttribPointer;
+ interface->fViewport = nullGLViewport;
+ interface->fBindFramebuffer = nullGLBindFramebuffer;
+ interface->fBindRenderbuffer = nullGLBindRenderbuffer;
+ interface->fCheckFramebufferStatus = nullGLCheckFramebufferStatus;
+ interface->fDeleteFramebuffers = nullGLDeleteFramebuffers;
+ interface->fDeleteRenderbuffers = nullGLDeleteRenderbuffers;
+ interface->fFramebufferRenderbuffer = nullGLFramebufferRenderbuffer;
+ interface->fFramebufferTexture2D = nullGLFramebufferTexture2D;
+ interface->fGenFramebuffers = nullGLGenIds;
+ interface->fGenRenderbuffers = nullGLGenIds;
+ interface->fGetFramebufferAttachmentParameteriv = nullGLGetFramebufferAttachmentParameteriv;
+ interface->fGetRenderbufferParameteriv = nullGLGetRenderbufferParameteriv;
+ interface->fRenderbufferStorage = nullGLRenderbufferStorage;
+ interface->fRenderbufferStorageMultisample = nullGLRenderbufferStorageMultisample;
+ interface->fBlitFramebuffer = nullGLBlitFramebuffer;
+ interface->fResolveMultisampleFramebuffer = nullGLResolveMultisampleFramebuffer;
+ interface->fMapBuffer = nullGLMapBuffer;
+ interface->fUnmapBuffer = nullGLUnmapBuffer;
+ interface->fBindFragDataLocationIndexed = nullGLBindFragDataLocationIndexed;
+ }
+ glInterface.get()->ref();
+ return glInterface.get();
+}
diff --git a/src/gpu/gl/GrGLDefaultInterface_native.cpp b/src/gpu/gl/GrGLDefaultInterface_native.cpp
new file mode 100644
index 0000000000..7b8b84a9a8
--- /dev/null
+++ b/src/gpu/gl/GrGLDefaultInterface_native.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLInterface.h"
+
+const GrGLInterface* GrGLDefaultInterface() {
+ return GrGLCreateNativeInterface();
+}
diff --git a/src/gpu/gl/GrGLDefaultInterface_none.cpp b/src/gpu/gl/GrGLDefaultInterface_none.cpp
new file mode 100644
index 0000000000..2cca135c0a
--- /dev/null
+++ b/src/gpu/gl/GrGLDefaultInterface_none.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLInterface.h"
+
+const GrGLInterface* GrGLDefaultInterface() {
+ return NULL;
+}
diff --git a/src/gpu/gl/GrGLIRect.h b/src/gpu/gl/GrGLIRect.h
new file mode 100644
index 0000000000..e94fa21a5a
--- /dev/null
+++ b/src/gpu/gl/GrGLIRect.h
@@ -0,0 +1,74 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGLIRect_DEFINED
+#define GrGLIRect_DEFINED
+
+#include "GrGLInterface.h"
+
+/**
+ * Helper struct for dealing with the fact that Ganesh and GL use different
+ * window coordinate systems (top-down vs bottom-up)
+ */
+struct GrGLIRect {
+ GrGLint fLeft;
+ GrGLint fBottom;
+ GrGLsizei fWidth;
+ GrGLsizei fHeight;
+
+ void pushToGLViewport(const GrGLInterface* gl) const {
+ GR_GL_CALL(gl, Viewport(fLeft, fBottom, fWidth, fHeight));
+ }
+
+ void pushToGLScissor(const GrGLInterface* gl) const {
+ GR_GL_CALL(gl, Scissor(fLeft, fBottom, fWidth, fHeight));
+ }
+
+ void setFromGLViewport(const GrGLInterface* gl) {
+ GR_STATIC_ASSERT(sizeof(GrGLIRect) == 4*sizeof(GrGLint));
+ GR_GL_GetIntegerv(gl, GR_GL_VIEWPORT, (GrGLint*) this);
+ }
+
+ // sometimes we have a GrIRect from the client that we
+ // want to simultaneously make relative to GL's viewport
+ // and convert from top-down to bottom-up.
+ void setRelativeTo(const GrGLIRect& glRect,
+ int leftOffset,
+ int topOffset,
+ int width,
+ int height) {
+ fLeft = glRect.fLeft + leftOffset;
+ fWidth = width;
+ fBottom = glRect.fBottom + (glRect.fHeight - topOffset - height);
+ fHeight = height;
+
+ GrAssert(fLeft >= 0);
+ GrAssert(fWidth >= 0);
+ GrAssert(fBottom >= 0);
+ GrAssert(fHeight >= 0);
+ }
+
+ bool contains(const GrGLIRect& glRect) const {
+ return fLeft <= glRect.fLeft &&
+ fBottom <= glRect.fBottom &&
+ fLeft + fWidth >= glRect.fLeft + glRect.fWidth &&
+ fBottom + fHeight >= glRect.fBottom + glRect.fHeight;
+ }
+
+ void invalidate() {fLeft = fWidth = fBottom = fHeight = -1;}
+
+ bool operator ==(const GrGLIRect& glRect) const {
+ return 0 == memcmp(this, &glRect, sizeof(GrGLIRect));
+ }
+
+ bool operator !=(const GrGLIRect& glRect) const {return !(*this == glRect);}
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLIndexBuffer.cpp b/src/gpu/gl/GrGLIndexBuffer.cpp
new file mode 100644
index 0000000000..b64668ede2
--- /dev/null
+++ b/src/gpu/gl/GrGLIndexBuffer.cpp
@@ -0,0 +1,131 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#include "GrGLIndexBuffer.h"
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+GrGLIndexBuffer::GrGLIndexBuffer(GrGpuGL* gpu,
+ GrGLuint id,
+ size_t sizeInBytes,
+ bool dynamic)
+ : INHERITED(gpu, sizeInBytes, dynamic)
+ , fBufferID(id)
+ , fLockPtr(NULL) {
+
+}
+
+void GrGLIndexBuffer::onRelease() {
+ // make sure we've not been abandoned
+ if (fBufferID) {
+ GPUGL->notifyIndexBufferDelete(this);
+ GL_CALL(DeleteBuffers(1, &fBufferID));
+ fBufferID = 0;
+ }
+}
+
+void GrGLIndexBuffer::onAbandon() {
+ fBufferID = 0;
+ fLockPtr = NULL;
+}
+
+void GrGLIndexBuffer::bind() const {
+ GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, fBufferID));
+ GPUGL->notifyIndexBufferBind(this);
+}
+
+GrGLuint GrGLIndexBuffer::bufferID() const {
+ return fBufferID;
+}
+
+void* GrGLIndexBuffer::lock() {
+ GrAssert(fBufferID);
+ GrAssert(!isLocked());
+ if (this->getGpu()->getCaps().fBufferLockSupport) {
+ this->bind();
+ // Let driver know it can discard the old data
+ GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+ this->sizeInBytes(),
+ NULL,
+ this->dynamic() ? GR_GL_DYNAMIC_DRAW :
+ GR_GL_STATIC_DRAW));
+ GR_GL_CALL_RET(GPUGL->glInterface(),
+ fLockPtr,
+ MapBuffer(GR_GL_ELEMENT_ARRAY_BUFFER,
+ GR_GL_WRITE_ONLY));
+
+ return fLockPtr;
+ }
+ return NULL;
+}
+
+void* GrGLIndexBuffer::lockPtr() const {
+ return fLockPtr;
+}
+
+void GrGLIndexBuffer::unlock() {
+ GrAssert(fBufferID);
+ GrAssert(isLocked());
+ GrAssert(this->getGpu()->getCaps().fBufferLockSupport);
+
+ this->bind();
+ GL_CALL(UnmapBuffer(GR_GL_ELEMENT_ARRAY_BUFFER));
+ fLockPtr = NULL;
+}
+
+bool GrGLIndexBuffer::isLocked() const {
+#if GR_DEBUG
+ if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
+ this->bind();
+ GrGLint mapped;
+ GL_CALL(GetBufferParameteriv(GR_GL_ELEMENT_ARRAY_BUFFER,
+ GR_GL_BUFFER_MAPPED, &mapped));
+ GrAssert(!!mapped == !!fLockPtr);
+ }
+#endif
+ return NULL != fLockPtr;
+}
+
+bool GrGLIndexBuffer::updateData(const void* src, size_t srcSizeInBytes) {
+ GrAssert(fBufferID);
+ GrAssert(!isLocked());
+ if (srcSizeInBytes > this->sizeInBytes()) {
+ return false;
+ }
+ this->bind();
+ GrGLenum usage = dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW;
+#if !GR_GL_USE_BUFFER_DATA_NULL_HINT
+ // Note that we're cheating on the size here. Currently no methods
+ // allow a partial update that preserves contents of non-updated
+ // portions of the buffer (and lock() does a glBufferData(..size, NULL..))
+ GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER, srcSizeInBytes, src, usage));
+#else
+ if (this->sizeInBytes() == srcSizeInBytes) {
+ GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+ srcSizeInBytes, src, usage));
+ } else {
+ // Before we call glBufferSubData we give the driver a hint using
+ // glBufferData with NULL. This makes the old buffer contents
+ // inaccessible to future draws. The GPU may still be processing draws
+ // that reference the old contents. With this hint it can assign a
+ // different allocation for the new contents to avoid flushing the gpu
+ // past draws consuming the old contents.
+ GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+ this->sizeInBytes(), NULL, usage));
+ GL_CALL(BufferSubData(GR_GL_ELEMENT_ARRAY_BUFFER,
+ 0, srcSizeInBytes, src));
+ }
+#endif
+ return true;
+}
+
diff --git a/src/gpu/gl/GrGLIndexBuffer.h b/src/gpu/gl/GrGLIndexBuffer.h
new file mode 100644
index 0000000000..9f32890b28
--- /dev/null
+++ b/src/gpu/gl/GrGLIndexBuffer.h
@@ -0,0 +1,55 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGLIndexBuffer_DEFINED
+#define GrGLIndexBuffer_DEFINED
+
+#include "../GrIndexBuffer.h"
+#include "GrGLInterface.h"
+
+class GrGpuGL;
+
+class GrGLIndexBuffer : public GrIndexBuffer {
+
+public:
+
+ virtual ~GrGLIndexBuffer() { this->release(); }
+
+ GrGLuint bufferID() const;
+
+ // overrides of GrIndexBuffer
+ virtual void* lock();
+ virtual void* lockPtr() const;
+ virtual void unlock();
+ virtual bool isLocked() const;
+ virtual bool updateData(const void* src, size_t srcSizeInBytes);
+
+protected:
+ GrGLIndexBuffer(GrGpuGL* gpu,
+ GrGLuint id,
+ size_t sizeInBytes,
+ bool dynamic);
+
+ // overrides of GrResource
+ virtual void onAbandon();
+ virtual void onRelease();
+
+private:
+ void bind() const;
+
+ GrGLuint fBufferID;
+ void* fLockPtr;
+
+ friend class GrGpuGL;
+
+ typedef GrIndexBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
new file mode 100644
index 0000000000..c1ac088b5f
--- /dev/null
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -0,0 +1,527 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrTypes.h"
+#include "GrGLInterface.h"
+#include "GrGLDefines.h"
+
+#include <stdio.h>
+
+#if GR_GL_PER_GL_FUNC_CALLBACK
+namespace {
+void GrGLDefaultInterfaceCallback(const GrGLInterface*) {}
+}
+#endif
+
+GrGLBinding GrGLGetBindingInUseFromString(const char* versionString) {
+ if (NULL == versionString) {
+ GrAssert(!"NULL GL version string.");
+ return kNone_GrGLBinding;
+ }
+
+ int major, minor;
+
+ // check for desktop
+ int n = sscanf(versionString, "%d.%d", &major, &minor);
+ if (2 == n) {
+ return kDesktop_GrGLBinding;
+ }
+
+ // check for ES 1
+ char profile[2];
+ n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
+ &major, &minor);
+ if (4 == n) {
+ // we no longer support ES1.
+ return kNone_GrGLBinding;
+ }
+
+ // check for ES2
+ n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
+ if (2 == n) {
+ return kES2_GrGLBinding;
+ }
+ return kNone_GrGLBinding;
+}
+
+GrGLVersion GrGLGetVersionFromString(const char* versionString) {
+ if (NULL == versionString) {
+ GrAssert(!"NULL GL version string.");
+ return 0;
+ }
+
+ int major, minor;
+
+ int n = sscanf(versionString, "%d.%d", &major, &minor);
+ if (2 == n) {
+ return GR_GL_VER(major, minor);
+ }
+
+ char profile[2];
+ n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
+ &major, &minor);
+ if (4 == n) {
+ return GR_GL_VER(major, minor);
+ }
+
+ n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
+ if (2 == n) {
+ return GR_GL_VER(major, minor);
+ }
+
+ return 0;
+}
+
+GrGLSLVersion GrGLGetGLSLVersionFromString(const char* versionString) {
+ if (NULL == versionString) {
+ GrAssert(!"NULL GLSL version string.");
+ return 0;
+ }
+
+ int major, minor;
+
+ int n = sscanf(versionString, "%d.%d", &major, &minor);
+ if (2 == n) {
+ return GR_GLSL_VER(major, minor);
+ }
+
+ n = sscanf(versionString, "OpenGL ES GLSL ES %d.%d", &major, &minor);
+ if (2 == n) {
+ return GR_GLSL_VER(major, minor);
+ }
+ return 0;
+}
+
+bool GrGLHasExtensionFromString(const char* ext, const char* extensionString) {
+ int extLength = strlen(ext);
+
+ while (true) {
+ int n = strcspn(extensionString, " ");
+ if (n == extLength && 0 == strncmp(ext, extensionString, n)) {
+ return true;
+ }
+ if (0 == extensionString[n]) {
+ return false;
+ }
+ extensionString += n+1;
+ }
+
+ return false;
+}
+
+bool GrGLHasExtension(const GrGLInterface* gl, const char* ext) {
+ const GrGLubyte* glstr;
+ GR_GL_CALL_RET(gl, glstr, GetString(GR_GL_EXTENSIONS));
+ return GrGLHasExtensionFromString(ext, (const char*) glstr);
+}
+
+GrGLBinding GrGLGetBindingInUse(const GrGLInterface* gl) {
+ const GrGLubyte* v;
+ GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
+ return GrGLGetBindingInUseFromString((const char*) v);
+}
+
+GrGLVersion GrGLGetVersion(const GrGLInterface* gl) {
+ const GrGLubyte* v;
+ GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
+ return GrGLGetVersionFromString((const char*) v);
+}
+
+GrGLSLVersion GrGLGetGLSLVersion(const GrGLInterface* gl) {
+ const GrGLubyte* v;
+ GR_GL_CALL_RET(gl, v, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
+ return GrGLGetGLSLVersionFromString((const char*) v);
+}
+
+GrGLInterface::GrGLInterface() {
+ fBindingsExported = kNone_GrGLBinding;
+
+ fActiveTexture = NULL;
+ fAttachShader = NULL;
+ fBeginQuery = NULL;
+ fBindAttribLocation = NULL;
+ fBindBuffer = NULL;
+ fBindFragDataLocation = NULL;
+ fBindTexture = NULL;
+ fBlendColor = NULL;
+ fBlendFunc = NULL;
+ fBufferData = NULL;
+ fBufferSubData = NULL;
+ fClear = NULL;
+ fClearColor = NULL;
+ fClearStencil = NULL;
+ fColorMask = NULL;
+ fColorPointer = NULL;
+ fCompileShader = NULL;
+ fCompressedTexImage2D = NULL;
+ fCreateProgram = NULL;
+ fCreateShader = NULL;
+ fCullFace = NULL;
+ fDeleteBuffers = NULL;
+ fDeleteProgram = NULL;
+ fDeleteQueries = NULL;
+ fDeleteShader = NULL;
+ fDeleteTextures = NULL;
+ fDepthMask = NULL;
+ fDisable = NULL;
+ fDisableVertexAttribArray = NULL;
+ fDrawArrays = NULL;
+ fDrawBuffer = NULL;
+ fDrawBuffers = NULL;
+ fDrawElements = NULL;
+ fEndQuery = NULL;
+ fFinish = NULL;
+ fFlush = NULL;
+ fEnable = NULL;
+ fEnableVertexAttribArray = NULL;
+ fFrontFace = NULL;
+ fGenBuffers = NULL;
+ fGenQueries = NULL;
+ fGenTextures = NULL;
+ fGetBufferParameteriv = NULL;
+ fGetError = NULL;
+ fGetIntegerv = NULL;
+ fGetQueryiv = NULL;
+ fGetQueryObjecti64v = NULL;
+ fGetQueryObjectiv = NULL;
+ fGetQueryObjectui64v = NULL;
+ fGetQueryObjectuiv = NULL;
+ fGetProgramInfoLog = NULL;
+ fGetProgramiv = NULL;
+ fGetShaderInfoLog = NULL;
+ fGetShaderiv = NULL;
+ fGetString = NULL;
+ fGetTexLevelParameteriv = NULL;
+ fGetUniformLocation = NULL;
+ fLineWidth = NULL;
+ fLinkProgram = NULL;
+ fPixelStorei = NULL;
+ fQueryCounter = NULL;
+ fReadBuffer = NULL;
+ fReadPixels = NULL;
+ fScissor = NULL;
+ fShaderSource = NULL;
+ fStencilFunc = NULL;
+ fStencilFuncSeparate = NULL;
+ fStencilMask = NULL;
+ fStencilMaskSeparate = NULL;
+ fStencilOp = NULL;
+ fStencilOpSeparate = NULL;
+ fTexImage2D = NULL;
+ fTexParameteri = NULL;
+ fTexStorage2D = NULL;
+ fTexSubImage2D = NULL;
+ fUniform1f = NULL;
+ fUniform1i = NULL;
+ fUniform1fv = NULL;
+ fUniform1iv = NULL;
+ fUniform2f = NULL;
+ fUniform2i = NULL;
+ fUniform2fv = NULL;
+ fUniform2iv = NULL;
+ fUniform3f = NULL;
+ fUniform3i = NULL;
+ fUniform3fv = NULL;
+ fUniform3iv = NULL;
+ fUniform4f = NULL;
+ fUniform4i = NULL;
+ fUniform4fv = NULL;
+ fUniform4iv = NULL;
+ fUniformMatrix2fv = NULL;
+ fUniformMatrix3fv = NULL;
+ fUniformMatrix4fv = NULL;
+ fUseProgram = NULL;
+ fVertexAttrib4fv = NULL;
+ fVertexAttribPointer = NULL;
+ fViewport = NULL;
+ fBindFramebuffer = NULL;
+ fBindRenderbuffer = NULL;
+ fCheckFramebufferStatus = NULL;
+ fDeleteFramebuffers = NULL;
+ fDeleteRenderbuffers = NULL;
+ fFramebufferRenderbuffer = NULL;
+ fFramebufferTexture2D = NULL;
+ fGenFramebuffers = NULL;
+ fGenRenderbuffers = NULL;
+ fGetFramebufferAttachmentParameteriv = NULL;
+ fGetRenderbufferParameteriv = NULL;
+ fRenderbufferStorage = NULL;
+ fRenderbufferStorageMultisample = NULL;
+ fBlitFramebuffer = NULL;
+ fResolveMultisampleFramebuffer = NULL;
+ fMapBuffer = NULL;
+ fUnmapBuffer = NULL;
+ fBindFragDataLocationIndexed = NULL;
+
+#if GR_GL_PER_GL_FUNC_CALLBACK
+ fCallback = GrGLDefaultInterfaceCallback;
+ fCallbackData = 0;
+#endif
+}
+
+bool GrGLInterface::validate(GrGLBinding binding) const {
+
+ // kNone must be 0 so that the check we're about to do can never succeed if
+ // binding == kNone.
+ GR_STATIC_ASSERT(kNone_GrGLBinding == 0);
+
+ if (0 == (binding & fBindingsExported)) {
+ return false;
+ }
+
+ // functions that are always required
+ if (NULL == fActiveTexture ||
+ NULL == fAttachShader ||
+ NULL == fBindAttribLocation ||
+ NULL == fBindBuffer ||
+ NULL == fBindTexture ||
+ NULL == fBlendFunc ||
+ NULL == fBufferData ||
+ NULL == fBufferSubData ||
+ NULL == fClear ||
+ NULL == fClearColor ||
+ NULL == fClearStencil ||
+ NULL == fColorMask ||
+ NULL == fCompileShader ||
+ NULL == fCreateProgram ||
+ NULL == fCreateShader ||
+ NULL == fCullFace ||
+ NULL == fDeleteBuffers ||
+ NULL == fDeleteProgram ||
+ NULL == fDeleteShader ||
+ NULL == fDeleteTextures ||
+ NULL == fDepthMask ||
+ NULL == fDisable ||
+ NULL == fDisableVertexAttribArray ||
+ NULL == fDrawArrays ||
+ NULL == fDrawElements ||
+ NULL == fEnable ||
+ NULL == fEnableVertexAttribArray ||
+ NULL == fFrontFace ||
+ NULL == fGenBuffers ||
+ NULL == fGenTextures ||
+ NULL == fGetBufferParameteriv ||
+ NULL == fGetError ||
+ NULL == fGetIntegerv ||
+ NULL == fGetProgramInfoLog ||
+ NULL == fGetProgramiv ||
+ NULL == fGetShaderInfoLog ||
+ NULL == fGetShaderiv ||
+ NULL == fGetString ||
+ NULL == fGetUniformLocation ||
+ NULL == fLinkProgram ||
+ NULL == fPixelStorei ||
+ NULL == fReadPixels ||
+ NULL == fScissor ||
+ NULL == fShaderSource ||
+ NULL == fStencilFunc ||
+ NULL == fStencilMask ||
+ NULL == fStencilOp ||
+ NULL == fTexImage2D ||
+ NULL == fTexParameteri ||
+ NULL == fTexSubImage2D ||
+ NULL == fUniform1f ||
+ NULL == fUniform1i ||
+ NULL == fUniform1fv ||
+ NULL == fUniform1iv ||
+ NULL == fUniform2f ||
+ NULL == fUniform2i ||
+ NULL == fUniform2fv ||
+ NULL == fUniform2iv ||
+ NULL == fUniform3f ||
+ NULL == fUniform3i ||
+ NULL == fUniform3fv ||
+ NULL == fUniform3iv ||
+ NULL == fUniform4f ||
+ NULL == fUniform4i ||
+ NULL == fUniform4fv ||
+ NULL == fUniform4iv ||
+ NULL == fUniformMatrix2fv ||
+ NULL == fUniformMatrix3fv ||
+ NULL == fUniformMatrix4fv ||
+ NULL == fUseProgram ||
+ NULL == fVertexAttrib4fv ||
+ NULL == fVertexAttribPointer ||
+ NULL == fViewport ||
+ NULL == fBindFramebuffer ||
+ NULL == fBindRenderbuffer ||
+ NULL == fCheckFramebufferStatus ||
+ NULL == fDeleteFramebuffers ||
+ NULL == fDeleteRenderbuffers ||
+ NULL == fFinish ||
+ NULL == fFlush ||
+ NULL == fFramebufferRenderbuffer ||
+ NULL == fFramebufferTexture2D ||
+ NULL == fGetFramebufferAttachmentParameteriv ||
+ NULL == fGetRenderbufferParameteriv ||
+ NULL == fGenFramebuffers ||
+ NULL == fGenRenderbuffers ||
+ NULL == fRenderbufferStorage) {
+ return false;
+ }
+
+ const char* ext;
+ GrGLVersion glVer = GrGLGetVersion(this);
+ ext = (const char*)fGetString(GR_GL_EXTENSIONS);
+
+ // Now check that baseline ES/Desktop fns not covered above are present
+ // and that we have fn pointers for any advertised extensions that we will
+ // try to use.
+
+ // these functions are part of ES2, we assume they are available
+ // On the desktop we assume they are available if the extension
+ // is present or GL version is high enough.
+ if (kES2_GrGLBinding == binding) {
+ if (NULL == fBlendColor ||
+ NULL == fStencilFuncSeparate ||
+ NULL == fStencilMaskSeparate ||
+ NULL == fStencilOpSeparate) {
+ return false;
+ }
+ } else if (kDesktop_GrGLBinding == binding) {
+ if (glVer >= GR_GL_VER(2,0)) {
+ if (NULL == fStencilFuncSeparate ||
+ NULL == fStencilMaskSeparate ||
+ NULL == fStencilOpSeparate) {
+ return false;
+ }
+ }
+ if (glVer >= GR_GL_VER(3,0) && NULL == fBindFragDataLocation) {
+ return false;
+ }
+ if (glVer >= GR_GL_VER(2,0) ||
+ GrGLHasExtensionFromString("GL_ARB_draw_buffers", ext)) {
+ if (NULL == fDrawBuffers) {
+ return false;
+ }
+ }
+ if (glVer >= GR_GL_VER(1,4) ||
+ GrGLHasExtensionFromString("GL_EXT_blend_color", ext)) {
+ if (NULL == fBlendColor) {
+ return false;
+ }
+ }
+ if (glVer >= GR_GL_VER(1,5) ||
+ GrGLHasExtensionFromString("GL_ARB_occlusion_query", ext)) {
+ if (NULL == fGenQueries ||
+ NULL == fDeleteQueries ||
+ NULL == fBeginQuery ||
+ NULL == fEndQuery ||
+ NULL == fGetQueryiv ||
+ NULL == fGetQueryObjectiv ||
+ NULL == fGetQueryObjectuiv) {
+ return false;
+ }
+ }
+ if (glVer >= GR_GL_VER(3,3) ||
+ GrGLHasExtensionFromString("GL_ARB_timer_query", ext) ||
+ GrGLHasExtensionFromString("GL_EXT_timer_query", ext)) {
+ if (NULL == fGetQueryObjecti64v ||
+ NULL == fGetQueryObjectui64v) {
+ return false;
+ }
+ }
+ if (glVer >= GR_GL_VER(3,3) ||
+ GrGLHasExtensionFromString("GL_ARB_timer_query", ext)) {
+ if (NULL == fQueryCounter) {
+ return false;
+ }
+ }
+ }
+
+ // optional function on desktop before 1.3
+ if (kDesktop_GrGLBinding != binding ||
+ (glVer >= GR_GL_VER(1,3) ||
+ GrGLHasExtensionFromString("GL_ARB_texture_compression", ext))) {
+ if (NULL == fCompressedTexImage2D) {
+ return false;
+ }
+ }
+
+ // part of desktop GL, but not ES
+ if (kDesktop_GrGLBinding == binding &&
+ (NULL == fLineWidth ||
+ NULL == fGetTexLevelParameteriv ||
+ NULL == fDrawBuffer ||
+ NULL == fReadBuffer)) {
+ return false;
+ }
+
+ // GL_EXT_texture_storage is part of desktop 4.2
+ // There is a desktop ARB extension and an ES+desktop EXT extension
+ if (kDesktop_GrGLBinding == binding) {
+ if (glVer >= GR_GL_VER(4,2) ||
+ GrGLHasExtensionFromString("GL_ARB_texture_storage", ext) ||
+ GrGLHasExtensionFromString("GL_EXT_texture_storage", ext)) {
+ if (NULL == fTexStorage2D) {
+ return false;
+ }
+ }
+ } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", ext)) {
+ if (NULL == fTexStorage2D) {
+ return false;
+ }
+ }
+
+ // FBO MSAA
+ if (kDesktop_GrGLBinding == binding) {
+ // GL 3.0 and the ARB extension have multisample + blit
+ if (glVer >= GR_GL_VER(3,0) || GrGLHasExtensionFromString("GL_ARB_framebuffer_object", ext)) {
+ if (NULL == fRenderbufferStorageMultisample ||
+ NULL == fBlitFramebuffer) {
+ return false;
+ }
+ } else {
+ if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit", ext) &&
+ NULL == fBlitFramebuffer) {
+ return false;
+ }
+ if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample", ext) &&
+ NULL == fRenderbufferStorageMultisample) {
+ return false;
+ }
+ }
+ } else {
+ if (GrGLHasExtensionFromString("GL_CHROMIUM_framebuffer_multisample", ext)) {
+ if (NULL == fRenderbufferStorageMultisample ||
+ NULL == fBlitFramebuffer) {
+ return false;
+ }
+ }
+ if (GrGLHasExtensionFromString("GL_APPLE_framebuffer_multisample", ext)) {
+ if (NULL == fRenderbufferStorageMultisample ||
+ NULL == fResolveMultisampleFramebuffer) {
+ return false;
+ }
+ }
+ }
+
+ // On ES buffer mapping is an extension. On Desktop
+ // buffer mapping was part of original VBO extension
+ // which we require.
+ if (kDesktop_GrGLBinding == binding ||
+ GrGLHasExtensionFromString("GL_OES_mapbuffer", ext)) {
+ if (NULL == fMapBuffer ||
+ NULL == fUnmapBuffer) {
+ return false;
+ }
+ }
+
+ // Dual source blending
+ if (kDesktop_GrGLBinding == binding &&
+ (glVer >= GR_GL_VER(3,3) ||
+ GrGLHasExtensionFromString("GL_ARB_blend_func_extended", ext))) {
+ if (NULL == fBindFragDataLocationIndexed) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
new file mode 100644
index 0000000000..fb9debf348
--- /dev/null
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -0,0 +1,1889 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLProgram.h"
+
+#include "../GrAllocator.h"
+#include "GrGLShaderVar.h"
+#include "SkTrace.h"
+#include "SkXfermode.h"
+
+namespace {
+
+enum {
+ /// Used to mark a StageUniLocation field that should be bound
+ /// to a uniform during getUniformLocationsAndInitCache().
+ kUseUniform = 2000
+};
+
+} // namespace
+
+#define PRINT_SHADERS 0
+
+typedef GrTAllocator<GrGLShaderVar> VarArray;
+
+// number of each input/output type in a single allocation block
+static const int gVarsPerBlock = 8;
+// except FS outputs where we expect 2 at most.
+static const int gMaxFSOutputs = 2;
+
+struct ShaderCodeSegments {
+ ShaderCodeSegments()
+ : fVSUnis(gVarsPerBlock)
+ , fVSAttrs(gVarsPerBlock)
+ , fVSOutputs(gVarsPerBlock)
+ , fGSInputs(gVarsPerBlock)
+ , fGSOutputs(gVarsPerBlock)
+ , fFSInputs(gVarsPerBlock)
+ , fFSUnis(gVarsPerBlock)
+ , fFSOutputs(gMaxFSOutputs)
+ , fUsesGS(false) {}
+ GrStringBuilder fHeader; // VS+FS, GLSL version, etc
+ VarArray fVSUnis;
+ VarArray fVSAttrs;
+ VarArray fVSOutputs;
+ VarArray fGSInputs;
+ VarArray fGSOutputs;
+ VarArray fFSInputs;
+ GrStringBuilder fGSHeader; // layout qualifiers specific to GS
+ VarArray fFSUnis;
+ VarArray fFSOutputs;
+ GrStringBuilder fFSFunctions;
+ GrStringBuilder fVSCode;
+ GrStringBuilder fGSCode;
+ GrStringBuilder fFSCode;
+
+ bool fUsesGS;
+};
+
+typedef GrGLProgram::ProgramDesc::StageDesc StageDesc;
+
+#if GR_GL_ATTRIBUTE_MATRICES
+ #define VIEW_MATRIX_NAME "aViewM"
+#else
+ #define VIEW_MATRIX_NAME "uViewM"
+#endif
+
+#define POS_ATTR_NAME "aPosition"
+#define COL_ATTR_NAME "aColor"
+#define COV_ATTR_NAME "aCoverage"
+#define EDGE_ATTR_NAME "aEdge"
+#define COL_UNI_NAME "uColor"
+#define COV_UNI_NAME "uCoverage"
+#define EDGES_UNI_NAME "uEdges"
+#define COL_FILTER_UNI_NAME "uColorFilter"
+#define COL_MATRIX_UNI_NAME "uColorMatrix"
+#define COL_MATRIX_VEC_UNI_NAME "uColorMatrixVec"
+
+namespace {
+inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
+ *s = "aTexCoord";
+ s->appendS32(coordIdx);
+}
+
+inline GrGLShaderVar::Type float_vector_type(int count) {
+ GR_STATIC_ASSERT(GrGLShaderVar::kFloat_Type == 0);
+ GR_STATIC_ASSERT(GrGLShaderVar::kVec2f_Type == 1);
+ GR_STATIC_ASSERT(GrGLShaderVar::kVec3f_Type == 2);
+ GR_STATIC_ASSERT(GrGLShaderVar::kVec4f_Type == 3);
+ GrAssert(count > 0 && count <= 4);
+ return (GrGLShaderVar::Type)(count - 1);
+}
+
+inline const char* float_vector_type_str(int count) {
+ return GrGLShaderVar::TypeString(float_vector_type(count));
+}
+
+inline const char* vector_homog_coord(int count) {
+ static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
+ GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS));
+ return HOMOGS[count];
+}
+
+inline const char* vector_nonhomog_coords(int count) {
+ static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"};
+ GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(NONHOMOGS));
+ return NONHOMOGS[count];
+}
+
+inline const char* vector_all_coords(int count) {
+ static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
+ GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL));
+ return ALL[count];
+}
+
+inline const char* all_ones_vec(int count) {
+ static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
+ "vec3(1,1,1)", "vec4(1,1,1,1)"};
+ GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC));
+ return ONESVEC[count];
+}
+
+inline const char* all_zeros_vec(int count) {
+ static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
+ "vec3(0,0,0)", "vec4(0,0,0,0)"};
+ GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC));
+ return ZEROSVEC[count];
+}
+
+inline const char* declared_color_output_name() { return "fsColorOut"; }
+inline const char* dual_source_output_name() { return "dualSourceOut"; }
+
+inline void tex_matrix_name(int stage, GrStringBuilder* s) {
+#if GR_GL_ATTRIBUTE_MATRICES
+ *s = "aTexM";
+#else
+ *s = "uTexM";
+#endif
+ s->appendS32(stage);
+}
+
+inline void normalized_texel_size_name(int stage, GrStringBuilder* s) {
+ *s = "uTexelSize";
+ s->appendS32(stage);
+}
+
+inline void sampler_name(int stage, GrStringBuilder* s) {
+ *s = "uSampler";
+ s->appendS32(stage);
+}
+
+inline void radial2_param_name(int stage, GrStringBuilder* s) {
+ *s = "uRadial2Params";
+ s->appendS32(stage);
+}
+
+inline void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder* i) {
+ *k = "uKernel";
+ k->appendS32(stage);
+ *i = "uImageIncrement";
+ i->appendS32(stage);
+}
+
+inline void tex_domain_name(int stage, GrStringBuilder* s) {
+ *s = "uTexDom";
+ s->appendS32(stage);
+}
+}
+
+GrGLProgram::GrGLProgram() {
+}
+
+GrGLProgram::~GrGLProgram() {
+}
+
+void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
+ GrBlendCoeff* dstCoeff) const {
+ switch (fProgramDesc.fDualSrcOutput) {
+ case ProgramDesc::kNone_DualSrcOutput:
+ break;
+ // the prog will write a coverage value to the secondary
+ // output and the dst is blended by one minus that value.
+ case ProgramDesc::kCoverage_DualSrcOutput:
+ case ProgramDesc::kCoverageISA_DualSrcOutput:
+ case ProgramDesc::kCoverageISC_DualSrcOutput:
+ *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff;
+ break;
+ default:
+ GrCrash("Unexpected dual source blend output");
+ break;
+ }
+}
+
+// assigns modulation of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
+// if either var is "" then assign to the other var
+// if both are "" then assign all ones
+static inline void modulate_helper(const char* outputVar,
+ const char* var0,
+ const char* var1,
+ GrStringBuilder* code) {
+ GrAssert(NULL != outputVar);
+ GrAssert(NULL != var0);
+ GrAssert(NULL != var1);
+ GrAssert(NULL != code);
+
+ bool has0 = '\0' != *var0;
+ bool has1 = '\0' != *var1;
+
+ if (!has0 && !has1) {
+ code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
+ } else if (!has0) {
+ code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
+ } else if (!has1) {
+ code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
+ } else {
+ code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
+ }
+}
+
+// assigns addition of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
+// if either var is "" then assign to the other var
+// if both are "" then assign all zeros
+static inline void add_helper(const char* outputVar,
+ const char* var0,
+ const char* var1,
+ GrStringBuilder* code) {
+ GrAssert(NULL != outputVar);
+ GrAssert(NULL != var0);
+ GrAssert(NULL != var1);
+ GrAssert(NULL != code);
+
+ bool has0 = '\0' != *var0;
+ bool has1 = '\0' != *var1;
+
+ if (!has0 && !has1) {
+ code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
+ } else if (!has0) {
+ code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
+ } else if (!has1) {
+ code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
+ } else {
+ code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
+ }
+}
+
+// given two blend coeffecients determine whether the src
+// and/or dst computation can be omitted.
+static inline void needBlendInputs(SkXfermode::Coeff srcCoeff,
+ SkXfermode::Coeff dstCoeff,
+ bool* needSrcValue,
+ bool* needDstValue) {
+ if (SkXfermode::kZero_Coeff == srcCoeff) {
+ switch (dstCoeff) {
+ // these all read the src
+ case SkXfermode::kSC_Coeff:
+ case SkXfermode::kISC_Coeff:
+ case SkXfermode::kSA_Coeff:
+ case SkXfermode::kISA_Coeff:
+ *needSrcValue = true;
+ break;
+ default:
+ *needSrcValue = false;
+ break;
+ }
+ } else {
+ *needSrcValue = true;
+ }
+ if (SkXfermode::kZero_Coeff == dstCoeff) {
+ switch (srcCoeff) {
+ // these all read the dst
+ case SkXfermode::kDC_Coeff:
+ case SkXfermode::kIDC_Coeff:
+ case SkXfermode::kDA_Coeff:
+ case SkXfermode::kIDA_Coeff:
+ *needDstValue = true;
+ break;
+ default:
+ *needDstValue = false;
+ break;
+ }
+ } else {
+ *needDstValue = true;
+ }
+}
+
+/**
+ * Create a blend_coeff * value string to be used in shader code. Sets empty
+ * string if result is trivially zero.
+ */
+static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff,
+ const char* src, const char* dst,
+ const char* value) {
+ switch (coeff) {
+ case SkXfermode::kZero_Coeff: /** 0 */
+ *str = "";
+ break;
+ case SkXfermode::kOne_Coeff: /** 1 */
+ *str = value;
+ break;
+ case SkXfermode::kSC_Coeff:
+ str->printf("(%s * %s)", src, value);
+ break;
+ case SkXfermode::kISC_Coeff:
+ str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value);
+ break;
+ case SkXfermode::kDC_Coeff:
+ str->printf("(%s * %s)", dst, value);
+ break;
+ case SkXfermode::kIDC_Coeff:
+ str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value);
+ break;
+ case SkXfermode::kSA_Coeff: /** src alpha */
+ str->printf("(%s.a * %s)", src, value);
+ break;
+ case SkXfermode::kISA_Coeff: /** inverse src alpha (i.e. 1 - sa) */
+ str->printf("((1.0 - %s.a) * %s)", src, value);
+ break;
+ case SkXfermode::kDA_Coeff: /** dst alpha */
+ str->printf("(%s.a * %s)", dst, value);
+ break;
+ case SkXfermode::kIDA_Coeff: /** inverse dst alpha (i.e. 1 - da) */
+ str->printf("((1.0 - %s.a) * %s)", dst, value);
+ break;
+ default:
+ GrCrash("Unexpected xfer coeff.");
+ break;
+ }
+}
+/**
+ * Adds a line to the fragment shader code which modifies the color by
+ * the specified color filter.
+ */
+static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
+ SkXfermode::Coeff uniformCoeff,
+ SkXfermode::Coeff colorCoeff,
+ const char* inColor) {
+ GrStringBuilder colorStr, constStr;
+ blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME,
+ inColor, inColor);
+ blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME,
+ inColor, COL_FILTER_UNI_NAME);
+
+ add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
+}
+/**
+ * Adds code to the fragment shader code which modifies the color by
+ * the specified color matrix.
+ */
+static void addColorMatrix(GrStringBuilder* fsCode, const char * outputVar,
+ const char* inColor) {
+ fsCode->appendf("\t%s = %s * vec4(%s.rgb / %s.a, %s.a) + %s;\n", outputVar, COL_MATRIX_UNI_NAME, inColor, inColor, inColor, COL_MATRIX_VEC_UNI_NAME);
+ fsCode->appendf("\t%s.rgb *= %s.a;\n", outputVar, outputVar);
+}
+
+namespace {
+
+// Adds a var that is computed in the VS and read in FS.
+// If there is a GS it will just pass it through.
+void append_varying(GrGLShaderVar::Type type,
+ const char* name,
+ ShaderCodeSegments* segments,
+ const char** vsOutName = NULL,
+ const char** fsInName = NULL) {
+ segments->fVSOutputs.push_back();
+ segments->fVSOutputs.back().setType(type);
+ segments->fVSOutputs.back().setTypeModifier(
+ GrGLShaderVar::kOut_TypeModifier);
+ segments->fVSOutputs.back().accessName()->printf("v%s", name);
+ if (vsOutName) {
+ *vsOutName = segments->fVSOutputs.back().getName().c_str();
+ }
+ // input to FS comes either from VS or GS
+ const GrStringBuilder* fsName;
+ if (segments->fUsesGS) {
+ // if we have a GS take each varying in as an array
+ // and output as non-array.
+ segments->fGSInputs.push_back();
+ segments->fGSInputs.back().setType(type);
+ segments->fGSInputs.back().setTypeModifier(
+ GrGLShaderVar::kIn_TypeModifier);
+ segments->fGSInputs.back().setUnsizedArray();
+ *segments->fGSInputs.back().accessName() =
+ segments->fVSOutputs.back().getName();
+ segments->fGSOutputs.push_back();
+ segments->fGSOutputs.back().setType(type);
+ segments->fGSOutputs.back().setTypeModifier(
+ GrGLShaderVar::kOut_TypeModifier);
+ segments->fGSOutputs.back().accessName()->printf("g%s", name);
+ fsName = segments->fGSOutputs.back().accessName();
+ } else {
+ fsName = segments->fVSOutputs.back().accessName();
+ }
+ segments->fFSInputs.push_back();
+ segments->fFSInputs.back().setType(type);
+ segments->fFSInputs.back().setTypeModifier(
+ GrGLShaderVar::kIn_TypeModifier);
+ segments->fFSInputs.back().setName(*fsName);
+ if (fsInName) {
+ *fsInName = fsName->c_str();
+ }
+}
+
+// version of above that adds a stage number to the
+// the var name (for uniqueness)
+void append_varying(GrGLShaderVar::Type type,
+ const char* name,
+ int stageNum,
+ ShaderCodeSegments* segments,
+ const char** vsOutName = NULL,
+ const char** fsInName = NULL) {
+ GrStringBuilder nameWithStage(name);
+ nameWithStage.appendS32(stageNum);
+ append_varying(type, nameWithStage.c_str(), segments, vsOutName, fsInName);
+}
+}
+
+void GrGLProgram::genEdgeCoverage(const GrGLInterface* gl,
+ GrVertexLayout layout,
+ CachedData* programData,
+ GrStringBuilder* coverageVar,
+ ShaderCodeSegments* segments) const {
+ if (fProgramDesc.fEdgeAANumEdges > 0) {
+ segments->fFSUnis.push_back().set(GrGLShaderVar::kVec3f_Type,
+ GrGLShaderVar::kUniform_TypeModifier,
+ EDGES_UNI_NAME,
+ fProgramDesc.fEdgeAANumEdges);
+ programData->fUniLocations.fEdgesUni = kUseUniform;
+ int count = fProgramDesc.fEdgeAANumEdges;
+ segments->fFSCode.append(
+ "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n");
+ for (int i = 0; i < count; i++) {
+ segments->fFSCode.append("\tfloat a");
+ segments->fFSCode.appendS32(i);
+ segments->fFSCode.append(" = clamp(dot(" EDGES_UNI_NAME "[");
+ segments->fFSCode.appendS32(i);
+ segments->fFSCode.append("], pos), 0.0, 1.0);\n");
+ }
+ if (fProgramDesc.fEdgeAAConcave && (count & 0x01) == 0) {
+ // For concave polys, we consider the edges in pairs.
+ segments->fFSFunctions.append("float cross2(vec2 a, vec2 b) {\n");
+ segments->fFSFunctions.append("\treturn dot(a, vec2(b.y, -b.x));\n");
+ segments->fFSFunctions.append("}\n");
+ for (int i = 0; i < count; i += 2) {
+ segments->fFSCode.appendf("\tfloat eb%d;\n", i / 2);
+ segments->fFSCode.appendf("\tif (cross2(" EDGES_UNI_NAME "[%d].xy, " EDGES_UNI_NAME "[%d].xy) < 0.0) {\n", i, i + 1);
+ segments->fFSCode.appendf("\t\teb%d = a%d * a%d;\n", i / 2, i, i + 1);
+ segments->fFSCode.append("\t} else {\n");
+ segments->fFSCode.appendf("\t\teb%d = a%d + a%d - a%d * a%d;\n", i / 2, i, i + 1, i, i + 1);
+ segments->fFSCode.append("\t}\n");
+ }
+ segments->fFSCode.append("\tfloat edgeAlpha = ");
+ for (int i = 0; i < count / 2 - 1; i++) {
+ segments->fFSCode.appendf("min(eb%d, ", i);
+ }
+ segments->fFSCode.appendf("eb%d", count / 2 - 1);
+ for (int i = 0; i < count / 2 - 1; i++) {
+ segments->fFSCode.append(")");
+ }
+ segments->fFSCode.append(";\n");
+ } else {
+ segments->fFSCode.append("\tfloat edgeAlpha = ");
+ for (int i = 0; i < count - 1; i++) {
+ segments->fFSCode.appendf("min(a%d * a%d, ", i, i + 1);
+ }
+ segments->fFSCode.appendf("a%d * a0", count - 1);
+ for (int i = 0; i < count - 1; i++) {
+ segments->fFSCode.append(")");
+ }
+ segments->fFSCode.append(";\n");
+ }
+ *coverageVar = "edgeAlpha";
+ } else if (layout & GrDrawTarget::kEdge_VertexLayoutBit) {
+ const char *vsName, *fsName;
+ append_varying(GrGLShaderVar::kVec4f_Type, "Edge", segments,
+ &vsName, &fsName);
+ segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kAttribute_TypeModifier, EDGE_ATTR_NAME);
+ segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
+ if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
+ segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName);
+ segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+ } else if (GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType) {
+ segments->fFSCode.appendf("\tfloat edgeAlpha;\n");
+ // keep the derivative instructions outside the conditional
+ segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
+ segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
+ segments->fFSCode.appendf("\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
+ // today we know z and w are in device space. We could use derivatives
+ segments->fFSCode.appendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, fsName);
+ segments->fFSCode.append ("\t} else {\n");
+ segments->fFSCode.appendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+ "\t\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
+ fsName, fsName);
+ segments->fFSCode.appendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
+ segments->fFSCode.appendf("\t\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n"
+ "\t}\n");
+ if (gl->supportsES2()) {
+ segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
+ }
+ } else {
+ GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType);
+ segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
+ segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
+ segments->fFSCode.appendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+ "\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
+ fsName, fsName);
+ segments->fFSCode.appendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
+ segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
+ segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+ if (gl->supportsES2()) {
+ segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
+ }
+ }
+ *coverageVar = "edgeAlpha";
+ } else {
+ coverageVar->reset();
+ }
+}
+
+namespace {
+
+void genInputColor(GrGLProgram::ProgramDesc::ColorInput colorInput,
+ GrGLProgram::CachedData* programData,
+ ShaderCodeSegments* segments,
+ GrStringBuilder* inColor) {
+ switch (colorInput) {
+ case GrGLProgram::ProgramDesc::kAttribute_ColorInput: {
+ segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kAttribute_TypeModifier,
+ COL_ATTR_NAME);
+ const char *vsName, *fsName;
+ append_varying(GrGLShaderVar::kVec4f_Type, "Color", segments, &vsName, &fsName);
+ segments->fVSCode.appendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
+ *inColor = fsName;
+ } break;
+ case GrGLProgram::ProgramDesc::kUniform_ColorInput:
+ segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kUniform_TypeModifier,
+ COL_UNI_NAME);
+ programData->fUniLocations.fColorUni = kUseUniform;
+ *inColor = COL_UNI_NAME;
+ break;
+ case GrGLProgram::ProgramDesc::kTransBlack_ColorInput:
+ GrAssert(!"needComputedColor should be false.");
+ break;
+ case GrGLProgram::ProgramDesc::kSolidWhite_ColorInput:
+ break;
+ default:
+ GrCrash("Unknown color type.");
+ break;
+ }
+}
+
+void genAttributeCoverage(ShaderCodeSegments* segments,
+ GrStringBuilder* inOutCoverage) {
+ segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kAttribute_TypeModifier,
+ COV_ATTR_NAME);
+ const char *vsName, *fsName;
+ append_varying(GrGLShaderVar::kVec4f_Type, "Coverage",
+ segments, &vsName, &fsName);
+ segments->fVSCode.appendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
+ if (inOutCoverage->size()) {
+ segments->fFSCode.appendf("\tvec4 attrCoverage = %s * %s;\n",
+ fsName, inOutCoverage->c_str());
+ *inOutCoverage = "attrCoverage";
+ } else {
+ *inOutCoverage = fsName;
+ }
+}
+
+void genUniformCoverage(ShaderCodeSegments* segments,
+ GrGLProgram::CachedData* programData,
+ GrStringBuilder* inOutCoverage) {
+ segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kUniform_TypeModifier,
+ COV_UNI_NAME);
+ programData->fUniLocations.fCoverageUni = kUseUniform;
+ if (inOutCoverage->size()) {
+ segments->fFSCode.appendf("\tvec4 uniCoverage = %s * %s;\n",
+ COV_UNI_NAME, inOutCoverage->c_str());
+ *inOutCoverage = "uniCoverage";
+ } else {
+ *inOutCoverage = COV_UNI_NAME;
+ }
+}
+
+}
+
+void GrGLProgram::genGeometryShader(const GrGLInterface* gl,
+ GrGLSLGeneration glslGeneration,
+ ShaderCodeSegments* segments) const {
+#if GR_GL_EXPERIMENTAL_GS
+ if (fProgramDesc.fExperimentalGS) {
+ GrAssert(glslGeneration >= k150_GrGLSLGeneration);
+ segments->fGSHeader.append("layout(triangles) in;\n"
+ "layout(triangle_strip, max_vertices = 6) out;\n");
+ segments->fGSCode.append("void main() {\n"
+ "\tfor (int i = 0; i < 3; ++i) {\n"
+ "\t\tgl_Position = gl_in[i].gl_Position;\n");
+ if (this->fProgramDesc.fEmitsPointSize) {
+ segments->fGSCode.append("\t\tgl_PointSize = 1.0;\n");
+ }
+ GrAssert(segments->fGSInputs.count() == segments->fGSOutputs.count());
+ int count = segments->fGSInputs.count();
+ for (int i = 0; i < count; ++i) {
+ segments->fGSCode.appendf("\t\t%s = %s[i];\n",
+ segments->fGSOutputs[i].getName().c_str(),
+ segments->fGSInputs[i].getName().c_str());
+ }
+ segments->fGSCode.append("\t\tEmitVertex();\n"
+ "\t}\n"
+ "\tEndPrimitive();\n"
+ "}\n");
+ }
+#endif
+}
+
+const char* GrGLProgram::adjustInColor(const GrStringBuilder& inColor) const {
+ if (inColor.size()) {
+ return inColor.c_str();
+ } else {
+ if (ProgramDesc::kSolidWhite_ColorInput == fProgramDesc.fColorInput) {
+ return all_ones_vec(4);
+ } else {
+ return all_zeros_vec(4);
+ }
+ }
+}
+
+
+bool GrGLProgram::genProgram(const GrGLInterface* gl,
+ GrGLSLGeneration glslGeneration,
+ GrGLProgram::CachedData* programData) const {
+
+ ShaderCodeSegments segments;
+ const uint32_t& layout = fProgramDesc.fVertexLayout;
+
+ programData->fUniLocations.reset();
+
+#if GR_GL_EXPERIMENTAL_GS
+ segments.fUsesGS = fProgramDesc.fExperimentalGS;
+#endif
+
+ SkXfermode::Coeff colorCoeff, uniformCoeff;
+ bool applyColorMatrix = SkToBool(fProgramDesc.fColorMatrixEnabled);
+ // The rest of transfer mode color filters have not been implemented
+ if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
+ GR_DEBUGCODE(bool success =)
+ SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
+ (fProgramDesc.fColorFilterXfermode),
+ &uniformCoeff, &colorCoeff);
+ GR_DEBUGASSERT(success);
+ } else {
+ colorCoeff = SkXfermode::kOne_Coeff;
+ uniformCoeff = SkXfermode::kZero_Coeff;
+ }
+
+ // no need to do the color filter / matrix at all if coverage is 0. The
+ // output color is scaled by the coverage. All the dual source outputs are
+ // scaled by the coverage as well.
+ if (ProgramDesc::kTransBlack_ColorInput == fProgramDesc.fCoverageInput) {
+ colorCoeff = SkXfermode::kZero_Coeff;
+ uniformCoeff = SkXfermode::kZero_Coeff;
+ applyColorMatrix = false;
+ }
+
+ // If we know the final color is going to be all zeros then we can
+ // simplify the color filter coeffecients. needComputedColor will then
+ // come out false below.
+ if (ProgramDesc::kTransBlack_ColorInput == fProgramDesc.fColorInput) {
+ colorCoeff = SkXfermode::kZero_Coeff;
+ if (SkXfermode::kDC_Coeff == uniformCoeff ||
+ SkXfermode::kDA_Coeff == uniformCoeff) {
+ uniformCoeff = SkXfermode::kZero_Coeff;
+ } else if (SkXfermode::kIDC_Coeff == uniformCoeff ||
+ SkXfermode::kIDA_Coeff == uniformCoeff) {
+ uniformCoeff = SkXfermode::kOne_Coeff;
+ }
+ }
+
+ bool needColorFilterUniform;
+ bool needComputedColor;
+ needBlendInputs(uniformCoeff, colorCoeff,
+ &needColorFilterUniform, &needComputedColor);
+
+ // the dual source output has no canonical var name, have to
+ // declare an output, which is incompatible with gl_FragColor/gl_FragData.
+ bool dualSourceOutputWritten = false;
+ segments.fHeader.printf(GrGetGLSLVersionDecl(gl->fBindingsExported,
+ glslGeneration));
+
+ GrGLShaderVar colorOutput;
+ bool isColorDeclared = GrGLSLSetupFSColorOuput(glslGeneration,
+ declared_color_output_name(),
+ &colorOutput);
+ if (isColorDeclared) {
+ segments.fFSOutputs.push_back(colorOutput);
+ }
+
+#if GR_GL_ATTRIBUTE_MATRICES
+ segments.fVSAttrs.push_back().set(GrGLShaderVar::kMat33f_Type,
+ GrGLShaderVar::kAttribute_TypeModifier, VIEW_MATRIX_NAME);
+ programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
+#else
+ segments.fVSUnis.push_back().set(GrGLShaderVar::kMat33f_Type,
+ GrGLShaderVar::kUniform_TypeModifier, VIEW_MATRIX_NAME);
+ programData->fUniLocations.fViewMatrixUni = kUseUniform;
+#endif
+ segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
+ GrGLShaderVar::kAttribute_TypeModifier, POS_ATTR_NAME);
+
+ segments.fVSCode.append(
+ "void main() {\n"
+ "\tvec3 pos3 = " VIEW_MATRIX_NAME " * vec3("POS_ATTR_NAME", 1);\n"
+ "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n");
+
+ // incoming color to current stage being processed.
+ GrStringBuilder inColor;
+
+ if (needComputedColor) {
+ genInputColor((ProgramDesc::ColorInput) fProgramDesc.fColorInput,
+ programData, &segments, &inColor);
+ }
+
+ // we output point size in the GS if present
+ if (fProgramDesc.fEmitsPointSize && !segments.fUsesGS){
+ segments.fVSCode.append("\tgl_PointSize = 1.0;\n");
+ }
+
+ segments.fFSCode.append("void main() {\n");
+
+ // add texture coordinates that are used to the list of vertex attr decls
+ GrStringBuilder texCoordAttrs[GrDrawState::kMaxTexCoords];
+ for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+ if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
+ tex_attr_name(t, texCoordAttrs + t);
+ segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
+ GrGLShaderVar::kAttribute_TypeModifier,
+ texCoordAttrs[t].c_str());
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // compute the final color
+
+ // if we have color stages string them together, feeding the output color
+ // of each to the next and generating code for each stage.
+ if (needComputedColor) {
+ GrStringBuilder outColor;
+ for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) {
+ if (fProgramDesc.fStages[s].isEnabled()) {
+ // create var to hold stage result
+ outColor = "color";
+ outColor.appendS32(s);
+ segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
+
+ const char* inCoords;
+ // figure out what our input coords are
+ if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
+ layout) {
+ inCoords = POS_ATTR_NAME;
+ } else {
+ int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
+ // we better have input tex coordinates if stage is enabled.
+ GrAssert(tcIdx >= 0);
+ GrAssert(texCoordAttrs[tcIdx].size());
+ inCoords = texCoordAttrs[tcIdx].c_str();
+ }
+
+ this->genStageCode(gl,
+ s,
+ fProgramDesc.fStages[s],
+ inColor.size() ? inColor.c_str() : NULL,
+ outColor.c_str(),
+ inCoords,
+ &segments,
+ &programData->fUniLocations.fStages[s]);
+ inColor = outColor;
+ }
+ }
+ }
+
+ // if have all ones or zeros for the "dst" input to the color filter then we
+ // may be able to make additional optimizations.
+ if (needColorFilterUniform && needComputedColor && !inColor.size()) {
+ GrAssert(ProgramDesc::kSolidWhite_ColorInput == fProgramDesc.fColorInput);
+ bool uniformCoeffIsZero = SkXfermode::kIDC_Coeff == uniformCoeff ||
+ SkXfermode::kIDA_Coeff == uniformCoeff;
+ if (uniformCoeffIsZero) {
+ uniformCoeff = SkXfermode::kZero_Coeff;
+ bool bogus;
+ needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
+ &needColorFilterUniform, &bogus);
+ }
+ }
+ if (needColorFilterUniform) {
+ segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kUniform_TypeModifier,
+ COL_FILTER_UNI_NAME);
+ programData->fUniLocations.fColorFilterUni = kUseUniform;
+ }
+ bool wroteFragColorZero = false;
+ if (SkXfermode::kZero_Coeff == uniformCoeff &&
+ SkXfermode::kZero_Coeff == colorCoeff &&
+ !applyColorMatrix) {
+ segments.fFSCode.appendf("\t%s = %s;\n",
+ colorOutput.getName().c_str(),
+ all_zeros_vec(4));
+ wroteFragColorZero = true;
+ } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
+ segments.fFSCode.appendf("\tvec4 filteredColor;\n");
+ const char* color = adjustInColor(inColor);
+ addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff,
+ colorCoeff, color);
+ inColor = "filteredColor";
+ }
+ if (applyColorMatrix) {
+ segments.fFSUnis.push_back().set(GrGLShaderVar::kMat44f_Type,
+ GrGLShaderVar::kUniform_TypeModifier,
+ COL_MATRIX_UNI_NAME);
+ segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kUniform_TypeModifier,
+ COL_MATRIX_VEC_UNI_NAME);
+ programData->fUniLocations.fColorMatrixUni = kUseUniform;
+ programData->fUniLocations.fColorMatrixVecUni = kUseUniform;
+ segments.fFSCode.appendf("\tvec4 matrixedColor;\n");
+ const char* color = adjustInColor(inColor);
+ addColorMatrix(&segments.fFSCode, "matrixedColor", color);
+ inColor = "matrixedColor";
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // compute the partial coverage (coverage stages and edge aa)
+
+ GrStringBuilder inCoverage;
+ bool coverageIsZero = ProgramDesc::kTransBlack_ColorInput ==
+ fProgramDesc.fCoverageInput;
+ // we don't need to compute coverage at all if we know the final shader
+ // output will be zero and we don't have a dual src blend output.
+ if (!wroteFragColorZero ||
+ ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+
+ if (!coverageIsZero) {
+ this->genEdgeCoverage(gl,
+ layout,
+ programData,
+ &inCoverage,
+ &segments);
+
+ switch (fProgramDesc.fCoverageInput) {
+ case ProgramDesc::kSolidWhite_ColorInput:
+ // empty string implies solid white
+ break;
+ case ProgramDesc::kAttribute_ColorInput:
+ genAttributeCoverage(&segments, &inCoverage);
+ break;
+ case ProgramDesc::kUniform_ColorInput:
+ genUniformCoverage(&segments, programData, &inCoverage);
+ break;
+ default:
+ GrCrash("Unexpected input coverage.");
+ }
+
+ GrStringBuilder outCoverage;
+ const int& startStage = fProgramDesc.fFirstCoverageStage;
+ for (int s = startStage; s < GrDrawState::kNumStages; ++s) {
+ if (fProgramDesc.fStages[s].isEnabled()) {
+ // create var to hold stage output
+ outCoverage = "coverage";
+ outCoverage.appendS32(s);
+ segments.fFSCode.appendf("\tvec4 %s;\n",
+ outCoverage.c_str());
+
+ const char* inCoords;
+ // figure out what our input coords are
+ if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
+ layout) {
+ inCoords = POS_ATTR_NAME;
+ } else {
+ int tcIdx =
+ GrDrawTarget::VertexTexCoordsForStage(s, layout);
+ // we better have input tex coordinates if stage is
+ // enabled.
+ GrAssert(tcIdx >= 0);
+ GrAssert(texCoordAttrs[tcIdx].size());
+ inCoords = texCoordAttrs[tcIdx].c_str();
+ }
+
+ genStageCode(gl, s,
+ fProgramDesc.fStages[s],
+ inCoverage.size() ? inCoverage.c_str() : NULL,
+ outCoverage.c_str(),
+ inCoords,
+ &segments,
+ &programData->fUniLocations.fStages[s]);
+ inCoverage = outCoverage;
+ }
+ }
+ }
+ if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+ segments.fFSOutputs.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kOut_TypeModifier,
+ dual_source_output_name());
+ bool outputIsZero = coverageIsZero;
+ GrStringBuilder coeff;
+ if (!outputIsZero &&
+ ProgramDesc::kCoverage_DualSrcOutput !=
+ fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
+ if (!inColor.size()) {
+ outputIsZero = true;
+ } else {
+ if (fProgramDesc.fDualSrcOutput ==
+ ProgramDesc::kCoverageISA_DualSrcOutput) {
+ coeff.printf("(1 - %s.a)", inColor.c_str());
+ } else {
+ coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
+ }
+ }
+ }
+ if (outputIsZero) {
+ segments.fFSCode.appendf("\t%s = %s;\n",
+ dual_source_output_name(),
+ all_zeros_vec(4));
+ } else {
+ modulate_helper(dual_source_output_name(),
+ coeff.c_str(),
+ inCoverage.c_str(),
+ &segments.fFSCode);
+ }
+ dualSourceOutputWritten = true;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // combine color and coverage as frag color
+
+ if (!wroteFragColorZero) {
+ if (coverageIsZero) {
+ segments.fFSCode.appendf("\t%s = %s;\n",
+ colorOutput.getName().c_str(),
+ all_zeros_vec(4));
+ } else {
+ modulate_helper(colorOutput.getName().c_str(),
+ inColor.c_str(),
+ inCoverage.c_str(),
+ &segments.fFSCode);
+ }
+ if (ProgramDesc::kNo_OutputPM == fProgramDesc.fOutputPM) {
+ segments.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(%s.rgb / %s.a, %s.a);\n",
+ colorOutput.getName().c_str(),
+ colorOutput.getName().c_str(),
+ colorOutput.getName().c_str(),
+ colorOutput.getName().c_str(),
+ colorOutput.getName().c_str());
+ }
+ }
+
+ segments.fVSCode.append("}\n");
+ segments.fFSCode.append("}\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // insert GS
+#if GR_DEBUG
+ this->genGeometryShader(gl, glslGeneration, &segments);
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////
+ // compile and setup attribs and unis
+
+ if (!CompileShaders(gl, glslGeneration, segments, programData)) {
+ return false;
+ }
+
+ if (!this->bindOutputsAttribsAndLinkProgram(gl, texCoordAttrs,
+ isColorDeclared,
+ dualSourceOutputWritten,
+ programData)) {
+ return false;
+ }
+
+ this->getUniformLocationsAndInitCache(gl, programData);
+
+ return true;
+}
+
+namespace {
+
+inline void expand_decls(const VarArray& vars,
+ const GrGLInterface* gl,
+ GrStringBuilder* string,
+ GrGLSLGeneration gen) {
+ const int count = vars.count();
+ for (int i = 0; i < count; ++i) {
+ vars[i].appendDecl(gl, string, gen);
+ }
+}
+
+inline void print_shader(int stringCnt,
+ const char** strings,
+ int* stringLengths) {
+ for (int i = 0; i < stringCnt; ++i) {
+ if (NULL == stringLengths || stringLengths[i] < 0) {
+ GrPrintf(strings[i]);
+ } else {
+ GrPrintf("%.*s", stringLengths[i], strings[i]);
+ }
+ }
+}
+
+typedef SkTArray<const char*, true> StrArray;
+#define PREALLOC_STR_ARRAY(N) SkSTArray<(N), const char*, true>
+
+typedef SkTArray<int, true> LengthArray;
+#define PREALLOC_LENGTH_ARRAY(N) SkSTArray<(N), int, true>
+
+// these shouldn't relocate
+typedef GrTAllocator<GrStringBuilder> TempArray;
+#define PREALLOC_TEMP_ARRAY(N) GrSTAllocator<(N), GrStringBuilder>
+
+inline void append_string(const GrStringBuilder& str,
+ StrArray* strings,
+ LengthArray* lengths) {
+ int length = (int) str.size();
+ if (length) {
+ strings->push_back(str.c_str());
+ lengths->push_back(length);
+ }
+ GrAssert(strings->count() == lengths->count());
+}
+
+inline void append_decls(const VarArray& vars,
+ const GrGLInterface* gl,
+ StrArray* strings,
+ LengthArray* lengths,
+ TempArray* temp,
+ GrGLSLGeneration gen) {
+ expand_decls(vars, gl, &temp->push_back(), gen);
+ append_string(temp->back(), strings, lengths);
+}
+
+}
+
+bool GrGLProgram::CompileShaders(const GrGLInterface* gl,
+ GrGLSLGeneration glslGeneration,
+ const ShaderCodeSegments& segments,
+ CachedData* programData) {
+ enum { kPreAllocStringCnt = 8 };
+
+ PREALLOC_STR_ARRAY(kPreAllocStringCnt) strs;
+ PREALLOC_LENGTH_ARRAY(kPreAllocStringCnt) lengths;
+ PREALLOC_TEMP_ARRAY(kPreAllocStringCnt) temps;
+
+ GrStringBuilder unis;
+ GrStringBuilder inputs;
+ GrStringBuilder outputs;
+
+ append_string(segments.fHeader, &strs, &lengths);
+ append_decls(segments.fVSUnis, gl, &strs, &lengths, &temps, glslGeneration);
+ append_decls(segments.fVSAttrs, gl, &strs, &lengths,
+ &temps, glslGeneration);
+ append_decls(segments.fVSOutputs, gl, &strs, &lengths,
+ &temps, glslGeneration);
+ append_string(segments.fVSCode, &strs, &lengths);
+
+#if PRINT_SHADERS
+ print_shader(strs.count(), &strs[0], &lengths[0]);
+ GrPrintf("\n");
+#endif
+
+ programData->fVShaderID =
+ CompileShader(gl, GR_GL_VERTEX_SHADER, strs.count(),
+ &strs[0], &lengths[0]);
+
+ if (!programData->fVShaderID) {
+ return false;
+ }
+ if (segments.fUsesGS) {
+ strs.reset();
+ lengths.reset();
+ temps.reset();
+ append_string(segments.fHeader, &strs, &lengths);
+ append_string(segments.fGSHeader, &strs, &lengths);
+ append_decls(segments.fGSInputs, gl, &strs, &lengths,
+ &temps, glslGeneration);
+ append_decls(segments.fGSOutputs, gl, &strs, &lengths,
+ &temps, glslGeneration);
+ append_string(segments.fGSCode, &strs, &lengths);
+#if PRINT_SHADERS
+ print_shader(strs.count(), &strs[0], &lengths[0]);
+ GrPrintf("\n");
+#endif
+ programData->fGShaderID =
+ CompileShader(gl, GR_GL_GEOMETRY_SHADER, strs.count(),
+ &strs[0], &lengths[0]);
+ } else {
+ programData->fGShaderID = 0;
+ }
+
+ strs.reset();
+ lengths.reset();
+ temps.reset();
+
+ append_string(segments.fHeader, &strs, &lengths);
+ GrStringBuilder precisionStr(GrGetGLSLShaderPrecisionDecl(gl->fBindingsExported));
+ append_string(precisionStr, &strs, &lengths);
+ append_decls(segments.fFSUnis, gl, &strs, &lengths, &temps, glslGeneration);
+ append_decls(segments.fFSInputs, gl, &strs, &lengths,
+ &temps, glslGeneration);
+ // We shouldn't have declared outputs on 1.10
+ GrAssert(k110_GrGLSLGeneration != glslGeneration ||
+ segments.fFSOutputs.empty());
+ append_decls(segments.fFSOutputs, gl, &strs, &lengths,
+ &temps, glslGeneration);
+ append_string(segments.fFSFunctions, &strs, &lengths);
+ append_string(segments.fFSCode, &strs, &lengths);
+
+#if PRINT_SHADERS
+ print_shader(strs.count(), &strs[0], &lengths[0]);
+ GrPrintf("\n");
+#endif
+
+ programData->fFShaderID =
+ CompileShader(gl, GR_GL_FRAGMENT_SHADER, strs.count(),
+ &strs[0], &lengths[0]);
+
+ if (!programData->fFShaderID) {
+ return false;
+ }
+
+ return true;
+}
+
+GrGLuint GrGLProgram::CompileShader(const GrGLInterface* gl,
+ GrGLenum type,
+ int stringCnt,
+ const char** strings,
+ int* stringLengths) {
+ SK_TRACE_EVENT1("GrGLProgram::CompileShader",
+ "stringCount", SkStringPrintf("%i", stringCnt).c_str());
+
+ GrGLuint shader;
+ GR_GL_CALL_RET(gl, shader, CreateShader(type));
+ if (0 == shader) {
+ return 0;
+ }
+
+ GrGLint compiled = GR_GL_INIT_ZERO;
+ GR_GL_CALL(gl, ShaderSource(shader, stringCnt, strings, stringLengths));
+ GR_GL_CALL(gl, CompileShader(shader));
+ GR_GL_CALL(gl, GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled));
+
+ if (!compiled) {
+ GrGLint infoLen = GR_GL_INIT_ZERO;
+ GR_GL_CALL(gl, GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen));
+ SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
+ if (infoLen > 0) {
+ // retrieve length even though we don't need it to workaround
+ // bug in chrome cmd buffer param validation.
+ GrGLsizei length = GR_GL_INIT_ZERO;
+ GR_GL_CALL(gl, GetShaderInfoLog(shader, infoLen+1,
+ &length, (char*)log.get()));
+ print_shader(stringCnt, strings, stringLengths);
+ GrPrintf("\n%s", log.get());
+ }
+ GrAssert(!"Shader compilation failed!");
+ GR_GL_CALL(gl, DeleteShader(shader));
+ return 0;
+ }
+ return shader;
+}
+
+bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
+ const GrGLInterface* gl,
+ GrStringBuilder texCoordAttrNames[],
+ bool bindColorOut,
+ bool bindDualSrcOut,
+ CachedData* programData) const {
+ GR_GL_CALL_RET(gl, programData->fProgramID, CreateProgram());
+ if (!programData->fProgramID) {
+ return false;
+ }
+ const GrGLint& progID = programData->fProgramID;
+
+ GR_GL_CALL(gl, AttachShader(progID, programData->fVShaderID));
+ if (programData->fGShaderID) {
+ GR_GL_CALL(gl, AttachShader(progID, programData->fGShaderID));
+ }
+ GR_GL_CALL(gl, AttachShader(progID, programData->fFShaderID));
+
+ if (bindColorOut) {
+ GR_GL_CALL(gl, BindFragDataLocation(programData->fProgramID,
+ 0, declared_color_output_name()));
+ }
+ if (bindDualSrcOut) {
+ GR_GL_CALL(gl, BindFragDataLocationIndexed(programData->fProgramID,
+ 0, 1, dual_source_output_name()));
+ }
+
+ // Bind the attrib locations to same values for all shaders
+ GR_GL_CALL(gl, BindAttribLocation(progID, PositionAttributeIdx(),
+ POS_ATTR_NAME));
+ for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+ if (texCoordAttrNames[t].size()) {
+ GR_GL_CALL(gl, BindAttribLocation(progID,
+ TexCoordAttributeIdx(t),
+ texCoordAttrNames[t].c_str()));
+ }
+ }
+
+ if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
+ GR_GL_CALL(gl, BindAttribLocation(progID,
+ ViewMatrixAttributeIdx(),
+ VIEW_MATRIX_NAME));
+ }
+
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ const StageUniLocations& unis = programData->fUniLocations.fStages[s];
+ if (kSetAsAttribute == unis.fTextureMatrixUni) {
+ GrStringBuilder matName;
+ tex_matrix_name(s, &matName);
+ GR_GL_CALL(gl, BindAttribLocation(progID,
+ TextureMatrixAttributeIdx(s),
+ matName.c_str()));
+ }
+ }
+
+ GR_GL_CALL(gl, BindAttribLocation(progID, ColorAttributeIdx(),
+ COL_ATTR_NAME));
+ GR_GL_CALL(gl, BindAttribLocation(progID, CoverageAttributeIdx(),
+ COV_ATTR_NAME));
+ GR_GL_CALL(gl, BindAttribLocation(progID, EdgeAttributeIdx(),
+ EDGE_ATTR_NAME));
+
+ GR_GL_CALL(gl, LinkProgram(progID));
+
+ GrGLint linked = GR_GL_INIT_ZERO;
+ GR_GL_CALL(gl, GetProgramiv(progID, GR_GL_LINK_STATUS, &linked));
+ if (!linked) {
+ GrGLint infoLen = GR_GL_INIT_ZERO;
+ GR_GL_CALL(gl, GetProgramiv(progID, GR_GL_INFO_LOG_LENGTH, &infoLen));
+ SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
+ if (infoLen > 0) {
+ // retrieve length even though we don't need it to workaround
+ // bug in chrome cmd buffer param validation.
+ GrGLsizei length = GR_GL_INIT_ZERO;
+ GR_GL_CALL(gl, GetProgramInfoLog(progID, infoLen+1,
+ &length, (char*)log.get()));
+ GrPrintf((char*)log.get());
+ }
+ GrAssert(!"Error linking program");
+ GR_GL_CALL(gl, DeleteProgram(progID));
+ programData->fProgramID = 0;
+ return false;
+ }
+ return true;
+}
+
+void GrGLProgram::getUniformLocationsAndInitCache(const GrGLInterface* gl,
+ CachedData* programData) const {
+ const GrGLint& progID = programData->fProgramID;
+
+ if (kUseUniform == programData->fUniLocations.fViewMatrixUni) {
+ GR_GL_CALL_RET(gl, programData->fUniLocations.fViewMatrixUni,
+ GetUniformLocation(progID, VIEW_MATRIX_NAME));
+ GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni);
+ }
+ if (kUseUniform == programData->fUniLocations.fColorUni) {
+ GR_GL_CALL_RET(gl, programData->fUniLocations.fColorUni,
+ GetUniformLocation(progID, COL_UNI_NAME));
+ GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
+ }
+ if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
+ GR_GL_CALL_RET(gl, programData->fUniLocations.fColorFilterUni,
+ GetUniformLocation(progID, COL_FILTER_UNI_NAME));
+ GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
+ }
+
+ if (kUseUniform == programData->fUniLocations.fColorMatrixUni) {
+ GR_GL_CALL_RET(gl, programData->fUniLocations.fColorMatrixUni,
+ GetUniformLocation(progID, COL_MATRIX_UNI_NAME));
+ }
+
+ if (kUseUniform == programData->fUniLocations.fColorMatrixVecUni) {
+ GR_GL_CALL_RET(gl, programData->fUniLocations.fColorMatrixVecUni,
+ GetUniformLocation(progID, COL_MATRIX_VEC_UNI_NAME));
+ }
+ if (kUseUniform == programData->fUniLocations.fCoverageUni) {
+ GR_GL_CALL_RET(gl, programData->fUniLocations.fCoverageUni,
+ GetUniformLocation(progID, COV_UNI_NAME));
+ GrAssert(kUnusedUniform != programData->fUniLocations.fCoverageUni);
+ }
+
+ if (kUseUniform == programData->fUniLocations.fEdgesUni) {
+ GR_GL_CALL_RET(gl, programData->fUniLocations.fEdgesUni,
+ GetUniformLocation(progID, EDGES_UNI_NAME));
+ GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
+ } else {
+ programData->fUniLocations.fEdgesUni = kUnusedUniform;
+ }
+
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ StageUniLocations& locations = programData->fUniLocations.fStages[s];
+ if (fProgramDesc.fStages[s].isEnabled()) {
+ if (kUseUniform == locations.fTextureMatrixUni) {
+ GrStringBuilder texMName;
+ tex_matrix_name(s, &texMName);
+ GR_GL_CALL_RET(gl, locations.fTextureMatrixUni,
+ GetUniformLocation(progID, texMName.c_str()));
+ GrAssert(kUnusedUniform != locations.fTextureMatrixUni);
+ }
+
+ if (kUseUniform == locations.fSamplerUni) {
+ GrStringBuilder samplerName;
+ sampler_name(s, &samplerName);
+ GR_GL_CALL_RET(gl, locations.fSamplerUni,
+ GetUniformLocation(progID,samplerName.c_str()));
+ GrAssert(kUnusedUniform != locations.fSamplerUni);
+ }
+
+ if (kUseUniform == locations.fNormalizedTexelSizeUni) {
+ GrStringBuilder texelSizeName;
+ normalized_texel_size_name(s, &texelSizeName);
+ GR_GL_CALL_RET(gl, locations.fNormalizedTexelSizeUni,
+ GetUniformLocation(progID, texelSizeName.c_str()));
+ GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni);
+ }
+
+ if (kUseUniform == locations.fRadial2Uni) {
+ GrStringBuilder radial2ParamName;
+ radial2_param_name(s, &radial2ParamName);
+ GR_GL_CALL_RET(gl, locations.fRadial2Uni,
+ GetUniformLocation(progID, radial2ParamName.c_str()));
+ GrAssert(kUnusedUniform != locations.fRadial2Uni);
+ }
+
+ if (kUseUniform == locations.fTexDomUni) {
+ GrStringBuilder texDomName;
+ tex_domain_name(s, &texDomName);
+ GR_GL_CALL_RET(gl, locations.fTexDomUni,
+ GetUniformLocation(progID, texDomName.c_str()));
+ GrAssert(kUnusedUniform != locations.fTexDomUni);
+ }
+
+ GrStringBuilder kernelName, imageIncrementName;
+ convolve_param_names(s, &kernelName, &imageIncrementName);
+ if (kUseUniform == locations.fKernelUni) {
+ GR_GL_CALL_RET(gl, locations.fKernelUni,
+ GetUniformLocation(progID, kernelName.c_str()));
+ GrAssert(kUnusedUniform != locations.fKernelUni);
+ }
+
+ if (kUseUniform == locations.fImageIncrementUni) {
+ GR_GL_CALL_RET(gl, locations.fImageIncrementUni,
+ GetUniformLocation(progID,
+ imageIncrementName.c_str()));
+ GrAssert(kUnusedUniform != locations.fImageIncrementUni);
+ }
+ }
+ }
+ GR_GL_CALL(gl, UseProgram(progID));
+
+ // init sampler unis and set bogus values for state tracking
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ if (kUnusedUniform != programData->fUniLocations.fStages[s].fSamplerUni) {
+ GR_GL_CALL(gl, Uniform1i(programData->fUniLocations.fStages[s].fSamplerUni, s));
+ }
+ programData->fTextureMatrices[s] = GrMatrix::InvalidMatrix();
+ programData->fRadial2CenterX1[s] = GR_ScalarMax;
+ programData->fRadial2Radius0[s] = -GR_ScalarMax;
+ programData->fTextureWidth[s] = -1;
+ programData->fTextureHeight[s] = -1;
+ }
+ programData->fViewMatrix = GrMatrix::InvalidMatrix();
+ programData->fColor = GrColor_ILLEGAL;
+ programData->fColorFilterColor = GrColor_ILLEGAL;
+}
+
+//============================================================================
+// Stage code generation
+//============================================================================
+
+namespace {
+
+bool isRadialMapping(GrGLProgram::StageDesc::CoordMapping mapping) {
+ return
+ (GrGLProgram::StageDesc::kRadial2Gradient_CoordMapping == mapping ||
+ GrGLProgram::StageDesc::kRadial2GradientDegenerate_CoordMapping == mapping);
+}
+
+GrGLShaderVar* genRadialVS(int stageNum,
+ ShaderCodeSegments* segments,
+ GrGLProgram::StageUniLocations* locations,
+ const char** radial2VaryingVSName,
+ const char** radial2VaryingFSName,
+ const char* varyingVSName,
+ int varyingDims, int coordDims) {
+
+ GrGLShaderVar* radial2FSParams = &segments->fFSUnis.push_back();
+ radial2FSParams->setType(GrGLShaderVar::kFloat_Type);
+ radial2FSParams->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+ radial2FSParams->setArrayCount(6);
+ radial2_param_name(stageNum, radial2FSParams->accessName());
+ segments->fVSUnis.push_back(*radial2FSParams).setEmitPrecision(true);
+
+ locations->fRadial2Uni = kUseUniform;
+
+ // for radial grads without perspective we can pass the linear
+ // part of the quadratic as a varying.
+ if (varyingDims == coordDims) {
+ GrAssert(2 == coordDims);
+ append_varying(GrGLShaderVar::kFloat_Type,
+ "Radial2BCoeff",
+ stageNum,
+ segments,
+ radial2VaryingVSName,
+ radial2VaryingFSName);
+
+ GrStringBuilder radial2p2;
+ GrStringBuilder radial2p3;
+ radial2FSParams->appendArrayAccess(2, &radial2p2);
+ radial2FSParams->appendArrayAccess(3, &radial2p3);
+
+ // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
+ const char* r2ParamName = radial2FSParams->getName().c_str();
+ segments->fVSCode.appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
+ *radial2VaryingVSName, radial2p2.c_str(),
+ varyingVSName, radial2p3.c_str());
+ }
+
+ return radial2FSParams;
+}
+
+bool genRadial2GradientCoordMapping(int stageNum,
+ ShaderCodeSegments* segments,
+ const char* radial2VaryingFSName,
+ GrGLShaderVar* radial2Params,
+ GrStringBuilder& sampleCoords,
+ GrStringBuilder& fsCoordName,
+ int varyingDims,
+ int coordDims) {
+ GrStringBuilder cName("c");
+ GrStringBuilder ac4Name("ac4");
+ GrStringBuilder rootName("root");
+
+ cName.appendS32(stageNum);
+ ac4Name.appendS32(stageNum);
+ rootName.appendS32(stageNum);
+
+ GrStringBuilder radial2p0;
+ GrStringBuilder radial2p1;
+ GrStringBuilder radial2p2;
+ GrStringBuilder radial2p3;
+ GrStringBuilder radial2p4;
+ GrStringBuilder radial2p5;
+ radial2Params->appendArrayAccess(0, &radial2p0);
+ radial2Params->appendArrayAccess(1, &radial2p1);
+ radial2Params->appendArrayAccess(2, &radial2p2);
+ radial2Params->appendArrayAccess(3, &radial2p3);
+ radial2Params->appendArrayAccess(4, &radial2p4);
+ radial2Params->appendArrayAccess(5, &radial2p5);
+
+ // if we were able to interpolate the linear component bVar is the varying
+ // otherwise compute it
+ GrStringBuilder bVar;
+ if (coordDims == varyingDims) {
+ bVar = radial2VaryingFSName;
+ GrAssert(2 == varyingDims);
+ } else {
+ GrAssert(3 == varyingDims);
+ bVar = "b";
+ bVar.appendS32(stageNum);
+ segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
+ bVar.c_str(), radial2p2.c_str(),
+ fsCoordName.c_str(), radial2p3.c_str());
+ }
+
+ // c = (x^2)+(y^2) - params[4]
+ segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s;\n",
+ cName.c_str(), fsCoordName.c_str(),
+ fsCoordName.c_str(),
+ radial2p4.c_str());
+ // ac4 = 4.0 * params[0] * c
+ segments->fFSCode.appendf("\tfloat %s = %s * 4.0 * %s;\n",
+ ac4Name.c_str(), radial2p0.c_str(),
+ cName.c_str());
+
+ // root = sqrt(b^2-4ac)
+ // (abs to avoid exception due to fp precision)
+ segments->fFSCode.appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
+ rootName.c_str(), bVar.c_str(), bVar.c_str(),
+ ac4Name.c_str());
+
+ // x coord is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
+ // y coord is 0.5 (texture is effectively 1D)
+ sampleCoords.printf("vec2((-%s + %s * %s) * %s, 0.5)",
+ bVar.c_str(), radial2p5.c_str(),
+ rootName.c_str(), radial2p1.c_str());
+ return true;
+}
+
+bool genRadial2GradientDegenerateCoordMapping(int stageNum,
+ ShaderCodeSegments* segments,
+ const char* radial2VaryingFSName,
+ GrGLShaderVar* radial2Params,
+ GrStringBuilder& sampleCoords,
+ GrStringBuilder& fsCoordName,
+ int varyingDims,
+ int coordDims) {
+ GrStringBuilder cName("c");
+
+ cName.appendS32(stageNum);
+
+ GrStringBuilder radial2p2;
+ GrStringBuilder radial2p3;
+ GrStringBuilder radial2p4;
+ radial2Params->appendArrayAccess(2, &radial2p2);
+ radial2Params->appendArrayAccess(3, &radial2p3);
+ radial2Params->appendArrayAccess(4, &radial2p4);
+
+ // if we were able to interpolate the linear component bVar is the varying
+ // otherwise compute it
+ GrStringBuilder bVar;
+ if (coordDims == varyingDims) {
+ bVar = radial2VaryingFSName;
+ GrAssert(2 == varyingDims);
+ } else {
+ GrAssert(3 == varyingDims);
+ bVar = "b";
+ bVar.appendS32(stageNum);
+ segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
+ bVar.c_str(), radial2p2.c_str(),
+ fsCoordName.c_str(), radial2p3.c_str());
+ }
+
+ // c = (x^2)+(y^2) - params[4]
+ segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s;\n",
+ cName.c_str(), fsCoordName.c_str(),
+ fsCoordName.c_str(),
+ radial2p4.c_str());
+
+ // x coord is: -c/b
+ // y coord is 0.5 (texture is effectively 1D)
+ sampleCoords.printf("vec2((-%s / %s), 0.5)", cName.c_str(), bVar.c_str());
+ return true;
+}
+
+void gen2x2FS(int stageNum,
+ ShaderCodeSegments* segments,
+ GrGLProgram::StageUniLocations* locations,
+ GrStringBuilder* sampleCoords,
+ const char* samplerName,
+ const char* texelSizeName,
+ const char* swizzle,
+ const char* fsOutColor,
+ GrStringBuilder& texFunc,
+ GrStringBuilder& modulate,
+ bool complexCoord,
+ int coordDims) {
+ locations->fNormalizedTexelSizeUni = kUseUniform;
+ if (complexCoord) {
+ // assign the coord to a var rather than compute 4x.
+ GrStringBuilder coordVar("tCoord");
+ coordVar.appendS32(stageNum);
+ segments->fFSCode.appendf("\t%s %s = %s;\n",
+ float_vector_type_str(coordDims),
+ coordVar.c_str(), sampleCoords->c_str());
+ *sampleCoords = coordVar;
+ }
+ GrAssert(2 == coordDims);
+ GrStringBuilder accumVar("accum");
+ accumVar.appendS32(stageNum);
+ segments->fFSCode.appendf("\tvec4 %s = %s(%s, %s + vec2(-%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
+ segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
+ segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(-%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
+ segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
+ segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
+
+}
+
+void genConvolutionVS(int stageNum,
+ const StageDesc& desc,
+ ShaderCodeSegments* segments,
+ GrGLProgram::StageUniLocations* locations,
+ GrGLShaderVar** kernel,
+ const char** imageIncrementName,
+ const char* varyingVSName) {
+ //GrGLShaderVar* kernel = &segments->fFSUnis.push_back();
+ *kernel = &segments->fFSUnis.push_back();
+ (*kernel)->setType(GrGLShaderVar::kFloat_Type);
+ (*kernel)->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+ (*kernel)->setArrayCount(desc.fKernelWidth);
+ GrGLShaderVar* imgInc = &segments->fFSUnis.push_back();
+ imgInc->setType(GrGLShaderVar::kVec2f_Type);
+ imgInc->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+
+ convolve_param_names(stageNum,
+ (*kernel)->accessName(),
+ imgInc->accessName());
+ *imageIncrementName = imgInc->getName().c_str();
+
+ // need image increment in both VS and FS
+ segments->fVSUnis.push_back(*imgInc).setEmitPrecision(true);
+
+ locations->fKernelUni = kUseUniform;
+ locations->fImageIncrementUni = kUseUniform;
+ float scale = (desc.fKernelWidth - 1) * 0.5f;
+ segments->fVSCode.appendf("\t%s -= vec2(%g, %g) * %s;\n",
+ varyingVSName, scale, scale,
+ *imageIncrementName);
+}
+
+void genConvolutionFS(int stageNum,
+ const StageDesc& desc,
+ ShaderCodeSegments* segments,
+ const char* samplerName,
+ GrGLShaderVar* kernel,
+ const char* swizzle,
+ const char* imageIncrementName,
+ const char* fsOutColor,
+ GrStringBuilder& sampleCoords,
+ GrStringBuilder& texFunc,
+ GrStringBuilder& modulate) {
+ GrStringBuilder sumVar("sum");
+ sumVar.appendS32(stageNum);
+ GrStringBuilder coordVar("coord");
+ coordVar.appendS32(stageNum);
+
+ GrStringBuilder kernelIndex;
+ kernel->appendArrayAccess("i", &kernelIndex);
+
+ segments->fFSCode.appendf("\tvec4 %s = vec4(0, 0, 0, 0);\n",
+ sumVar.c_str());
+ segments->fFSCode.appendf("\tvec2 %s = %s;\n",
+ coordVar.c_str(),
+ sampleCoords.c_str());
+ segments->fFSCode.appendf("\tfor (int i = 0; i < %d; i++) {\n",
+ desc.fKernelWidth);
+ segments->fFSCode.appendf("\t\t%s += %s(%s, %s)%s * %s;\n",
+ sumVar.c_str(), texFunc.c_str(),
+ samplerName, coordVar.c_str(), swizzle,
+ kernelIndex.c_str());
+ segments->fFSCode.appendf("\t\t%s += %s;\n",
+ coordVar.c_str(),
+ imageIncrementName);
+ segments->fFSCode.appendf("\t}\n");
+ segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
+ sumVar.c_str(), modulate.c_str());
+}
+
+}
+
+void GrGLProgram::genStageCode(const GrGLInterface* gl,
+ int stageNum,
+ const GrGLProgram::StageDesc& desc,
+ const char* fsInColor, // NULL means no incoming color
+ const char* fsOutColor,
+ const char* vsInCoord,
+ ShaderCodeSegments* segments,
+ StageUniLocations* locations) const {
+
+ GrAssert(stageNum >= 0 && stageNum <= GrDrawState::kNumStages);
+ GrAssert((desc.fInConfigFlags & StageDesc::kInConfigBitMask) ==
+ desc.fInConfigFlags);
+
+ // First decide how many coords are needed to access the texture
+ // Right now it's always 2 but we could start using 1D textures for
+ // gradients.
+ static const int coordDims = 2;
+ int varyingDims;
+ /// Vertex Shader Stuff
+
+ // decide whether we need a matrix to transform texture coords
+ // and whether the varying needs a perspective coord.
+ const char* matName = NULL;
+ if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
+ varyingDims = coordDims;
+ } else {
+ GrGLShaderVar* mat;
+ #if GR_GL_ATTRIBUTE_MATRICES
+ mat = &segments->fVSAttrs.push_back();
+ mat->setTypeModifier(GrGLShaderVar::kAttribute_TypeModifier);
+ locations->fTextureMatrixUni = kSetAsAttribute;
+ #else
+ mat = &segments->fVSUnis.push_back();
+ mat->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+ locations->fTextureMatrixUni = kUseUniform;
+ #endif
+ tex_matrix_name(stageNum, mat->accessName());
+ mat->setType(GrGLShaderVar::kMat33f_Type);
+ matName = mat->getName().c_str();
+
+ if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
+ varyingDims = coordDims;
+ } else {
+ varyingDims = coordDims + 1;
+ }
+ }
+
+ segments->fFSUnis.push_back().set(GrGLShaderVar::kSampler2D_Type,
+ GrGLShaderVar::kUniform_TypeModifier, "");
+ sampler_name(stageNum, segments->fFSUnis.back().accessName());
+ locations->fSamplerUni = kUseUniform;
+ const char* samplerName = segments->fFSUnis.back().getName().c_str();
+
+ const char* texelSizeName = NULL;
+ if (StageDesc::k2x2_FetchMode == desc.fFetchMode) {
+ segments->fFSUnis.push_back().set(GrGLShaderVar::kVec2f_Type,
+ GrGLShaderVar::kUniform_TypeModifier, "");
+ normalized_texel_size_name(stageNum, segments->fFSUnis.back().accessName());
+ texelSizeName = segments->fFSUnis.back().getName().c_str();
+ }
+
+ const char *varyingVSName, *varyingFSName;
+ append_varying(float_vector_type(varyingDims),
+ "Stage",
+ stageNum,
+ segments,
+ &varyingVSName,
+ &varyingFSName);
+
+ if (!matName) {
+ GrAssert(varyingDims == coordDims);
+ segments->fVSCode.appendf("\t%s = %s;\n", varyingVSName, vsInCoord);
+ } else {
+ // varying = texMatrix * texCoord
+ segments->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
+ varyingVSName, matName, vsInCoord,
+ vector_all_coords(varyingDims));
+ }
+
+ GrGLShaderVar* radial2Params = NULL;
+ const char* radial2VaryingVSName = NULL;
+ const char* radial2VaryingFSName = NULL;
+
+ if (isRadialMapping((StageDesc::CoordMapping) desc.fCoordMapping)) {
+ radial2Params = genRadialVS(stageNum, segments,
+ locations,
+ &radial2VaryingVSName,
+ &radial2VaryingFSName,
+ varyingVSName,
+ varyingDims, coordDims);
+ }
+
+ GrGLShaderVar* kernel = NULL;
+ const char* imageIncrementName = NULL;
+ if (StageDesc::kConvolution_FetchMode == desc.fFetchMode) {
+ genConvolutionVS(stageNum, desc, segments, locations,
+ &kernel, &imageIncrementName, varyingVSName);
+ }
+
+ /// Fragment Shader Stuff
+ GrStringBuilder fsCoordName;
+ // function used to access the shader, may be made projective
+ GrStringBuilder texFunc("texture2D");
+ if (desc.fOptFlags & (StageDesc::kIdentityMatrix_OptFlagBit |
+ StageDesc::kNoPerspective_OptFlagBit)) {
+ GrAssert(varyingDims == coordDims);
+ fsCoordName = varyingFSName;
+ } else {
+ // if we have to do some special op on the varyings to get
+ // our final tex coords then when in perspective we have to
+ // do an explicit divide. Otherwise, we can use a Proj func.
+ if (StageDesc::kIdentity_CoordMapping == desc.fCoordMapping &&
+ StageDesc::kSingle_FetchMode == desc.fFetchMode) {
+ texFunc.append("Proj");
+ fsCoordName = varyingFSName;
+ } else {
+ fsCoordName = "inCoord";
+ fsCoordName.appendS32(stageNum);
+ segments->fFSCode.appendf("\t%s %s = %s%s / %s%s;\n",
+ GrGLShaderVar::TypeString(float_vector_type(coordDims)),
+ fsCoordName.c_str(),
+ varyingFSName,
+ vector_nonhomog_coords(varyingDims),
+ varyingFSName,
+ vector_homog_coord(varyingDims));
+ }
+ }
+
+ GrStringBuilder sampleCoords;
+ bool complexCoord = false;
+ switch (desc.fCoordMapping) {
+ case StageDesc::kIdentity_CoordMapping:
+ sampleCoords = fsCoordName;
+ break;
+ case StageDesc::kSweepGradient_CoordMapping:
+ sampleCoords.printf("vec2(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5, 0.5)", fsCoordName.c_str(), fsCoordName.c_str());
+ complexCoord = true;
+ break;
+ case StageDesc::kRadialGradient_CoordMapping:
+ sampleCoords.printf("vec2(length(%s.xy), 0.5)", fsCoordName.c_str());
+ complexCoord = true;
+ break;
+ case StageDesc::kRadial2Gradient_CoordMapping:
+ complexCoord = genRadial2GradientCoordMapping(
+ stageNum, segments,
+ radial2VaryingFSName, radial2Params,
+ sampleCoords, fsCoordName,
+ varyingDims, coordDims);
+
+ break;
+ case StageDesc::kRadial2GradientDegenerate_CoordMapping:
+ complexCoord = genRadial2GradientDegenerateCoordMapping(
+ stageNum, segments,
+ radial2VaryingFSName, radial2Params,
+ sampleCoords, fsCoordName,
+ varyingDims, coordDims);
+ break;
+
+ };
+
+ const char* swizzle = "";
+ if (desc.fInConfigFlags & StageDesc::kSwapRAndB_InConfigFlag) {
+ GrAssert(!(desc.fInConfigFlags & StageDesc::kSmearAlpha_InConfigFlag));
+ swizzle = ".bgra";
+ } else if (desc.fInConfigFlags & StageDesc::kSmearAlpha_InConfigFlag) {
+ GrAssert(!(desc.fInConfigFlags &
+ StageDesc::kMulRGBByAlpha_InConfigFlag));
+ swizzle = ".aaaa";
+ }
+
+ GrStringBuilder modulate;
+ if (NULL != fsInColor) {
+ modulate.printf(" * %s", fsInColor);
+ }
+
+ if (desc.fOptFlags &
+ StageDesc::kCustomTextureDomain_OptFlagBit) {
+ GrStringBuilder texDomainName;
+ tex_domain_name(stageNum, &texDomainName);
+ segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kUniform_TypeModifier, texDomainName);
+ GrStringBuilder coordVar("clampCoord");
+ segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
+ float_vector_type_str(coordDims),
+ coordVar.c_str(),
+ sampleCoords.c_str(),
+ texDomainName.c_str(),
+ texDomainName.c_str());
+ sampleCoords = coordVar;
+ locations->fTexDomUni = kUseUniform;
+ }
+
+ switch (desc.fFetchMode) {
+ case StageDesc::k2x2_FetchMode:
+ GrAssert(!(desc.fInConfigFlags &
+ StageDesc::kMulRGBByAlpha_InConfigFlag));
+ gen2x2FS(stageNum, segments, locations, &sampleCoords,
+ samplerName, texelSizeName, swizzle, fsOutColor,
+ texFunc, modulate, complexCoord, coordDims);
+ break;
+ case StageDesc::kConvolution_FetchMode:
+ GrAssert(!(desc.fInConfigFlags &
+ StageDesc::kMulRGBByAlpha_InConfigFlag));
+ genConvolutionFS(stageNum, desc, segments,
+ samplerName, kernel, swizzle, imageIncrementName, fsOutColor,
+ sampleCoords, texFunc, modulate);
+ break;
+ default:
+ if (desc.fInConfigFlags & StageDesc::kMulRGBByAlpha_InConfigFlag) {
+ GrAssert(!(desc.fInConfigFlags &
+ StageDesc::kSmearAlpha_InConfigFlag));
+ segments->fFSCode.appendf("\t%s = %s(%s, %s)%s;\n",
+ fsOutColor, texFunc.c_str(),
+ samplerName, sampleCoords.c_str(),
+ swizzle);
+ segments->fFSCode.appendf("\t%s = vec4(%s.rgb*%s.a,%s.a)%s;\n",
+ fsOutColor, fsOutColor, fsOutColor,
+ fsOutColor, modulate.c_str());
+ } else {
+ segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n",
+ fsOutColor, texFunc.c_str(),
+ samplerName, sampleCoords.c_str(),
+ swizzle, modulate.c_str());
+ }
+ }
+}
+
+
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
new file mode 100644
index 0000000000..dc6093ed16
--- /dev/null
+++ b/src/gpu/gl/GrGLProgram.h
@@ -0,0 +1,390 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLProgram_DEFINED
+#define GrGLProgram_DEFINED
+
+#include "../GrDrawState.h"
+#include "GrGLInterface.h"
+#include "GrGLSL.h"
+#include "../GrStringBuilder.h"
+#include "../GrGpu.h"
+
+#include "SkXfermode.h"
+
+class GrBinHashKeyBuilder;
+
+struct ShaderCodeSegments;
+
+// optionally compile the experimental GS code. Set to GR_DEBUG
+// so that debug build bots will execute the code.
+#define GR_GL_EXPERIMENTAL_GS GR_DEBUG
+
+/**
+ * This class manages a GPU program and records per-program information.
+ * We can specify the attribute locations so that they are constant
+ * across our shaders. But the driver determines the uniform locations
+ * at link time. We don't need to remember the sampler uniform location
+ * because we will bind a texture slot to it and never change it
+ * Uniforms are program-local so we can't rely on fHWState to hold the
+ * previous uniform state after a program change.
+ */
+class GrGLProgram {
+public:
+
+ class CachedData;
+
+ GrGLProgram();
+ ~GrGLProgram();
+
+ /**
+ * This is the heavy initilization routine for building a GLProgram.
+ * The result of heavy init is not stored in datamembers of GrGLProgam,
+ * but in a separate cacheable container.
+ */
+ bool genProgram(const GrGLInterface* gl,
+ GrGLSLGeneration glslVersion,
+ CachedData* programData) const;
+
+ /**
+ * The shader may modify the blend coeffecients. Params are in/out
+ */
+ void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
+
+ /**
+ * Attribute indices. These should not overlap. Matrices consume 3 slots.
+ */
+ static int PositionAttributeIdx() { return 0; }
+ static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
+ static int ColorAttributeIdx() { return 1 + GrDrawState::kMaxTexCoords; }
+ static int CoverageAttributeIdx() {
+ return 2 + GrDrawState::kMaxTexCoords;
+ }
+ static int EdgeAttributeIdx() { return 3 + GrDrawState::kMaxTexCoords; }
+
+ static int ViewMatrixAttributeIdx() {
+ return 4 + GrDrawState::kMaxTexCoords;
+ }
+ static int TextureMatrixAttributeIdx(int stage) {
+ return 7 + GrDrawState::kMaxTexCoords + 3 * stage;
+ }
+
+public:
+
+ // Parameters that affect code generation
+ // These structs should be kept compact; they are the input to an
+ // expensive hash key generator.
+ struct ProgramDesc {
+ ProgramDesc() {
+ // since we use this as part of a key we can't have any unitialized
+ // padding
+ memset(this, 0, sizeof(ProgramDesc));
+ }
+
+ enum OutputPM {
+ // PM-color OR color with no alpha channel
+ kYes_OutputPM,
+ // nonPM-color with alpha channel
+ kNo_OutputPM,
+
+ kOutputPMCnt
+ };
+
+ struct StageDesc {
+ enum OptFlagBits {
+ kNoPerspective_OptFlagBit = 1 << 0,
+ kIdentityMatrix_OptFlagBit = 1 << 1,
+ kCustomTextureDomain_OptFlagBit = 1 << 2,
+ kIsEnabled_OptFlagBit = 1 << 7
+ };
+ enum FetchMode {
+ kSingle_FetchMode,
+ k2x2_FetchMode,
+ kConvolution_FetchMode,
+
+ kFetchModeCnt,
+ };
+ /**
+ Flags set based on a src texture's pixel config. The operations
+ described are performed after reading a texel.
+ */
+ enum InConfigFlags {
+ kNone_InConfigFlag = 0x0,
+
+ /**
+ Swap the R and B channels. This is incompatible with
+ kSmearAlpha. It is prefereable to perform the swizzle outside
+ the shader using GL_ARB_texture_swizzle if possible rather
+ than setting this flag.
+ */
+ kSwapRAndB_InConfigFlag = 0x1,
+
+ /**
+ Smear alpha across all four channels. This is incompatible with
+ kSwapRAndB and kPremul. It is prefereable to perform the
+ smear outside the shader using GL_ARB_texture_swizzle if
+ possible rather than setting this flag.
+ */
+ kSmearAlpha_InConfigFlag = 0x2,
+
+ /**
+ Multiply r,g,b by a after texture reads. This flag incompatible
+ with kSmearAlpha and may only be used with FetchMode kSingle.
+ */
+ kMulRGBByAlpha_InConfigFlag = 0x4,
+
+ kDummyInConfigFlag,
+ kInConfigBitMask = (kDummyInConfigFlag-1) |
+ (kDummyInConfigFlag-2)
+ };
+ enum CoordMapping {
+ kIdentity_CoordMapping,
+ kRadialGradient_CoordMapping,
+ kSweepGradient_CoordMapping,
+ kRadial2Gradient_CoordMapping,
+ // need different shader computation when quadratic
+ // eq describing the gradient degenerates to a linear eq.
+ kRadial2GradientDegenerate_CoordMapping,
+ kCoordMappingCnt
+ };
+
+ uint8_t fOptFlags;
+ uint8_t fInConfigFlags; // bitfield of InConfigFlags values
+ uint8_t fFetchMode; // casts to enum FetchMode
+ uint8_t fCoordMapping; // casts to enum CoordMapping
+ uint8_t fKernelWidth;
+
+ GR_STATIC_ASSERT((InConfigFlags)(uint8_t)kInConfigBitMask ==
+ kInConfigBitMask);
+
+ inline bool isEnabled() const {
+ return SkToBool(fOptFlags & kIsEnabled_OptFlagBit);
+ }
+ inline void setEnabled(bool newValue) {
+ if (newValue) {
+ fOptFlags |= kIsEnabled_OptFlagBit;
+ } else {
+ fOptFlags &= ~kIsEnabled_OptFlagBit;
+ }
+ }
+ };
+
+ // Specifies where the intitial color comes from before the stages are
+ // applied.
+ enum ColorInput {
+ kSolidWhite_ColorInput,
+ kTransBlack_ColorInput,
+ kAttribute_ColorInput,
+ kUniform_ColorInput,
+
+ kColorInputCnt
+ };
+ // Dual-src blending makes use of a secondary output color that can be
+ // used as a per-pixel blend coeffecient. This controls whether a
+ // secondary source is output and what value it holds.
+ enum DualSrcOutput {
+ kNone_DualSrcOutput,
+ kCoverage_DualSrcOutput,
+ kCoverageISA_DualSrcOutput,
+ kCoverageISC_DualSrcOutput,
+
+ kDualSrcOutputCnt
+ };
+
+ GrDrawState::VertexEdgeType fVertexEdgeType;
+
+ // stripped of bits that don't affect prog generation
+ GrVertexLayout fVertexLayout;
+
+ StageDesc fStages[GrDrawState::kNumStages];
+
+ // To enable experimental geometry shader code (not for use in
+ // production)
+#if GR_GL_EXPERIMENTAL_GS
+ bool fExperimentalGS;
+#endif
+
+ uint8_t fColorInput; // casts to enum ColorInput
+ uint8_t fCoverageInput; // casts to enum CoverageInput
+ uint8_t fOutputPM; // cases to enum OutputPM
+ uint8_t fDualSrcOutput; // casts to enum DualSrcOutput
+ int8_t fFirstCoverageStage;
+ SkBool8 fEmitsPointSize;
+ SkBool8 fEdgeAAConcave;
+ SkBool8 fColorMatrixEnabled;
+
+ int8_t fEdgeAANumEdges;
+ uint8_t fColorFilterXfermode; // casts to enum SkXfermode::Mode
+ int8_t fPadding[3];
+
+ } fProgramDesc;
+ GR_STATIC_ASSERT(!(sizeof(ProgramDesc) % 4));
+
+ // for code readability
+ typedef ProgramDesc::StageDesc StageDesc;
+
+private:
+
+ const ProgramDesc& getDesc() { return fProgramDesc; }
+ const char* adjustInColor(const GrStringBuilder& inColor) const;
+
+public:
+ enum {
+ kUnusedUniform = -1,
+ kSetAsAttribute = 1000,
+ };
+
+ struct StageUniLocations {
+ GrGLint fTextureMatrixUni;
+ GrGLint fNormalizedTexelSizeUni;
+ GrGLint fSamplerUni;
+ GrGLint fRadial2Uni;
+ GrGLint fTexDomUni;
+ GrGLint fKernelUni;
+ GrGLint fImageIncrementUni;
+ void reset() {
+ fTextureMatrixUni = kUnusedUniform;
+ fNormalizedTexelSizeUni = kUnusedUniform;
+ fSamplerUni = kUnusedUniform;
+ fRadial2Uni = kUnusedUniform;
+ fTexDomUni = kUnusedUniform;
+ fKernelUni = kUnusedUniform;
+ fImageIncrementUni = kUnusedUniform;
+ }
+ };
+
+ struct UniLocations {
+ GrGLint fViewMatrixUni;
+ GrGLint fColorUni;
+ GrGLint fCoverageUni;
+ GrGLint fEdgesUni;
+ GrGLint fColorFilterUni;
+ GrGLint fColorMatrixUni;
+ GrGLint fColorMatrixVecUni;
+ StageUniLocations fStages[GrDrawState::kNumStages];
+ void reset() {
+ fViewMatrixUni = kUnusedUniform;
+ fColorUni = kUnusedUniform;
+ fCoverageUni = kUnusedUniform;
+ fEdgesUni = kUnusedUniform;
+ fColorFilterUni = kUnusedUniform;
+ fColorMatrixUni = kUnusedUniform;
+ fColorMatrixVecUni = kUnusedUniform;
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ fStages[s].reset();
+ }
+ }
+ };
+
+ class CachedData : public ::GrNoncopyable {
+ public:
+ CachedData() {
+ }
+
+ ~CachedData() {
+ }
+
+ void copyAndTakeOwnership(CachedData& other) {
+ memcpy(this, &other, sizeof(*this));
+ }
+
+ public:
+
+ // IDs
+ GrGLuint fVShaderID;
+ GrGLuint fGShaderID;
+ GrGLuint fFShaderID;
+ GrGLuint fProgramID;
+ // shader uniform locations (-1 if shader doesn't use them)
+ UniLocations fUniLocations;
+
+ GrMatrix fViewMatrix;
+
+ // these reflect the current values of uniforms
+ // (GL uniform values travel with program)
+ GrColor fColor;
+ GrColor fCoverage;
+ GrColor fColorFilterColor;
+ GrMatrix fTextureMatrices[GrDrawState::kNumStages];
+ // width and height used for normalized texel size
+ int fTextureWidth[GrDrawState::kNumStages];
+ int fTextureHeight[GrDrawState::kNumStages];
+ GrScalar fRadial2CenterX1[GrDrawState::kNumStages];
+ GrScalar fRadial2Radius0[GrDrawState::kNumStages];
+ bool fRadial2PosRoot[GrDrawState::kNumStages];
+ GrRect fTextureDomain[GrDrawState::kNumStages];
+
+ private:
+ enum Constants {
+ kUniLocationPreAllocSize = 8
+ };
+
+ }; // CachedData
+
+ enum Constants {
+ kProgramKeySize = sizeof(ProgramDesc)
+ };
+
+ // Provide an opaque ProgramDesc
+ const uint32_t* keyData() const{
+ return reinterpret_cast<const uint32_t*>(&fProgramDesc);
+ }
+
+private:
+
+ // Determines which uniforms will need to be bound.
+ void genStageCode(const GrGLInterface* gl,
+ int stageNum,
+ const ProgramDesc::StageDesc& desc,
+ const char* fsInColor, // NULL means no incoming color
+ const char* fsOutColor,
+ const char* vsInCoord,
+ ShaderCodeSegments* segments,
+ StageUniLocations* locations) const;
+
+ void genGeometryShader(const GrGLInterface* gl,
+ GrGLSLGeneration glslVersion,
+ ShaderCodeSegments* segments) const;
+
+ // generates code to compute coverage based on edge AA.
+ void genEdgeCoverage(const GrGLInterface* gl,
+ GrVertexLayout layout,
+ CachedData* programData,
+ GrStringBuilder* coverageVar,
+ ShaderCodeSegments* segments) const;
+
+ static bool CompileShaders(const GrGLInterface* gl,
+ GrGLSLGeneration glslVersion,
+ const ShaderCodeSegments& segments,
+ CachedData* programData);
+
+ // Compiles a GL shader, returns shader ID or 0 if failed
+ // params have same meaning as glShaderSource
+ static GrGLuint CompileShader(const GrGLInterface* gl,
+ GrGLenum type, int stringCnt,
+ const char** strings,
+ int* stringLengths);
+
+ // Creates a GL program ID, binds shader attributes to GL vertex attrs, and
+ // links the program
+ bool bindOutputsAttribsAndLinkProgram(
+ const GrGLInterface* gl,
+ GrStringBuilder texCoordAttrNames[GrDrawState::kMaxTexCoords],
+ bool bindColorOut,
+ bool bindDualSrcOut,
+ CachedData* programData) const;
+
+ // Binds uniforms; initializes cache to invalid values.
+ void getUniformLocationsAndInitCache(const GrGLInterface* gl,
+ CachedData* programData) const;
+
+ friend class GrGpuGLShaders;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
new file mode 100644
index 0000000000..c98914a3b8
--- /dev/null
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -0,0 +1,97 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLRenderTarget.h"
+
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+void GrGLRenderTarget::init(const Desc& desc,
+ const GrGLIRect& viewport,
+ GrGLTexID* texID) {
+ fRTFBOID = desc.fRTFBOID;
+ fTexFBOID = desc.fTexFBOID;
+ fMSColorRenderbufferID = desc.fMSColorRenderbufferID;
+ fViewport = viewport;
+ fOwnIDs = desc.fOwnIDs;
+ fTexIDObj = texID;
+ GrSafeRef(fTexIDObj);
+}
+
+GrGLRenderTarget::GrGLRenderTarget(GrGpuGL* gpu,
+ const Desc& desc,
+ const GrGLIRect& viewport,
+ GrGLTexID* texID,
+ GrGLTexture* texture)
+ : INHERITED(gpu,
+ texture,
+ viewport.fWidth,
+ viewport.fHeight,
+ desc.fConfig,
+ desc.fSampleCnt) {
+ GrAssert(NULL != texID);
+ GrAssert(NULL != texture);
+ // FBO 0 can't also be a texture, right?
+ GrAssert(0 != desc.fRTFBOID);
+ GrAssert(0 != desc.fTexFBOID);
+
+ // we assume this is true, TODO: get rid of viewport as a param.
+ GrAssert(viewport.fWidth == texture->width());
+ GrAssert(viewport.fHeight == texture->height());
+
+ this->init(desc, viewport, texID);
+}
+
+GrGLRenderTarget::GrGLRenderTarget(GrGpuGL* gpu,
+ const Desc& desc,
+ const GrGLIRect& viewport)
+ : INHERITED(gpu,
+ NULL,
+ viewport.fWidth,
+ viewport.fHeight,
+ desc.fConfig,
+ desc.fSampleCnt) {
+ this->init(desc, viewport, NULL);
+}
+
+void GrGLRenderTarget::onRelease() {
+ GPUGL->notifyRenderTargetDelete(this);
+ if (fOwnIDs) {
+ if (fTexFBOID) {
+ GL_CALL(DeleteFramebuffers(1, &fTexFBOID));
+ }
+ if (fRTFBOID && fRTFBOID != fTexFBOID) {
+ GL_CALL(DeleteFramebuffers(1, &fRTFBOID));
+ }
+ if (fMSColorRenderbufferID) {
+ GL_CALL(DeleteRenderbuffers(1, &fMSColorRenderbufferID));
+ }
+ }
+ fRTFBOID = 0;
+ fTexFBOID = 0;
+ fMSColorRenderbufferID = 0;
+ GrSafeUnref(fTexIDObj);
+ fTexIDObj = NULL;
+ this->setStencilBuffer(NULL);
+}
+
+void GrGLRenderTarget::onAbandon() {
+ fRTFBOID = 0;
+ fTexFBOID = 0;
+ fMSColorRenderbufferID = 0;
+ if (NULL != fTexIDObj) {
+ fTexIDObj->abandon();
+ fTexIDObj = NULL;
+ }
+ this->setStencilBuffer(NULL);
+}
+
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
new file mode 100644
index 0000000000..eb817df6f0
--- /dev/null
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -0,0 +1,110 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLRenderTarget_DEFINED
+#define GrGLRenderTarget_DEFINED
+
+#include "GrGLIRect.h"
+#include "GrRenderTarget.h"
+#include "GrScalar.h"
+
+class GrGpuGL;
+class GrGLTexture;
+class GrGLTexID;
+
+class GrGLRenderTarget : public GrRenderTarget {
+
+public:
+ // set fTexFBOID to this value to indicate that it is multisampled but
+ // Gr doesn't know how to resolve it.
+ enum { kUnresolvableFBOID = 0 };
+
+ struct Desc {
+ GrGLuint fRTFBOID;
+ GrGLuint fTexFBOID;
+ GrGLuint fMSColorRenderbufferID;
+ bool fOwnIDs;
+ GrPixelConfig fConfig;
+ int fSampleCnt;
+ };
+
+ // creates a GrGLRenderTarget associated with a texture
+ GrGLRenderTarget(GrGpuGL* gpu,
+ const Desc& desc,
+ const GrGLIRect& viewport,
+ GrGLTexID* texID,
+ GrGLTexture* texture);
+
+ // creates an independent GrGLRenderTarget
+ GrGLRenderTarget(GrGpuGL* gpu,
+ const Desc& desc,
+ const GrGLIRect& viewport);
+
+ virtual ~GrGLRenderTarget() { this->release(); }
+
+ void setViewport(const GrGLIRect& rect) { fViewport = rect; }
+ const GrGLIRect& getViewport() const { return fViewport; }
+
+ // The following two functions return the same ID when a
+ // texture-rendertarget is multisampled, and different IDs when
+ // it is.
+ // FBO ID used to render into
+ GrGLuint renderFBOID() const { return fRTFBOID; }
+ // FBO ID that has texture ID attached.
+ GrGLuint textureFBOID() const { return fTexFBOID; }
+
+ // override of GrRenderTarget
+ virtual intptr_t getRenderTargetHandle() const {
+ return this->renderFBOID();
+ }
+ virtual intptr_t getRenderTargetResolvedHandle() const {
+ return this->textureFBOID();
+ }
+ virtual ResolveType getResolveType() const {
+
+ if (!this->isMultisampled() ||
+ fRTFBOID == fTexFBOID) {
+ // catches FBO 0 and non MSAA case
+ return kAutoResolves_ResolveType;
+ } else if (kUnresolvableFBOID == fTexFBOID) {
+ return kCantResolve_ResolveType;
+ } else {
+ return kCanResolve_ResolveType;
+ }
+ }
+
+protected:
+ // override of GrResource
+ virtual void onAbandon();
+ virtual void onRelease();
+
+private:
+ GrGLuint fRTFBOID;
+ GrGLuint fTexFBOID;
+
+ GrGLuint fMSColorRenderbufferID;
+
+ // Should this object delete IDs when it is destroyed or does someone
+ // else own them.
+ bool fOwnIDs;
+
+ // when we switch to this rendertarget we want to set the viewport to
+ // only render to to content area (as opposed to the whole allocation) and
+ // we want the rendering to be at top left (GL has origin in bottom left)
+ GrGLIRect fViewport;
+
+ // non-NULL if this RT was created by Gr with an associated GrGLTexture.
+ GrGLTexID* fTexIDObj;
+
+ void init(const Desc& desc, const GrGLIRect& viewport, GrGLTexID* texID);
+
+ typedef GrRenderTarget INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLSL.cpp b/src/gpu/gl/GrGLSL.cpp
new file mode 100644
index 0000000000..e933ee87c9
--- /dev/null
+++ b/src/gpu/gl/GrGLSL.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLSL.h"
+#include "GrGLShaderVar.h"
+
+GrGLSLGeneration GrGetGLSLGeneration(GrGLBinding binding,
+ const GrGLInterface* gl) {
+ GrGLSLVersion ver = GrGLGetGLSLVersion(gl);
+ switch (binding) {
+ case kDesktop_GrGLBinding:
+ GrAssert(ver >= GR_GLSL_VER(1,10));
+ if (ver >= GR_GLSL_VER(1,50)) {
+ return k150_GrGLSLGeneration;
+ } else if (ver >= GR_GLSL_VER(1,30)) {
+ return k130_GrGLSLGeneration;
+ } else {
+ return k110_GrGLSLGeneration;
+ }
+ case kES2_GrGLBinding:
+ // version 1.00 of ES GLSL based on ver 1.20 of desktop GLSL
+ GrAssert(ver >= GR_GL_VER(1,00));
+ return k110_GrGLSLGeneration;
+ default:
+ GrCrash("Unknown GL Binding");
+ return k110_GrGLSLGeneration; // suppress warning
+ }
+}
+
+const char* GrGetGLSLVersionDecl(GrGLBinding binding,
+ GrGLSLGeneration gen) {
+ switch (gen) {
+ case k110_GrGLSLGeneration:
+ if (kES2_GrGLBinding == binding) {
+ // ES2s shader language is based on version 1.20 but is version
+ // 1.00 of the ES language.
+ return "#version 100\n";
+ } else {
+ GrAssert(kDesktop_GrGLBinding == binding);
+ return "#version 110\n";
+ }
+ case k130_GrGLSLGeneration:
+ GrAssert(kDesktop_GrGLBinding == binding);
+ return "#version 130\n";
+ case k150_GrGLSLGeneration:
+ GrAssert(kDesktop_GrGLBinding == binding);
+ return "#version 150\n";
+ default:
+ GrCrash("Unknown GL version.");
+ return ""; // suppress warning
+ }
+}
+
+const char* GrGetGLSLVarPrecisionDeclType(GrGLBinding binding) {
+ if (kES2_GrGLBinding == binding) {
+ return "mediump";
+ } else {
+ return " ";
+ }
+}
+
+const char* GrGetGLSLShaderPrecisionDecl(GrGLBinding binding) {
+ if (kES2_GrGLBinding == binding) {
+ return "precision mediump float;\n";
+ } else {
+ return "";
+ }
+}
+
+bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
+ const char* nameIfDeclared,
+ GrGLShaderVar* var) {
+ bool declaredOutput = k110_GrGLSLGeneration != gen;
+ var->set(GrGLShaderVar::kVec4f_Type,
+ GrGLShaderVar::kOut_TypeModifier,
+ declaredOutput ? nameIfDeclared : "gl_FragColor");
+ return declaredOutput;
+}
diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h
new file mode 100644
index 0000000000..5b9c5b631f
--- /dev/null
+++ b/src/gpu/gl/GrGLSL.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLSL_DEFINED
+#define GrGLSL_DEFINED
+
+#include "GrGLInterface.h"
+
+class GrGLShaderVar;
+
+// Limited set of GLSL versions we build shaders for. Caller should round
+// down the GLSL version to one of these enums.
+enum GrGLSLGeneration {
+ /**
+ * Desktop GLSL 1.10 and ES2 shading lang (based on desktop GLSL 1.20)
+ */
+ k110_GrGLSLGeneration,
+ /**
+ * Desktop GLSL 1.30
+ */
+ k130_GrGLSLGeneration,
+ /**
+ * Dekstop GLSL 1.50
+ */
+ k150_GrGLSLGeneration,
+};
+
+/**
+ * Gets the most recent GLSL Generation compatible with the OpenGL context.
+ */
+GrGLSLGeneration GrGetGLSLGeneration(GrGLBinding binding,
+ const GrGLInterface* gl);
+
+/**
+ * Returns a string to include at the begining of a shader to declare the GLSL
+ * version.
+ */
+const char* GrGetGLSLVersionDecl(GrGLBinding binding,
+ GrGLSLGeneration v);
+
+/**
+ * Returns a string to include in a variable decleration to set the fp precision
+ * or an emptry string if precision is not required.
+ */
+const char* GrGetGLSLVarPrecisionDeclType(GrGLBinding binding);
+
+/**
+ * Returns a string to set the default fp precision for an entire shader, or
+ * an emptry string if precision is not required.
+ */
+const char* GrGetGLSLShaderPrecisionDecl(GrGLBinding binding);
+
+/**
+ * Depending on the GLSL version being emitted there may be an assumed output
+ * variable from the fragment shader for the color. Otherwise, the shader must
+ * declare an output variable for the color. If this function returns true:
+ * * Parameter var's name will be set to nameIfDeclared
+ * * The variable must be declared in the fragment shader
+ * * The variable has to be bound as the color output
+ * (using glBindFragDataLocation)
+ * If the function returns false:
+ * * Parameter var's name will be set to the GLSL built-in color output name.
+ * * Do not declare the variable in the shader.
+ * * Do not use glBindFragDataLocation to bind the variable
+ * In either case var is initialized to represent the color output in the
+ * shader.
+ */
+ bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
+ const char* nameIfDeclared,
+ GrGLShaderVar* var);
+
+#endif
diff --git a/src/gpu/gl/GrGLShaderVar.h b/src/gpu/gl/GrGLShaderVar.h
new file mode 100644
index 0000000000..95c54f3399
--- /dev/null
+++ b/src/gpu/gl/GrGLShaderVar.h
@@ -0,0 +1,304 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLShaderVar_DEFINED
+#define GrGLShaderVar_DEFINED
+
+#include "GrGLInterface.h"
+#include "GrGLSL.h"
+#include "../GrStringBuilder.h"
+
+#define USE_UNIFORM_FLOAT_ARRAYS true
+
+/**
+ * Represents a variable in a shader
+ */
+class GrGLShaderVar {
+public:
+
+ enum Type {
+ kFloat_Type,
+ kVec2f_Type,
+ kVec3f_Type,
+ kVec4f_Type,
+ kMat33f_Type,
+ kMat44f_Type,
+ kSampler2D_Type,
+ };
+
+ /**
+ * Early versions of GLSL have Varying and Attribute; those are later
+ * deprecated, but we still need to know whether a Varying variable
+ * should be treated as In or Out.
+ */
+ enum TypeModifier {
+ kNone_TypeModifier,
+ kOut_TypeModifier,
+ kIn_TypeModifier,
+ kUniform_TypeModifier,
+ kAttribute_TypeModifier
+ };
+
+ /**
+ * Defaults to a float with no precision specifier
+ */
+ GrGLShaderVar() {
+ fType = kFloat_Type;
+ fTypeModifier = kNone_TypeModifier;
+ fCount = kNonArray;
+ fEmitPrecision = false;
+ fUseUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS;
+ }
+
+ GrGLShaderVar(const GrGLShaderVar& var)
+ : fType(var.fType)
+ , fTypeModifier(var.fTypeModifier)
+ , fName(var.fName)
+ , fCount(var.fCount)
+ , fEmitPrecision(var.fEmitPrecision)
+ , fUseUniformFloatArrays(var.fUseUniformFloatArrays) {}
+
+ /**
+ * Values for array count that have special meaning. We allow 1-sized arrays.
+ */
+ enum {
+ kNonArray = 0, // not an array
+ kUnsizedArray = -1, // an unsized array (declared with [])
+ };
+
+ /**
+ * Sets as a non-array.
+ */
+ void set(Type type,
+ TypeModifier typeModifier,
+ const GrStringBuilder& name,
+ bool emitPrecision = false,
+ bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+ fType = type;
+ fTypeModifier = typeModifier;
+ fName = name;
+ fCount = kNonArray;
+ fEmitPrecision = emitPrecision;
+ fUseUniformFloatArrays = useUniformFloatArrays;
+ }
+
+ /**
+ * Sets as a non-array.
+ */
+ void set(Type type,
+ TypeModifier typeModifier,
+ const char* name,
+ bool specifyPrecision = false,
+ bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+ fType = type;
+ fTypeModifier = typeModifier;
+ fName = name;
+ fCount = kNonArray;
+ fEmitPrecision = specifyPrecision;
+ fUseUniformFloatArrays = useUniformFloatArrays;
+ }
+
+ /**
+ * Set all var options
+ */
+ void set(Type type,
+ TypeModifier typeModifier,
+ const GrStringBuilder& name,
+ int count,
+ bool specifyPrecision = false,
+ bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+ fType = type;
+ fTypeModifier = typeModifier;
+ fName = name;
+ fCount = count;
+ fEmitPrecision = specifyPrecision;
+ fUseUniformFloatArrays = useUniformFloatArrays;
+ }
+
+ /**
+ * Set all var options
+ */
+ void set(Type type,
+ TypeModifier typeModifier,
+ const char* name,
+ int count,
+ bool specifyPrecision = false,
+ bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+ fType = type;
+ fTypeModifier = typeModifier;
+ fName = name;
+ fCount = count;
+ fEmitPrecision = specifyPrecision;
+ fUseUniformFloatArrays = useUniformFloatArrays;
+ }
+
+ /**
+ * Is the var an array.
+ */
+ bool isArray() const { return kNonArray != fCount; }
+ /**
+ * Is this an unsized array, (i.e. declared with []).
+ */
+ bool isUnsizedArray() const { return kUnsizedArray == fCount; }
+ /**
+ * Get the array length of the var.
+ */
+ int getArrayCount() const { return fCount; }
+ /**
+ * Set the array length of the var
+ */
+ void setArrayCount(int count) { fCount = count; }
+ /**
+ * Set to be a non-array.
+ */
+ void setNonArray() { fCount = kNonArray; }
+ /**
+ * Set to be an unsized array.
+ */
+ void setUnsizedArray() { fCount = kUnsizedArray; }
+
+ /**
+ * Access the var name as a writable string
+ */
+ GrStringBuilder* accessName() { return &fName; }
+ /**
+ * Set the var name
+ */
+ void setName(const GrStringBuilder& n) { fName = n; }
+ void setName(const char* n) { fName = n; }
+ /**
+ * Get the var name.
+ */
+ const GrStringBuilder& getName() const { return fName; }
+
+ /**
+ * Get the type of the var
+ */
+ Type getType() const { return fType; }
+ /**
+ * Set the type of the var
+ */
+ void setType(Type type) { fType = type; }
+
+ TypeModifier getTypeModifier() const { return fTypeModifier; }
+ void setTypeModifier(TypeModifier type) { fTypeModifier = type; }
+
+ /**
+ * Must the variable declaration emit a precision specifier
+ */
+ bool emitsPrecision() const { return fEmitPrecision; }
+ /**
+ * Specify whether the declaration should specify precision
+ */
+ void setEmitPrecision(bool p) { fEmitPrecision = p; }
+
+ /**
+ * Write a declaration of this variable to out.
+ */
+ void appendDecl(const GrGLInterface* gl, GrStringBuilder* out,
+ GrGLSLGeneration gen) const {
+ if (this->getTypeModifier() != kNone_TypeModifier) {
+ out->append(TypeModifierString(this->getTypeModifier(), gen));
+ out->append(" ");
+ }
+ if (this->emitsPrecision()) {
+ out->append(PrecisionString(gl));
+ out->append(" ");
+ }
+ Type effectiveType = this->getType();
+ if (this->isArray()) {
+ if (this->isUnsizedArray()) {
+ out->appendf("%s %s[]",
+ TypeString(effectiveType),
+ this->getName().c_str());
+ } else {
+ GrAssert(this->getArrayCount() > 0);
+ out->appendf("%s %s[%d]",
+ TypeString(effectiveType),
+ this->getName().c_str(),
+ this->getArrayCount());
+ }
+ } else {
+ out->appendf("%s %s",
+ TypeString(effectiveType),
+ this->getName().c_str());
+ }
+ out->append(";\n");
+ }
+
+ static const char* TypeString(Type t) {
+ switch (t) {
+ case kFloat_Type:
+ return "float";
+ case kVec2f_Type:
+ return "vec2";
+ case kVec3f_Type:
+ return "vec3";
+ case kVec4f_Type:
+ return "vec4";
+ case kMat33f_Type:
+ return "mat3";
+ case kMat44f_Type:
+ return "mat4";
+ case kSampler2D_Type:
+ return "sampler2D";
+ default:
+ GrCrash("Unknown shader var type.");
+ return ""; // suppress warning
+ }
+ }
+
+ void appendArrayAccess(int index, GrStringBuilder* out) {
+ out->appendf("%s[%d]%s",
+ this->getName().c_str(),
+ index,
+ fUseUniformFloatArrays ? "" : ".x");
+ }
+
+ void appendArrayAccess(const char* indexName, GrStringBuilder* out) {
+ out->appendf("%s[%s]%s",
+ this->getName().c_str(),
+ indexName,
+ fUseUniformFloatArrays ? "" : ".x");
+ }
+
+private:
+ static const char* PrecisionString(const GrGLInterface* gl) {
+ return gl->supportsDesktop() ? "" : "mediump";
+ }
+
+ static const char* TypeModifierString(TypeModifier t,
+ GrGLSLGeneration gen) {
+ switch (t) {
+ case kNone_TypeModifier:
+ return "";
+ case kOut_TypeModifier:
+ return k110_GrGLSLGeneration == gen ? "varying" : "out";
+ case kIn_TypeModifier:
+ return k110_GrGLSLGeneration == gen ? "varying" : "in";
+ case kUniform_TypeModifier:
+ return "uniform";
+ case kAttribute_TypeModifier:
+ return k110_GrGLSLGeneration == gen ? "attribute" : "in";
+ default:
+ GrCrash("Unknown shader variable type modifier.");
+ return ""; // suppress warning
+ }
+ }
+
+ Type fType;
+ TypeModifier fTypeModifier;
+ GrStringBuilder fName;
+ int fCount;
+ bool fEmitPrecision;
+ /// Work around driver bugs on some hardware that don't correctly
+ /// support uniform float []
+ bool fUseUniformFloatArrays;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLStencilBuffer.cpp b/src/gpu/gl/GrGLStencilBuffer.cpp
new file mode 100644
index 0000000000..bc0bb364e8
--- /dev/null
+++ b/src/gpu/gl/GrGLStencilBuffer.cpp
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLStencilBuffer.h"
+#include "GrGpuGL.h"
+
+GrGLStencilBuffer::~GrGLStencilBuffer() {
+ this->release();
+}
+
+size_t GrGLStencilBuffer::sizeInBytes() const {
+ uint64_t size = this->width();
+ size *= this->height();
+ size *= fFormat.fTotalBits;
+ size *= GrMax(1,this->numSamples());
+ return static_cast<size_t>(size / 8);
+}
+
+void GrGLStencilBuffer::onRelease() {
+ if (0 != fRenderbufferID) {
+ GrGpuGL* gpuGL = (GrGpuGL*) this->getGpu();
+ const GrGLInterface* gl = gpuGL->glInterface();
+ GR_GL_CALL(gl, DeleteRenderbuffers(1, &fRenderbufferID));
+ fRenderbufferID = 0;
+ }
+ INHERITED::onRelease();
+}
+
+void GrGLStencilBuffer::onAbandon() {
+ fRenderbufferID = 0;
+ INHERITED::onAbandon();
+}
+
+
diff --git a/src/gpu/gl/GrGLStencilBuffer.h b/src/gpu/gl/GrGLStencilBuffer.h
new file mode 100644
index 0000000000..02501e7cd2
--- /dev/null
+++ b/src/gpu/gl/GrGLStencilBuffer.h
@@ -0,0 +1,60 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLStencilBuffer_DEFINED
+#define GrGLStencilBuffer_DEFINED
+
+#include "GrGLInterface.h"
+#include "../GrStencilBuffer.h"
+
+class GrGLStencilBuffer : public GrStencilBuffer {
+public:
+ static const GrGLenum kUnknownInternalFormat = ~0;
+ struct Format {
+ GrGLenum fInternalFormat;
+ GrGLuint fStencilBits;
+ GrGLuint fTotalBits;
+ bool fPacked;
+ };
+
+ GrGLStencilBuffer(GrGpu* gpu, GrGLint rbid,
+ int width, int height,
+ int sampleCnt,
+ const Format& format)
+ : GrStencilBuffer(gpu, width, height, format.fStencilBits, sampleCnt)
+ , fFormat(format)
+ , fRenderbufferID(rbid) {
+ }
+
+ virtual ~GrGLStencilBuffer();
+
+ virtual size_t sizeInBytes() const;
+
+ GrGLuint renderbufferID() const {
+ return fRenderbufferID;
+ }
+
+ const Format& format() const { return fFormat; }
+
+protected:
+ virtual void onRelease();
+
+ virtual void onAbandon();
+
+private:
+ Format fFormat;
+ // may be zero for external SBs associated with external RTs
+ // (we don't require the client to give us the id, just tell
+ // us how many bits of stencil there are).
+ GrGLuint fRenderbufferID;
+
+ typedef GrStencilBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
new file mode 100644
index 0000000000..0a38da3da9
--- /dev/null
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -0,0 +1,90 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLTexture.h"
+
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+const GrGLenum* GrGLTexture::WrapMode2GLWrap() {
+ static const GrGLenum repeatModes[] = {
+ GR_GL_CLAMP_TO_EDGE,
+ GR_GL_REPEAT,
+ GR_GL_MIRRORED_REPEAT
+ };
+ return repeatModes;
+};
+
+void GrGLTexture::init(GrGpuGL* gpu,
+ const Desc& textureDesc,
+ const GrGLRenderTarget::Desc* rtDesc) {
+
+ GrAssert(0 != textureDesc.fTextureID);
+
+ fTexParams.invalidate();
+ fTexParamsTimestamp = GrGpu::kExpiredTimestamp;
+ fTexIDObj = new GrGLTexID(GPUGL->glInterface(),
+ textureDesc.fTextureID,
+ textureDesc.fOwnsID);
+ fOrientation = textureDesc.fOrientation;
+
+ if (NULL != rtDesc) {
+ // we render to the top left
+ GrGLIRect vp;
+ vp.fLeft = 0;
+ vp.fWidth = textureDesc.fWidth;
+ vp.fBottom = 0;
+ vp.fHeight = textureDesc.fHeight;
+
+ fRenderTarget = new GrGLRenderTarget(gpu, *rtDesc, vp, fTexIDObj, this);
+ }
+}
+
+GrGLTexture::GrGLTexture(GrGpuGL* gpu,
+ const Desc& textureDesc)
+ : INHERITED(gpu,
+ textureDesc.fWidth,
+ textureDesc.fHeight,
+ textureDesc.fConfig) {
+ this->init(gpu, textureDesc, NULL);
+}
+
+GrGLTexture::GrGLTexture(GrGpuGL* gpu,
+ const Desc& textureDesc,
+ const GrGLRenderTarget::Desc& rtDesc)
+ : INHERITED(gpu,
+ textureDesc.fWidth,
+ textureDesc.fHeight,
+ textureDesc.fConfig) {
+ this->init(gpu, textureDesc, &rtDesc);
+}
+
+void GrGLTexture::onRelease() {
+ INHERITED::onRelease();
+ GPUGL->notifyTextureDelete(this);
+ if (NULL != fTexIDObj) {
+ fTexIDObj->unref();
+ fTexIDObj = NULL;
+ }
+}
+
+void GrGLTexture::onAbandon() {
+ INHERITED::onAbandon();
+ if (NULL != fTexIDObj) {
+ fTexIDObj->abandon();
+ }
+}
+
+intptr_t GrGLTexture::getTextureHandle() const {
+ return fTexIDObj->id();
+}
+
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
new file mode 100644
index 0000000000..d13fc4463a
--- /dev/null
+++ b/src/gpu/gl/GrGLTexture.h
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLTexture_DEFINED
+#define GrGLTexture_DEFINED
+
+#include "../GrGpu.h"
+#include "GrGLRenderTarget.h"
+
+/**
+ * A ref counted tex id that deletes the texture in its destructor.
+ */
+class GrGLTexID : public GrRefCnt {
+
+public:
+ GrGLTexID(const GrGLInterface* gl, GrGLuint texID, bool ownsID)
+ : fGL(gl)
+ , fTexID(texID)
+ , fOwnsID(ownsID) {
+ }
+
+ virtual ~GrGLTexID() {
+ if (0 != fTexID && fOwnsID) {
+ GR_GL_CALL(fGL, DeleteTextures(1, &fTexID));
+ }
+ }
+
+ void abandon() { fTexID = 0; }
+ GrGLuint id() const { return fTexID; }
+
+private:
+ const GrGLInterface* fGL;
+ GrGLuint fTexID;
+ bool fOwnsID;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+class GrGLTexture : public GrTexture {
+
+public:
+ enum Orientation {
+ kBottomUp_Orientation,
+ kTopDown_Orientation,
+ };
+
+ struct TexParams {
+ GrGLenum fFilter;
+ GrGLenum fWrapS;
+ GrGLenum fWrapT;
+ GrGLenum fSwizzleRGBA[4];
+ void invalidate() { memset(this, 0xff, sizeof(TexParams)); }
+ };
+
+ struct Desc {
+ int fWidth;
+ int fHeight;
+ GrPixelConfig fConfig;
+ GrGLuint fTextureID;
+ bool fOwnsID;
+ Orientation fOrientation;
+ };
+
+ // creates a texture that is also an RT
+ GrGLTexture(GrGpuGL* gpu,
+ const Desc& textureDesc,
+ const GrGLRenderTarget::Desc& rtDesc);
+
+ // creates a non-RT texture
+ GrGLTexture(GrGpuGL* gpu,
+ const Desc& textureDesc);
+
+
+ virtual ~GrGLTexture() { this->release(); }
+
+ virtual intptr_t getTextureHandle() const;
+
+ // these functions
+ const TexParams& getCachedTexParams(GrGpu::ResetTimestamp* timestamp) const {
+ *timestamp = fTexParamsTimestamp;
+ return fTexParams;
+ }
+ void setCachedTexParams(const TexParams& texParams,
+ GrGpu::ResetTimestamp timestamp) {
+ fTexParams = texParams;
+ fTexParamsTimestamp = timestamp;
+ }
+ GrGLuint textureID() const { return fTexIDObj->id(); }
+
+ // Ganesh assumes texture coordinates have their origin
+ // in the top-left corner of the image. OpenGL, however,
+ // has the origin in the lower-left corner. For content that
+ // is loaded by Ganesh we just push the content "upside down"
+ // (by GL's understanding of the world) in glTex*Image and the
+ // addressing just works out. However, content generated by GL
+ // (FBO or externally imported texture) will be updside down
+ // and it is up to the GrGpuGL derivative to handle y-mirroing.
+ Orientation orientation() const { return fOrientation; }
+
+ static const GrGLenum* WrapMode2GLWrap();
+
+protected:
+
+ // overrides of GrTexture
+ virtual void onAbandon();
+ virtual void onRelease();
+
+private:
+ TexParams fTexParams;
+ GrGpu::ResetTimestamp fTexParamsTimestamp;
+ GrGLTexID* fTexIDObj;
+ Orientation fOrientation;
+
+ void init(GrGpuGL* gpu,
+ const Desc& textureDesc,
+ const GrGLRenderTarget::Desc* rtDesc);
+
+ typedef GrTexture INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLUtil.cpp b/src/gpu/gl/GrGLUtil.cpp
new file mode 100644
index 0000000000..f12b407b1a
--- /dev/null
+++ b/src/gpu/gl/GrGLUtil.cpp
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLConfig.h"
+#include "GrGLInterface.h"
+
+void GrGLClearErr(const GrGLInterface* gl) {
+ while (GR_GL_NO_ERROR != gl->fGetError()) {}
+}
+
+void GrGLCheckErr(const GrGLInterface* gl,
+ const char* location,
+ const char* call) {
+ uint32_t err = GR_GL_GET_ERROR(gl);
+ if (GR_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");
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_GL_LOG_CALLS
+ bool gLogCallsGL = !!(GR_GL_LOG_CALLS_START);
+#endif
+
+#if GR_GL_CHECK_ERROR
+ bool gCheckErrorGL = !!(GR_GL_CHECK_ERROR_START);
+#endif
+
diff --git a/src/gpu/gl/GrGLVertexBuffer.cpp b/src/gpu/gl/GrGLVertexBuffer.cpp
new file mode 100644
index 0000000000..33c1e7e3e0
--- /dev/null
+++ b/src/gpu/gl/GrGLVertexBuffer.cpp
@@ -0,0 +1,126 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#include "GrGLVertexBuffer.h"
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+GrGLVertexBuffer::GrGLVertexBuffer(GrGpuGL* gpu,
+ GrGLuint id,
+ size_t sizeInBytes,
+ bool dynamic)
+ : INHERITED(gpu, sizeInBytes, dynamic)
+ , fBufferID(id)
+ , fLockPtr(NULL) {
+}
+
+void GrGLVertexBuffer::onRelease() {
+ // make sure we've not been abandoned
+ if (fBufferID) {
+ GPUGL->notifyVertexBufferDelete(this);
+ GL_CALL(DeleteBuffers(1, &fBufferID));
+ fBufferID = 0;
+ }
+}
+
+void GrGLVertexBuffer::onAbandon() {
+ fBufferID = 0;
+ fLockPtr = NULL;
+}
+
+void GrGLVertexBuffer::bind() const {
+ GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, fBufferID));
+ GPUGL->notifyVertexBufferBind(this);
+}
+
+GrGLuint GrGLVertexBuffer::bufferID() const {
+ return fBufferID;
+}
+
+void* GrGLVertexBuffer::lock() {
+ GrAssert(fBufferID);
+ GrAssert(!isLocked());
+ if (this->getGpu()->getCaps().fBufferLockSupport) {
+ this->bind();
+ // Let driver know it can discard the old data
+ GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, this->sizeInBytes(), NULL,
+ this->dynamic() ? GR_GL_DYNAMIC_DRAW :
+ GR_GL_STATIC_DRAW));
+ GR_GL_CALL_RET(GPUGL->glInterface(),
+ fLockPtr,
+ MapBuffer(GR_GL_ARRAY_BUFFER, GR_GL_WRITE_ONLY));
+ return fLockPtr;
+ }
+ return NULL;
+}
+
+void* GrGLVertexBuffer::lockPtr() const {
+ return fLockPtr;
+}
+
+void GrGLVertexBuffer::unlock() {
+
+ GrAssert(fBufferID);
+ GrAssert(isLocked());
+ GrAssert(this->getGpu()->getCaps().fBufferLockSupport);
+
+ this->bind();
+ GL_CALL(UnmapBuffer(GR_GL_ARRAY_BUFFER));
+ fLockPtr = NULL;
+}
+
+bool GrGLVertexBuffer::isLocked() const {
+ GrAssert(!this->isValid() || fBufferID);
+#if GR_DEBUG
+ if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
+ GrGLint mapped;
+ this->bind();
+ GL_CALL(GetBufferParameteriv(GR_GL_ARRAY_BUFFER,
+ GR_GL_BUFFER_MAPPED, &mapped));
+ GrAssert(!!mapped == !!fLockPtr);
+ }
+#endif
+ return NULL != fLockPtr;
+}
+
+bool GrGLVertexBuffer::updateData(const void* src, size_t srcSizeInBytes) {
+ GrAssert(fBufferID);
+ GrAssert(!isLocked());
+ if (srcSizeInBytes > this->sizeInBytes()) {
+ return false;
+ }
+ this->bind();
+ GrGLenum usage = dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW;
+#if !GR_GL_USE_BUFFER_DATA_NULL_HINT
+ // Note that we're cheating on the size here. Currently no methods
+ // allow a partial update that preserves contents of non-updated
+ // portions of the buffer (and lock() does a glBufferData(..size, NULL..))
+ GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage));
+#else
+ if (this->sizeInBytes() == srcSizeInBytes) {
+ GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage));
+ } else {
+ // Before we call glBufferSubData we give the driver a hint using
+ // glBufferData with NULL. This makes the old buffer contents
+ // inaccessible to future draws. The GPU may still be processing draws
+ // that reference the old contents. With this hint it can assign a
+ // different allocation for the new contents to avoid flushing the gpu
+ // past draws consuming the old contents.
+ GL_CALL(BufferData(GR_GL_ARRAY_BUFFER,
+ this->sizeInBytes(), NULL, usage));
+ GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 0, srcSizeInBytes, src));
+ }
+#endif
+ return true;
+}
+
diff --git a/src/gpu/gl/GrGLVertexBuffer.h b/src/gpu/gl/GrGLVertexBuffer.h
new file mode 100644
index 0000000000..ff03d7a425
--- /dev/null
+++ b/src/gpu/gl/GrGLVertexBuffer.h
@@ -0,0 +1,52 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGLVertexBuffer_DEFINED
+#define GrGLVertexBuffer_DEFINED
+
+#include "../GrVertexBuffer.h"
+#include "GrGLInterface.h"
+
+class GrGpuGL;
+
+class GrGLVertexBuffer : public GrVertexBuffer {
+
+public:
+ virtual ~GrGLVertexBuffer() { this->release(); }
+ // overrides of GrVertexBuffer
+ virtual void* lock();
+ virtual void* lockPtr() const;
+ virtual void unlock();
+ virtual bool isLocked() const;
+ virtual bool updateData(const void* src, size_t srcSizeInBytes);
+ GrGLuint bufferID() const;
+
+protected:
+ GrGLVertexBuffer(GrGpuGL* gpu,
+ GrGLuint id,
+ size_t sizeInBytes,
+ bool dynamic);
+
+ // overrides of GrResource
+ virtual void onAbandon();
+ virtual void onRelease();
+
+private:
+ void bind() const;
+
+ GrGLuint fBufferID;
+ void* fLockPtr;
+
+ friend class GrGpuGL;
+
+ typedef GrVertexBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
new file mode 100644
index 0000000000..6ce6089693
--- /dev/null
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -0,0 +1,2544 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGpuGL.h"
+#include "GrGLStencilBuffer.h"
+#include "GrTypes.h"
+#include "SkTemplates.h"
+
+static const GrGLuint GR_MAX_GLUINT = ~0;
+static const GrGLint GR_INVAL_GLINT = ~0;
+
+#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
+#define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glInterface(), RET, X)
+
+// we use a spare texture unit to avoid
+// mucking with the state of any of the stages.
+static const int SPARE_TEX_UNIT = GrDrawState::kNumStages;
+
+#define SKIP_CACHE_CHECK true
+
+#if GR_GL_CHECK_ALLOC_WITH_GET_ERROR
+ #define CLEAR_ERROR_BEFORE_ALLOC(iface) GrGLClearErr(iface)
+ #define GL_ALLOC_CALL(iface, call) GR_GL_CALL_NOERRCHECK(iface, call)
+ #define CHECK_ALLOC_ERROR(iface) GR_GL_GET_ERROR(iface)
+#else
+ #define CLEAR_ERROR_BEFORE_ALLOC(iface)
+ #define GL_ALLOC_CALL(iface, call) GR_GL_CALL(iface, call)
+ #define CHECK_ALLOC_ERROR(iface) GR_GL_NO_ERROR
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::GLCaps::markConfigAsValidColorAttachment(GrPixelConfig config) {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+ return;
+#endif
+ GrAssert(config < kGrPixelConfigCount);
+ int u32Idx = config / 32;
+ int bitIdx = config % 32;
+ fVerifiedColorAttachmentConfigs[u32Idx] |= (1 << bitIdx);
+}
+
+bool GrGpuGL::GLCaps::isConfigVerifiedColorAttachment(
+ GrPixelConfig config) const {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+ return false;
+#endif
+ GrAssert((unsigned)config < kGrPixelConfigCount);
+ int u32Idx = config / 32;
+ int bitIdx = config % 32;
+ return SkToBool(fVerifiedColorAttachmentConfigs[u32Idx] & (1 << bitIdx));
+}
+
+void GrGpuGL::GLCaps::markColorConfigAndStencilFormatAsVerified(
+ GrPixelConfig config,
+ const GrGLStencilBuffer::Format& format) {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+ return;
+#endif
+ GrAssert((unsigned)config < kGrPixelConfigCount);
+ int count = fStencilFormats.count();
+ // we expect a really small number of possible formats so linear search
+ // should be OK
+ GrAssert(count < 16);
+ for (int i = 0; i < count; ++i) {
+ if (format.fInternalFormat ==
+ fStencilFormats[i].fFormat.fInternalFormat) {
+ int u32Idx = config / 32;
+ int bitIdx = config % 32;
+ fStencilFormats[i].fVerifiedColorConfigs[u32Idx] |= (1 << bitIdx);
+ return;
+ }
+ }
+ SkDEBUGFAIL("Why are we seeing a stencil format that GLCaps doesn't know about.");
+}
+
+bool GrGpuGL::GLCaps::isColorConfigAndStencilFormatVerified(
+ GrPixelConfig config,
+ const GrGLStencilBuffer::Format& format) const {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+ return false;
+#endif
+ GrAssert((unsigned)config < kGrPixelConfigCount);
+ int count = fStencilFormats.count();
+ // we expect a really small number of possible formats so linear search
+ // should be OK
+ GrAssert(count < 16);
+ for (int i = 0; i < count; ++i) {
+ if (format.fInternalFormat ==
+ fStencilFormats[i].fFormat.fInternalFormat) {
+ int u32Idx = config / 32;
+ int bitIdx = config % 32;
+ return SkToBool(fStencilFormats[i].fVerifiedColorConfigs[u32Idx] &
+ (1 << bitIdx));
+ }
+ }
+ SkDEBUGFAIL("Why are we seeing a stencil format that GLCaps doesn't know about.");
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const GrGLenum gXfermodeCoeff2Blend[] = {
+ GR_GL_ZERO,
+ GR_GL_ONE,
+ GR_GL_SRC_COLOR,
+ GR_GL_ONE_MINUS_SRC_COLOR,
+ GR_GL_DST_COLOR,
+ GR_GL_ONE_MINUS_DST_COLOR,
+ GR_GL_SRC_ALPHA,
+ GR_GL_ONE_MINUS_SRC_ALPHA,
+ GR_GL_DST_ALPHA,
+ GR_GL_ONE_MINUS_DST_ALPHA,
+ GR_GL_CONSTANT_COLOR,
+ GR_GL_ONE_MINUS_CONSTANT_COLOR,
+ GR_GL_CONSTANT_ALPHA,
+ GR_GL_ONE_MINUS_CONSTANT_ALPHA,
+
+ // extended blend coeffs
+ GR_GL_SRC1_COLOR,
+ GR_GL_ONE_MINUS_SRC1_COLOR,
+ GR_GL_SRC1_ALPHA,
+ GR_GL_ONE_MINUS_SRC1_ALPHA,
+};
+
+bool GrGpuGL::BlendCoeffReferencesConstant(GrBlendCoeff coeff) {
+ static const bool gCoeffReferencesBlendConst[] = {
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ true,
+ true,
+ true,
+
+ // extended blend coeffs
+ false,
+ false,
+ false,
+ false,
+ };
+ return gCoeffReferencesBlendConst[coeff];
+ GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+
+ GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
+ GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
+ GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
+ GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
+ GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
+ GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
+ GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
+ GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
+ GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
+ GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
+ GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
+ GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
+ GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
+ GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
+
+ GR_STATIC_ASSERT(14 == kS2C_BlendCoeff);
+ GR_STATIC_ASSERT(15 == kIS2C_BlendCoeff);
+ GR_STATIC_ASSERT(16 == kS2A_BlendCoeff);
+ GR_STATIC_ASSERT(17 == kIS2A_BlendCoeff);
+
+ // assertion for gXfermodeCoeff2Blend have to be in GrGpu scope
+ GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
+ GrSamplerState::SampleMode mode,
+ GrMatrix* matrix) {
+ GrAssert(NULL != texture);
+ GrAssert(NULL != matrix);
+ GrGLTexture::Orientation orientation = texture->orientation();
+ if (GrGLTexture::kBottomUp_Orientation == orientation) {
+ GrMatrix invY;
+ invY.setAll(GR_Scalar1, 0, 0,
+ 0, -GR_Scalar1, GR_Scalar1,
+ 0, 0, GrMatrix::I()[8]);
+ matrix->postConcat(invY);
+ } else {
+ GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
+ }
+}
+
+bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
+ const GrSamplerState& sampler) {
+ GrAssert(NULL != texture);
+ if (!sampler.getMatrix().isIdentity()) {
+ return false;
+ }
+ GrGLTexture::Orientation orientation = texture->orientation();
+ if (GrGLTexture::kBottomUp_Orientation == orientation) {
+ return false;
+ } else {
+ GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool gPrintStartupSpew;
+
+static bool fbo_test(const GrGLInterface* gl, int w, int h) {
+
+ GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE0 + SPARE_TEX_UNIT));
+
+ GrGLuint testFBO;
+ GR_GL_CALL(gl, GenFramebuffers(1, &testFBO));
+ GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, testFBO));
+ GrGLuint testRTTex;
+ GR_GL_CALL(gl, GenTextures(1, &testRTTex));
+ GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, testRTTex));
+ // some implementations require texture to be mip-map complete before
+ // FBO with level 0 bound as color attachment will be framebuffer complete.
+ GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_MIN_FILTER,
+ GR_GL_NEAREST));
+ GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 0, GR_GL_RGBA, w, h,
+ 0, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, NULL));
+ GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0));
+ GR_GL_CALL(gl, FramebufferTexture2D(GR_GL_FRAMEBUFFER,
+ GR_GL_COLOR_ATTACHMENT0,
+ GR_GL_TEXTURE_2D, testRTTex, 0));
+ GrGLenum status;
+ GR_GL_CALL_RET(gl, status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+ GR_GL_CALL(gl, DeleteFramebuffers(1, &testFBO));
+ GR_GL_CALL(gl, DeleteTextures(1, &testRTTex));
+
+ return status == GR_GL_FRAMEBUFFER_COMPLETE;
+}
+
+GrGpuGL::GrGpuGL(const GrGLContextInfo& ctxInfo) : fGLContextInfo(ctxInfo) {
+
+ GrAssert(ctxInfo.isInitialized());
+
+ fPrintedCaps = false;
+
+ GrGLClearErr(fGLContextInfo.interface());
+
+ if (gPrintStartupSpew) {
+ const GrGLubyte* ext;
+ GL_CALL_RET(ext, GetString(GR_GL_EXTENSIONS));
+ const GrGLubyte* vendor;
+ const GrGLubyte* renderer;
+ const GrGLubyte* version;
+ GL_CALL_RET(vendor, GetString(GR_GL_VENDOR));
+ GL_CALL_RET(renderer, GetString(GR_GL_RENDERER));
+ GL_CALL_RET(version, GetString(GR_GL_VERSION));
+ GrPrintf("------------------------- create GrGpuGL %p --------------\n",
+ this);
+ GrPrintf("------ VENDOR %s\n", vendor);
+ GrPrintf("------ RENDERER %s\n", renderer);
+ GrPrintf("------ VERSION %s\n", version);
+ GrPrintf("------ EXTENSIONS\n %s \n", ext);
+ }
+
+ this->resetDirtyFlags();
+
+ this->initCaps();
+
+ fLastSuccessfulStencilFmtIdx = 0;
+}
+
+GrGpuGL::~GrGpuGL() {
+ // This must be called by before the GrDrawTarget destructor
+ this->releaseGeometry();
+ // This subclass must do this before the base class destructor runs
+ // since we will unref the GrGLInterface.
+ this->releaseResources();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const GrGLuint kUnknownBitCount = ~0;
+
+void GrGpuGL::initCaps() {
+ GrGLint maxTextureUnits;
+ // check FS and fixed-function texture unit limits
+ // we only use textures in the fragment stage currently.
+ // checks are > to make sure we have a spare unit.
+ const GrGLInterface* gl = this->glInterface();
+ GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+ GrAssert(maxTextureUnits > GrDrawState::kNumStages);
+ if (kES2_GrGLBinding != this->glBinding()) {
+ GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_UNITS, &maxTextureUnits);
+ GrAssert(maxTextureUnits > GrDrawState::kNumStages);
+ }
+ if (kES2_GrGLBinding == this->glBinding()) {
+ GR_GL_GetIntegerv(gl, GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS,
+ &fGLCaps.fMaxFragmentUniformVectors);
+ } else if (kDesktop_GrGLBinding != this->glBinding()) {
+ GrGLint max;
+ GR_GL_GetIntegerv(gl, GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max);
+ fGLCaps.fMaxFragmentUniformVectors = max / 4;
+ } else {
+ fGLCaps.fMaxFragmentUniformVectors = 16;
+ }
+
+ GrGLint numFormats;
+ GR_GL_GetIntegerv(gl, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats);
+ SkAutoSTMalloc<10, GrGLint> formats(numFormats);
+ GR_GL_GetIntegerv(gl, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats);
+ for (int i = 0; i < numFormats; ++i) {
+ if (formats[i] == GR_GL_PALETTE8_RGBA8) {
+ fCaps.f8BitPaletteSupport = true;
+ break;
+ }
+ }
+
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ // 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).
+ fCaps.fTwoSidedStencilSupport = (this->glVersion() >= GR_GL_VER(2,0));
+ // supported on GL 1.4 and higher or by extension
+ fCaps.fStencilWrapOpsSupport = (this->glVersion() >= GR_GL_VER(1,4)) ||
+ this->hasExtension("GL_EXT_stencil_wrap");
+ } else {
+ // ES 2 has two sided stencil and stencil wrap
+ fCaps.fTwoSidedStencilSupport = true;
+ fCaps.fStencilWrapOpsSupport = true;
+ }
+
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ fGLCaps.fRGBA8RenderbufferSupport = true;
+ } else {
+ fGLCaps.fRGBA8RenderbufferSupport =
+ this->hasExtension("GL_OES_rgb8_rgba8") ||
+ this->hasExtension("GL_ARM_rgba8");
+ }
+
+
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ fGLCaps.fBGRAFormatSupport = this->glVersion() >= GR_GL_VER(1,2) ||
+ this->hasExtension("GL_EXT_bgra");
+ } else {
+ bool hasBGRAExt = false;
+ if (this->hasExtension("GL_APPLE_texture_format_BGRA8888")) {
+ fGLCaps.fBGRAFormatSupport = true;
+ } else if (this->hasExtension("GL_EXT_texture_format_BGRA8888")) {
+ fGLCaps.fBGRAFormatSupport = true;
+ fGLCaps.fBGRAIsInternalFormat = true;
+ }
+ GrAssert(fGLCaps.fBGRAFormatSupport ||
+ kSkia8888_PM_GrPixelConfig != kBGRA_8888_PM_GrPixelConfig);
+ }
+
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ fGLCaps.fTextureSwizzleSupport = this->glVersion() >= GR_GL_VER(3,3) ||
+ this->hasExtension("GL_ARB_texture_swizzle");
+ } else {
+ fGLCaps.fTextureSwizzleSupport = false;
+ }
+
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ fGLCaps.fUnpackRowLengthSupport = true;
+ fGLCaps.fUnpackFlipYSupport = false;
+ fGLCaps.fPackRowLengthSupport = true;
+ fGLCaps.fPackFlipYSupport = false;
+ } else {
+ fGLCaps.fUnpackRowLengthSupport =this->hasExtension("GL_EXT_unpack_subimage");
+ fGLCaps.fUnpackFlipYSupport = this->hasExtension("GL_CHROMIUM_flipy");
+ // no extension for pack row length
+ fGLCaps.fPackRowLengthSupport = false;
+ fGLCaps.fPackFlipYSupport =
+ this->hasExtension("GL_ANGLE_pack_reverse_row_order");
+ }
+
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ fCaps.fBufferLockSupport = true; // we require VBO support and the desktop VBO
+ // extension includes glMapBuffer.
+ } else {
+ fCaps.fBufferLockSupport = this->hasExtension("GL_OES_mapbuffer");
+ }
+
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ if (this->glVersion() >= GR_GL_VER(2,0) ||
+ this->hasExtension("GL_ARB_texture_non_power_of_two")) {
+ fCaps.fNPOTTextureTileSupport = true;
+ } else {
+ fCaps.fNPOTTextureTileSupport = false;
+ }
+ } else {
+ // Unextended ES2 supports NPOT textures with clamp_to_edge and non-mip filters only
+ fCaps.fNPOTTextureTileSupport = this->hasExtension("GL_OES_texture_npot");
+ }
+
+ fGLCaps.fTextureUsageSupport = (kES2_GrGLBinding == this->glBinding()) &&
+ this->hasExtension("GL_ANGLE_texture_usage");
+
+ // Tex storage is in desktop 4.2 and can be an extension to desktop or ES.
+ fGLCaps.fTexStorageSupport = (kDesktop_GrGLBinding == this->glBinding() &&
+ this->glVersion() >= GR_GL_VER(4,2)) ||
+ this->hasExtension("GL_ARB_texture_storage") ||
+ this->hasExtension("GL_EXT_texture_storage");
+
+ fCaps.fHWAALineSupport = (kDesktop_GrGLBinding == this->glBinding());
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Experiments to determine limitations that can't be queried.
+ // TODO: Make these a preprocess that generate some compile time constants.
+ // TODO: probe once at startup, rather than once per context creation.
+
+ GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_SIZE, &fCaps.fMaxTextureSize);
+ GR_GL_GetIntegerv(gl, GR_GL_MAX_RENDERBUFFER_SIZE, &fCaps.fMaxRenderTargetSize);
+ // Our render targets are always created with textures as the color
+ // attachment, hence this min:
+ fCaps.fMaxRenderTargetSize = GrMin(fCaps.fMaxTextureSize, fCaps.fMaxRenderTargetSize);
+
+ this->initFSAASupport();
+ this->initStencilFormats();
+}
+
+void GrGpuGL::initFSAASupport() {
+
+ fGLCaps.fMSFBOType = GLCaps::kNone_MSFBO;
+ if (kDesktop_GrGLBinding != this->glBinding()) {
+ if (this->hasExtension("GL_CHROMIUM_framebuffer_multisample")) {
+ // chrome's extension is equivalent to the EXT msaa
+ // and fbo_blit extensions.
+ fGLCaps.fMSFBOType = GLCaps::kDesktopEXT_MSFBO;
+ } else if (this->hasExtension("GL_APPLE_framebuffer_multisample")) {
+ fGLCaps.fMSFBOType = GLCaps::kAppleES_MSFBO;
+ }
+ } else {
+ if ((this->glVersion() >= GR_GL_VER(3,0)) || this->hasExtension("GL_ARB_framebuffer_object")) {
+ fGLCaps.fMSFBOType = GLCaps::kDesktopARB_MSFBO;
+ } else if (this->hasExtension("GL_EXT_framebuffer_multisample") &&
+ this->hasExtension("GL_EXT_framebuffer_blit")) {
+ fGLCaps.fMSFBOType = GLCaps::kDesktopEXT_MSFBO;
+ }
+ }
+
+ fCaps.fFSAASupport = GLCaps::kNone_MSFBO != fGLCaps.fMSFBOType;
+}
+
+void GrGpuGL::initStencilFormats() {
+
+ // Build up list of legal stencil formats (though perhaps not supported on
+ // the particular gpu/driver) from most preferred to least.
+
+ // these consts are in order of most preferred to least preferred
+ // we don't bother with GL_STENCIL_INDEX1 or GL_DEPTH32F_STENCIL8
+
+ // Omitting fVerifiedColorConfigs from initializer list should init to 0.
+ static const GLCaps::StencilFormat
+ // internal Format stencil bits total bits packed?
+ gS8 = {{GR_GL_STENCIL_INDEX8, 8, 8, false}, {0U}},
+ gS16 = {{GR_GL_STENCIL_INDEX16, 16, 16, false}, {0U}},
+ gD24S8 = {{GR_GL_DEPTH24_STENCIL8, 8, 32, true }, {0U}},
+ gS4 = {{GR_GL_STENCIL_INDEX4, 4, 4, false}, {0U}},
+ gS = {{GR_GL_STENCIL_INDEX, kUnknownBitCount, kUnknownBitCount, false}, {0U}},
+ gDS = {{GR_GL_DEPTH_STENCIL, kUnknownBitCount, kUnknownBitCount, true }, {0U}};
+
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ bool supportsPackedDS = this->glVersion() >= GR_GL_VER(3,0) ||
+ this->hasExtension("GL_EXT_packed_depth_stencil") ||
+ this->hasExtension("GL_ARB_framebuffer_object");
+
+ // S1 thru S16 formats are in GL 3.0+, EXT_FBO, and ARB_FBO since we
+ // require FBO support we can expect these are legal formats and don't
+ // check. These also all support the unsized GL_STENCIL_INDEX.
+ fGLCaps.fStencilFormats.push_back() = gS8;
+ fGLCaps.fStencilFormats.push_back() = gS16;
+ if (supportsPackedDS) {
+ fGLCaps.fStencilFormats.push_back() = gD24S8;
+ }
+ fGLCaps.fStencilFormats.push_back() = gS4;
+ if (supportsPackedDS) {
+ fGLCaps.fStencilFormats.push_back() = gDS;
+ }
+ } else {
+ // ES2 has STENCIL_INDEX8 without extensions but requires extensions
+ // for other formats.
+ // ES doesn't support using the unsized format.
+
+ fGLCaps.fStencilFormats.push_back() = gS8;
+ //fStencilFormats.push_back() = gS16;
+ if (this->hasExtension("GL_OES_packed_depth_stencil")) {
+ fGLCaps.fStencilFormats.push_back() = gD24S8;
+ }
+ if (this->hasExtension("GL_OES_stencil4")) {
+ fGLCaps.fStencilFormats.push_back() = gS4;
+ }
+ }
+#if GR_DEBUG
+ // ensure that initially all color / stencil format combos have unverified
+ // fbo status.
+ for (int i = 0; i < fGLCaps.fStencilFormats.count(); ++i) {
+ int numU32 =
+ GR_ARRAY_COUNT(fGLCaps.fStencilFormats[i].fVerifiedColorConfigs);
+ for (int j = 0; j < numU32; ++j) {
+ GrAssert(0 == fGLCaps.fStencilFormats[i].fVerifiedColorConfigs[j]);
+ }
+ }
+#endif
+}
+
+GrPixelConfig GrGpuGL::preferredReadPixelsConfig(GrPixelConfig config) const {
+ if (GR_GL_RGBA_8888_PIXEL_OPS_SLOW && GrPixelConfigIsRGBA8888(config)) {
+ return GrPixelConfigSwapRAndB(config);
+ } else {
+ return config;
+ }
+}
+
+GrPixelConfig GrGpuGL::preferredWritePixelsConfig(GrPixelConfig config) const {
+ if (GR_GL_RGBA_8888_PIXEL_OPS_SLOW && GrPixelConfigIsRGBA8888(config)) {
+ return GrPixelConfigSwapRAndB(config);
+ } else {
+ return config;
+ }
+}
+
+bool GrGpuGL::fullReadPixelsIsFasterThanPartial() const {
+ return SkToBool(GR_GL_FULL_READPIXELS_FASTER_THAN_PARTIAL);
+}
+
+void GrGpuGL::onResetContext() {
+ if (gPrintStartupSpew && !fPrintedCaps) {
+ fPrintedCaps = true;
+ this->getCaps().print();
+ fGLCaps.print();
+ }
+
+ // We detect cases when blending is effectively off
+ fHWBlendDisabled = false;
+ GL_CALL(Enable(GR_GL_BLEND));
+
+ // we don't use the zb at all
+ GL_CALL(Disable(GR_GL_DEPTH_TEST));
+ GL_CALL(DepthMask(GR_GL_FALSE));
+
+ GL_CALL(Disable(GR_GL_CULL_FACE));
+ GL_CALL(FrontFace(GR_GL_CCW));
+ fHWDrawState.setDrawFace(GrDrawState::kBoth_DrawFace);
+
+ GL_CALL(Disable(GR_GL_DITHER));
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ GL_CALL(Disable(GR_GL_LINE_SMOOTH));
+ GL_CALL(Disable(GR_GL_POINT_SMOOTH));
+ GL_CALL(Disable(GR_GL_MULTISAMPLE));
+ fHWAAState.fMSAAEnabled = false;
+ fHWAAState.fSmoothLineEnabled = false;
+ }
+
+ GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+ fHWDrawState.resetStateFlags();
+
+ // we only ever use lines in hairline mode
+ GL_CALL(LineWidth(1));
+
+ // invalid
+ fActiveTextureUnitIdx = -1;
+
+ // illegal values
+ fHWDrawState.setBlendFunc((GrBlendCoeff)0xFF, (GrBlendCoeff)0xFF);
+
+ fHWDrawState.setBlendConstant(0x00000000);
+ GL_CALL(BlendColor(0,0,0,0));
+
+ fHWDrawState.setColor(GrColor_ILLEGAL);
+
+ fHWDrawState.setViewMatrix(GrMatrix::InvalidMatrix());
+
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ fHWDrawState.setTexture(s, NULL);
+ fHWDrawState.sampler(s)->setRadial2Params(-GR_ScalarMax,
+ -GR_ScalarMax,
+ true);
+ *fHWDrawState.sampler(s)->matrix() = GrMatrix::InvalidMatrix();
+ fHWDrawState.sampler(s)->setConvolutionParams(0, NULL, NULL);
+ }
+
+ fHWBounds.fScissorRect.invalidate();
+ fHWBounds.fScissorEnabled = false;
+ GL_CALL(Disable(GR_GL_SCISSOR_TEST));
+ fHWBounds.fViewportRect.invalidate();
+
+ fHWDrawState.stencil()->invalidate();
+ fHWStencilClip = false;
+ fClipInStencil = false;
+
+ fHWGeometryState.fIndexBuffer = NULL;
+ fHWGeometryState.fVertexBuffer = NULL;
+
+ fHWGeometryState.fArrayPtrsDirty = true;
+
+ GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+ fHWDrawState.setRenderTarget(NULL);
+
+ // we assume these values
+ if (this->glCaps().fUnpackRowLengthSupport) {
+ GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
+ }
+ if (this->glCaps().fPackRowLengthSupport) {
+ GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0));
+ }
+ if (this->glCaps().fUnpackFlipYSupport) {
+ GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE));
+ }
+ if (this->glCaps().fPackFlipYSupport) {
+ GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, GR_GL_FALSE));
+ }
+}
+
+GrTexture* GrGpuGL::onCreatePlatformTexture(const GrPlatformTextureDesc& desc) {
+ GrGLTexture::Desc glTexDesc;
+ if (!configToGLFormats(desc.fConfig, false, NULL, NULL, NULL)) {
+ return NULL;
+ }
+
+ glTexDesc.fWidth = desc.fWidth;
+ glTexDesc.fHeight = desc.fHeight;
+ glTexDesc.fConfig = desc.fConfig;
+ glTexDesc.fTextureID = static_cast<GrGLuint>(desc.fTextureHandle);
+ glTexDesc.fOwnsID = false;
+ glTexDesc.fOrientation = GrGLTexture::kBottomUp_Orientation;
+
+ GrGLTexture* texture = NULL;
+ if (desc.fFlags & kRenderTarget_GrPlatformTextureFlag) {
+ GrGLRenderTarget::Desc glRTDesc;
+ glRTDesc.fRTFBOID = 0;
+ glRTDesc.fTexFBOID = 0;
+ glRTDesc.fMSColorRenderbufferID = 0;
+ glRTDesc.fOwnIDs = true;
+ glRTDesc.fConfig = desc.fConfig;
+ glRTDesc.fSampleCnt = desc.fSampleCnt;
+ if (!this->createRenderTargetObjects(glTexDesc.fWidth,
+ glTexDesc.fHeight,
+ glTexDesc.fTextureID,
+ &glRTDesc)) {
+ return NULL;
+ }
+ texture = new GrGLTexture(this, glTexDesc, glRTDesc);
+ } else {
+ texture = new GrGLTexture(this, glTexDesc);
+ }
+ if (NULL == texture) {
+ return NULL;
+ }
+
+ this->setSpareTextureUnit();
+ return texture;
+}
+
+GrRenderTarget* GrGpuGL::onCreatePlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
+ GrGLRenderTarget::Desc glDesc;
+ glDesc.fConfig = desc.fConfig;
+ glDesc.fRTFBOID = static_cast<GrGLuint>(desc.fRenderTargetHandle);
+ glDesc.fMSColorRenderbufferID = 0;
+ glDesc.fTexFBOID = GrGLRenderTarget::kUnresolvableFBOID;
+ glDesc.fSampleCnt = desc.fSampleCnt;
+ glDesc.fOwnIDs = false;
+ GrGLIRect viewport;
+ viewport.fLeft = 0;
+ viewport.fBottom = 0;
+ viewport.fWidth = desc.fWidth;
+ viewport.fHeight = desc.fHeight;
+
+ GrRenderTarget* tgt = new GrGLRenderTarget(this, glDesc, viewport);
+ if (desc.fStencilBits) {
+ GrGLStencilBuffer::Format format;
+ format.fInternalFormat = GrGLStencilBuffer::kUnknownInternalFormat;
+ format.fPacked = false;
+ format.fStencilBits = desc.fStencilBits;
+ format.fTotalBits = desc.fStencilBits;
+ GrGLStencilBuffer* sb = new GrGLStencilBuffer(this,
+ 0,
+ desc.fWidth,
+ desc.fHeight,
+ desc.fSampleCnt,
+ format);
+ tgt->setStencilBuffer(sb);
+ sb->unref();
+ }
+ return tgt;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::onWriteTexturePixels(GrTexture* texture,
+ int left, int top, int width, int height,
+ GrPixelConfig config, const void* buffer,
+ size_t rowBytes) {
+ if (NULL == buffer) {
+ return;
+ }
+ GrGLTexture* glTex = static_cast<GrGLTexture*>(texture);
+
+ this->setSpareTextureUnit();
+ GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTex->textureID()));
+ GrGLTexture::Desc desc;
+ desc.fConfig = glTex->config();
+ desc.fWidth = glTex->width();
+ desc.fHeight = glTex->height();
+ desc.fOrientation = glTex->orientation();
+ desc.fTextureID = glTex->textureID();
+
+ this->uploadTexData(desc, false,
+ left, top, width, height,
+ config, buffer, rowBytes);
+}
+
+namespace {
+bool adjust_pixel_ops_params(int surfaceWidth,
+ int surfaceHeight,
+ size_t bpp,
+ int* left, int* top, int* width, int* height,
+ const void** data,
+ size_t* rowBytes) {
+ if (!*rowBytes) {
+ *rowBytes = *width * bpp;
+ }
+
+ GrIRect subRect = GrIRect::MakeXYWH(*left, *top, *width, *height);
+ GrIRect bounds = GrIRect::MakeWH(surfaceWidth, surfaceHeight);
+
+ if (!subRect.intersect(bounds)) {
+ return false;
+ }
+ *data = reinterpret_cast<const void*>(reinterpret_cast<intptr_t>(*data) +
+ (subRect.fTop - *top) * *rowBytes + (subRect.fLeft - *left) * bpp);
+
+ *left = subRect.fLeft;
+ *top = subRect.fTop;
+ *width = subRect.width();
+ *height = subRect.height();
+ return true;
+}
+}
+
+bool GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc,
+ bool isNewTexture,
+ int left, int top, int width, int height,
+ GrPixelConfig dataConfig,
+ const void* data,
+ size_t rowBytes) {
+ GrAssert(NULL != data || isNewTexture);
+
+ size_t bpp = GrBytesPerPixel(dataConfig);
+ if (!adjust_pixel_ops_params(desc.fWidth, desc.fHeight, bpp, &left, &top,
+ &width, &height, &data, &rowBytes)) {
+ return false;
+ }
+ size_t trimRowBytes = width * bpp;
+
+ // in case we need a temporary, trimmed copy of the src pixels
+ SkAutoSMalloc<128 * 128> tempStorage;
+
+ bool useTexStorage = isNewTexture &&
+ this->glCaps().fTexStorageSupport;
+ if (useTexStorage) {
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ // 565 is not a sized internal format on desktop GL. So on desktop
+ // with 565 we always use an unsized internal format to let the
+ // system pick the best sized format to convert the 565 data to.
+ // Since glTexStorage only allows sized internal formats we will
+ // instead fallback to glTexImage2D.
+ useTexStorage = desc.fConfig != kRGB_565_GrPixelConfig;
+ } else {
+ // ES doesn't allow paletted textures to be used with tex storage
+ useTexStorage = desc.fConfig != kIndex_8_GrPixelConfig;
+ }
+ }
+
+ GrGLenum internalFormat;
+ GrGLenum externalFormat;
+ GrGLenum externalType;
+ // glTexStorage requires sized internal formats on both desktop and ES. ES
+ // glTexImage requires an unsized format.
+ if (!this->configToGLFormats(dataConfig, useTexStorage, &internalFormat,
+ &externalFormat, &externalType)) {
+ return false;
+ }
+
+ if (!isNewTexture && GR_GL_PALETTE8_RGBA8 == internalFormat) {
+ // paletted textures cannot be updated
+ return false;
+ }
+
+ /*
+ * check whether to allocate a temporary buffer for flipping y or
+ * because our srcData has extra bytes past each row. If so, we need
+ * to trim those off here, since GL ES may not let us specify
+ * GL_UNPACK_ROW_LENGTH.
+ */
+ bool restoreGLRowLength = false;
+ bool swFlipY = false;
+ bool glFlipY = false;
+ if (NULL != data) {
+ if (GrGLTexture::kBottomUp_Orientation == desc.fOrientation) {
+ if (this->glCaps().fUnpackFlipYSupport) {
+ glFlipY = true;
+ } else {
+ swFlipY = true;
+ }
+ }
+ if (this->glCaps().fUnpackRowLengthSupport && !swFlipY) {
+ // can't use this for flipping, only non-neg values allowed. :(
+ if (rowBytes != trimRowBytes) {
+ GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
+ GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
+ restoreGLRowLength = true;
+ }
+ } else {
+ if (trimRowBytes != rowBytes || swFlipY) {
+ // copy data into our new storage, skipping the trailing bytes
+ size_t trimSize = height * trimRowBytes;
+ const char* src = (const char*)data;
+ if (swFlipY) {
+ src += (height - 1) * rowBytes;
+ }
+ char* dst = (char*)tempStorage.reset(trimSize);
+ for (int y = 0; y < height; y++) {
+ memcpy(dst, src, trimRowBytes);
+ if (swFlipY) {
+ src -= rowBytes;
+ } else {
+ src += rowBytes;
+ }
+ dst += trimRowBytes;
+ }
+ // now point data to our copied version
+ data = tempStorage.get();
+ }
+ }
+ if (glFlipY) {
+ GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_TRUE));
+ }
+ GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, static_cast<GrGLint>(bpp)));
+ }
+ bool succeeded = true;
+ if (isNewTexture &&
+ 0 == left && 0 == top &&
+ desc.fWidth == width && desc.fHeight == height) {
+ CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+ if (useTexStorage) {
+ // We never resize or change formats of textures. We don't use
+ // mipmaps currently.
+ GL_ALLOC_CALL(this->glInterface(),
+ TexStorage2D(GR_GL_TEXTURE_2D,
+ 1, // levels
+ internalFormat,
+ desc.fWidth, desc.fHeight));
+ } else {
+ if (GR_GL_PALETTE8_RGBA8 == internalFormat) {
+ GrGLsizei imageSize = desc.fWidth * desc.fHeight +
+ kGrColorTableSize;
+ GL_ALLOC_CALL(this->glInterface(),
+ CompressedTexImage2D(GR_GL_TEXTURE_2D,
+ 0, // level
+ internalFormat,
+ desc.fWidth, desc.fHeight,
+ 0, // border
+ imageSize,
+ data));
+ } else {
+ GL_ALLOC_CALL(this->glInterface(),
+ TexImage2D(GR_GL_TEXTURE_2D,
+ 0, // level
+ internalFormat,
+ desc.fWidth, desc.fHeight,
+ 0, // border
+ externalFormat, externalType,
+ data));
+ }
+ }
+ GrGLenum error = CHECK_ALLOC_ERROR(this->glInterface());
+ if (error != GR_GL_NO_ERROR) {
+ succeeded = false;
+ } else {
+ // if we have data and we used TexStorage to create the texture, we
+ // now upload with TexSubImage.
+ if (NULL != data && useTexStorage) {
+ GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D,
+ 0, // level
+ left, top,
+ width, height,
+ externalFormat, externalType,
+ data));
+ }
+ }
+ } else {
+ if (swFlipY || glFlipY) {
+ top = desc.fHeight - (top + height);
+ }
+ GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D,
+ 0, // level
+ left, top,
+ width, height,
+ externalFormat, externalType, data));
+ }
+
+ if (restoreGLRowLength) {
+ GrAssert(this->glCaps().fUnpackRowLengthSupport);
+ GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
+ }
+ if (glFlipY) {
+ GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE));
+ }
+ return succeeded;
+}
+
+bool GrGpuGL::createRenderTargetObjects(int width, int height,
+ GrGLuint texID,
+ GrGLRenderTarget::Desc* desc) {
+ desc->fMSColorRenderbufferID = 0;
+ desc->fRTFBOID = 0;
+ desc->fTexFBOID = 0;
+ desc->fOwnIDs = true;
+
+ GrGLenum status;
+ GrGLint err;
+
+ GrGLenum msColorFormat = 0; // suppress warning
+
+ GL_CALL(GenFramebuffers(1, &desc->fTexFBOID));
+ if (!desc->fTexFBOID) {
+ goto FAILED;
+ }
+
+
+ // If we are using multisampling we will create two FBOS. We render
+ // to one and then resolve to the texture bound to the other.
+ if (desc->fSampleCnt > 0) {
+ if (GLCaps::kNone_MSFBO == fGLCaps.fMSFBOType) {
+ goto FAILED;
+ }
+ GL_CALL(GenFramebuffers(1, &desc->fRTFBOID));
+ GL_CALL(GenRenderbuffers(1, &desc->fMSColorRenderbufferID));
+ if (!desc->fRTFBOID ||
+ !desc->fMSColorRenderbufferID ||
+ !this->configToGLFormats(desc->fConfig,
+ // GLES requires sized internal formats
+ kES2_GrGLBinding == this->glBinding(),
+ &msColorFormat, NULL, NULL)) {
+ goto FAILED;
+ }
+ } else {
+ desc->fRTFBOID = desc->fTexFBOID;
+ }
+
+ // below here we may bind the FBO
+ fHWDrawState.setRenderTarget(NULL);
+ if (desc->fRTFBOID != desc->fTexFBOID) {
+ GrAssert(desc->fSampleCnt > 1);
+ GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER,
+ desc->fMSColorRenderbufferID));
+ CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+ GL_ALLOC_CALL(this->glInterface(),
+ RenderbufferStorageMultisample(GR_GL_RENDERBUFFER,
+ desc->fSampleCnt,
+ msColorFormat,
+ width, height));
+ err = CHECK_ALLOC_ERROR(this->glInterface());
+ if (err != GR_GL_NO_ERROR) {
+ goto FAILED;
+ }
+ GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, desc->fRTFBOID));
+ GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_COLOR_ATTACHMENT0,
+ GR_GL_RENDERBUFFER,
+ desc->fMSColorRenderbufferID));
+ if (!fGLCaps.isConfigVerifiedColorAttachment(desc->fConfig)) {
+ GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+ if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+ goto FAILED;
+ }
+ fGLCaps.markConfigAsValidColorAttachment(desc->fConfig);
+ }
+ }
+ GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, desc->fTexFBOID));
+
+ GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER,
+ GR_GL_COLOR_ATTACHMENT0,
+ GR_GL_TEXTURE_2D,
+ texID, 0));
+ if (!fGLCaps.isConfigVerifiedColorAttachment(desc->fConfig)) {
+ GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+ if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+ goto FAILED;
+ }
+ fGLCaps.markConfigAsValidColorAttachment(desc->fConfig);
+ }
+
+ return true;
+
+FAILED:
+ if (desc->fMSColorRenderbufferID) {
+ GL_CALL(DeleteRenderbuffers(1, &desc->fMSColorRenderbufferID));
+ }
+ if (desc->fRTFBOID != desc->fTexFBOID) {
+ GL_CALL(DeleteFramebuffers(1, &desc->fRTFBOID));
+ }
+ if (desc->fTexFBOID) {
+ GL_CALL(DeleteFramebuffers(1, &desc->fTexFBOID));
+ }
+ return false;
+}
+
+// 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
+
+GrTexture* GrGpuGL::onCreateTexture(const GrTextureDesc& desc,
+ const void* srcData,
+ size_t rowBytes) {
+
+#if GR_COLLECT_STATS
+ ++fStats.fTextureCreateCnt;
+#endif
+
+ GrGLTexture::Desc glTexDesc;
+ GrGLRenderTarget::Desc glRTDesc;
+
+ // Attempt to catch un- or wrongly initialized sample counts;
+ GrAssert(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64);
+
+ glTexDesc.fWidth = desc.fWidth;
+ glTexDesc.fHeight = desc.fHeight;
+ glTexDesc.fConfig = desc.fConfig;
+ glTexDesc.fOwnsID = true;
+
+ glRTDesc.fMSColorRenderbufferID = 0;
+ glRTDesc.fRTFBOID = 0;
+ glRTDesc.fTexFBOID = 0;
+ glRTDesc.fOwnIDs = true;
+ glRTDesc.fConfig = glTexDesc.fConfig;
+
+ bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit);
+
+ const Caps& caps = this->getCaps();
+
+ // We keep GrRenderTargets in GL's normal orientation so that they
+ // can be drawn to by the outside world without the client having
+ // to render upside down.
+ glTexDesc.fOrientation = renderTarget ? GrGLTexture::kBottomUp_Orientation :
+ GrGLTexture::kTopDown_Orientation;
+
+ glRTDesc.fSampleCnt = desc.fSampleCnt;
+ if (GLCaps::kNone_MSFBO == fGLCaps.fMSFBOType &&
+ desc.fSampleCnt) {
+ GrPrintf("MSAA RT requested but not supported on this platform.");
+ }
+
+ if (renderTarget) {
+ if (glTexDesc.fWidth > caps.fMaxRenderTargetSize ||
+ glTexDesc.fHeight > caps.fMaxRenderTargetSize) {
+ return return_null_texture();
+ }
+ }
+
+ GL_CALL(GenTextures(1, &glTexDesc.fTextureID));
+ if (renderTarget && this->glCaps().fTextureUsageSupport) {
+ // provides a hint about how this texture will be used
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_USAGE,
+ GR_GL_FRAMEBUFFER_ATTACHMENT));
+ }
+ if (!glTexDesc.fTextureID) {
+ return return_null_texture();
+ }
+
+ this->setSpareTextureUnit();
+ GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTexDesc.fTextureID));
+
+ // Some drivers like to know filter/wrap before seeing glTexImage2D. Some
+ // drivers have a bug where an FBO won't be complete if it includes a
+ // texture that is not mipmap complete (considering the filter in use).
+ GrGLTexture::TexParams initialTexParams;
+ // we only set a subset here so invalidate first
+ initialTexParams.invalidate();
+ initialTexParams.fFilter = GR_GL_NEAREST;
+ initialTexParams.fWrapS = GR_GL_CLAMP_TO_EDGE;
+ initialTexParams.fWrapT = GR_GL_CLAMP_TO_EDGE;
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_MAG_FILTER,
+ initialTexParams.fFilter));
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_MIN_FILTER,
+ initialTexParams.fFilter));
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_WRAP_S,
+ initialTexParams.fWrapS));
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_WRAP_T,
+ initialTexParams.fWrapT));
+ if (!this->uploadTexData(glTexDesc, true, 0, 0,
+ glTexDesc.fWidth, glTexDesc.fHeight,
+ desc.fConfig, srcData, rowBytes)) {
+ GL_CALL(DeleteTextures(1, &glTexDesc.fTextureID));
+ return return_null_texture();
+ }
+
+ GrGLTexture* tex;
+ if (renderTarget) {
+#if GR_COLLECT_STATS
+ ++fStats.fRenderTargetCreateCnt;
+#endif
+ if (!this->createRenderTargetObjects(glTexDesc.fWidth,
+ glTexDesc.fHeight,
+ glTexDesc.fTextureID,
+ &glRTDesc)) {
+ GL_CALL(DeleteTextures(1, &glTexDesc.fTextureID));
+ return return_null_texture();
+ }
+ tex = new GrGLTexture(this, glTexDesc, glRTDesc);
+ } else {
+ tex = new GrGLTexture(this, glTexDesc);
+ }
+ tex->setCachedTexParams(initialTexParams, this->getResetTimestamp());
+#ifdef TRACE_TEXTURE_CREATION
+ GrPrintf("--- new texture [%d] size=(%d %d) config=%d\n",
+ glTexDesc.fTextureID, desc.fWidth, desc.fHeight, desc.fConfig);
+#endif
+ return tex;
+}
+
+namespace {
+void inline get_stencil_rb_sizes(const GrGLInterface* gl,
+ GrGLuint rb,
+ GrGLStencilBuffer::Format* format) {
+ // we shouldn't ever know one size and not the other
+ GrAssert((kUnknownBitCount == format->fStencilBits) ==
+ (kUnknownBitCount == format->fTotalBits));
+ if (kUnknownBitCount == format->fStencilBits) {
+ GR_GL_GetRenderbufferParameteriv(gl, GR_GL_RENDERBUFFER,
+ GR_GL_RENDERBUFFER_STENCIL_SIZE,
+ (GrGLint*)&format->fStencilBits);
+ if (format->fPacked) {
+ GR_GL_GetRenderbufferParameteriv(gl, GR_GL_RENDERBUFFER,
+ GR_GL_RENDERBUFFER_DEPTH_SIZE,
+ (GrGLint*)&format->fTotalBits);
+ format->fTotalBits += format->fStencilBits;
+ } else {
+ format->fTotalBits = format->fStencilBits;
+ }
+ }
+}
+}
+
+bool GrGpuGL::createStencilBufferForRenderTarget(GrRenderTarget* rt,
+ int width, int height) {
+
+ // All internally created RTs are also textures. We don't create
+ // SBs for a client's standalone RT (that is RT that isnt also a texture).
+ GrAssert(rt->asTexture());
+ GrAssert(width >= rt->width());
+ GrAssert(height >= rt->height());
+
+ int samples = rt->numSamples();
+ GrGLuint sbID;
+ GL_CALL(GenRenderbuffers(1, &sbID));
+ if (!sbID) {
+ return false;
+ }
+
+ GrGLStencilBuffer* sb = NULL;
+
+ int stencilFmtCnt = fGLCaps.fStencilFormats.count();
+ for (int i = 0; i < stencilFmtCnt; ++i) {
+ GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, sbID));
+ // we start with the last stencil format that succeeded in hopes
+ // that we won't go through this loop more than once after the
+ // first (painful) stencil creation.
+ int sIdx = (i + fLastSuccessfulStencilFmtIdx) % stencilFmtCnt;
+ const GLCaps::StencilFormat& sFmt = fGLCaps.fStencilFormats[sIdx];
+ CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+ // we do this "if" so that we don't call the multisample
+ // version on a GL that doesn't have an MSAA extension.
+ if (samples > 1) {
+ GL_ALLOC_CALL(this->glInterface(),
+ RenderbufferStorageMultisample(GR_GL_RENDERBUFFER,
+ samples,
+ sFmt.fFormat.fInternalFormat,
+ width, height));
+ } else {
+ GL_ALLOC_CALL(this->glInterface(),
+ RenderbufferStorage(GR_GL_RENDERBUFFER,
+ sFmt.fFormat.fInternalFormat,
+ width, height));
+ }
+
+ GrGLenum err = CHECK_ALLOC_ERROR(this->glInterface());
+ if (err == GR_GL_NO_ERROR) {
+ // After sized formats we attempt an unsized format and take whatever
+ // sizes GL gives us. In that case we query for the size.
+ GrGLStencilBuffer::Format format = sFmt.fFormat;
+ get_stencil_rb_sizes(this->glInterface(), sbID, &format);
+ sb = new GrGLStencilBuffer(this, sbID, width, height,
+ samples, format);
+ if (this->attachStencilBufferToRenderTarget(sb, rt)) {
+ fLastSuccessfulStencilFmtIdx = sIdx;
+ rt->setStencilBuffer(sb);
+ sb->unref();
+ return true;
+ }
+ sb->abandon(); // otherwise we lose sbID
+ sb->unref();
+ }
+ }
+ GL_CALL(DeleteRenderbuffers(1, &sbID));
+ return false;
+}
+
+bool GrGpuGL::attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
+ GrRenderTarget* rt) {
+ GrGLRenderTarget* glrt = (GrGLRenderTarget*) rt;
+
+ GrGLuint fbo = glrt->renderFBOID();
+
+ if (NULL == sb) {
+ if (NULL != rt->getStencilBuffer()) {
+ GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_STENCIL_ATTACHMENT,
+ GR_GL_RENDERBUFFER, 0));
+ GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_DEPTH_ATTACHMENT,
+ GR_GL_RENDERBUFFER, 0));
+#if GR_DEBUG
+ GrGLenum status;
+ GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+ GrAssert(GR_GL_FRAMEBUFFER_COMPLETE == status);
+#endif
+ }
+ return true;
+ } else {
+ GrGLStencilBuffer* glsb = (GrGLStencilBuffer*) sb;
+ GrGLuint rb = glsb->renderbufferID();
+
+ fHWDrawState.setRenderTarget(NULL);
+ GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fbo));
+ GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_STENCIL_ATTACHMENT,
+ GR_GL_RENDERBUFFER, rb));
+ if (glsb->format().fPacked) {
+ GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_DEPTH_ATTACHMENT,
+ GR_GL_RENDERBUFFER, rb));
+ } else {
+ GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_DEPTH_ATTACHMENT,
+ GR_GL_RENDERBUFFER, 0));
+ }
+
+ GrGLenum status;
+ if (!fGLCaps.isColorConfigAndStencilFormatVerified(rt->config(),
+ glsb->format())) {
+ GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+ if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+ GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_STENCIL_ATTACHMENT,
+ GR_GL_RENDERBUFFER, 0));
+ if (glsb->format().fPacked) {
+ GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_DEPTH_ATTACHMENT,
+ GR_GL_RENDERBUFFER, 0));
+ }
+ return false;
+ } else {
+ fGLCaps.markColorConfigAndStencilFormatAsVerified(
+ rt->config(),
+ glsb->format());
+ }
+ }
+ return true;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrVertexBuffer* GrGpuGL::onCreateVertexBuffer(uint32_t size, bool dynamic) {
+ GrGLuint id;
+ GL_CALL(GenBuffers(1, &id));
+ if (id) {
+ GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, id));
+ fHWGeometryState.fArrayPtrsDirty = true;
+ CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+ // make sure driver can allocate memory for this buffer
+ GL_ALLOC_CALL(this->glInterface(),
+ BufferData(GR_GL_ARRAY_BUFFER,
+ size,
+ NULL, // data ptr
+ dynamic ? GR_GL_DYNAMIC_DRAW :
+ GR_GL_STATIC_DRAW));
+ if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) {
+ GL_CALL(DeleteBuffers(1, &id));
+ // deleting bound buffer does implicit bind to 0
+ fHWGeometryState.fVertexBuffer = NULL;
+ return NULL;
+ }
+ GrGLVertexBuffer* vertexBuffer = new GrGLVertexBuffer(this, id,
+ size, dynamic);
+ fHWGeometryState.fVertexBuffer = vertexBuffer;
+ return vertexBuffer;
+ }
+ return NULL;
+}
+
+GrIndexBuffer* GrGpuGL::onCreateIndexBuffer(uint32_t size, bool dynamic) {
+ GrGLuint id;
+ GL_CALL(GenBuffers(1, &id));
+ if (id) {
+ GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, id));
+ CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+ // make sure driver can allocate memory for this buffer
+ GL_ALLOC_CALL(this->glInterface(),
+ BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+ size,
+ NULL, // data ptr
+ dynamic ? GR_GL_DYNAMIC_DRAW :
+ GR_GL_STATIC_DRAW));
+ if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) {
+ GL_CALL(DeleteBuffers(1, &id));
+ // deleting bound buffer does implicit bind to 0
+ fHWGeometryState.fIndexBuffer = NULL;
+ return NULL;
+ }
+ GrIndexBuffer* indexBuffer = new GrGLIndexBuffer(this, id,
+ size, dynamic);
+ fHWGeometryState.fIndexBuffer = indexBuffer;
+ return indexBuffer;
+ }
+ return NULL;
+}
+
+void GrGpuGL::flushScissor(const GrIRect* rect) {
+ const GrDrawState& drawState = this->getDrawState();
+ const GrGLRenderTarget* rt =
+ static_cast<const GrGLRenderTarget*>(drawState.getRenderTarget());
+
+ GrAssert(NULL != rt);
+ const GrGLIRect& vp = rt->getViewport();
+
+ GrGLIRect scissor;
+ if (NULL != rect) {
+ scissor.setRelativeTo(vp, rect->fLeft, rect->fTop,
+ rect->width(), rect->height());
+ if (scissor.contains(vp)) {
+ rect = NULL;
+ }
+ }
+
+ if (NULL != rect) {
+ if (fHWBounds.fScissorRect != scissor) {
+ scissor.pushToGLScissor(this->glInterface());
+ fHWBounds.fScissorRect = scissor;
+ }
+ if (!fHWBounds.fScissorEnabled) {
+ GL_CALL(Enable(GR_GL_SCISSOR_TEST));
+ fHWBounds.fScissorEnabled = true;
+ }
+ } else {
+ if (fHWBounds.fScissorEnabled) {
+ GL_CALL(Disable(GR_GL_SCISSOR_TEST));
+ fHWBounds.fScissorEnabled = false;
+ }
+ }
+}
+
+void GrGpuGL::onClear(const GrIRect* rect, GrColor color) {
+ const GrDrawState& drawState = this->getDrawState();
+ const GrRenderTarget* rt = drawState.getRenderTarget();
+ // parent class should never let us get here with no RT
+ GrAssert(NULL != rt);
+
+ GrIRect clippedRect;
+ if (NULL != rect) {
+ // flushScissor expects rect to be clipped to the target.
+ clippedRect = *rect;
+ GrIRect rtRect = SkIRect::MakeWH(rt->width(), rt->height());
+ if (clippedRect.intersect(rtRect)) {
+ rect = &clippedRect;
+ } else {
+ return;
+ }
+ }
+ this->flushRenderTarget(rect);
+ this->flushScissor(rect);
+
+ GrGLfloat r, g, b, a;
+ static const GrGLfloat scale255 = 1.f / 255.f;
+ a = GrColorUnpackA(color) * scale255;
+ GrGLfloat scaleRGB = scale255;
+ if (GrPixelConfigIsUnpremultiplied(rt->config())) {
+ scaleRGB *= a;
+ }
+ r = GrColorUnpackR(color) * scaleRGB;
+ g = GrColorUnpackG(color) * scaleRGB;
+ b = GrColorUnpackB(color) * scaleRGB;
+
+ GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+ fHWDrawState.disableState(GrDrawState::kNoColorWrites_StateBit);
+ GL_CALL(ClearColor(r, g, b, a));
+ GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
+}
+
+void GrGpuGL::clearStencil() {
+ if (NULL == this->getDrawState().getRenderTarget()) {
+ return;
+ }
+
+ this->flushRenderTarget(&GrIRect::EmptyIRect());
+
+ if (fHWBounds.fScissorEnabled) {
+ GL_CALL(Disable(GR_GL_SCISSOR_TEST));
+ fHWBounds.fScissorEnabled = false;
+ }
+ GL_CALL(StencilMask(0xffffffff));
+ GL_CALL(ClearStencil(0));
+ GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
+ fHWDrawState.stencil()->invalidate();
+}
+
+void GrGpuGL::clearStencilClip(const GrIRect& rect, bool insideClip) {
+ const GrDrawState& drawState = this->getDrawState();
+ const GrRenderTarget* rt = drawState.getRenderTarget();
+ GrAssert(NULL != rt);
+
+ // this should only be called internally when we know we have a
+ // stencil buffer.
+ GrAssert(NULL != rt->getStencilBuffer());
+ GrGLint stencilBitCount = rt->getStencilBuffer()->bits();
+#if 0
+ GrAssert(stencilBitCount > 0);
+ GrGLint clipStencilMask = (1 << (stencilBitCount - 1));
+#else
+ // we could just clear the clip bit but when we go through
+ // ANGLE a partial stencil mask will cause clears to be
+ // turned into draws. Our contract on GrDrawTarget says that
+ // changing the clip between stencil passes may or may not
+ // zero the client's clip bits. So we just clear the whole thing.
+ static const GrGLint clipStencilMask = ~0;
+#endif
+ GrGLint value;
+ if (insideClip) {
+ value = (1 << (stencilBitCount - 1));
+ } else {
+ value = 0;
+ }
+ this->flushRenderTarget(&GrIRect::EmptyIRect());
+ this->flushScissor(&rect);
+ GL_CALL(StencilMask(clipStencilMask));
+ GL_CALL(ClearStencil(value));
+ GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
+ fHWDrawState.stencil()->invalidate();
+}
+
+void GrGpuGL::onForceRenderTargetFlush() {
+ this->flushRenderTarget(&GrIRect::EmptyIRect());
+}
+
+bool GrGpuGL::readPixelsWillPayForYFlip(GrRenderTarget* renderTarget,
+ int left, int top,
+ int width, int height,
+ GrPixelConfig config,
+ size_t rowBytes) const {
+ // if GL can do the flip then we'll never pay for it.
+ if (this->glCaps().fPackFlipYSupport) {
+ return false;
+ }
+
+ // If we have to do memcpy to handle non-trim rowBytes then we
+ // get the flip for free. Otherwise it costs.
+ if (this->glCaps().fPackRowLengthSupport) {
+ return true;
+ }
+ // If we have to do memcpys to handle rowBytes then y-flip is free
+ // Note the rowBytes might be tight to the passed in data, but if data
+ // gets clipped in x to the target the rowBytes will no longer be tight.
+ if (left >= 0 && (left + width) < renderTarget->width()) {
+ return 0 == rowBytes ||
+ GrBytesPerPixel(config) * width == rowBytes;
+ } else {
+ return false;
+ }
+}
+
+bool GrGpuGL::onReadPixels(GrRenderTarget* target,
+ int left, int top,
+ int width, int height,
+ GrPixelConfig config,
+ void* buffer,
+ size_t rowBytes,
+ bool invertY) {
+ GrGLenum format;
+ GrGLenum type;
+ if (!this->configToGLFormats(config, false, NULL, &format, &type)) {
+ return false;
+ }
+ size_t bpp = GrBytesPerPixel(config);
+ if (!adjust_pixel_ops_params(target->width(), target->height(), bpp,
+ &left, &top, &width, &height,
+ const_cast<const void**>(&buffer),
+ &rowBytes)) {
+ return false;
+ }
+
+ // resolve the render target if necessary
+ GrGLRenderTarget* tgt = static_cast<GrGLRenderTarget*>(target);
+ GrDrawState::AutoRenderTargetRestore artr;
+ switch (tgt->getResolveType()) {
+ case GrGLRenderTarget::kCantResolve_ResolveType:
+ return false;
+ case GrGLRenderTarget::kAutoResolves_ResolveType:
+ artr.set(this->drawState(), target);
+ this->flushRenderTarget(&GrIRect::EmptyIRect());
+ break;
+ case GrGLRenderTarget::kCanResolve_ResolveType:
+ this->onResolveRenderTarget(tgt);
+ // we don't track the state of the READ FBO ID.
+ GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER,
+ tgt->textureFBOID()));
+ break;
+ default:
+ GrCrash("Unknown resolve type");
+ }
+
+ const GrGLIRect& glvp = tgt->getViewport();
+
+ // the read rect is viewport-relative
+ GrGLIRect readRect;
+ readRect.setRelativeTo(glvp, left, top, width, height);
+
+ size_t tightRowBytes = bpp * width;
+ if (0 == rowBytes) {
+ rowBytes = tightRowBytes;
+ }
+ size_t readDstRowBytes = tightRowBytes;
+ void* readDst = buffer;
+
+ // determine if GL can read using the passed rowBytes or if we need
+ // a scratch buffer.
+ SkAutoSMalloc<32 * sizeof(GrColor)> scratch;
+ if (rowBytes != tightRowBytes) {
+ if (this->glCaps().fPackRowLengthSupport) {
+ GrAssert(!(rowBytes % sizeof(GrColor)));
+ GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, rowBytes / sizeof(GrColor)));
+ readDstRowBytes = rowBytes;
+ } else {
+ scratch.reset(tightRowBytes * height);
+ readDst = scratch.get();
+ }
+ }
+ if (!invertY && this->glCaps().fPackFlipYSupport) {
+ GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, 1));
+ }
+ GL_CALL(ReadPixels(readRect.fLeft, readRect.fBottom,
+ readRect.fWidth, readRect.fHeight,
+ format, type, readDst));
+ if (readDstRowBytes != tightRowBytes) {
+ GrAssert(this->glCaps().fPackRowLengthSupport);
+ GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0));
+ }
+ if (!invertY && this->glCaps().fPackFlipYSupport) {
+ GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, 0));
+ invertY = true;
+ }
+
+ // now reverse the order of the rows, since GL's are bottom-to-top, but our
+ // API presents top-to-bottom. We must preserve the padding contents. Note
+ // that the above readPixels did not overwrite the padding.
+ if (readDst == buffer) {
+ GrAssert(rowBytes == readDstRowBytes);
+ if (!invertY) {
+ scratch.reset(tightRowBytes);
+ void* tmpRow = scratch.get();
+ // flip y in-place by rows
+ const int halfY = height >> 1;
+ char* top = reinterpret_cast<char*>(buffer);
+ char* bottom = top + (height - 1) * rowBytes;
+ for (int y = 0; y < halfY; y++) {
+ memcpy(tmpRow, top, tightRowBytes);
+ memcpy(top, bottom, tightRowBytes);
+ memcpy(bottom, tmpRow, tightRowBytes);
+ top += rowBytes;
+ bottom -= rowBytes;
+ }
+ }
+ } else {
+ GrAssert(readDst != buffer); GrAssert(rowBytes != tightRowBytes);
+ // copy from readDst to buffer while flipping y
+ const int halfY = height >> 1;
+ const char* src = reinterpret_cast<const char*>(readDst);
+ char* dst = reinterpret_cast<char*>(buffer);
+ if (!invertY) {
+ dst += (height-1) * rowBytes;
+ }
+ for (int y = 0; y < height; y++) {
+ memcpy(dst, src, tightRowBytes);
+ src += readDstRowBytes;
+ if (invertY) {
+ dst += rowBytes;
+ } else {
+ dst -= rowBytes;
+ }
+ }
+ }
+ return true;
+}
+
+void GrGpuGL::flushRenderTarget(const GrIRect* bound) {
+
+ GrGLRenderTarget* rt =
+ static_cast<GrGLRenderTarget*>(this->drawState()->getRenderTarget());
+ GrAssert(NULL != rt);
+
+ if (fHWDrawState.getRenderTarget() != rt) {
+ GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, rt->renderFBOID()));
+ #if GR_COLLECT_STATS
+ ++fStats.fRenderTargetChngCnt;
+ #endif
+ #if GR_DEBUG
+ GrGLenum status;
+ GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+ if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+ GrPrintf("GrGpuGL::flushRenderTarget glCheckFramebufferStatus %x\n", status);
+ }
+ #endif
+ fDirtyFlags.fRenderTargetChanged = true;
+ fHWDrawState.setRenderTarget(rt);
+ const GrGLIRect& vp = rt->getViewport();
+ if (fHWBounds.fViewportRect != vp) {
+ vp.pushToGLViewport(this->glInterface());
+ fHWBounds.fViewportRect = vp;
+ }
+ }
+ if (NULL == bound || !bound->isEmpty()) {
+ rt->flagAsNeedingResolve(bound);
+ }
+}
+
+GrGLenum gPrimitiveType2GLMode[] = {
+ GR_GL_TRIANGLES,
+ GR_GL_TRIANGLE_STRIP,
+ GR_GL_TRIANGLE_FAN,
+ GR_GL_POINTS,
+ GR_GL_LINES,
+ GR_GL_LINE_STRIP
+};
+
+#define SWAP_PER_DRAW 0
+
+#if SWAP_PER_DRAW
+ #if GR_MAC_BUILD
+ #include <AGL/agl.h>
+ #elif GR_WIN32_BUILD
+ void SwapBuf() {
+ DWORD procID = GetCurrentProcessId();
+ HWND hwnd = GetTopWindow(GetDesktopWindow());
+ while(hwnd) {
+ DWORD wndProcID = 0;
+ GetWindowThreadProcessId(hwnd, &wndProcID);
+ if(wndProcID == procID) {
+ SwapBuffers(GetDC(hwnd));
+ }
+ hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
+ }
+ }
+ #endif
+#endif
+
+void GrGpuGL::onGpuDrawIndexed(GrPrimitiveType type,
+ uint32_t startVertex,
+ uint32_t startIndex,
+ uint32_t vertexCount,
+ uint32_t indexCount) {
+ GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+
+ GrGLvoid* indices = (GrGLvoid*)(sizeof(uint16_t) * startIndex);
+
+ GrAssert(NULL != fHWGeometryState.fIndexBuffer);
+ GrAssert(NULL != fHWGeometryState.fVertexBuffer);
+
+ // our setupGeometry better have adjusted this to zero since
+ // DrawElements always draws from the begining of the arrays for idx 0.
+ GrAssert(0 == startVertex);
+
+ GL_CALL(DrawElements(gPrimitiveType2GLMode[type], indexCount,
+ GR_GL_UNSIGNED_SHORT, indices));
+#if SWAP_PER_DRAW
+ glFlush();
+ #if GR_MAC_BUILD
+ aglSwapBuffers(aglGetCurrentContext());
+ int set_a_break_pt_here = 9;
+ aglSwapBuffers(aglGetCurrentContext());
+ #elif GR_WIN32_BUILD
+ SwapBuf();
+ int set_a_break_pt_here = 9;
+ SwapBuf();
+ #endif
+#endif
+}
+
+void GrGpuGL::onGpuDrawNonIndexed(GrPrimitiveType type,
+ uint32_t startVertex,
+ uint32_t vertexCount) {
+ GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+
+ GrAssert(NULL != fHWGeometryState.fVertexBuffer);
+
+ // our setupGeometry better have adjusted this to zero.
+ // DrawElements doesn't take an offset so we always adjus the startVertex.
+ GrAssert(0 == startVertex);
+
+ // pass 0 for parameter first. We have to adjust gl*Pointer() to
+ // account for startVertex in the DrawElements case. So we always
+ // rely on setupGeometry to have accounted for startVertex.
+ GL_CALL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount));
+#if SWAP_PER_DRAW
+ glFlush();
+ #if GR_MAC_BUILD
+ aglSwapBuffers(aglGetCurrentContext());
+ int set_a_break_pt_here = 9;
+ aglSwapBuffers(aglGetCurrentContext());
+ #elif GR_WIN32_BUILD
+ SwapBuf();
+ int set_a_break_pt_here = 9;
+ SwapBuf();
+ #endif
+#endif
+}
+
+void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) {
+
+ GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(target);
+
+ if (rt->needsResolve()) {
+ GrAssert(GLCaps::kNone_MSFBO != fGLCaps.fMSFBOType);
+ GrAssert(rt->textureFBOID() != rt->renderFBOID());
+ GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER,
+ rt->renderFBOID()));
+ GL_CALL(BindFramebuffer(GR_GL_DRAW_FRAMEBUFFER,
+ rt->textureFBOID()));
+ #if GR_COLLECT_STATS
+ ++fStats.fRenderTargetChngCnt;
+ #endif
+ // make sure we go through flushRenderTarget() since we've modified
+ // the bound DRAW FBO ID.
+ fHWDrawState.setRenderTarget(NULL);
+ const GrGLIRect& vp = rt->getViewport();
+ const GrIRect dirtyRect = rt->getResolveRect();
+ GrGLIRect r;
+ r.setRelativeTo(vp, dirtyRect.fLeft, dirtyRect.fTop,
+ dirtyRect.width(), dirtyRect.height());
+
+ if (GLCaps::kAppleES_MSFBO == fGLCaps.fMSFBOType) {
+ // Apple's extension uses the scissor as the blit bounds.
+ GL_CALL(Enable(GR_GL_SCISSOR_TEST));
+ GL_CALL(Scissor(r.fLeft, r.fBottom,
+ r.fWidth, r.fHeight));
+ GL_CALL(ResolveMultisampleFramebuffer());
+ fHWBounds.fScissorRect.invalidate();
+ fHWBounds.fScissorEnabled = true;
+ } else {
+ if (GLCaps::kDesktopARB_MSFBO != fGLCaps.fMSFBOType) {
+ // this respects the scissor during the blit, so disable it.
+ GrAssert(GLCaps::kDesktopEXT_MSFBO == fGLCaps.fMSFBOType);
+ this->flushScissor(NULL);
+ }
+ int right = r.fLeft + r.fWidth;
+ int top = r.fBottom + r.fHeight;
+ GL_CALL(BlitFramebuffer(r.fLeft, r.fBottom, right, top,
+ r.fLeft, r.fBottom, right, top,
+ GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
+ }
+ rt->flagAsResolved();
+ }
+}
+
+static const GrGLenum grToGLStencilFunc[] = {
+ GR_GL_ALWAYS, // kAlways_StencilFunc
+ GR_GL_NEVER, // kNever_StencilFunc
+ GR_GL_GREATER, // kGreater_StencilFunc
+ GR_GL_GEQUAL, // kGEqual_StencilFunc
+ GR_GL_LESS, // kLess_StencilFunc
+ GR_GL_LEQUAL, // kLEqual_StencilFunc,
+ GR_GL_EQUAL, // kEqual_StencilFunc,
+ GR_GL_NOTEQUAL, // kNotEqual_StencilFunc,
+};
+GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilFunc) == kBasicStencilFuncCount);
+GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+GR_STATIC_ASSERT(1 == kNever_StencilFunc);
+GR_STATIC_ASSERT(2 == kGreater_StencilFunc);
+GR_STATIC_ASSERT(3 == kGEqual_StencilFunc);
+GR_STATIC_ASSERT(4 == kLess_StencilFunc);
+GR_STATIC_ASSERT(5 == kLEqual_StencilFunc);
+GR_STATIC_ASSERT(6 == kEqual_StencilFunc);
+GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc);
+
+static const GrGLenum grToGLStencilOp[] = {
+ GR_GL_KEEP, // kKeep_StencilOp
+ GR_GL_REPLACE, // kReplace_StencilOp
+ GR_GL_INCR_WRAP, // kIncWrap_StencilOp
+ GR_GL_INCR, // kIncClamp_StencilOp
+ GR_GL_DECR_WRAP, // kDecWrap_StencilOp
+ GR_GL_DECR, // kDecClamp_StencilOp
+ GR_GL_ZERO, // kZero_StencilOp
+ GR_GL_INVERT, // kInvert_StencilOp
+};
+GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilOp) == kStencilOpCount);
+GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+GR_STATIC_ASSERT(1 == kReplace_StencilOp);
+GR_STATIC_ASSERT(2 == kIncWrap_StencilOp);
+GR_STATIC_ASSERT(3 == kIncClamp_StencilOp);
+GR_STATIC_ASSERT(4 == kDecWrap_StencilOp);
+GR_STATIC_ASSERT(5 == kDecClamp_StencilOp);
+GR_STATIC_ASSERT(6 == kZero_StencilOp);
+GR_STATIC_ASSERT(7 == kInvert_StencilOp);
+
+void GrGpuGL::flushStencil() {
+ const GrDrawState& drawState = this->getDrawState();
+
+ const GrStencilSettings* settings = &drawState.getStencil();
+
+ // use stencil for clipping if clipping is enabled and the clip
+ // has been written into the stencil.
+ bool stencilClip = fClipInStencil && drawState.isClipState();
+ bool drawClipToStencil =
+ drawState.isStateFlagEnabled(kModifyStencilClip_StateBit);
+ bool stencilChange = (fHWDrawState.getStencil() != *settings) ||
+ (fHWStencilClip != stencilClip) ||
+ (fHWDrawState.isStateFlagEnabled(kModifyStencilClip_StateBit) !=
+ drawClipToStencil);
+
+ if (stencilChange) {
+
+ // we can't simultaneously perform stencil-clipping and
+ // modify the stencil clip
+ GrAssert(!stencilClip || !drawClipToStencil);
+
+ if (settings->isDisabled()) {
+ if (stencilClip) {
+ settings = &gClipStencilSettings;
+ }
+ }
+
+ if (settings->isDisabled()) {
+ GL_CALL(Disable(GR_GL_STENCIL_TEST));
+ } else {
+ GL_CALL(Enable(GR_GL_STENCIL_TEST));
+ #if GR_DEBUG
+ if (!this->getCaps().fStencilWrapOpsSupport) {
+ GrAssert(settings->frontPassOp() != kIncWrap_StencilOp);
+ GrAssert(settings->frontPassOp() != kDecWrap_StencilOp);
+ GrAssert(settings->frontFailOp() != kIncWrap_StencilOp);
+ GrAssert(settings->backFailOp() != kDecWrap_StencilOp);
+ GrAssert(settings->backPassOp() != kIncWrap_StencilOp);
+ GrAssert(settings->backPassOp() != kDecWrap_StencilOp);
+ GrAssert(settings->backFailOp() != kIncWrap_StencilOp);
+ GrAssert(settings->frontFailOp() != kDecWrap_StencilOp);
+ }
+ #endif
+ int stencilBits = 0;
+ GrStencilBuffer* stencilBuffer =
+ drawState.getRenderTarget()->getStencilBuffer();
+ if (NULL != stencilBuffer) {
+ stencilBits = stencilBuffer->bits();
+ }
+ // TODO: dynamically attach a stencil buffer
+ GrAssert(stencilBits || settings->isDisabled());
+
+ GrGLuint clipStencilMask = 0;
+ GrGLuint userStencilMask = ~0;
+ if (stencilBits > 0) {
+ clipStencilMask = 1 << (stencilBits - 1);
+ userStencilMask = clipStencilMask - 1;
+ }
+
+ unsigned int frontRef = settings->frontFuncRef();
+ unsigned int frontMask = settings->frontFuncMask();
+ unsigned int frontWriteMask = settings->frontWriteMask();
+ GrGLenum frontFunc;
+
+ if (drawClipToStencil) {
+ GrAssert(settings->frontFunc() < kBasicStencilFuncCount);
+ frontFunc = grToGLStencilFunc[settings->frontFunc()];
+ } else {
+ frontFunc = grToGLStencilFunc[ConvertStencilFunc(
+ stencilClip, settings->frontFunc())];
+
+ ConvertStencilFuncAndMask(settings->frontFunc(),
+ stencilClip,
+ clipStencilMask,
+ userStencilMask,
+ &frontRef,
+ &frontMask);
+ frontWriteMask &= userStencilMask;
+ }
+ GrAssert((size_t)
+ settings->frontFailOp() < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert((size_t)
+ settings->frontPassOp() < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert((size_t)
+ settings->backFailOp() < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert((size_t)
+ settings->backPassOp() < GR_ARRAY_COUNT(grToGLStencilOp));
+ if (this->getCaps().fTwoSidedStencilSupport) {
+ GrGLenum backFunc;
+
+ unsigned int backRef = settings->backFuncRef();
+ unsigned int backMask = settings->backFuncMask();
+ unsigned int backWriteMask = settings->backWriteMask();
+
+
+ if (drawClipToStencil) {
+ GrAssert(settings->backFunc() < kBasicStencilFuncCount);
+ backFunc = grToGLStencilFunc[settings->backFunc()];
+ } else {
+ backFunc = grToGLStencilFunc[ConvertStencilFunc(
+ stencilClip, settings->backFunc())];
+ ConvertStencilFuncAndMask(settings->backFunc(),
+ stencilClip,
+ clipStencilMask,
+ userStencilMask,
+ &backRef,
+ &backMask);
+ backWriteMask &= userStencilMask;
+ }
+
+ GL_CALL(StencilFuncSeparate(GR_GL_FRONT, frontFunc,
+ frontRef, frontMask));
+ GL_CALL(StencilMaskSeparate(GR_GL_FRONT, frontWriteMask));
+ GL_CALL(StencilFuncSeparate(GR_GL_BACK, backFunc,
+ backRef, backMask));
+ GL_CALL(StencilMaskSeparate(GR_GL_BACK, backWriteMask));
+ GL_CALL(StencilOpSeparate(GR_GL_FRONT,
+ grToGLStencilOp[settings->frontFailOp()],
+ grToGLStencilOp[settings->frontPassOp()],
+ grToGLStencilOp[settings->frontPassOp()]));
+
+ GL_CALL(StencilOpSeparate(GR_GL_BACK,
+ grToGLStencilOp[settings->backFailOp()],
+ grToGLStencilOp[settings->backPassOp()],
+ grToGLStencilOp[settings->backPassOp()]));
+ } else {
+ GL_CALL(StencilFunc(frontFunc, frontRef, frontMask));
+ GL_CALL(StencilMask(frontWriteMask));
+ GL_CALL(StencilOp(grToGLStencilOp[settings->frontFailOp()],
+ grToGLStencilOp[settings->frontPassOp()],
+ grToGLStencilOp[settings->frontPassOp()]));
+ }
+ }
+ *fHWDrawState.stencil() = *settings;
+ fHWStencilClip = stencilClip;
+ }
+}
+
+void GrGpuGL::flushAAState(GrPrimitiveType type) {
+ const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ // ES doesn't support toggling GL_MULTISAMPLE and doesn't have
+ // smooth lines.
+
+ // we prefer smooth lines over multisampled lines
+ // msaa should be disabled if drawing smooth lines.
+ if (GrIsPrimTypeLines(type)) {
+ bool smooth = this->willUseHWAALines();
+ if (!fHWAAState.fSmoothLineEnabled && smooth) {
+ GL_CALL(Enable(GR_GL_LINE_SMOOTH));
+ fHWAAState.fSmoothLineEnabled = true;
+ } else if (fHWAAState.fSmoothLineEnabled && !smooth) {
+ GL_CALL(Disable(GR_GL_LINE_SMOOTH));
+ fHWAAState.fSmoothLineEnabled = false;
+ }
+ if (rt->isMultisampled() &&
+ fHWAAState.fMSAAEnabled) {
+ GL_CALL(Disable(GR_GL_MULTISAMPLE));
+ fHWAAState.fMSAAEnabled = false;
+ }
+ } else if (rt->isMultisampled() &&
+ this->getDrawState().isHWAntialiasState() !=
+ fHWAAState.fMSAAEnabled) {
+ if (fHWAAState.fMSAAEnabled) {
+ GL_CALL(Disable(GR_GL_MULTISAMPLE));
+ fHWAAState.fMSAAEnabled = false;
+ } else {
+ GL_CALL(Enable(GR_GL_MULTISAMPLE));
+ fHWAAState.fMSAAEnabled = true;
+ }
+ }
+ }
+}
+
+void GrGpuGL::flushBlend(GrPrimitiveType type,
+ GrBlendCoeff srcCoeff,
+ GrBlendCoeff dstCoeff) {
+ if (GrIsPrimTypeLines(type) && this->willUseHWAALines()) {
+ if (fHWBlendDisabled) {
+ GL_CALL(Enable(GR_GL_BLEND));
+ fHWBlendDisabled = false;
+ }
+ if (kSA_BlendCoeff != fHWDrawState.getSrcBlendCoeff() ||
+ kISA_BlendCoeff != fHWDrawState.getDstBlendCoeff()) {
+ GL_CALL(BlendFunc(gXfermodeCoeff2Blend[kSA_BlendCoeff],
+ gXfermodeCoeff2Blend[kISA_BlendCoeff]));
+ fHWDrawState.setBlendFunc(kSA_BlendCoeff, kISA_BlendCoeff);
+ }
+ } else {
+ // any optimization to disable blending should
+ // have already been applied and tweaked the coeffs
+ // to (1, 0).
+ bool blendOff = kOne_BlendCoeff == srcCoeff &&
+ kZero_BlendCoeff == dstCoeff;
+ if (fHWBlendDisabled != blendOff) {
+ if (blendOff) {
+ GL_CALL(Disable(GR_GL_BLEND));
+ } else {
+ GL_CALL(Enable(GR_GL_BLEND));
+ }
+ fHWBlendDisabled = blendOff;
+ }
+ if (!blendOff) {
+ if (fHWDrawState.getSrcBlendCoeff() != srcCoeff ||
+ fHWDrawState.getDstBlendCoeff() != dstCoeff) {
+ GL_CALL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
+ gXfermodeCoeff2Blend[dstCoeff]));
+ fHWDrawState.setBlendFunc(srcCoeff, dstCoeff);
+ }
+ GrColor blendConst = fCurrDrawState.getBlendConstant();
+ if ((BlendCoeffReferencesConstant(srcCoeff) ||
+ BlendCoeffReferencesConstant(dstCoeff)) &&
+ fHWDrawState.getBlendConstant() != blendConst) {
+
+ float c[] = {
+ GrColorUnpackR(blendConst) / 255.f,
+ GrColorUnpackG(blendConst) / 255.f,
+ GrColorUnpackB(blendConst) / 255.f,
+ GrColorUnpackA(blendConst) / 255.f
+ };
+ GL_CALL(BlendColor(c[0], c[1], c[2], c[3]));
+ fHWDrawState.setBlendConstant(blendConst);
+ }
+ }
+ }
+}
+
+namespace {
+
+unsigned gr_to_gl_filter(GrSamplerState::Filter filter) {
+ switch (filter) {
+ case GrSamplerState::kBilinear_Filter:
+ case GrSamplerState::k4x4Downsample_Filter:
+ return GR_GL_LINEAR;
+ case GrSamplerState::kNearest_Filter:
+ case GrSamplerState::kConvolution_Filter:
+ return GR_GL_NEAREST;
+ default:
+ GrAssert(!"Unknown filter type");
+ return GR_GL_LINEAR;
+ }
+}
+
+const GrGLenum* get_swizzle(GrPixelConfig config,
+ const GrSamplerState& sampler) {
+ if (GrPixelConfigIsAlphaOnly(config)) {
+ static const GrGLenum gAlphaSmear[] = { GR_GL_ALPHA, GR_GL_ALPHA,
+ GR_GL_ALPHA, GR_GL_ALPHA };
+ return gAlphaSmear;
+ } else if (sampler.swapsRAndB()) {
+ static const GrGLenum gRedBlueSwap[] = { GR_GL_BLUE, GR_GL_GREEN,
+ GR_GL_RED, GR_GL_ALPHA };
+ return gRedBlueSwap;
+ } else {
+ static const GrGLenum gStraight[] = { GR_GL_RED, GR_GL_GREEN,
+ GR_GL_BLUE, GR_GL_ALPHA };
+ return gStraight;
+ }
+}
+
+void set_tex_swizzle(GrGLenum swizzle[4], const GrGLInterface* gl) {
+ // should add texparameteri to interface to make 1 instead of 4 calls here
+ GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_SWIZZLE_R,
+ swizzle[0]));
+ GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_SWIZZLE_G,
+ swizzle[1]));
+ GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_SWIZZLE_B,
+ swizzle[2]));
+ GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_SWIZZLE_A,
+ swizzle[3]));
+}
+}
+
+bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
+
+ GrDrawState* drawState = this->drawState();
+ // GrGpu::setupClipAndFlushState should have already checked this
+ // and bailed if not true.
+ GrAssert(NULL != drawState->getRenderTarget());
+
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ // bind texture and set sampler state
+ if (this->isStageEnabled(s)) {
+ GrGLTexture* nextTexture =
+ static_cast<GrGLTexture*>(drawState->getTexture(s));
+
+ // true for now, but maybe not with GrEffect.
+ GrAssert(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.
+ GrGLRenderTarget* texRT =
+ static_cast<GrGLRenderTarget*>(nextTexture->asRenderTarget());
+ if (NULL != texRT) {
+ this->onResolveRenderTarget(texRT);
+ }
+
+ if (fHWDrawState.getTexture(s) != nextTexture) {
+ setTextureUnit(s);
+ GL_CALL(BindTexture(GR_GL_TEXTURE_2D, nextTexture->textureID()));
+ #if GR_COLLECT_STATS
+ ++fStats.fTextureChngCnt;
+ #endif
+ //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
+ fHWDrawState.setTexture(s, nextTexture);
+ // The texture matrix has to compensate for texture width/height
+ // and NPOT-embedded-in-POT
+ fDirtyFlags.fTextureChangedMask |= (1 << s);
+ }
+
+ const GrSamplerState& sampler = drawState->getSampler(s);
+ ResetTimestamp timestamp;
+ const GrGLTexture::TexParams& oldTexParams =
+ nextTexture->getCachedTexParams(&timestamp);
+ bool setAll = timestamp < this->getResetTimestamp();
+ GrGLTexture::TexParams newTexParams;
+
+ newTexParams.fFilter = gr_to_gl_filter(sampler.getFilter());
+
+ const GrGLenum* wraps = GrGLTexture::WrapMode2GLWrap();
+ newTexParams.fWrapS = wraps[sampler.getWrapX()];
+ newTexParams.fWrapT = wraps[sampler.getWrapY()];
+ memcpy(newTexParams.fSwizzleRGBA,
+ get_swizzle(nextTexture->config(), sampler),
+ sizeof(newTexParams.fSwizzleRGBA));
+ if (setAll || newTexParams.fFilter != oldTexParams.fFilter) {
+ setTextureUnit(s);
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_MAG_FILTER,
+ newTexParams.fFilter));
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_MIN_FILTER,
+ newTexParams.fFilter));
+ }
+ if (setAll || newTexParams.fWrapS != oldTexParams.fWrapS) {
+ setTextureUnit(s);
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_WRAP_S,
+ newTexParams.fWrapS));
+ }
+ if (setAll || newTexParams.fWrapT != oldTexParams.fWrapT) {
+ setTextureUnit(s);
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+ GR_GL_TEXTURE_WRAP_T,
+ newTexParams.fWrapT));
+ }
+ if (this->glCaps().fTextureSwizzleSupport &&
+ (setAll ||
+ memcmp(newTexParams.fSwizzleRGBA,
+ oldTexParams.fSwizzleRGBA,
+ sizeof(newTexParams.fSwizzleRGBA)))) {
+ setTextureUnit(s);
+ set_tex_swizzle(newTexParams.fSwizzleRGBA,
+ this->glInterface());
+ }
+ nextTexture->setCachedTexParams(newTexParams,
+ this->getResetTimestamp());
+ }
+ }
+
+ GrIRect* rect = NULL;
+ GrIRect clipBounds;
+ if (drawState->isClipState() &&
+ fClip.hasConservativeBounds()) {
+ fClip.getConservativeBounds().roundOut(&clipBounds);
+ rect = &clipBounds;
+ }
+ this->flushRenderTarget(rect);
+ this->flushAAState(type);
+
+ if (drawState->isDitherState() != fHWDrawState.isDitherState()) {
+ if (drawState->isDitherState()) {
+ GL_CALL(Enable(GR_GL_DITHER));
+ } else {
+ GL_CALL(Disable(GR_GL_DITHER));
+ }
+ }
+
+ if (drawState->isColorWriteDisabled() !=
+ fHWDrawState.isColorWriteDisabled()) {
+ GrGLenum mask;
+ if (drawState->isColorWriteDisabled()) {
+ mask = GR_GL_FALSE;
+ } else {
+ mask = GR_GL_TRUE;
+ }
+ GL_CALL(ColorMask(mask, mask, mask, mask));
+ }
+
+ if (fHWDrawState.getDrawFace() != drawState->getDrawFace()) {
+ switch (fCurrDrawState.getDrawFace()) {
+ case GrDrawState::kCCW_DrawFace:
+ GL_CALL(Enable(GR_GL_CULL_FACE));
+ GL_CALL(CullFace(GR_GL_BACK));
+ break;
+ case GrDrawState::kCW_DrawFace:
+ GL_CALL(Enable(GR_GL_CULL_FACE));
+ GL_CALL(CullFace(GR_GL_FRONT));
+ break;
+ case GrDrawState::kBoth_DrawFace:
+ GL_CALL(Disable(GR_GL_CULL_FACE));
+ break;
+ default:
+ GrCrash("Unknown draw face.");
+ }
+ fHWDrawState.setDrawFace(drawState->getDrawFace());
+ }
+
+#if GR_DEBUG
+ // check for circular rendering
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ GrAssert(!this->isStageEnabled(s) ||
+ NULL == drawState->getRenderTarget() ||
+ NULL == drawState->getTexture(s) ||
+ drawState->getTexture(s)->asRenderTarget() !=
+ drawState->getRenderTarget());
+ }
+#endif
+
+ this->flushStencil();
+
+ // This copy must happen after flushStencil() is called. flushStencil()
+ // relies on detecting when the kModifyStencilClip_StateBit state has
+ // changed since the last draw.
+ fHWDrawState.copyStateFlags(*drawState);
+ return true;
+}
+
+void GrGpuGL::notifyVertexBufferBind(const GrGLVertexBuffer* buffer) {
+ if (fHWGeometryState.fVertexBuffer != buffer) {
+ fHWGeometryState.fArrayPtrsDirty = true;
+ fHWGeometryState.fVertexBuffer = buffer;
+ }
+}
+
+void GrGpuGL::notifyVertexBufferDelete(const GrGLVertexBuffer* buffer) {
+ if (fHWGeometryState.fVertexBuffer == buffer) {
+ // deleting bound buffer does implied bind to 0
+ fHWGeometryState.fVertexBuffer = NULL;
+ fHWGeometryState.fArrayPtrsDirty = true;
+ }
+}
+
+void GrGpuGL::notifyIndexBufferBind(const GrGLIndexBuffer* buffer) {
+ fHWGeometryState.fIndexBuffer = buffer;
+}
+
+void GrGpuGL::notifyIndexBufferDelete(const GrGLIndexBuffer* buffer) {
+ if (fHWGeometryState.fIndexBuffer == buffer) {
+ // deleting bound buffer does implied bind to 0
+ fHWGeometryState.fIndexBuffer = NULL;
+ }
+}
+
+void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) {
+ GrAssert(NULL != renderTarget);
+ GrDrawState* drawState = this->drawState();
+ if (drawState->getRenderTarget() == renderTarget) {
+ drawState->setRenderTarget(NULL);
+ }
+ if (fHWDrawState.getRenderTarget() == renderTarget) {
+ fHWDrawState.setRenderTarget(NULL);
+ }
+}
+
+void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) {
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ GrDrawState* drawState = this->drawState();
+ if (drawState->getTexture(s) == texture) {
+ fCurrDrawState.setTexture(s, NULL);
+ }
+ if (fHWDrawState.getTexture(s) == texture) {
+ // deleting bound texture does implied bind to 0
+ fHWDrawState.setTexture(s, NULL);
+ }
+ }
+}
+
+bool GrGpuGL::configToGLFormats(GrPixelConfig config,
+ bool getSizedInternalFormat,
+ GrGLenum* internalFormat,
+ GrGLenum* externalFormat,
+ GrGLenum* externalType) {
+ GrGLenum dontCare;
+ if (NULL == internalFormat) {
+ internalFormat = &dontCare;
+ }
+ if (NULL == externalFormat) {
+ externalFormat = &dontCare;
+ }
+ if (NULL == externalType) {
+ externalType = &dontCare;
+ }
+
+ switch (config) {
+ case kRGBA_8888_PM_GrPixelConfig:
+ case kRGBA_8888_UPM_GrPixelConfig:
+ *internalFormat = GR_GL_RGBA;
+ *externalFormat = GR_GL_RGBA;
+ if (getSizedInternalFormat) {
+ *internalFormat = GR_GL_RGBA8;
+ } else {
+ *internalFormat = GR_GL_RGBA;
+ }
+ *externalType = GR_GL_UNSIGNED_BYTE;
+ break;
+ case kBGRA_8888_PM_GrPixelConfig:
+ case kBGRA_8888_UPM_GrPixelConfig:
+ if (!fGLCaps.fBGRAFormatSupport) {
+ return false;
+ }
+ if (fGLCaps.fBGRAIsInternalFormat) {
+ if (getSizedInternalFormat) {
+ *internalFormat = GR_GL_BGRA8;
+ } else {
+ *internalFormat = GR_GL_BGRA;
+ }
+ } else {
+ if (getSizedInternalFormat) {
+ *internalFormat = GR_GL_RGBA8;
+ } else {
+ *internalFormat = GR_GL_RGBA;
+ }
+ }
+ *externalFormat = GR_GL_BGRA;
+ *externalType = GR_GL_UNSIGNED_BYTE;
+ break;
+ case kRGB_565_GrPixelConfig:
+ *internalFormat = GR_GL_RGB;
+ *externalFormat = GR_GL_RGB;
+ if (getSizedInternalFormat) {
+ if (this->glBinding() == kDesktop_GrGLBinding) {
+ return false;
+ } else {
+ *internalFormat = GR_GL_RGB565;
+ }
+ } else {
+ *internalFormat = GR_GL_RGB;
+ }
+ *externalType = GR_GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case kRGBA_4444_GrPixelConfig:
+ *internalFormat = GR_GL_RGBA;
+ *externalFormat = GR_GL_RGBA;
+ if (getSizedInternalFormat) {
+ *internalFormat = GR_GL_RGBA4;
+ } else {
+ *internalFormat = GR_GL_RGBA;
+ }
+ *externalType = GR_GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+ case kIndex_8_GrPixelConfig:
+ if (this->getCaps().f8BitPaletteSupport) {
+ *internalFormat = GR_GL_PALETTE8_RGBA8;
+ // glCompressedTexImage doesn't take external params
+ *externalFormat = GR_GL_PALETTE8_RGBA8;
+ // no sized/unsized internal format distinction here
+ *internalFormat = GR_GL_PALETTE8_RGBA8;
+ // unused with CompressedTexImage
+ *externalType = GR_GL_UNSIGNED_BYTE;
+ } else {
+ return false;
+ }
+ break;
+ case kAlpha_8_GrPixelConfig:
+ *internalFormat = GR_GL_ALPHA;
+ *externalFormat = GR_GL_ALPHA;
+ if (getSizedInternalFormat) {
+ *internalFormat = GR_GL_ALPHA8;
+ } else {
+ *internalFormat = GR_GL_ALPHA;
+ }
+ *externalType = GR_GL_UNSIGNED_BYTE;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void GrGpuGL::setTextureUnit(int unit) {
+ GrAssert(unit >= 0 && unit < GrDrawState::kNumStages);
+ if (fActiveTextureUnitIdx != unit) {
+ GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + unit));
+ fActiveTextureUnitIdx = unit;
+ }
+}
+
+void GrGpuGL::setSpareTextureUnit() {
+ if (fActiveTextureUnitIdx != (GR_GL_TEXTURE0 + SPARE_TEX_UNIT)) {
+ GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + SPARE_TEX_UNIT));
+ fActiveTextureUnitIdx = SPARE_TEX_UNIT;
+ }
+}
+
+void GrGpuGL::resetDirtyFlags() {
+ Gr_bzero(&fDirtyFlags, sizeof(fDirtyFlags));
+}
+
+void GrGpuGL::setBuffers(bool indexed,
+ int* extraVertexOffset,
+ int* extraIndexOffset) {
+
+ GrAssert(NULL != extraVertexOffset);
+
+ const GeometryPoolState& geoPoolState = this->getGeomPoolState();
+
+ GrGLVertexBuffer* vbuf;
+ switch (this->getGeomSrc().fVertexSrc) {
+ case kBuffer_GeometrySrcType:
+ *extraVertexOffset = 0;
+ vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer;
+ break;
+ case kArray_GeometrySrcType:
+ case kReserved_GeometrySrcType:
+ this->finalizeReservedVertices();
+ *extraVertexOffset = geoPoolState.fPoolStartVertex;
+ vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer;
+ break;
+ default:
+ vbuf = NULL; // suppress warning
+ GrCrash("Unknown geometry src type!");
+ }
+
+ GrAssert(NULL != vbuf);
+ GrAssert(!vbuf->isLocked());
+ if (fHWGeometryState.fVertexBuffer != vbuf) {
+ GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, vbuf->bufferID()));
+ fHWGeometryState.fArrayPtrsDirty = true;
+ fHWGeometryState.fVertexBuffer = vbuf;
+ }
+
+ if (indexed) {
+ GrAssert(NULL != extraIndexOffset);
+
+ GrGLIndexBuffer* ibuf;
+ switch (this->getGeomSrc().fIndexSrc) {
+ case kBuffer_GeometrySrcType:
+ *extraIndexOffset = 0;
+ ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer;
+ break;
+ case kArray_GeometrySrcType:
+ case kReserved_GeometrySrcType:
+ this->finalizeReservedIndices();
+ *extraIndexOffset = geoPoolState.fPoolStartIndex;
+ ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer;
+ break;
+ default:
+ ibuf = NULL; // suppress warning
+ GrCrash("Unknown geometry src type!");
+ }
+
+ GrAssert(NULL != ibuf);
+ GrAssert(!ibuf->isLocked());
+ if (fHWGeometryState.fIndexBuffer != ibuf) {
+ GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, ibuf->bufferID()));
+ fHWGeometryState.fIndexBuffer = ibuf;
+ }
+ }
+}
+
+int GrGpuGL::getMaxEdges() const {
+ // FIXME: This is a pessimistic estimate based on how many other things
+ // want to add uniforms. This should be centralized somewhere.
+ return GR_CT_MIN(fGLCaps.fMaxFragmentUniformVectors - 8,
+ GrDrawState::kMaxEdges);
+}
+
+void GrGpuGL::GLCaps::print() const {
+ for (int i = 0; i < fStencilFormats.count(); ++i) {
+ GrPrintf("Stencil Format %d, stencil bits: %02d, total bits: %02d\n",
+ i,
+ fStencilFormats[i].fFormat.fStencilBits,
+ fStencilFormats[i].fFormat.fTotalBits);
+ }
+
+ GR_STATIC_ASSERT(0 == kNone_MSFBO);
+ GR_STATIC_ASSERT(1 == kDesktopARB_MSFBO);
+ GR_STATIC_ASSERT(2 == kDesktopEXT_MSFBO);
+ GR_STATIC_ASSERT(3 == kAppleES_MSFBO);
+ static const char* gMSFBOExtStr[] = {
+ "None",
+ "ARB",
+ "EXT",
+ "Apple",
+ };
+ GrPrintf("MSAA Type: %s\n", gMSFBOExtStr[fMSFBOType]);
+ GrPrintf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors);
+ GrPrintf("Support RGBA8 Render Buffer: %s\n",
+ (fRGBA8RenderbufferSupport ? "YES": "NO"));
+ GrPrintf("BGRA is an internal format: %s\n",
+ (fBGRAIsInternalFormat ? "YES": "NO"));
+ GrPrintf("Support texture swizzle: %s\n",
+ (fTextureSwizzleSupport ? "YES": "NO"));
+ GrPrintf("Unpack Row length support: %s\n",
+ (fUnpackRowLengthSupport ? "YES": "NO"));
+ GrPrintf("Unpack Flip Y support: %s\n",
+ (fUnpackFlipYSupport ? "YES": "NO"));
+ GrPrintf("Pack Row length support: %s\n",
+ (fPackRowLengthSupport ? "YES": "NO"));
+ GrPrintf("Pack Flip Y support: %s\n",
+ (fPackFlipYSupport ? "YES": "NO"));
+}
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
new file mode 100644
index 0000000000..2e7e90be8b
--- /dev/null
+++ b/src/gpu/gl/GrGpuGL.h
@@ -0,0 +1,365 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGpuGL_DEFINED
+#define GrGpuGL_DEFINED
+
+#include "../GrDrawState.h"
+#include "../GrGpu.h"
+#include "GrGLContextInfo.h"
+#include "GrGLIndexBuffer.h"
+#include "GrGLIRect.h"
+#include "GrGLStencilBuffer.h"
+#include "GrGLTexture.h"
+#include "GrGLVertexBuffer.h"
+
+class GrGpuGL : public GrGpu {
+public:
+ virtual ~GrGpuGL();
+
+ const GrGLInterface* glInterface() const {
+ return fGLContextInfo.interface();
+ }
+ GrGLBinding glBinding() const { return fGLContextInfo.binding(); }
+ GrGLVersion glVersion() const { return fGLContextInfo.version(); }
+ GrGLSLGeneration glslGeneration() const {
+ return fGLContextInfo.glslGeneration();
+ }
+
+ // GrGpu overrides
+ virtual GrPixelConfig preferredReadPixelsConfig(GrPixelConfig config)
+ const SK_OVERRIDE;
+ virtual GrPixelConfig preferredWritePixelsConfig(GrPixelConfig config)
+ const SK_OVERRIDE;
+ virtual bool readPixelsWillPayForYFlip(
+ GrRenderTarget* renderTarget,
+ int left, int top,
+ int width, int height,
+ GrPixelConfig config,
+ size_t rowBytes) const SK_OVERRIDE;
+ virtual bool fullReadPixelsIsFasterThanPartial() const SK_OVERRIDE;
+
+protected:
+ GrGpuGL(const GrGLContextInfo& ctxInfo);
+
+ struct GLCaps {
+ GLCaps()
+ // make defaults be the most restrictive
+ : fStencilFormats(8) // prealloc space for stencil formats
+ , fMSFBOType(kNone_MSFBO)
+ , fMaxFragmentUniformVectors(0)
+ , fRGBA8RenderbufferSupport(false)
+ , fBGRAFormatSupport(false)
+ , fBGRAIsInternalFormat(false)
+ , fTextureSwizzleSupport(false)
+ , fUnpackRowLengthSupport(false)
+ , fUnpackFlipYSupport(false)
+ , fPackRowLengthSupport(false)
+ , fPackFlipYSupport(false)
+ , fTextureUsageSupport(false)
+ , fTexStorageSupport(false) {
+ memset(&fVerifiedColorAttachmentConfigs, 0,
+ sizeof(fVerifiedColorAttachmentConfigs));
+ }
+
+ // Call to note that a color config has been verified as a valid
+ // color attachment. This may save future calls to
+ // CheckFramebufferStatus
+ void markConfigAsValidColorAttachment(GrPixelConfig config);
+
+ // Call to check whether a config has been verified as a valid color
+ // attachment.
+ bool isConfigVerifiedColorAttachment(GrPixelConfig config) const;
+
+ // Call to note that a color config / stencil format pair passed
+ // FBO status check. We may skip calling CheckFramebufferStatus for
+ // this combination in the future.
+ void markColorConfigAndStencilFormatAsVerified(
+ GrPixelConfig config,
+ const GrGLStencilBuffer::Format& format);
+
+ // Call to check whether color config / stencil format pair has already
+ // passed FBO status check.
+ bool isColorConfigAndStencilFormatVerified(
+ GrPixelConfig config,
+ const GrGLStencilBuffer::Format& format) const;
+
+ void print() const;
+
+ struct StencilFormat {
+ GrGLStencilBuffer::Format fFormat;
+ uint32_t fVerifiedColorConfigs[(kGrPixelConfigCount + 31) / 32];
+ };
+
+ SkTArray<StencilFormat, true> fStencilFormats;
+
+
+ enum {
+ /**
+ * no support for MSAA FBOs
+ */
+ kNone_MSFBO = 0,
+ /**
+ * GL3.0-style MSAA FBO (GL_ARB_framebuffer_object)
+ */
+ kDesktopARB_MSFBO,
+ /**
+ * earlier GL_EXT_framebuffer* extensions
+ */
+ kDesktopEXT_MSFBO,
+ /**
+ * GL_APPLE_framebuffer_multisample ES extension
+ */
+ kAppleES_MSFBO,
+ } fMSFBOType;
+
+ // The maximum number of fragment uniform vectors (GLES has min. 16).
+ int fMaxFragmentUniformVectors;
+
+ // ES requires an extension to support RGBA8 in RenderBufferStorage
+ bool fRGBA8RenderbufferSupport;
+
+ // Is GL_BGRA supported
+ bool fBGRAFormatSupport;
+
+ // Depending on the ES extensions present the BGRA external format may
+ // correspond either a BGRA or RGBA internalFormat. On desktop GL it is
+ // RGBA
+ bool fBGRAIsInternalFormat;
+
+ // GL_ARB_texture_swizzle support
+ bool fTextureSwizzleSupport;
+
+ // Is there support for GL_UNPACK_ROW_LENGTH
+ bool fUnpackRowLengthSupport;
+
+ // Is there support for GL_UNPACK_FLIP_Y
+ bool fUnpackFlipYSupport;
+
+ // Is there support for GL_PACK_ROW_LENGTH
+ bool fPackRowLengthSupport;
+
+ // Is there support for GL_PACK_REVERSE_ROW_ORDER
+ bool fPackFlipYSupport;
+
+ // Is there support for texture parameter GL_TEXTURE_USAGE
+ bool fTextureUsageSupport;
+
+ // Is there support for glTexStorage
+ bool fTexStorageSupport;
+
+ private:
+ uint32_t fVerifiedColorAttachmentConfigs[(kGrPixelConfigCount + 31) / 32];
+ } fGLCaps;
+
+ struct {
+ size_t fVertexOffset;
+ GrVertexLayout fVertexLayout;
+ const GrVertexBuffer* fVertexBuffer;
+ const GrIndexBuffer* fIndexBuffer;
+ bool fArrayPtrsDirty;
+ } fHWGeometryState;
+
+ struct AAState {
+ bool fMSAAEnabled;
+ bool fSmoothLineEnabled;
+ } fHWAAState;
+
+ GrDrawState fHWDrawState;
+ bool fHWStencilClip;
+
+ // As flush of GL state proceeds it updates fHDrawState
+ // to reflect the new state. Later parts of the state flush
+ // may perform cascaded changes but cannot refer to fHWDrawState.
+ // These code paths can refer to the dirty flags. Subclass should
+ // call resetDirtyFlags after its flush is complete
+ struct {
+ bool fRenderTargetChanged : 1;
+ int fTextureChangedMask;
+ } fDirtyFlags;
+ GR_STATIC_ASSERT(8 * sizeof(int) >= GrDrawState::kNumStages);
+
+ // clears the dirty flags
+ void resetDirtyFlags();
+
+ // last scissor / viewport scissor state seen by the GL.
+ struct {
+ bool fScissorEnabled;
+ GrGLIRect fScissorRect;
+ GrGLIRect fViewportRect;
+ } fHWBounds;
+
+ const GLCaps& glCaps() const { return fGLCaps; }
+
+ // GrGpu overrides
+ virtual void onResetContext() SK_OVERRIDE;
+
+ virtual GrTexture* onCreateTexture(const GrTextureDesc& desc,
+ const void* srcData,
+ size_t rowBytes);
+ virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size,
+ bool dynamic);
+ virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
+ bool dynamic);
+ virtual GrTexture* onCreatePlatformTexture(const GrPlatformTextureDesc& desc) SK_OVERRIDE;
+ virtual GrRenderTarget* onCreatePlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) SK_OVERRIDE;
+ virtual bool createStencilBufferForRenderTarget(GrRenderTarget* rt,
+ int width, int height);
+ virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
+ GrRenderTarget* rt);
+
+ virtual void onClear(const GrIRect* rect, GrColor color);
+
+ virtual void onForceRenderTargetFlush();
+
+ virtual bool onReadPixels(GrRenderTarget* target,
+ int left, int top,
+ int width, int height,
+ GrPixelConfig,
+ void* buffer,
+ size_t rowBytes,
+ bool invertY) SK_OVERRIDE;
+
+ virtual void onWriteTexturePixels(GrTexture* texture,
+ int left, int top, int width, int height,
+ GrPixelConfig config, const void* buffer,
+ size_t rowBytes) SK_OVERRIDE;
+
+ virtual void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE;
+
+
+ virtual void onGpuDrawIndexed(GrPrimitiveType type,
+ uint32_t startVertex,
+ uint32_t startIndex,
+ uint32_t vertexCount,
+ uint32_t indexCount);
+ virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
+ uint32_t vertexCount,
+ uint32_t numVertices);
+ virtual void flushScissor(const GrIRect* rect);
+ virtual void clearStencil();
+ virtual void clearStencilClip(const GrIRect& rect, bool insideClip);
+ virtual int getMaxEdges() const;
+
+ // binds texture unit in GL
+ void setTextureUnit(int unitIdx);
+
+ // binds appropriate vertex and index buffers, also returns any extra
+ // extra verts or indices to offset by.
+ void setBuffers(bool indexed,
+ int* extraVertexOffset,
+ int* extraIndexOffset);
+
+ // flushes state that is common to fixed and programmable GL
+ // dither
+ // line smoothing
+ // texture binding
+ // sampler state (filtering, tiling)
+ // FBO binding
+ // line width
+ bool flushGLStateCommon(GrPrimitiveType type);
+
+ // Subclasses should call this to flush the blend state.
+ // The params should be the final coeffecients to apply
+ // (after any blending optimizations or dual source blending considerations
+ // have been accounted for).
+ void flushBlend(GrPrimitiveType type,
+ GrBlendCoeff srcCoeff,
+ GrBlendCoeff dstCoeff);
+
+ bool hasExtension(const char* ext) const {
+ return fGLContextInfo.hasExtension(ext);
+}
+
+ // adjusts texture matrix to account for orientation
+ static void AdjustTextureMatrix(const GrGLTexture* texture,
+ GrSamplerState::SampleMode mode,
+ GrMatrix* matrix);
+
+ // subclass may try to take advantage of identity tex matrices.
+ // This helper determines if matrix will be identity after all
+ // adjustments are applied.
+ static bool TextureMatrixIsIdentity(const GrGLTexture* texture,
+ const GrSamplerState& sampler);
+
+ static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
+
+private:
+ // Inits GrDrawTarget::Caps and GLCaps, sublcass may enable
+ // additional caps.
+ void initCaps();
+
+ void initFSAASupport();
+
+ // determines valid stencil formats
+ void initStencilFormats();
+
+ // notify callbacks to update state tracking when related
+ // objects are bound to GL or deleted outside of the class
+ void notifyVertexBufferBind(const GrGLVertexBuffer* buffer);
+ void notifyVertexBufferDelete(const GrGLVertexBuffer* buffer);
+ void notifyIndexBufferBind(const GrGLIndexBuffer* buffer);
+ void notifyIndexBufferDelete(const GrGLIndexBuffer* buffer);
+ void notifyTextureDelete(GrGLTexture* texture);
+ void notifyRenderTargetDelete(GrRenderTarget* renderTarget);
+
+ void setSpareTextureUnit();
+
+ // bound is region that may be modified and therefore has to be resolved.
+ // NULL means whole target. Can be an empty rect.
+ void flushRenderTarget(const GrIRect* bound);
+ void flushStencil();
+ void flushAAState(GrPrimitiveType type);
+
+ bool configToGLFormats(GrPixelConfig config,
+ bool getSizedInternal,
+ GrGLenum* internalFormat,
+ GrGLenum* externalFormat,
+ GrGLenum* externalType);
+ // helper for onCreateTexture and writeTexturePixels
+ bool uploadTexData(const GrGLTexture::Desc& desc,
+ bool isNewTexture,
+ int left, int top, int width, int height,
+ GrPixelConfig dataConfig,
+ const void* data,
+ size_t rowBytes);
+
+ bool createRenderTargetObjects(int width, int height,
+ GrGLuint texID,
+ GrGLRenderTarget::Desc* desc);
+
+ friend class GrGLVertexBuffer;
+ friend class GrGLIndexBuffer;
+ friend class GrGLTexture;
+ friend class GrGLRenderTarget;
+
+ GrGLContextInfo fGLContextInfo;
+
+ // we want to clear stencil buffers when they are created. We want to clear
+ // the entire buffer even if it is larger than the color attachment. We
+ // attach it to this fbo with no color attachment to do the initial clear.
+ GrGLuint fStencilClearFBO;
+
+ bool fHWBlendDisabled;
+
+ int fActiveTextureUnitIdx;
+
+ // we record what stencil format worked last time to hopefully exit early
+ // from our loop that tries stencil formats and calls check fb status.
+ int fLastSuccessfulStencilFmtIdx;
+
+
+ bool fPrintedCaps;
+
+ typedef GrGpu INHERITED;
+};
+
+#endif
+
diff --git a/src/gpu/gl/GrGpuGLShaders.cpp b/src/gpu/gl/GrGpuGLShaders.cpp
new file mode 100644
index 0000000000..968b893349
--- /dev/null
+++ b/src/gpu/gl/GrGpuGLShaders.cpp
@@ -0,0 +1,1188 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "../GrBinHashKey.h"
+#include "GrGLProgram.h"
+#include "GrGLSL.h"
+#include "GrGpuGLShaders.h"
+#include "../GrGpuVertex.h"
+#include "GrNoncopyable.h"
+#include "../GrStringBuilder.h"
+#include "../GrRandom.h"
+
+#define SKIP_CACHE_CHECK true
+#define GR_UINT32_MAX static_cast<uint32_t>(-1)
+
+#include "../GrTHashCache.h"
+
+class GrGpuGLShaders::ProgramCache : public ::GrNoncopyable {
+private:
+ class Entry;
+
+ typedef GrBinHashKey<Entry, GrGLProgram::kProgramKeySize> ProgramHashKey;
+
+ class Entry : public ::GrNoncopyable {
+ public:
+ Entry() {}
+ void copyAndTakeOwnership(Entry& entry) {
+ fProgramData.copyAndTakeOwnership(entry.fProgramData);
+ fKey = entry.fKey; // ownership transfer
+ fLRUStamp = entry.fLRUStamp;
+ }
+
+ public:
+ int compare(const ProgramHashKey& key) const { return fKey.compare(key); }
+
+ public:
+ GrGLProgram::CachedData fProgramData;
+ ProgramHashKey fKey;
+ unsigned int fLRUStamp;
+ };
+
+ GrTHashTable<Entry, ProgramHashKey, 8> fHashCache;
+
+ // We may have kMaxEntries+1 shaders in the GL context because
+ // we create a new shader before evicting from the cache.
+ enum {
+ kMaxEntries = 32
+ };
+ Entry fEntries[kMaxEntries];
+ int fCount;
+ unsigned int fCurrLRUStamp;
+ const GrGLInterface* fGL;
+ GrGLSLGeneration fGLSLGeneration;
+
+public:
+ ProgramCache(const GrGLInterface* gl,
+ GrGLSLGeneration glslGeneration)
+ : fCount(0)
+ , fCurrLRUStamp(0)
+ , fGL(gl)
+ , fGLSLGeneration(glslGeneration) {
+ }
+
+ ~ProgramCache() {
+ for (int i = 0; i < fCount; ++i) {
+ GrGpuGLShaders::DeleteProgram(fGL, &fEntries[i].fProgramData);
+ }
+ }
+
+ void abandon() {
+ fCount = 0;
+ }
+
+ void invalidateViewMatrices() {
+ for (int i = 0; i < fCount; ++i) {
+ // set to illegal matrix
+ fEntries[i].fProgramData.fViewMatrix = GrMatrix::InvalidMatrix();
+ }
+ }
+
+ GrGLProgram::CachedData* getProgramData(const GrGLProgram& desc) {
+ Entry newEntry;
+ newEntry.fKey.setKeyData(desc.keyData());
+
+ Entry* entry = fHashCache.find(newEntry.fKey);
+ if (NULL == entry) {
+ if (!desc.genProgram(fGL, fGLSLGeneration,
+ &newEntry.fProgramData)) {
+ return NULL;
+ }
+ if (fCount < kMaxEntries) {
+ entry = fEntries + fCount;
+ ++fCount;
+ } else {
+ GrAssert(kMaxEntries == fCount);
+ entry = fEntries;
+ for (int i = 1; i < kMaxEntries; ++i) {
+ if (fEntries[i].fLRUStamp < entry->fLRUStamp) {
+ entry = fEntries + i;
+ }
+ }
+ fHashCache.remove(entry->fKey, entry);
+ GrGpuGLShaders::DeleteProgram(fGL, &entry->fProgramData);
+ }
+ entry->copyAndTakeOwnership(newEntry);
+ fHashCache.insert(entry->fKey, entry);
+ }
+
+ entry->fLRUStamp = fCurrLRUStamp;
+ if (GR_UINT32_MAX == fCurrLRUStamp) {
+ // wrap around! just trash our LRU, one time hit.
+ for (int i = 0; i < fCount; ++i) {
+ fEntries[i].fLRUStamp = 0;
+ }
+ }
+ ++fCurrLRUStamp;
+ return &entry->fProgramData;
+ }
+};
+
+void GrGpuGLShaders::abandonResources(){
+ INHERITED::abandonResources();
+ fProgramCache->abandon();
+}
+
+void GrGpuGLShaders::DeleteProgram(const GrGLInterface* gl,
+ CachedData* programData) {
+ GR_GL_CALL(gl, DeleteShader(programData->fVShaderID));
+ if (programData->fGShaderID) {
+ GR_GL_CALL(gl, DeleteShader(programData->fGShaderID));
+ }
+ GR_GL_CALL(gl, DeleteShader(programData->fFShaderID));
+ GR_GL_CALL(gl, DeleteProgram(programData->fProgramID));
+ GR_DEBUGCODE(memset(programData, 0, sizeof(*programData));)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
+
+namespace {
+
+// GrRandoms nextU() values have patterns in the low bits
+// So using nextU() % array_count might never take some values.
+int random_int(GrRandom* r, int count) {
+ return (int)(r->nextF() * count);
+}
+
+// min is inclusive, max is exclusive
+int random_int(GrRandom* r, int min, int max) {
+ return (int)(r->nextF() * (max-min)) + min;
+}
+
+bool random_bool(GrRandom* r) {
+ return r->nextF() > .5f;
+}
+
+}
+
+bool GrGpuGLShaders::programUnitTest() {
+
+ GrGLSLGeneration glslGeneration =
+ GrGetGLSLGeneration(this->glBinding(), this->glInterface());
+ static const int STAGE_OPTS[] = {
+ 0,
+ StageDesc::kNoPerspective_OptFlagBit,
+ StageDesc::kIdentity_CoordMapping
+ };
+ static const int IN_CONFIG_FLAGS[] = {
+ StageDesc::kNone_InConfigFlag,
+ StageDesc::kSwapRAndB_InConfigFlag,
+ StageDesc::kSwapRAndB_InConfigFlag | StageDesc::kMulRGBByAlpha_InConfigFlag,
+ StageDesc::kMulRGBByAlpha_InConfigFlag,
+ StageDesc::kSmearAlpha_InConfigFlag,
+ };
+ GrGLProgram program;
+ ProgramDesc& pdesc = program.fProgramDesc;
+
+ static const int NUM_TESTS = 512;
+
+ GrRandom random;
+ for (int t = 0; t < NUM_TESTS; ++t) {
+
+#if 0
+ GrPrintf("\nTest Program %d\n-------------\n", t);
+ static const int stop = -1;
+ if (t == stop) {
+ int breakpointhere = 9;
+ }
+#endif
+
+ pdesc.fVertexLayout = 0;
+ pdesc.fEmitsPointSize = random.nextF() > .5f;
+ pdesc.fColorInput = random_int(&random, ProgramDesc::kColorInputCnt);
+ pdesc.fCoverageInput = random_int(&random, ProgramDesc::kColorInputCnt);
+
+ pdesc.fColorFilterXfermode = random_int(&random, SkXfermode::kCoeffModesCnt);
+
+ pdesc.fFirstCoverageStage = random_int(&random, GrDrawState::kNumStages);
+
+ pdesc.fVertexLayout |= random_bool(&random) ?
+ GrDrawTarget::kCoverage_VertexLayoutBit :
+ 0;
+
+#if GR_GL_EXPERIMENTAL_GS
+ pdesc.fExperimentalGS = this->getCaps().fGeometryShaderSupport &&
+ random_bool(&random);
+#endif
+ pdesc.fOutputPM = random_int(&random, ProgramDesc::kOutputPMCnt);
+
+ bool edgeAA = random_bool(&random);
+ if (edgeAA) {
+ bool vertexEdgeAA = random_bool(&random);
+ if (vertexEdgeAA) {
+ pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
+ if (this->getCaps().fShaderDerivativeSupport) {
+ pdesc.fVertexEdgeType = (GrDrawState::VertexEdgeType) random_int(&random, GrDrawState::kVertexEdgeTypeCnt);
+ } else {
+ pdesc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
+ }
+ pdesc.fEdgeAANumEdges = 0;
+ } else {
+ pdesc.fEdgeAANumEdges = random_int(&random, 1, this->getMaxEdges());
+ pdesc.fEdgeAAConcave = random_bool(&random);
+ }
+ } else {
+ pdesc.fEdgeAANumEdges = 0;
+ }
+
+ pdesc.fColorMatrixEnabled = random_bool(&random);
+
+ if (this->getCaps().fDualSourceBlendingSupport) {
+ pdesc.fDualSrcOutput = random_int(&random, ProgramDesc::kDualSrcOutputCnt);
+ } else {
+ pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
+ }
+
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ // enable the stage?
+ if (random_bool(&random)) {
+ // use separate tex coords?
+ if (random_bool(&random)) {
+ int t = random_int(&random, GrDrawState::kMaxTexCoords);
+ pdesc.fVertexLayout |= StageTexCoordVertexLayoutBit(s, t);
+ } else {
+ pdesc.fVertexLayout |= StagePosAsTexCoordVertexLayoutBit(s);
+ }
+ }
+ // use text-formatted verts?
+ if (random_bool(&random)) {
+ pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
+ }
+ StageDesc& stage = pdesc.fStages[s];
+ stage.fOptFlags = STAGE_OPTS[random_int(&random, GR_ARRAY_COUNT(STAGE_OPTS))];
+ stage.fInConfigFlags = IN_CONFIG_FLAGS[random_int(&random, GR_ARRAY_COUNT(IN_CONFIG_FLAGS))];
+ stage.fCoordMapping = random_int(&random, StageDesc::kCoordMappingCnt);
+ stage.fFetchMode = random_int(&random, StageDesc::kFetchModeCnt);
+ // convolution shaders don't work with persp tex matrix
+ if (stage.fFetchMode == StageDesc::kConvolution_FetchMode) {
+ stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+ }
+ stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
+ switch (stage.fFetchMode) {
+ case StageDesc::kSingle_FetchMode:
+ stage.fKernelWidth = 0;
+ break;
+ case StageDesc::kConvolution_FetchMode:
+ stage.fKernelWidth = random_int(&random, 2, 8);
+ stage.fInConfigFlags &= ~StageDesc::kMulRGBByAlpha_InConfigFlag;
+ break;
+ case StageDesc::k2x2_FetchMode:
+ stage.fKernelWidth = 0;
+ stage.fInConfigFlags &= ~StageDesc::kMulRGBByAlpha_InConfigFlag;
+ break;
+ }
+ }
+ CachedData cachedData;
+ if (!program.genProgram(this->glInterface(),
+ glslGeneration,
+ &cachedData)) {
+ return false;
+ }
+ DeleteProgram(this->glInterface(), &cachedData);
+ }
+ return true;
+}
+
+GrGpuGLShaders::GrGpuGLShaders(const GrGLContextInfo& ctxInfo)
+ : GrGpuGL(ctxInfo) {
+
+ // Enable supported shader-related caps
+ if (kDesktop_GrGLBinding == this->glBinding()) {
+ fCaps.fDualSourceBlendingSupport =
+ this->glVersion() >= GR_GL_VER(3,3) ||
+ this->hasExtension("GL_ARB_blend_func_extended");
+ fCaps.fShaderDerivativeSupport = true;
+ // we don't support GL_ARB_geometry_shader4, just GL 3.2+ GS
+ fCaps.fGeometryShaderSupport =
+ this->glVersion() >= GR_GL_VER(3,2) &&
+ this->glslGeneration() >= k150_GrGLSLGeneration;
+ } else {
+ fCaps.fShaderDerivativeSupport =
+ this->hasExtension("GL_OES_standard_derivatives");
+ }
+
+ GR_GL_GetIntegerv(this->glInterface(),
+ GR_GL_MAX_VERTEX_ATTRIBS,
+ &fMaxVertexAttribs);
+
+ fProgramData = NULL;
+ fProgramCache = new ProgramCache(this->glInterface(),
+ this->glslGeneration());
+
+#if 0
+ this->programUnitTest();
+#endif
+}
+
+GrGpuGLShaders::~GrGpuGLShaders() {
+ delete fProgramCache;
+}
+
+const GrMatrix& GrGpuGLShaders::getHWViewMatrix() {
+ GrAssert(fProgramData);
+
+ if (GrGLProgram::kSetAsAttribute ==
+ fProgramData->fUniLocations.fViewMatrixUni) {
+ return fHWDrawState.getViewMatrix();
+ } else {
+ return fProgramData->fViewMatrix;
+ }
+}
+
+void GrGpuGLShaders::recordHWViewMatrix(const GrMatrix& matrix) {
+ GrAssert(fProgramData);
+ if (GrGLProgram::kSetAsAttribute ==
+ fProgramData->fUniLocations.fViewMatrixUni) {
+ fHWDrawState.setViewMatrix(matrix);
+ } else {
+ fProgramData->fViewMatrix = matrix;
+ }
+}
+
+const GrMatrix& GrGpuGLShaders::getHWSamplerMatrix(int stage) {
+ GrAssert(fProgramData);
+
+ if (GrGLProgram::kSetAsAttribute ==
+ fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni) {
+ return fHWDrawState.getSampler(stage).getMatrix();
+ } else {
+ return fProgramData->fTextureMatrices[stage];
+ }
+}
+
+void GrGpuGLShaders::recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
+ GrAssert(fProgramData);
+ if (GrGLProgram::kSetAsAttribute ==
+ fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni) {
+ *fHWDrawState.sampler(stage)->matrix() = matrix;
+ } else {
+ fProgramData->fTextureMatrices[stage] = matrix;
+ }
+}
+
+void GrGpuGLShaders::onResetContext() {
+ INHERITED::onResetContext();
+
+ fHWGeometryState.fVertexOffset = ~0;
+
+ // Third party GL code may have left vertex attributes enabled. Some GL
+ // implementations (osmesa) may read vetex attributes that are not required
+ // by the current shader. Therefore, we have to ensure that only the
+ // attributes we require for the current draw are enabled or we may cause an
+ // invalid read.
+
+ // Disable all vertex layout bits so that next flush will assume all
+ // optional vertex attributes are disabled.
+ fHWGeometryState.fVertexLayout = 0;
+
+ // We always use the this attribute and assume it is always enabled.
+ int posAttrIdx = GrGLProgram::PositionAttributeIdx();
+ GL_CALL(EnableVertexAttribArray(posAttrIdx));
+ // Disable all other vertex attributes.
+ for (int va = 0; va < fMaxVertexAttribs; ++va) {
+ if (va != posAttrIdx) {
+ GL_CALL(DisableVertexAttribArray(va));
+ }
+ }
+
+ fHWProgramID = 0;
+}
+
+void GrGpuGLShaders::flushViewMatrix() {
+ const GrMatrix& vm = this->getDrawState().getViewMatrix();
+ if (GrGpuGLShaders::getHWViewMatrix() != vm) {
+
+ const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
+ GrAssert(NULL != rt);
+ GrMatrix m;
+ m.setAll(
+ GrIntToScalar(2) / rt->width(), 0, -GR_Scalar1,
+ 0,-GrIntToScalar(2) / rt->height(), GR_Scalar1,
+ 0, 0, GrMatrix::I()[8]);
+ m.setConcat(m, vm);
+
+ // ES doesn't allow you to pass true to the transpose param,
+ // so do our own transpose
+ GrGLfloat mt[] = {
+ GrScalarToFloat(m[GrMatrix::kMScaleX]),
+ GrScalarToFloat(m[GrMatrix::kMSkewY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp0]),
+ GrScalarToFloat(m[GrMatrix::kMSkewX]),
+ GrScalarToFloat(m[GrMatrix::kMScaleY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp1]),
+ GrScalarToFloat(m[GrMatrix::kMTransX]),
+ GrScalarToFloat(m[GrMatrix::kMTransY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp2])
+ };
+
+ if (GrGLProgram::kSetAsAttribute ==
+ fProgramData->fUniLocations.fViewMatrixUni) {
+ int baseIdx = GrGLProgram::ViewMatrixAttributeIdx();
+ GL_CALL(VertexAttrib4fv(baseIdx + 0, mt+0));
+ GL_CALL(VertexAttrib4fv(baseIdx + 1, mt+3));
+ GL_CALL(VertexAttrib4fv(baseIdx + 2, mt+6));
+ } else {
+ GrAssert(GrGLProgram::kUnusedUniform !=
+ fProgramData->fUniLocations.fViewMatrixUni);
+ GL_CALL(UniformMatrix3fv(fProgramData->fUniLocations.fViewMatrixUni,
+ 1, false, mt));
+ }
+ this->recordHWViewMatrix(vm);
+ }
+}
+
+void GrGpuGLShaders::flushTextureDomain(int s) {
+ const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTexDomUni;
+ const GrDrawState& drawState = this->getDrawState();
+ if (GrGLProgram::kUnusedUniform != uni) {
+ const GrRect &texDom = drawState.getSampler(s).getTextureDomain();
+
+ if (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
+ fProgramData->fTextureDomain[s] != texDom) {
+
+ fProgramData->fTextureDomain[s] = texDom;
+
+ float values[4] = {
+ GrScalarToFloat(texDom.left()),
+ GrScalarToFloat(texDom.top()),
+ GrScalarToFloat(texDom.right()),
+ GrScalarToFloat(texDom.bottom())
+ };
+
+ const GrGLTexture* texture =
+ static_cast<const GrGLTexture*>(drawState.getTexture(s));
+ GrGLTexture::Orientation orientation = texture->orientation();
+
+ // vertical flip if necessary
+ if (GrGLTexture::kBottomUp_Orientation == orientation) {
+ values[1] = 1.0f - values[1];
+ values[3] = 1.0f - values[3];
+ // The top and bottom were just flipped, so correct the ordering
+ // of elements so that values = (l, t, r, b).
+ SkTSwap(values[1], values[3]);
+ }
+
+ GL_CALL(Uniform4fv(uni, 1, values));
+ }
+ }
+}
+
+void GrGpuGLShaders::flushTextureMatrix(int s) {
+ const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
+ const GrDrawState& drawState = this->getDrawState();
+ const GrGLTexture* texture =
+ static_cast<const GrGLTexture*>(drawState.getTexture(s));
+ if (NULL != texture) {
+ if (GrGLProgram::kUnusedUniform != uni &&
+ (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
+ this->getHWSamplerMatrix(s) != drawState.getSampler(s).getMatrix())) {
+
+ GrMatrix m = drawState.getSampler(s).getMatrix();
+ GrSamplerState::SampleMode mode =
+ drawState.getSampler(s).getSampleMode();
+ AdjustTextureMatrix(texture, mode, &m);
+
+ // ES doesn't allow you to pass true to the transpose param,
+ // so do our own transpose
+ GrGLfloat mt[] = {
+ GrScalarToFloat(m[GrMatrix::kMScaleX]),
+ GrScalarToFloat(m[GrMatrix::kMSkewY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp0]),
+ GrScalarToFloat(m[GrMatrix::kMSkewX]),
+ GrScalarToFloat(m[GrMatrix::kMScaleY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp1]),
+ GrScalarToFloat(m[GrMatrix::kMTransX]),
+ GrScalarToFloat(m[GrMatrix::kMTransY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp2])
+ };
+
+ if (GrGLProgram::kSetAsAttribute ==
+ fProgramData->fUniLocations.fStages[s].fTextureMatrixUni) {
+ int baseIdx = GrGLProgram::TextureMatrixAttributeIdx(s);
+ GL_CALL(VertexAttrib4fv(baseIdx + 0, mt+0));
+ GL_CALL(VertexAttrib4fv(baseIdx + 1, mt+3));
+ GL_CALL(VertexAttrib4fv(baseIdx + 2, mt+6));
+ } else {
+ GL_CALL(UniformMatrix3fv(uni, 1, false, mt));
+ }
+ this->recordHWSamplerMatrix(s, drawState.getSampler(s).getMatrix());
+ }
+ }
+}
+
+void GrGpuGLShaders::flushRadial2(int s) {
+
+ const int &uni = fProgramData->fUniLocations.fStages[s].fRadial2Uni;
+ const GrSamplerState& sampler = this->getDrawState().getSampler(s);
+ if (GrGLProgram::kUnusedUniform != uni &&
+ (fProgramData->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
+ fProgramData->fRadial2Radius0[s] != sampler.getRadial2Radius0() ||
+ fProgramData->fRadial2PosRoot[s] != sampler.isRadial2PosRoot())) {
+
+ GrScalar centerX1 = sampler.getRadial2CenterX1();
+ GrScalar radius0 = sampler.getRadial2Radius0();
+
+ GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
+
+ // when were in the degenerate (linear) case the second
+ // value will be INF but the program doesn't read it. (We
+ // use the same 6 uniforms even though we don't need them
+ // all in the linear case just to keep the code complexity
+ // down).
+ float values[6] = {
+ GrScalarToFloat(a),
+ 1 / (2.f * GrScalarToFloat(a)),
+ GrScalarToFloat(centerX1),
+ GrScalarToFloat(radius0),
+ GrScalarToFloat(GrMul(radius0, radius0)),
+ sampler.isRadial2PosRoot() ? 1.f : -1.f
+ };
+ GL_CALL(Uniform1fv(uni, 6, values));
+ fProgramData->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
+ fProgramData->fRadial2Radius0[s] = sampler.getRadial2Radius0();
+ fProgramData->fRadial2PosRoot[s] = sampler.isRadial2PosRoot();
+ }
+}
+
+void GrGpuGLShaders::flushConvolution(int s) {
+ const GrSamplerState& sampler = this->getDrawState().getSampler(s);
+ int kernelUni = fProgramData->fUniLocations.fStages[s].fKernelUni;
+ if (GrGLProgram::kUnusedUniform != kernelUni) {
+ GL_CALL(Uniform1fv(kernelUni, sampler.getKernelWidth(),
+ sampler.getKernel()));
+ }
+ int imageIncrementUni = fProgramData->fUniLocations.fStages[s].fImageIncrementUni;
+ if (GrGLProgram::kUnusedUniform != imageIncrementUni) {
+ GL_CALL(Uniform2fv(imageIncrementUni, 1, sampler.getImageIncrement()));
+ }
+}
+
+void GrGpuGLShaders::flushTexelSize(int s) {
+ const int& uni = fProgramData->fUniLocations.fStages[s].fNormalizedTexelSizeUni;
+ if (GrGLProgram::kUnusedUniform != uni) {
+ const GrGLTexture* texture =
+ static_cast<const GrGLTexture*>(this->getDrawState().getTexture(s));
+ if (texture->width() != fProgramData->fTextureWidth[s] ||
+ texture->height() != fProgramData->fTextureHeight[s]) {
+
+ float texelSize[] = {1.f / texture->width(),
+ 1.f / texture->height()};
+ GL_CALL(Uniform2fv(uni, 1, texelSize));
+ fProgramData->fTextureWidth[s] = texture->width();
+ fProgramData->fTextureHeight[s] = texture->height();
+ }
+ }
+}
+
+void GrGpuGLShaders::flushEdgeAAData() {
+ const int& uni = fProgramData->fUniLocations.fEdgesUni;
+ if (GrGLProgram::kUnusedUniform != uni) {
+ int count = this->getDrawState().getNumAAEdges();
+ GrDrawState::Edge edges[GrDrawState::kMaxEdges];
+ // Flip the edges in Y
+ float height =
+ static_cast<float>(this->getDrawState().getRenderTarget()->height());
+ for (int i = 0; i < count; ++i) {
+ edges[i] = this->getDrawState().getAAEdges()[i];
+ float b = edges[i].fY;
+ edges[i].fY = -b;
+ edges[i].fZ += b * height;
+ }
+ GL_CALL(Uniform3fv(uni, count, &edges[0].fX));
+ }
+}
+
+void GrGpuGLShaders::flushColorMatrix() {
+ const ProgramDesc& desc = fCurrentProgram.getDesc();
+ int matrixUni = fProgramData->fUniLocations.fColorMatrixUni;
+ int vecUni = fProgramData->fUniLocations.fColorMatrixVecUni;
+ if (GrGLProgram::kUnusedUniform != matrixUni
+ && GrGLProgram::kUnusedUniform != vecUni) {
+ const float* m = this->getDrawState().getColorMatrix();
+ GrGLfloat mt[] = {
+ m[0], m[5], m[10], m[15],
+ m[1], m[6], m[11], m[16],
+ m[2], m[7], m[12], m[17],
+ m[3], m[8], m[13], m[18],
+ };
+ static float scale = 1.0f / 255.0f;
+ GrGLfloat vec[] = {
+ m[4] * scale, m[9] * scale, m[14] * scale, m[19] * scale,
+ };
+ GL_CALL(UniformMatrix4fv(matrixUni, 1, false, mt));
+ GL_CALL(Uniform4fv(vecUni, 1, vec));
+ }
+}
+
+static const float ONE_OVER_255 = 1.f / 255.f;
+
+#define GR_COLOR_TO_VEC4(color) {\
+ GrColorUnpackR(color) * ONE_OVER_255,\
+ GrColorUnpackG(color) * ONE_OVER_255,\
+ GrColorUnpackB(color) * ONE_OVER_255,\
+ GrColorUnpackA(color) * ONE_OVER_255 \
+}
+
+void GrGpuGLShaders::flushColor(GrColor color) {
+ const ProgramDesc& desc = fCurrentProgram.getDesc();
+ const GrDrawState& drawState = this->getDrawState();
+
+ if (this->getGeomSrc().fVertexLayout & kColor_VertexLayoutBit) {
+ // color will be specified per-vertex as an attribute
+ // invalidate the const vertex attrib color
+ fHWDrawState.setColor(GrColor_ILLEGAL);
+ } else {
+ switch (desc.fColorInput) {
+ case ProgramDesc::kAttribute_ColorInput:
+ if (fHWDrawState.getColor() != color) {
+ // OpenGL ES only supports the float varieties of
+ // glVertexAttrib
+ float c[] = GR_COLOR_TO_VEC4(color);
+ GL_CALL(VertexAttrib4fv(GrGLProgram::ColorAttributeIdx(),
+ c));
+ fHWDrawState.setColor(color);
+ }
+ break;
+ case ProgramDesc::kUniform_ColorInput:
+ if (fProgramData->fColor != color) {
+ // OpenGL ES doesn't support unsigned byte varieties of
+ // glUniform
+ float c[] = GR_COLOR_TO_VEC4(color);
+ GrAssert(GrGLProgram::kUnusedUniform !=
+ fProgramData->fUniLocations.fColorUni);
+ GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorUni,
+ 1, c));
+ fProgramData->fColor = color;
+ }
+ break;
+ case ProgramDesc::kSolidWhite_ColorInput:
+ case ProgramDesc::kTransBlack_ColorInput:
+ break;
+ default:
+ GrCrash("Unknown color type.");
+ }
+ }
+ if (fProgramData->fUniLocations.fColorFilterUni
+ != GrGLProgram::kUnusedUniform
+ && fProgramData->fColorFilterColor
+ != drawState.getColorFilterColor()) {
+ float c[] = GR_COLOR_TO_VEC4(drawState.getColorFilterColor());
+ GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorFilterUni, 1, c));
+ fProgramData->fColorFilterColor = drawState.getColorFilterColor();
+ }
+}
+
+void GrGpuGLShaders::flushCoverage(GrColor coverage) {
+ const ProgramDesc& desc = fCurrentProgram.getDesc();
+ const GrDrawState& drawState = this->getDrawState();
+
+
+ if (this->getGeomSrc().fVertexLayout & kCoverage_VertexLayoutBit) {
+ // coverage will be specified per-vertex as an attribute
+ // invalidate the const vertex attrib coverage
+ fHWDrawState.setCoverage4(GrColor_ILLEGAL);
+ } else {
+ switch (desc.fCoverageInput) {
+ case ProgramDesc::kAttribute_ColorInput:
+ if (fHWDrawState.getCoverage() != coverage) {
+ // OpenGL ES only supports the float varieties of
+ // glVertexAttrib
+ float c[] = GR_COLOR_TO_VEC4(coverage);
+ GL_CALL(VertexAttrib4fv(GrGLProgram::CoverageAttributeIdx(),
+ c));
+ fHWDrawState.setCoverage(coverage);
+ }
+ break;
+ case ProgramDesc::kUniform_ColorInput:
+ if (fProgramData->fCoverage != coverage) {
+ // OpenGL ES doesn't support unsigned byte varieties of
+ // glUniform
+ float c[] = GR_COLOR_TO_VEC4(coverage);
+ GrAssert(GrGLProgram::kUnusedUniform !=
+ fProgramData->fUniLocations.fCoverageUni);
+ GL_CALL(Uniform4fv(fProgramData->fUniLocations.fCoverageUni,
+ 1, c));
+ fProgramData->fCoverage = coverage;
+ }
+ break;
+ case ProgramDesc::kSolidWhite_ColorInput:
+ case ProgramDesc::kTransBlack_ColorInput:
+ break;
+ default:
+ GrCrash("Unknown coverage type.");
+ }
+ }
+}
+
+bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
+ if (!flushGLStateCommon(type)) {
+ return false;
+ }
+
+ const GrDrawState& drawState = this->getDrawState();
+
+ if (fDirtyFlags.fRenderTargetChanged) {
+ // our coords are in pixel space and the GL matrices map to NDC
+ // so if the viewport changed, our matrix is now wrong.
+ fHWDrawState.setViewMatrix(GrMatrix::InvalidMatrix());
+ // we assume all shader matrices may be wrong after viewport changes
+ fProgramCache->invalidateViewMatrices();
+ }
+
+ GrBlendCoeff srcCoeff;
+ GrBlendCoeff dstCoeff;
+ BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
+ if (kSkipDraw_BlendOptFlag & blendOpts) {
+ return false;
+ }
+
+ this->buildProgram(type, blendOpts, dstCoeff);
+ fProgramData = fProgramCache->getProgramData(fCurrentProgram);
+ if (NULL == fProgramData) {
+ GrAssert(!"Failed to create program!");
+ return false;
+ }
+
+ if (fHWProgramID != fProgramData->fProgramID) {
+ GL_CALL(UseProgram(fProgramData->fProgramID));
+ fHWProgramID = fProgramData->fProgramID;
+ }
+ fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
+ this->flushBlend(type, srcCoeff, dstCoeff);
+
+ GrColor color;
+ GrColor coverage;
+ if (blendOpts & kEmitTransBlack_BlendOptFlag) {
+ color = 0;
+ coverage = 0;
+ } else if (blendOpts & kEmitCoverage_BlendOptFlag) {
+ color = 0xffffffff;
+ coverage = drawState.getCoverage();
+ } else {
+ color = drawState.getColor();
+ coverage = drawState.getCoverage();
+ }
+ this->flushColor(color);
+ this->flushCoverage(coverage);
+
+ this->flushViewMatrix();
+
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ if (this->isStageEnabled(s)) {
+ this->flushTextureMatrix(s);
+
+ this->flushRadial2(s);
+
+ this->flushConvolution(s);
+
+ this->flushTexelSize(s);
+
+ this->flushTextureDomain(s);
+ }
+ }
+ this->flushEdgeAAData();
+ this->flushColorMatrix();
+ resetDirtyFlags();
+ return true;
+}
+
+void GrGpuGLShaders::postDraw() {
+}
+
+void GrGpuGLShaders::setupGeometry(int* startVertex,
+ int* startIndex,
+ int vertexCount,
+ int indexCount) {
+
+ int newColorOffset;
+ int newCoverageOffset;
+ int newTexCoordOffsets[GrDrawState::kMaxTexCoords];
+ int newEdgeOffset;
+
+ GrGLsizei newStride = VertexSizeAndOffsetsByIdx(
+ this->getGeomSrc().fVertexLayout,
+ newTexCoordOffsets,
+ &newColorOffset,
+ &newCoverageOffset,
+ &newEdgeOffset);
+ int oldColorOffset;
+ int oldCoverageOffset;
+ int oldTexCoordOffsets[GrDrawState::kMaxTexCoords];
+ int oldEdgeOffset;
+
+ GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(
+ fHWGeometryState.fVertexLayout,
+ oldTexCoordOffsets,
+ &oldColorOffset,
+ &oldCoverageOffset,
+ &oldEdgeOffset);
+ bool indexed = NULL != startIndex;
+
+ int extraVertexOffset;
+ int extraIndexOffset;
+ this->setBuffers(indexed, &extraVertexOffset, &extraIndexOffset);
+
+ GrGLenum scalarType;
+ bool texCoordNorm;
+ if (this->getGeomSrc().fVertexLayout & kTextFormat_VertexLayoutBit) {
+ scalarType = GrGLTextType;
+ texCoordNorm = GR_GL_TEXT_TEXTURE_NORMALIZED;
+ } else {
+ scalarType = GrGLType;
+ texCoordNorm = false;
+ }
+
+ size_t vertexOffset = (*startVertex + extraVertexOffset) * newStride;
+ *startVertex = 0;
+ if (indexed) {
+ *startIndex += extraIndexOffset;
+ }
+
+ // all the Pointers must be set if any of these are true
+ bool allOffsetsChange = fHWGeometryState.fArrayPtrsDirty ||
+ vertexOffset != fHWGeometryState.fVertexOffset ||
+ newStride != oldStride;
+
+ // position and tex coord offsets change if above conditions are true
+ // or the type/normalization changed based on text vs nontext type coords.
+ bool posAndTexChange = allOffsetsChange ||
+ (((GrGLTextType != GrGLType) || GR_GL_TEXT_TEXTURE_NORMALIZED) &&
+ (kTextFormat_VertexLayoutBit &
+ (fHWGeometryState.fVertexLayout ^
+ this->getGeomSrc().fVertexLayout)));
+
+ if (posAndTexChange) {
+ int idx = GrGLProgram::PositionAttributeIdx();
+ GL_CALL(VertexAttribPointer(idx, 2, scalarType, false, newStride,
+ (GrGLvoid*)vertexOffset));
+ fHWGeometryState.fVertexOffset = vertexOffset;
+ }
+
+ for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+ if (newTexCoordOffsets[t] > 0) {
+ GrGLvoid* texCoordOffset = (GrGLvoid*)(vertexOffset + newTexCoordOffsets[t]);
+ int idx = GrGLProgram::TexCoordAttributeIdx(t);
+ if (oldTexCoordOffsets[t] <= 0) {
+ GL_CALL(EnableVertexAttribArray(idx));
+ GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm,
+ newStride, texCoordOffset));
+ } else if (posAndTexChange ||
+ newTexCoordOffsets[t] != oldTexCoordOffsets[t]) {
+ GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm,
+ newStride, texCoordOffset));
+ }
+ } else if (oldTexCoordOffsets[t] > 0) {
+ GL_CALL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t)));
+ }
+ }
+
+ if (newColorOffset > 0) {
+ GrGLvoid* colorOffset = (int8_t*)(vertexOffset + newColorOffset);
+ int idx = GrGLProgram::ColorAttributeIdx();
+ if (oldColorOffset <= 0) {
+ GL_CALL(EnableVertexAttribArray(idx));
+ GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+ true, newStride, colorOffset));
+ } else if (allOffsetsChange || newColorOffset != oldColorOffset) {
+ GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+ true, newStride, colorOffset));
+ }
+ } else if (oldColorOffset > 0) {
+ GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
+ }
+
+ if (newCoverageOffset > 0) {
+ GrGLvoid* coverageOffset = (int8_t*)(vertexOffset + newCoverageOffset);
+ int idx = GrGLProgram::CoverageAttributeIdx();
+ if (oldCoverageOffset <= 0) {
+ GL_CALL(EnableVertexAttribArray(idx));
+ GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+ true, newStride, coverageOffset));
+ } else if (allOffsetsChange || newCoverageOffset != oldCoverageOffset) {
+ GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+ true, newStride, coverageOffset));
+ }
+ } else if (oldCoverageOffset > 0) {
+ GL_CALL(DisableVertexAttribArray(GrGLProgram::CoverageAttributeIdx()));
+ }
+
+ if (newEdgeOffset > 0) {
+ GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset);
+ int idx = GrGLProgram::EdgeAttributeIdx();
+ if (oldEdgeOffset <= 0) {
+ GL_CALL(EnableVertexAttribArray(idx));
+ GL_CALL(VertexAttribPointer(idx, 4, scalarType,
+ false, newStride, edgeOffset));
+ } else if (allOffsetsChange || newEdgeOffset != oldEdgeOffset) {
+ GL_CALL(VertexAttribPointer(idx, 4, scalarType,
+ false, newStride, edgeOffset));
+ }
+ } else if (oldEdgeOffset > 0) {
+ GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx()));
+ }
+
+ fHWGeometryState.fVertexLayout = this->getGeomSrc().fVertexLayout;
+ fHWGeometryState.fArrayPtrsDirty = false;
+}
+
+void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
+ BlendOptFlags blendOpts,
+ GrBlendCoeff dstCoeff) {
+ ProgramDesc& desc = fCurrentProgram.fProgramDesc;
+ const GrDrawState& drawState = this->getDrawState();
+
+ // This should already have been caught
+ GrAssert(!(kSkipDraw_BlendOptFlag & blendOpts));
+
+ bool skipCoverage = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
+
+ bool skipColor = SkToBool(blendOpts & (kEmitTransBlack_BlendOptFlag |
+ kEmitCoverage_BlendOptFlag));
+
+ // The descriptor is used as a cache key. Thus when a field of the
+ // descriptor will not affect program generation (because of the vertex
+ // layout in use or other descriptor field settings) it should be set
+ // to a canonical value to avoid duplicate programs with different keys.
+
+ // Must initialize all fields or cache will have false negatives!
+ desc.fVertexLayout = this->getGeomSrc().fVertexLayout;
+
+ desc.fEmitsPointSize = kPoints_PrimitiveType == type;
+
+ bool requiresAttributeColors =
+ !skipColor && SkToBool(desc.fVertexLayout & kColor_VertexLayoutBit);
+ bool requiresAttributeCoverage =
+ !skipCoverage && SkToBool(desc.fVertexLayout &
+ kCoverage_VertexLayoutBit);
+
+ // fColorInput/fCoverageInput records how colors are specified for the.
+ // program. So we strip the bits from the layout to avoid false negatives
+ // when searching for an existing program in the cache.
+ desc.fVertexLayout &= ~(kColor_VertexLayoutBit | kCoverage_VertexLayoutBit);
+
+ desc.fColorFilterXfermode = skipColor ?
+ SkXfermode::kDst_Mode :
+ drawState.getColorFilterMode();
+
+ desc.fColorMatrixEnabled = drawState.isStateFlagEnabled(GrDrawState::kColorMatrix_StateBit);
+
+ // no reason to do edge aa or look at per-vertex coverage if coverage is
+ // ignored
+ if (skipCoverage) {
+ desc.fVertexLayout &= ~(kEdge_VertexLayoutBit |
+ kCoverage_VertexLayoutBit);
+ }
+
+ bool colorIsTransBlack = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
+ bool colorIsSolidWhite = (blendOpts & kEmitCoverage_BlendOptFlag) ||
+ (!requiresAttributeColors &&
+ 0xffffffff == drawState.getColor());
+ if (GR_AGGRESSIVE_SHADER_OPTS && colorIsTransBlack) {
+ desc.fColorInput = ProgramDesc::kTransBlack_ColorInput;
+ } else if (GR_AGGRESSIVE_SHADER_OPTS && colorIsSolidWhite) {
+ desc.fColorInput = ProgramDesc::kSolidWhite_ColorInput;
+ } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeColors) {
+ desc.fColorInput = ProgramDesc::kUniform_ColorInput;
+ } else {
+ desc.fColorInput = ProgramDesc::kAttribute_ColorInput;
+ }
+
+ bool covIsSolidWhite = !requiresAttributeCoverage &&
+ 0xffffffff == drawState.getCoverage();
+
+ if (skipCoverage) {
+ desc.fCoverageInput = ProgramDesc::kTransBlack_ColorInput;
+ } else if (covIsSolidWhite) {
+ desc.fCoverageInput = ProgramDesc::kSolidWhite_ColorInput;
+ } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeCoverage) {
+ desc.fCoverageInput = ProgramDesc::kUniform_ColorInput;
+ } else {
+ desc.fCoverageInput = ProgramDesc::kAttribute_ColorInput;
+ }
+
+ desc.fEdgeAANumEdges = skipCoverage ? 0 : drawState.getNumAAEdges();
+ desc.fEdgeAAConcave = desc.fEdgeAANumEdges > 0 &&
+ drawState.isConcaveEdgeAAState();
+
+ int lastEnabledStage = -1;
+
+ if (!skipCoverage && (desc.fVertexLayout &
+ GrDrawTarget::kEdge_VertexLayoutBit)) {
+ desc.fVertexEdgeType = drawState.getVertexEdgeType();
+ } else {
+ // use canonical value when not set to avoid cache misses
+ desc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
+ }
+
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ StageDesc& stage = desc.fStages[s];
+
+ stage.fOptFlags = 0;
+ stage.setEnabled(this->isStageEnabled(s));
+
+ bool skip = s < drawState.getFirstCoverageStage() ? skipColor :
+ skipCoverage;
+
+ if (!skip && stage.isEnabled()) {
+ lastEnabledStage = s;
+ const GrGLTexture* texture =
+ static_cast<const GrGLTexture*>(drawState.getTexture(s));
+ GrAssert(NULL != texture);
+ const GrSamplerState& sampler = drawState.getSampler(s);
+ // we matrix to invert when orientation is TopDown, so make sure
+ // we aren't in that case before flagging as identity.
+ if (TextureMatrixIsIdentity(texture, sampler)) {
+ stage.fOptFlags |= StageDesc::kIdentityMatrix_OptFlagBit;
+ } else if (!sampler.getMatrix().hasPerspective()) {
+ stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+ }
+ switch (sampler.getSampleMode()) {
+ case GrSamplerState::kNormal_SampleMode:
+ stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
+ break;
+ case GrSamplerState::kRadial_SampleMode:
+ stage.fCoordMapping = StageDesc::kRadialGradient_CoordMapping;
+ break;
+ case GrSamplerState::kRadial2_SampleMode:
+ if (sampler.radial2IsDegenerate()) {
+ stage.fCoordMapping =
+ StageDesc::kRadial2GradientDegenerate_CoordMapping;
+ } else {
+ stage.fCoordMapping =
+ StageDesc::kRadial2Gradient_CoordMapping;
+ }
+ break;
+ case GrSamplerState::kSweep_SampleMode:
+ stage.fCoordMapping = StageDesc::kSweepGradient_CoordMapping;
+ break;
+ default:
+ GrCrash("Unexpected sample mode!");
+ break;
+ }
+
+ switch (sampler.getFilter()) {
+ // these both can use a regular texture2D()
+ case GrSamplerState::kNearest_Filter:
+ case GrSamplerState::kBilinear_Filter:
+ stage.fFetchMode = StageDesc::kSingle_FetchMode;
+ break;
+ // performs 4 texture2D()s
+ case GrSamplerState::k4x4Downsample_Filter:
+ stage.fFetchMode = StageDesc::k2x2_FetchMode;
+ break;
+ // performs fKernelWidth texture2D()s
+ case GrSamplerState::kConvolution_Filter:
+ stage.fFetchMode = StageDesc::kConvolution_FetchMode;
+ break;
+ default:
+ GrCrash("Unexpected filter!");
+ break;
+ }
+
+ if (sampler.hasTextureDomain()) {
+ GrAssert(GrSamplerState::kClamp_WrapMode ==
+ sampler.getWrapX() &&
+ GrSamplerState::kClamp_WrapMode ==
+ sampler.getWrapY());
+ stage.fOptFlags |= StageDesc::kCustomTextureDomain_OptFlagBit;
+ }
+
+ stage.fInConfigFlags = 0;
+ if (!this->glCaps().fTextureSwizzleSupport) {
+ if (GrPixelConfigIsAlphaOnly(texture->config())) {
+ // if we don't have texture swizzle support then
+ // the shader must do an alpha smear after reading
+ // the texture
+ stage.fInConfigFlags |= StageDesc::kSmearAlpha_InConfigFlag;
+ } else if (sampler.swapsRAndB()) {
+ stage.fInConfigFlags |= StageDesc::kSwapRAndB_InConfigFlag;
+ }
+ }
+ if (GrPixelConfigIsUnpremultiplied(texture->config())) {
+ stage.fInConfigFlags |= StageDesc::kMulRGBByAlpha_InConfigFlag;
+ }
+
+ if (sampler.getFilter() == GrSamplerState::kConvolution_Filter) {
+ stage.fKernelWidth = sampler.getKernelWidth();
+ } else {
+ stage.fKernelWidth = 0;
+ }
+ } else {
+ stage.fOptFlags = 0;
+ stage.fCoordMapping = (StageDesc::CoordMapping) 0;
+ stage.fInConfigFlags = 0;
+ stage.fFetchMode = (StageDesc::FetchMode) 0;
+ stage.fKernelWidth = 0;
+ }
+ }
+
+ if (GrPixelConfigIsUnpremultiplied(drawState.getRenderTarget()->config())) {
+ desc.fOutputPM = ProgramDesc::kNo_OutputPM;
+ } else {
+ desc.fOutputPM = ProgramDesc::kYes_OutputPM;
+ }
+
+ desc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
+
+ // currently the experimental GS will only work with triangle prims
+ // (and it doesn't do anything other than pass through values from
+ // the VS to the FS anyway).
+#if 0 && GR_GL_EXPERIMENTAL_GS
+ desc.fExperimentalGS = this->getCaps().fGeometryShaderSupport;
+#endif
+
+ // we want to avoid generating programs with different "first cov stage"
+ // values when they would compute the same result.
+ // We set field in the desc to kNumStages when either there are no
+ // coverage stages or the distinction between coverage and color is
+ // immaterial.
+ int firstCoverageStage = GrDrawState::kNumStages;
+ desc.fFirstCoverageStage = GrDrawState::kNumStages;
+ bool hasCoverage = drawState.getFirstCoverageStage() <= lastEnabledStage;
+ if (hasCoverage) {
+ firstCoverageStage = drawState.getFirstCoverageStage();
+ }
+
+ // other coverage inputs
+ if (!hasCoverage) {
+ hasCoverage =
+ desc.fEdgeAANumEdges ||
+ requiresAttributeCoverage ||
+ (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit);
+ }
+
+ if (hasCoverage) {
+ // color filter is applied between color/coverage computation
+ if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
+ desc.fFirstCoverageStage = firstCoverageStage;
+ }
+
+ if (this->getCaps().fDualSourceBlendingSupport &&
+ !(blendOpts & (kEmitCoverage_BlendOptFlag |
+ kCoverageAsAlpha_BlendOptFlag))) {
+ if (kZero_BlendCoeff == dstCoeff) {
+ // write the coverage value to second color
+ desc.fDualSrcOutput = ProgramDesc::kCoverage_DualSrcOutput;
+ desc.fFirstCoverageStage = firstCoverageStage;
+ } else if (kSA_BlendCoeff == dstCoeff) {
+ // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
+ // cover
+ desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput;
+ desc.fFirstCoverageStage = firstCoverageStage;
+ } else if (kSC_BlendCoeff == dstCoeff) {
+ // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
+ // cover
+ desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput;
+ desc.fFirstCoverageStage = firstCoverageStage;
+ }
+ }
+ }
+}
diff --git a/src/gpu/gl/GrGpuGLShaders.h b/src/gpu/gl/GrGpuGLShaders.h
new file mode 100644
index 0000000000..39bc97471d
--- /dev/null
+++ b/src/gpu/gl/GrGpuGLShaders.h
@@ -0,0 +1,103 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGpuGLShaders_DEFINED
+#define GrGpuGLShaders_DEFINED
+
+#include "GrGpuGL.h"
+#include "GrGLProgram.h"
+
+class GrGpuGLProgram;
+
+// Programmable OpenGL or OpenGL ES 2.0
+class GrGpuGLShaders : public GrGpuGL {
+public:
+ GrGpuGLShaders(const GrGLContextInfo& ctxInfo);
+ virtual ~GrGpuGLShaders();
+
+ virtual void abandonResources();
+
+ bool programUnitTest();
+
+protected:
+ // overrides from GrGpu
+ virtual void onResetContext() SK_OVERRIDE;
+ virtual bool flushGraphicsState(GrPrimitiveType type);
+ virtual void setupGeometry(int* startVertex,
+ int* startIndex,
+ int vertexCount,
+ int indexCount);
+ virtual void postDraw();
+
+private:
+
+ // for readability of function impls
+ typedef GrGLProgram::ProgramDesc ProgramDesc;
+ typedef ProgramDesc::StageDesc StageDesc;
+ typedef GrGLProgram::CachedData CachedData;
+
+ class ProgramCache;
+
+ // Helpers to make code more readable
+ const GrMatrix& getHWViewMatrix();
+ void recordHWViewMatrix(const GrMatrix& matrix);
+ const GrMatrix& getHWSamplerMatrix(int stage);
+ void recordHWSamplerMatrix(int stage, const GrMatrix& matrix);
+
+ // sets the texture matrix uniform for currently bound program
+ void flushTextureMatrix(int stage);
+
+ // sets the texture domain uniform for currently bound program
+ void flushTextureDomain(int stage);
+
+ // sets the color specified by GrDrawState::setColor()
+ void flushColor(GrColor color);
+
+ // sets the color specified by GrDrawState::setCoverage()
+ void flushCoverage(GrColor color);
+
+ // sets the MVP matrix uniform for currently bound program
+ void flushViewMatrix();
+
+ // flushes the parameters to two point radial gradient
+ void flushRadial2(int stage);
+
+ // flushes the parameters for convolution
+ void flushConvolution(int stage);
+
+ // flushes the normalized texel size
+ void flushTexelSize(int stage);
+
+ // flushes the edges for edge AA
+ void flushEdgeAAData();
+
+ // flushes the color matrix
+ void flushColorMatrix();
+
+ static void DeleteProgram(const GrGLInterface* gl,
+ CachedData* programData);
+
+ void buildProgram(GrPrimitiveType typeBlend,
+ BlendOptFlags blendOpts,
+ GrBlendCoeff dstCoeff);
+
+ ProgramCache* fProgramCache;
+ CachedData* fProgramData;
+ GrGLuint fHWProgramID;
+ GrGLProgram fCurrentProgram;
+ // If we get rid of fixed function subclass this should move
+ // to the GLCaps struct in parent class
+ GrGLint fMaxVertexAttribs;
+
+ typedef GrGpuGL INHERITED;
+};
+
+#endif
+
diff --git a/src/gpu/gl/SkGLContext.cpp b/src/gpu/gl/SkGLContext.cpp
new file mode 100644
index 0000000000..6142d3c572
--- /dev/null
+++ b/src/gpu/gl/SkGLContext.cpp
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkGLContext.h"
+
+SkGLContext::SkGLContext()
+ : fFBO(0)
+ , fGL(NULL) {
+}
+
+SkGLContext::~SkGLContext() {
+ SkSafeUnref(fGL);
+}
+
+bool SkGLContext::hasExtension(const char* extensionName) const {
+ return GrGLHasExtensionFromString(extensionName, fExtensionString.c_str());
+}
+
+bool SkGLContext::init(int width, int height) {
+ if (fGL) {
+ fGL->unref();
+ this->destroyGLContext();
+ }
+
+ fGL = this->createGLContext();
+ if (fGL) {
+ fExtensionString =
+ reinterpret_cast<const char*>(SK_GL(*this,
+ GetString(GR_GL_EXTENSIONS)));
+ const char* versionStr =
+ reinterpret_cast<const char*>(SK_GL(*this,
+ GetString(GR_GL_VERSION)));
+ GrGLVersion version = GrGLGetVersionFromString(versionStr);
+
+ // clear any existing GL erorrs
+ GrGLenum error;
+ do {
+ error = SK_GL(*this, GetError());
+ } while (GR_GL_NO_ERROR != error);
+
+ GrGLuint cbID;
+ GrGLuint dsID;
+
+ GrGLBinding bindingInUse = GrGLGetBindingInUse(this->gl());
+
+ SK_GL(*this, GenFramebuffers(1, &fFBO));
+ SK_GL(*this, BindFramebuffer(GR_GL_FRAMEBUFFER, fFBO));
+ SK_GL(*this, GenRenderbuffers(1, &cbID));
+ SK_GL(*this, BindRenderbuffer(GR_GL_RENDERBUFFER, cbID));
+ if (kES2_GrGLBinding == bindingInUse) {
+ SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+ GR_GL_RGBA8,
+ width, height));
+ } else {
+ SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+ GR_GL_RGBA,
+ width, height));
+ }
+ SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_COLOR_ATTACHMENT0,
+ GR_GL_RENDERBUFFER,
+ cbID));
+ SK_GL(*this, GenRenderbuffers(1, &dsID));
+ SK_GL(*this, BindRenderbuffer(GR_GL_RENDERBUFFER, dsID));
+
+ // Some drivers that support packed depth stencil will only succeed
+ // in binding a packed format an FBO. However, we can't rely on packed
+ // depth stencil being available.
+ bool supportsPackedDepthStencil;
+ if (kES2_GrGLBinding == bindingInUse) {
+ supportsPackedDepthStencil =
+ this->hasExtension("GL_OES_packed_depth_stencil");
+ } else {
+ supportsPackedDepthStencil = version >= GR_GL_VER(3,0) ||
+ this->hasExtension("GL_EXT_packed_depth_stencil") ||
+ this->hasExtension("GL_ARB_framebuffer_object");
+ }
+
+ if (supportsPackedDepthStencil) {
+ // ES2 requires sized internal formats for RenderbufferStorage
+ // On Desktop we let the driver decide.
+ GrGLenum format = kES2_GrGLBinding == bindingInUse ?
+ GR_GL_DEPTH24_STENCIL8 :
+ GR_GL_DEPTH_STENCIL;
+ SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+ format,
+ width, height));
+ SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_DEPTH_ATTACHMENT,
+ GR_GL_RENDERBUFFER,
+ dsID));
+ } else {
+ GrGLenum format = kES2_GrGLBinding == bindingInUse ?
+ GR_GL_STENCIL_INDEX8 :
+ GR_GL_STENCIL_INDEX;
+ SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+ format,
+ width, height));
+ }
+ SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+ GR_GL_STENCIL_ATTACHMENT,
+ GR_GL_RENDERBUFFER,
+ dsID));
+ SK_GL(*this, Viewport(0, 0, width, height));
+ SK_GL(*this, ClearStencil(0));
+ SK_GL(*this, Clear(GR_GL_STENCIL_BUFFER_BIT));
+
+ error = SK_GL(*this, GetError());
+ GrGLenum status =
+ SK_GL(*this, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+
+ if (GR_GL_FRAMEBUFFER_COMPLETE != status ||
+ GR_GL_NO_ERROR != error) {
+ fFBO = 0;
+ fGL->unref();
+ fGL = NULL;
+ this->destroyGLContext();
+ return false;
+ } else {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/gpu/gl/SkNullGLContext.cpp b/src/gpu/gl/SkNullGLContext.cpp
new file mode 100644
index 0000000000..04e63d8bbe
--- /dev/null
+++ b/src/gpu/gl/SkNullGLContext.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkNullGLContext.h"
+
+const GrGLInterface* SkNullGLContext::createGLContext() {
+ return GrGLCreateNullInterface();
+};