| Index: src/gpu/gl/GrGLCreateNullInterface.cpp
|
| diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
|
| index d047e730f71a4cdb2db77abea8a412b89c4abad3..f852e30da5b8ab713d75eeb0a4d8c419d6176481 100644
|
| --- a/src/gpu/gl/GrGLCreateNullInterface.cpp
|
| +++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
|
| @@ -10,16 +10,15 @@
|
| #include "GrGLDefines.h"
|
| #include "SkTDArray.h"
|
| #include "GrGLNoOpInterface.h"
|
| +#include "SkTLS.h"
|
|
|
| -// Functions not declared in GrGLBogusInterface.h (not common with the Debug GL interface).
|
| -
|
| -namespace { // added to suppress 'no previous prototype' warning
|
| -
|
| -class GrBufferObj {
|
| +class BufferObj {
|
| public:
|
| - GrBufferObj(GrGLuint id) : fID(id), fDataPtr(NULL), fSize(0), fMapped(false) {
|
| + SK_DECLARE_INST_COUNT_ROOT(BufferObj);
|
| +
|
| + BufferObj(GrGLuint id) : fID(id), fDataPtr(NULL), fSize(0), fMapped(false) {
|
| }
|
| - ~GrBufferObj() { SkDELETE_ARRAY(fDataPtr); }
|
| + ~BufferObj() { SkDELETE_ARRAY(fDataPtr); }
|
|
|
| void allocate(GrGLsizeiptr size, const GrGLchar* dataPtr) {
|
| if (NULL != fDataPtr) {
|
| @@ -45,54 +44,104 @@ private:
|
| bool fMapped;
|
| };
|
|
|
| -// In debug builds we do asserts that ensure we agree with GL about when a buffer
|
| -// is mapped.
|
| -static SkTDArray<GrBufferObj*> gBuffers; // slot 0 is reserved for head of free list
|
| -static GrGLuint gCurrArrayBuffer;
|
| -static GrGLuint gCurrElementArrayBuffer;
|
| +// This class maintains a sparsely populated array of buffer pointers.
|
| +class BufferManager {
|
| +public:
|
| + SK_DECLARE_INST_COUNT_ROOT(BufferManager);
|
|
|
| -static GrBufferObj* look_up(GrGLuint id) {
|
| - GrBufferObj* buffer = gBuffers[id];
|
| - SkASSERT(NULL != buffer && buffer->id() == id);
|
| - return buffer;
|
| -}
|
| + BufferManager() : fFreeListHead(kFreeListEnd) {}
|
| +
|
| + ~BufferManager() {
|
| + // NULL out the entries that are really free list links rather than ptrs before deleting.
|
| + intptr_t curr = fFreeListHead;
|
| + while (kFreeListEnd != curr) {
|
| + intptr_t next = reinterpret_cast<intptr_t>(fBuffers[SkToS32(curr)]);
|
| + fBuffers[SkToS32(curr)] = NULL;
|
| + curr = next;
|
| + }
|
|
|
| -static GrBufferObj* create_buffer() {
|
| - if (0 == gBuffers.count()) {
|
| - // slot zero is reserved for the head of the free list
|
| - *gBuffers.append() = NULL;
|
| + fBuffers.deleteAll();
|
| }
|
|
|
| - GrGLuint id;
|
| - GrBufferObj* buffer;
|
| -
|
| - if (NULL == gBuffers[0]) {
|
| - // no free slots - create a new one
|
| - id = gBuffers.count();
|
| - buffer = SkNEW_ARGS(GrBufferObj, (id));
|
| - gBuffers.append(1, &buffer);
|
| - } else {
|
| - // recycle a slot from the free list
|
| - id = SkTCast<GrGLuint>(gBuffers[0]);
|
| - gBuffers[0] = gBuffers[id];
|
| -
|
| - buffer = SkNEW_ARGS(GrBufferObj, (id));
|
| - gBuffers[id] = buffer;
|
| + BufferObj* lookUp(GrGLuint id) {
|
| + BufferObj* buffer = fBuffers[id];
|
| + SkASSERT(NULL != buffer && buffer->id() == id);
|
| + return buffer;
|
| }
|
|
|
| - return buffer;
|
| -}
|
| + BufferObj* create() {
|
| + GrGLuint id;
|
| + BufferObj* buffer;
|
| +
|
| + if (kFreeListEnd == fFreeListHead) {
|
| + // no free slots - create a new one
|
| + id = fBuffers.count();
|
| + buffer = SkNEW_ARGS(BufferObj, (id));
|
| + *fBuffers.append() = buffer;
|
| + } else {
|
| + // grab the head of the free list and advance the head to the next free slot.
|
| + id = static_cast<GrGLuint>(fFreeListHead);
|
| + fFreeListHead = reinterpret_cast<intptr_t>(fBuffers[id]);
|
| +
|
| + buffer = SkNEW_ARGS(BufferObj, (id));
|
| + fBuffers[id] = buffer;
|
| + }
|
|
|
| -static void delete_buffer(GrBufferObj* buffer) {
|
| - SkASSERT(gBuffers.count() > 0);
|
| + return buffer;
|
| + }
|
|
|
| - GrGLuint id = buffer->id();
|
| - SkDELETE(buffer);
|
| + void free(BufferObj* buffer) {
|
| + SkASSERT(fBuffers.count() > 0);
|
|
|
| - // Add this slot to the free list
|
| - gBuffers[id] = gBuffers[0];
|
| - gBuffers[0] = SkTCast<GrBufferObj*>((const void*)(intptr_t)id);
|
| -}
|
| + GrGLuint id = buffer->id();
|
| + SkDELETE(buffer);
|
| +
|
| + fBuffers[id] = reinterpret_cast<BufferObj*>(fFreeListHead);
|
| + fFreeListHead = id;
|
| + }
|
| +
|
| +private:
|
| + static const intptr_t kFreeListEnd = -1;
|
| + // Index of the first entry of fBuffers in the free list. Free slots in fBuffers are indices to
|
| + // the next free slot. The last free slot has a value of kFreeListEnd.
|
| + intptr_t fFreeListHead;
|
| + SkTDArray<BufferObj*> fBuffers;
|
| +};
|
| +
|
| +/**
|
| + * The global-to-thread state object for the null interface. All null interfaces on the
|
| + * same thread currently share one of these. This means two null contexts on the same thread
|
| + * can interfere with each other. It may make sense to more integrate this into SkNullGLContext
|
| + * and use it's makeCurrent mechanism.
|
| + */
|
| +struct ThreadContext {
|
| +public:
|
| + SK_DECLARE_INST_COUNT_ROOT(ThreadContext);
|
| +
|
| + BufferManager fBufferManager;
|
| + GrGLuint fCurrArrayBuffer;
|
| + GrGLuint fCurrElementArrayBuffer;
|
| + GrGLuint fCurrProgramID;
|
| + GrGLuint fCurrShaderID;
|
| +
|
| + static ThreadContext* Get() {
|
| + return reinterpret_cast<ThreadContext*>(SkTLS::Get(Create, Delete));
|
| + }
|
| +
|
| + ThreadContext()
|
| + : fCurrArrayBuffer(0)
|
| + , fCurrElementArrayBuffer(0)
|
| + , fCurrProgramID(0)
|
| + , fCurrShaderID(0) {}
|
| +
|
| +private:
|
| + static void* Create() { return SkNEW(ThreadContext ); }
|
| + static void Delete(void* context) { SkDELETE(reinterpret_cast<ThreadContext *>(context)); }
|
| +};
|
| +
|
| +// Functions not declared in GrGLBogusInterface.h (not common with the Debug GL interface).
|
| +
|
| +namespace { // added to suppress 'no previous prototype' warning
|
|
|
| GrGLvoid GR_GL_FUNCTION_TYPE nullGLActiveTexture(GrGLenum texture) {}
|
| GrGLvoid GR_GL_FUNCTION_TYPE nullGLAttachShader(GrGLuint program, GrGLuint shader) {}
|
| @@ -102,9 +151,9 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindTexture(GrGLenum target, GrGLuint texture
|
| GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindVertexArray(GrGLuint id) {}
|
|
|
| GrGLvoid GR_GL_FUNCTION_TYPE nullGLGenBuffers(GrGLsizei n, GrGLuint* ids) {
|
| -
|
| + ThreadContext* ctx = ThreadContext::Get();
|
| for (int i = 0; i < n; ++i) {
|
| - GrBufferObj* buffer = create_buffer();
|
| + BufferObj* buffer = ctx->fBufferManager.create();
|
| ids[i] = buffer->id();
|
| }
|
| }
|
| @@ -115,14 +164,15 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target,
|
| GrGLsizeiptr size,
|
| const GrGLvoid* data,
|
| GrGLenum usage) {
|
| + ThreadContext* ctx = ThreadContext::Get();
|
| GrGLuint id = 0;
|
|
|
| switch (target) {
|
| case GR_GL_ARRAY_BUFFER:
|
| - id = gCurrArrayBuffer;
|
| + id = ctx->fCurrArrayBuffer;
|
| break;
|
| case GR_GL_ELEMENT_ARRAY_BUFFER:
|
| - id = gCurrElementArrayBuffer;
|
| + id = ctx->fCurrElementArrayBuffer;
|
| break;
|
| default:
|
| SkFAIL("Unexpected target to nullGLBufferData");
|
| @@ -130,7 +180,7 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target,
|
| }
|
|
|
| if (id > 0) {
|
| - GrBufferObj* buffer = look_up(id);
|
| + BufferObj* buffer = ctx->fBufferManager.lookUp(id);
|
| buffer->allocate(size, (const GrGLchar*) data);
|
| }
|
| }
|
| @@ -147,13 +197,11 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferRenderbuffer(GrGLenum target, GrGL
|
| GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {}
|
|
|
| GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateProgram() {
|
| - static GrGLuint gCurrID = 0;
|
| - return ++gCurrID;
|
| + return ++ThreadContext::Get()->fCurrProgramID;
|
| }
|
|
|
| GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateShader(GrGLenum type) {
|
| - static GrGLuint gCurrID = 0;
|
| - return ++gCurrID;
|
| + return ++ThreadContext::Get()->fCurrShaderID;
|
| }
|
|
|
| // same delete used for shaders and programs
|
| @@ -161,46 +209,49 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLDelete(GrGLuint program) {
|
| }
|
|
|
| GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindBuffer(GrGLenum target, GrGLuint buffer) {
|
| + ThreadContext* ctx = ThreadContext::Get();
|
| switch (target) {
|
| case GR_GL_ARRAY_BUFFER:
|
| - gCurrArrayBuffer = buffer;
|
| + ctx->fCurrArrayBuffer = buffer;
|
| break;
|
| case GR_GL_ELEMENT_ARRAY_BUFFER:
|
| - gCurrElementArrayBuffer = buffer;
|
| + ctx->fCurrElementArrayBuffer = buffer;
|
| break;
|
| }
|
| }
|
|
|
| // deleting a bound buffer has the side effect of binding 0
|
| GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteBuffers(GrGLsizei n, const GrGLuint* ids) {
|
| + ThreadContext* ctx = ThreadContext::Get();
|
| for (int i = 0; i < n; ++i) {
|
| - if (ids[i] == gCurrArrayBuffer) {
|
| - gCurrArrayBuffer = 0;
|
| + if (ids[i] == ctx->fCurrArrayBuffer) {
|
| + ctx->fCurrArrayBuffer = 0;
|
| }
|
| - if (ids[i] == gCurrElementArrayBuffer) {
|
| - gCurrElementArrayBuffer = 0;
|
| + if (ids[i] == ctx->fCurrElementArrayBuffer) {
|
| + ctx->fCurrElementArrayBuffer = 0;
|
| }
|
|
|
| - GrBufferObj* buffer = look_up(ids[i]);
|
| - delete_buffer(buffer);
|
| + BufferObj* buffer = ctx->fBufferManager.lookUp(ids[i]);
|
| + ctx->fBufferManager.free(buffer);
|
| }
|
| }
|
|
|
| GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBufferRange(GrGLenum target, GrGLintptr offset,
|
| GrGLsizeiptr length, GrGLbitfield access) {
|
| + ThreadContext* ctx = ThreadContext::Get();
|
| GrGLuint id = 0;
|
| switch (target) {
|
| case GR_GL_ARRAY_BUFFER:
|
| - id = gCurrArrayBuffer;
|
| + id = ctx->fCurrArrayBuffer;
|
| break;
|
| case GR_GL_ELEMENT_ARRAY_BUFFER:
|
| - id = gCurrElementArrayBuffer;
|
| + id = ctx->fCurrElementArrayBuffer;
|
| break;
|
| }
|
|
|
| if (id > 0) {
|
| // We just ignore the offset and length here.
|
| - GrBufferObj* buffer = look_up(id);
|
| + BufferObj* buffer = ctx->fBufferManager.lookUp(id);
|
| SkASSERT(!buffer->mapped());
|
| buffer->setMapped(true);
|
| return buffer->dataPtr();
|
| @@ -209,18 +260,19 @@ GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBufferRange(GrGLenum target, GrGLintptr o
|
| }
|
|
|
| GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) {
|
| + ThreadContext* ctx = ThreadContext::Get();
|
| GrGLuint id = 0;
|
| switch (target) {
|
| case GR_GL_ARRAY_BUFFER:
|
| - id = gCurrArrayBuffer;
|
| + id = ctx->fCurrArrayBuffer;
|
| break;
|
| case GR_GL_ELEMENT_ARRAY_BUFFER:
|
| - id = gCurrElementArrayBuffer;
|
| + id = ctx->fCurrElementArrayBuffer;
|
| break;
|
| }
|
|
|
| if (id > 0) {
|
| - GrBufferObj* buffer = look_up(id);
|
| + BufferObj* buffer = ctx->fBufferManager.lookUp(id);
|
| SkASSERT(!buffer->mapped());
|
| buffer->setMapped(true);
|
| return buffer->dataPtr();
|
| @@ -236,17 +288,18 @@ GrGLvoid GR_GL_FUNCTION_TYPE nullGLFlushMappedBufferRange(GrGLenum target,
|
|
|
|
|
| GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) {
|
| + ThreadContext* ctx = ThreadContext::Get();
|
| GrGLuint id = 0;
|
| switch (target) {
|
| case GR_GL_ARRAY_BUFFER:
|
| - id = gCurrArrayBuffer;
|
| + id = ctx->fCurrArrayBuffer;
|
| break;
|
| case GR_GL_ELEMENT_ARRAY_BUFFER:
|
| - id = gCurrElementArrayBuffer;
|
| + id = ctx->fCurrElementArrayBuffer;
|
| break;
|
| }
|
| if (id > 0) {
|
| - GrBufferObj* buffer = look_up(id);
|
| + BufferObj* buffer = ctx->fBufferManager.lookUp(id);
|
| SkASSERT(buffer->mapped());
|
| buffer->setMapped(false);
|
| return GR_GL_TRUE;
|
| @@ -257,20 +310,21 @@ GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) {
|
| }
|
|
|
| GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {
|
| + ThreadContext* ctx = ThreadContext::Get();
|
| switch (pname) {
|
| case GR_GL_BUFFER_MAPPED: {
|
| *params = GR_GL_FALSE;
|
| GrGLuint id = 0;
|
| switch (target) {
|
| case GR_GL_ARRAY_BUFFER:
|
| - id = gCurrArrayBuffer;
|
| + id = ctx->fCurrArrayBuffer;
|
| break;
|
| case GR_GL_ELEMENT_ARRAY_BUFFER:
|
| - id = gCurrElementArrayBuffer;
|
| + id = ctx->fCurrElementArrayBuffer;
|
| break;
|
| }
|
| if (id > 0) {
|
| - GrBufferObj* buffer = look_up(id);
|
| + BufferObj* buffer = ctx->fBufferManager.lookUp(id);
|
| if (buffer->mapped()) {
|
| *params = GR_GL_TRUE;
|
| }
|
|
|