diff options
Diffstat (limited to 'src/gpu/gl')
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(×tamp); + 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(); +}; |