| |
| /* |
| * 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 "NullGLContext.h" |
| #include "gl/GrGLTestInterface.h" |
| #include "gl/GrGLDefines.h" |
| #include "gl/GrGLInterface.h" |
| #include "gl/GrGLTypes.h" |
| #include "SkMutex.h" |
| #include "SkTDArray.h" |
| |
| namespace { |
| |
| class BufferObj { |
| public: |
| BufferObj(GrGLuint id) : fID(id), fDataPtr(nullptr), fSize(0), fMapped(false) {} |
| ~BufferObj() { delete[] fDataPtr; } |
| |
| void allocate(GrGLsizeiptr size, const GrGLchar* dataPtr) { |
| if (fDataPtr) { |
| SkASSERT(0 != fSize); |
| delete[] fDataPtr; |
| } |
| |
| fSize = size; |
| fDataPtr = new char[size]; |
| } |
| |
| GrGLuint id() const { return fID; } |
| GrGLchar* dataPtr() { return fDataPtr; } |
| GrGLsizeiptr size() const { return fSize; } |
| |
| void setMapped(bool mapped) { fMapped = mapped; } |
| bool mapped() const { return fMapped; } |
| |
| private: |
| GrGLuint fID; |
| GrGLchar* fDataPtr; |
| GrGLsizeiptr fSize; // size in bytes |
| bool fMapped; |
| }; |
| |
| // This class maintains a sparsely populated array of buffer pointers. |
| class BufferManager { |
| public: |
| BufferManager() : fFreeListHead(kFreeListEnd) {} |
| |
| ~BufferManager() { |
| // nullptr 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)] = nullptr; |
| curr = next; |
| } |
| |
| fBuffers.deleteAll(); |
| } |
| |
| BufferObj* lookUp(GrGLuint id) { |
| BufferObj* buffer = fBuffers[id]; |
| SkASSERT(buffer && buffer->id() == id); |
| return buffer; |
| } |
| |
| BufferObj* create() { |
| GrGLuint id; |
| BufferObj* buffer; |
| |
| if (kFreeListEnd == fFreeListHead) { |
| // no free slots - create a new one |
| id = fBuffers.count(); |
| buffer = new 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 = new BufferObj(id); |
| fBuffers[id] = buffer; |
| } |
| |
| return buffer; |
| } |
| |
| void free(BufferObj* buffer) { |
| SkASSERT(fBuffers.count() > 0); |
| |
| GrGLuint id = buffer->id(); |
| delete 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; |
| }; |
| |
| /** Null interface implementation */ |
| class NullInterface : public GrGLTestInterface { |
| public: |
| NullInterface() |
| : fCurrArrayBuffer(0) |
| , fCurrElementArrayBuffer(0) |
| , fCurrPixelPackBuffer(0) |
| , fCurrPixelUnpackBuffer(0) |
| , fCurrShaderID(0) |
| , fCurrGenericID(0) |
| , fCurrUniformLocation(0) { |
| this->init(kGL_GrGLStandard); |
| } |
| |
| GrGLenum checkFramebufferStatus(GrGLenum target) override { |
| return GR_GL_FRAMEBUFFER_COMPLETE; |
| } |
| |
| GrGLvoid genBuffers(GrGLsizei n, GrGLuint* ids) override { |
| for (int i = 0; i < n; ++i) { |
| BufferObj* buffer = fBufferManager.create(); |
| ids[i] = buffer->id(); |
| } |
| } |
| |
| GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, |
| GrGLenum usage) override { |
| GrGLuint id = 0; |
| |
| switch (target) { |
| case GR_GL_ARRAY_BUFFER: |
| id = fCurrArrayBuffer; |
| break; |
| case GR_GL_ELEMENT_ARRAY_BUFFER: |
| id = fCurrElementArrayBuffer; |
| break; |
| case GR_GL_PIXEL_PACK_BUFFER: |
| id = fCurrPixelPackBuffer; |
| break; |
| case GR_GL_PIXEL_UNPACK_BUFFER: |
| id = fCurrPixelUnpackBuffer; |
| break; |
| default: |
| SkFAIL("Unexpected target to nullGLBufferData"); |
| break; |
| } |
| |
| if (id > 0) { |
| BufferObj* buffer = fBufferManager.lookUp(id); |
| buffer->allocate(size, (const GrGLchar*) data); |
| } |
| } |
| |
| GrGLuint createProgram() override { |
| return ++fCurrProgramID; |
| } |
| |
| GrGLuint createShader(GrGLenum type) override { |
| return ++fCurrShaderID; |
| } |
| |
| GrGLvoid bindBuffer(GrGLenum target, GrGLuint buffer) override { |
| switch (target) { |
| case GR_GL_ARRAY_BUFFER: |
| fCurrArrayBuffer = buffer; |
| break; |
| case GR_GL_ELEMENT_ARRAY_BUFFER: |
| fCurrElementArrayBuffer = buffer; |
| break; |
| case GR_GL_PIXEL_PACK_BUFFER: |
| fCurrPixelPackBuffer = buffer; |
| break; |
| case GR_GL_PIXEL_UNPACK_BUFFER: |
| fCurrPixelUnpackBuffer = buffer; |
| break; |
| } |
| } |
| |
| // deleting a bound buffer has the side effect of binding 0 |
| GrGLvoid deleteBuffers(GrGLsizei n, const GrGLuint* ids) override { |
| for (int i = 0; i < n; ++i) { |
| if (ids[i] == fCurrArrayBuffer) { |
| fCurrArrayBuffer = 0; |
| } |
| if (ids[i] == fCurrElementArrayBuffer) { |
| fCurrElementArrayBuffer = 0; |
| } |
| if (ids[i] == fCurrPixelPackBuffer) { |
| fCurrPixelPackBuffer = 0; |
| } |
| if (ids[i] == fCurrPixelUnpackBuffer) { |
| fCurrPixelUnpackBuffer = 0; |
| } |
| |
| BufferObj* buffer = fBufferManager.lookUp(ids[i]); |
| fBufferManager.free(buffer); |
| } |
| } |
| |
| GrGLvoid genFramebuffers(GrGLsizei n, GrGLuint *framebuffers) override { |
| this->genGenericIds(n, framebuffers); |
| } |
| |
| GrGLvoid genQueries(GrGLsizei n, GrGLuint *ids) override { this->genGenericIds(n, ids); } |
| |
| GrGLvoid genRenderbuffers(GrGLsizei n, GrGLuint *renderbuffers) override { |
| this->genGenericIds(n, renderbuffers); |
| } |
| |
| GrGLvoid genTextures(GrGLsizei n, GrGLuint *textures) override { |
| this->genGenericIds(n, textures); |
| } |
| |
| GrGLvoid genVertexArrays(GrGLsizei n, GrGLuint *arrays) override { |
| this->genGenericIds(n, arrays); |
| } |
| |
| GrGLenum getError() override { return GR_GL_NO_ERROR; } |
| |
| GrGLvoid getIntegerv(GrGLenum pname, GrGLint* params) override { |
| // TODO: remove from Ganesh the #defines for gets we don't use. |
| // We would like to minimize gets overall due to performance issues |
| switch (pname) { |
| case GR_GL_CONTEXT_PROFILE_MASK: |
| *params = GR_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT; |
| break; |
| 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_VERTEX_TEXTURE_IMAGE_UNITS: |
| case GR_GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: |
| case GR_GL_MAX_TEXTURE_IMAGE_UNITS: |
| case GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: |
| *params = 8; |
| break; |
| case GR_GL_MAX_TEXTURE_COORDS: |
| *params = 8; |
| break; |
| case GR_GL_MAX_VERTEX_UNIFORM_VECTORS: |
| *params = kDefaultMaxVertexUniformVectors; |
| break; |
| case GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS: |
| *params = kDefaultMaxFragmentUniformVectors; |
| 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 = kDefaultMaxVertexAttribs; |
| break; |
| case GR_GL_MAX_VARYING_VECTORS: |
| *params = kDefaultMaxVaryingVectors; |
| break; |
| case GR_GL_NUM_EXTENSIONS: { |
| GrGLint i = 0; |
| while (kExtensions[i++]); |
| *params = i; |
| break; |
| } |
| default: |
| SkFAIL("Unexpected pname to GetIntegerv"); |
| } |
| } |
| |
| GrGLvoid getProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) override { |
| this->getShaderOrProgramiv(program, pname, params); |
| } |
| |
| GrGLvoid getProgramInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, |
| char* infolog) override { |
| this->getInfoLog(program, bufsize, length, infolog); |
| } |
| |
| GrGLvoid getMultisamplefv(GrGLenum pname, GrGLuint index, GrGLfloat* val) override { |
| val[0] = val[1] = 0.5f; |
| } |
| |
| GrGLvoid getQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) override { |
| switch (pname) { |
| case GR_GL_CURRENT_QUERY: |
| *params = 0; |
| break; |
| case GR_GL_QUERY_COUNTER_BITS: |
| *params = 32; |
| break; |
| default: |
| SkFAIL("Unexpected pname passed GetQueryiv."); |
| } |
| } |
| |
| GrGLvoid getQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) override { |
| this->queryResult(id, pname, params); |
| } |
| |
| GrGLvoid getQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) override { |
| this->queryResult(id, pname, params); |
| } |
| |
| GrGLvoid getQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) override { |
| this->queryResult(id, pname, params); |
| } |
| |
| GrGLvoid getQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) override { |
| this->queryResult(id, pname, params); |
| } |
| |
| GrGLvoid getShaderiv(GrGLuint shader, GrGLenum pname, GrGLint* params) override { |
| this->getShaderOrProgramiv(shader, pname, params); |
| } |
| |
| GrGLvoid getShaderInfoLog(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, |
| char* infolog) override { |
| this->getInfoLog(shader, bufsize, length, infolog); |
| } |
| |
| const GrGLubyte* getString(GrGLenum name) override { |
| switch (name) { |
| case GR_GL_EXTENSIONS: |
| return CombinedExtensionString(); |
| 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"; |
| case GR_GL_VENDOR: |
| return (const GrGLubyte*)"Null Vendor"; |
| case GR_GL_RENDERER: |
| return (const GrGLubyte*)"The Null (Non-)Renderer"; |
| default: |
| SkFAIL("Unexpected name passed to GetString"); |
| return nullptr; |
| } |
| } |
| |
| const GrGLubyte* getStringi(GrGLenum name, GrGLuint i) override { |
| switch (name) { |
| case GR_GL_EXTENSIONS: { |
| GrGLint count; |
| this->getIntegerv(GR_GL_NUM_EXTENSIONS, &count); |
| if ((GrGLint)i <= count) { |
| return (const GrGLubyte*) kExtensions[i]; |
| } else { |
| return nullptr; |
| } |
| } |
| default: |
| SkFAIL("Unexpected name passed to GetStringi"); |
| return nullptr; |
| } |
| } |
| |
| GrGLint getUniformLocation(GrGLuint program, const char* name) override { |
| return ++fCurrUniformLocation; |
| } |
| |
| GrGLvoid* mapBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length, |
| GrGLbitfield access) override { |
| GrGLuint id = 0; |
| switch (target) { |
| case GR_GL_ARRAY_BUFFER: |
| id = fCurrArrayBuffer; |
| break; |
| case GR_GL_ELEMENT_ARRAY_BUFFER: |
| id = fCurrElementArrayBuffer; |
| break; |
| case GR_GL_PIXEL_PACK_BUFFER: |
| id = fCurrPixelPackBuffer; |
| break; |
| case GR_GL_PIXEL_UNPACK_BUFFER: |
| id = fCurrPixelUnpackBuffer; |
| break; |
| } |
| |
| if (id > 0) { |
| // We just ignore the offset and length here. |
| BufferObj* buffer = fBufferManager.lookUp(id); |
| SkASSERT(!buffer->mapped()); |
| buffer->setMapped(true); |
| return buffer->dataPtr(); |
| } |
| return nullptr; |
| } |
| |
| GrGLvoid* mapBuffer(GrGLenum target, GrGLenum access) override { |
| GrGLuint id = 0; |
| switch (target) { |
| case GR_GL_ARRAY_BUFFER: |
| id = fCurrArrayBuffer; |
| break; |
| case GR_GL_ELEMENT_ARRAY_BUFFER: |
| id = fCurrElementArrayBuffer; |
| break; |
| case GR_GL_PIXEL_PACK_BUFFER: |
| id = fCurrPixelPackBuffer; |
| break; |
| case GR_GL_PIXEL_UNPACK_BUFFER: |
| id = fCurrPixelUnpackBuffer; |
| break; |
| } |
| |
| if (id > 0) { |
| BufferObj* buffer = fBufferManager.lookUp(id); |
| SkASSERT(!buffer->mapped()); |
| buffer->setMapped(true); |
| return buffer->dataPtr(); |
| } |
| |
| SkASSERT(false); |
| return nullptr; // no buffer bound to target |
| } |
| |
| GrGLboolean unmapBuffer(GrGLenum target) override { |
| GrGLuint id = 0; |
| switch (target) { |
| case GR_GL_ARRAY_BUFFER: |
| id = fCurrArrayBuffer; |
| break; |
| case GR_GL_ELEMENT_ARRAY_BUFFER: |
| id = fCurrElementArrayBuffer; |
| break; |
| case GR_GL_PIXEL_PACK_BUFFER: |
| id = fCurrPixelPackBuffer; |
| break; |
| case GR_GL_PIXEL_UNPACK_BUFFER: |
| id = fCurrPixelUnpackBuffer; |
| break; |
| } |
| if (id > 0) { |
| BufferObj* buffer = fBufferManager.lookUp(id); |
| SkASSERT(buffer->mapped()); |
| buffer->setMapped(false); |
| return GR_GL_TRUE; |
| } |
| |
| GrAlwaysAssert(false); |
| return GR_GL_FALSE; // GR_GL_INVALID_OPERATION; |
| } |
| |
| GrGLvoid getBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) override { |
| switch (pname) { |
| case GR_GL_BUFFER_MAPPED: { |
| *params = GR_GL_FALSE; |
| GrGLuint id = 0; |
| switch (target) { |
| case GR_GL_ARRAY_BUFFER: |
| id = fCurrArrayBuffer; |
| break; |
| case GR_GL_ELEMENT_ARRAY_BUFFER: |
| id = fCurrElementArrayBuffer; |
| break; |
| case GR_GL_PIXEL_PACK_BUFFER: |
| id = fCurrPixelPackBuffer; |
| break; |
| case GR_GL_PIXEL_UNPACK_BUFFER: |
| id = fCurrPixelUnpackBuffer; |
| break; |
| } |
| if (id > 0) { |
| BufferObj* buffer = fBufferManager.lookUp(id); |
| if (buffer->mapped()) { |
| *params = GR_GL_TRUE; |
| } |
| } |
| break; } |
| default: |
| SkFAIL("Unexpected pname to GetBufferParamateriv"); |
| break; |
| } |
| }; |
| |
| private: |
| BufferManager fBufferManager; |
| GrGLuint fCurrArrayBuffer; |
| GrGLuint fCurrElementArrayBuffer; |
| GrGLuint fCurrPixelPackBuffer; |
| GrGLuint fCurrPixelUnpackBuffer; |
| GrGLuint fCurrProgramID; |
| GrGLuint fCurrShaderID; |
| GrGLuint fCurrGenericID; |
| GrGLuint fCurrUniformLocation; |
| |
| // the OpenGLES 2.0 spec says this must be >= 128 |
| static const GrGLint kDefaultMaxVertexUniformVectors = 128; |
| |
| // the OpenGLES 2.0 spec says this must be >=16 |
| static const GrGLint kDefaultMaxFragmentUniformVectors = 16; |
| |
| // the OpenGLES 2.0 spec says this must be >= 8 |
| static const GrGLint kDefaultMaxVertexAttribs = 8; |
| |
| // the OpenGLES 2.0 spec says this must be >= 8 |
| static const GrGLint kDefaultMaxVaryingVectors = 8; |
| |
| static const char* kExtensions[]; |
| |
| static const GrGLubyte* CombinedExtensionString() { |
| static SkString gExtString; |
| static SkMutex gMutex; |
| gMutex.acquire(); |
| if (0 == gExtString.size()) { |
| int i = 0; |
| while (kExtensions[i]) { |
| if (i > 0) { |
| gExtString.append(" "); |
| } |
| gExtString.append(kExtensions[i]); |
| ++i; |
| } |
| } |
| gMutex.release(); |
| return (const GrGLubyte*) gExtString.c_str(); |
| } |
| |
| GrGLvoid genGenericIds(GrGLsizei n, GrGLuint* ids) { |
| for (int i = 0; i < n; ++i) { |
| ids[i] = ++fCurrGenericID; |
| } |
| } |
| |
| GrGLvoid getInfoLog(GrGLuint object, GrGLsizei bufsize, GrGLsizei* length, |
| char* infolog) { |
| if (length) { |
| *length = 0; |
| } |
| if (bufsize > 0) { |
| *infolog = 0; |
| } |
| } |
| |
| GrGLvoid getShaderOrProgramiv(GrGLuint object, 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: |
| SkFAIL("Unexpected pname to GetProgramiv"); |
| break; |
| } |
| } |
| |
| template <typename T> |
| void queryResult(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: |
| SkFAIL("Unexpected pname passed to GetQueryObject."); |
| break; |
| } |
| } |
| |
| typedef GrGLTestInterface INHERITED; |
| }; |
| |
| const char* NullInterface::kExtensions[] = { |
| "GL_ARB_framebuffer_object", |
| "GL_ARB_blend_func_extended", |
| "GL_ARB_timer_query", |
| "GL_ARB_draw_buffers", |
| "GL_ARB_occlusion_query", |
| "GL_EXT_stencil_wrap", |
| nullptr, // signifies the end of the array. |
| }; |
| |
| class NullGLContext : public sk_gpu_test::GLContext { |
| public: |
| NullGLContext() { this->init(new NullInterface); } |
| ~NullGLContext() override { this->teardown(); } |
| |
| private: |
| void onPlatformMakeCurrent() const override {}; |
| void onPlatformSwapBuffers() const override {} |
| GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return nullptr; } |
| }; |
| } // anonymous namespace |
| |
| namespace sk_gpu_test { |
| GLContext* CreateNullGLContext() { |
| GLContext* ctx = new NullGLContext(); |
| if (ctx->isValid()) { |
| return ctx; |
| } |
| delete ctx; |
| return nullptr; |
| } |
| } // namespace sk_gpu_test |
| |