aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorGravatar bsalomon <bsalomon@google.com>2016-03-30 08:35:09 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-03-30 08:35:09 -0700
commitfe3456cb006110d045b26ff3f8681b893a757b58 (patch)
tree8d07fbc43230b807d0cf7174c79d5757c397b07f /tools
parentb4a7f144b41dbe8341d26abb71d87b13d072a5cf (diff)
Move SkGLContext and some GrGLInterface implementations to skgputest module
BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1815823002 CQ_EXTRA_TRYBOTS=client.skia.compile:Build-Ubuntu-GCC-x86_64-Release-CMake-Trybot,Build-Mac-Clang-x86_64-Release-CMake-Trybot Review URL: https://codereview.chromium.org/1815823002
Diffstat (limited to 'tools')
-rw-r--r--tools/VisualBench/VisualBench.h2
-rw-r--r--tools/flags/SkCommonFlagsConfig.cpp4
-rw-r--r--tools/flags/SkCommonFlagsConfig.h2
-rwxr-xr-xtools/gpu/GrContextFactory.cpp157
-rw-r--r--tools/gpu/GrContextFactory.h145
-rw-r--r--tools/gpu/GrTest.cpp419
-rw-r--r--tools/gpu/GrTest.h42
-rw-r--r--tools/gpu/gl/GLContext.cpp188
-rw-r--r--tools/gpu/gl/GLContext.h141
-rw-r--r--tools/gpu/gl/angle/GLContext_angle.cpp320
-rw-r--r--tools/gpu/gl/angle/GLContext_angle.h30
-rw-r--r--tools/gpu/gl/command_buffer/GLContext_command_buffer.cpp345
-rw-r--r--tools/gpu/gl/command_buffer/GLContext_command_buffer.h68
-rw-r--r--tools/gpu/gl/debug/DebugGLContext.cpp1256
-rw-r--r--tools/gpu/gl/debug/DebugGLContext.h17
-rw-r--r--tools/gpu/gl/debug/GrBufferObj.cpp31
-rw-r--r--tools/gpu/gl/debug/GrBufferObj.h76
-rw-r--r--tools/gpu/gl/debug/GrFBBindableObj.h88
-rw-r--r--tools/gpu/gl/debug/GrFakeRefObj.h86
-rw-r--r--tools/gpu/gl/debug/GrFrameBufferObj.cpp67
-rw-r--r--tools/gpu/gl/debug/GrFrameBufferObj.h68
-rw-r--r--tools/gpu/gl/debug/GrProgramObj.cpp27
-rw-r--r--tools/gpu/gl/debug/GrProgramObj.h43
-rw-r--r--tools/gpu/gl/debug/GrRenderBufferObj.h40
-rw-r--r--tools/gpu/gl/debug/GrShaderObj.cpp14
-rw-r--r--tools/gpu/gl/debug/GrShaderObj.h36
-rw-r--r--tools/gpu/gl/debug/GrTextureObj.cpp14
-rw-r--r--tools/gpu/gl/debug/GrTextureObj.h57
-rw-r--r--tools/gpu/gl/debug/GrTextureUnitObj.cpp31
-rw-r--r--tools/gpu/gl/debug/GrTextureUnitObj.h44
-rw-r--r--tools/gpu/gl/debug/GrVertexArrayObj.h21
-rw-r--r--tools/gpu/gl/egl/CreatePlatformGLContext_egl.cpp337
-rw-r--r--tools/gpu/gl/glx/CreatePlatformGLContext_glx.cpp346
-rw-r--r--tools/gpu/gl/iOS/CreatePlatformGLContext_iOS.mm108
-rw-r--r--tools/gpu/gl/mac/CreatePlatformGLContext_mac.cpp128
-rw-r--r--tools/gpu/gl/mesa/GLContext_mesa.cpp151
-rw-r--r--tools/gpu/gl/mesa/GLContext_mesa.h17
-rw-r--r--tools/gpu/gl/mesa/osmesa_wrapper.h16
-rw-r--r--tools/gpu/gl/null/NullGLContext.cpp630
-rw-r--r--tools/gpu/gl/null/NullGLContext.h17
-rw-r--r--tools/gpu/gl/win/CreatePlatformGLContext_win.cpp204
-rw-r--r--tools/kilobench/kilobench.cpp21
-rw-r--r--tools/skiaserve/Request.cpp6
-rw-r--r--tools/skiaserve/Request.h4
44 files changed, 5850 insertions, 14 deletions
diff --git a/tools/VisualBench/VisualBench.h b/tools/VisualBench/VisualBench.h
index 1920a52b2b..e9fbdfb8ae 100644
--- a/tools/VisualBench/VisualBench.h
+++ b/tools/VisualBench/VisualBench.h
@@ -16,7 +16,7 @@
#include "SkSurface.h"
#include "VisualFlags.h"
#include "VisualModule.h"
-#include "gl/SkGLContext.h"
+#include "gl/GLContext.h"
class GrContext;
struct GrGLInterface;
diff --git a/tools/flags/SkCommonFlagsConfig.cpp b/tools/flags/SkCommonFlagsConfig.cpp
index 9e0fd60b39..1ee3130bd7 100644
--- a/tools/flags/SkCommonFlagsConfig.cpp
+++ b/tools/flags/SkCommonFlagsConfig.cpp
@@ -9,6 +9,10 @@
#include <stdlib.h>
+#if SK_SUPPORT_GPU
+using sk_gpu_test::GrContextFactory;
+#endif
+
static const char defaultConfigs[] =
"565 8888 gpu nonrendering"
#if SK_ANGLE
diff --git a/tools/flags/SkCommonFlagsConfig.h b/tools/flags/SkCommonFlagsConfig.h
index 39f1f9788b..abf5946847 100644
--- a/tools/flags/SkCommonFlagsConfig.h
+++ b/tools/flags/SkCommonFlagsConfig.h
@@ -50,7 +50,7 @@ class SkCommandLineConfig {
// * backends that represent a shorthand of above (such as "msaa16" representing "gpu(samples=16)")
class SkCommandLineConfigGpu : public SkCommandLineConfig {
public:
- typedef GrContextFactory::GLContextType ContextType;
+ typedef sk_gpu_test::GrContextFactory::GLContextType ContextType;
SkCommandLineConfigGpu(const SkString& tag, const SkTArray<SkString>& viaParts,
ContextType contextType, bool useNVPR, bool useDIText, int samples,
SkColorType colorType, SkColorProfileType profileType);
diff --git a/tools/gpu/GrContextFactory.cpp b/tools/gpu/GrContextFactory.cpp
new file mode 100755
index 0000000000..0cf0cc781a
--- /dev/null
+++ b/tools/gpu/GrContextFactory.cpp
@@ -0,0 +1,157 @@
+
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrContextFactory.h"
+#include "gl/GLContext.h"
+
+#if SK_ANGLE
+ #include "gl/angle/GLContext_angle.h"
+#endif
+#if SK_COMMAND_BUFFER
+ #include "gl/command_buffer/GLContext_command_buffer.h"
+#endif
+#include "gl/debug/DebugGLContext.h"
+#if SK_MESA
+ #include "gl/mesa/GLContext_mesa.h"
+#endif
+#include "gl/null/NullGLContext.h"
+#include "gl/GrGLGpu.h"
+#include "GrCaps.h"
+
+namespace sk_gpu_test {
+GrContextFactory::GrContextFactory() { }
+
+GrContextFactory::GrContextFactory(const GrContextOptions& opts)
+ : fGlobalOptions(opts) {
+}
+
+GrContextFactory::~GrContextFactory() {
+ this->destroyContexts();
+}
+
+void GrContextFactory::destroyContexts() {
+ for (Context& context : fContexts) {
+ if (context.fGLContext) {
+ context.fGLContext->makeCurrent();
+ }
+ if (!context.fGrContext->unique()) {
+ context.fGrContext->abandonContext();
+ }
+ context.fGrContext->unref();
+ delete(context.fGLContext);
+ }
+ fContexts.reset();
+}
+
+void GrContextFactory::abandonContexts() {
+ for (Context& context : fContexts) {
+ if (context.fGLContext) {
+ context.fGLContext->makeCurrent();
+ context.fGLContext->testAbandon();
+ delete(context.fGLContext);
+ context.fGLContext = nullptr;
+ }
+ context.fGrContext->abandonContext();
+ }
+}
+
+GrContextFactory::ContextInfo GrContextFactory::getContextInfo(GLContextType type,
+ GLContextOptions options) {
+ for (int i = 0; i < fContexts.count(); ++i) {
+ Context& context = fContexts[i];
+ if (!context.fGLContext) {
+ continue;
+ }
+ if (context.fType == type &&
+ context.fOptions == options) {
+ context.fGLContext->makeCurrent();
+ return ContextInfo(context.fGrContext, context.fGLContext);
+ }
+ }
+ SkAutoTDelete<GLContext> glCtx;
+ SkAutoTUnref<GrContext> grCtx;
+ switch (type) {
+ case kNative_GLContextType:
+ glCtx.reset(CreatePlatformGLContext(kNone_GrGLStandard));
+ break;
+ case kGL_GLContextType:
+ glCtx.reset(CreatePlatformGLContext(kGL_GrGLStandard));
+ break;
+ case kGLES_GLContextType:
+ glCtx.reset(CreatePlatformGLContext(kGLES_GrGLStandard));
+ break;
+#if SK_ANGLE
+#ifdef SK_BUILD_FOR_WIN
+ case kANGLE_GLContextType:
+ glCtx.reset(CreateANGLEDirect3DGLContext());
+ break;
+#endif
+ case kANGLE_GL_GLContextType:
+ glCtx.reset(CreateANGLEOpenGLGLContext());
+ break;
+#endif
+#if SK_COMMAND_BUFFER
+ case kCommandBuffer_GLContextType:
+ glCtx.reset(CommandBufferGLContext::Create());
+ break;
+#endif
+#if SK_MESA
+ case kMESA_GLContextType:
+ glCtx.reset(CreateMesaGLContext());
+ break;
+#endif
+ case kNull_GLContextType:
+ glCtx.reset(CreateNullGLContext());
+ break;
+ case kDebug_GLContextType:
+ glCtx.reset(CreateDebugGLContext());
+ break;
+ }
+ if (nullptr == glCtx.get()) {
+ return ContextInfo();
+ }
+
+ SkASSERT(glCtx->isValid());
+
+ // Block NVPR from non-NVPR types.
+ SkAutoTUnref<const GrGLInterface> glInterface(SkRef(glCtx->gl()));
+ if (!(kEnableNVPR_GLContextOptions & options)) {
+ glInterface.reset(GrGLInterfaceRemoveNVPR(glInterface));
+ if (!glInterface) {
+ return ContextInfo();
+ }
+ }
+
+ glCtx->makeCurrent();
+ GrBackendContext p3dctx = reinterpret_cast<GrBackendContext>(glInterface.get());
+#ifdef SK_VULKAN
+ if (kEnableNVPR_GLContextOptions & options) {
+ return ContextInfo();
+ } else {
+ grCtx.reset(GrContext::Create(kVulkan_GrBackend, p3dctx, fGlobalOptions));
+ }
+#else
+ grCtx.reset(GrContext::Create(kOpenGL_GrBackend, p3dctx, fGlobalOptions));
+#endif
+ if (!grCtx.get()) {
+ return ContextInfo();
+ }
+ if (kEnableNVPR_GLContextOptions & options) {
+ if (!grCtx->caps()->shaderCaps()->pathRenderingSupport()) {
+ return ContextInfo();
+ }
+ }
+
+ Context& context = fContexts.push_back();
+ context.fGLContext = glCtx.release();
+ context.fGrContext = SkRef(grCtx.get());
+ context.fType = type;
+ context.fOptions = options;
+ return ContextInfo(context.fGrContext, context.fGLContext);
+}
+} // namespace sk_gpu_test
diff --git a/tools/gpu/GrContextFactory.h b/tools/gpu/GrContextFactory.h
new file mode 100644
index 0000000000..7e5c10e039
--- /dev/null
+++ b/tools/gpu/GrContextFactory.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrContextFactory_DEFINED
+#define GrContextFactory_DEFINED
+
+#include "GrContext.h"
+#include "GrContextOptions.h"
+
+#include "gl/GLContext.h"
+#include "SkTArray.h"
+
+namespace sk_gpu_test {
+/**
+ * This is a simple class that is useful in test apps that use different
+ * GrContexts backed by different types of GL contexts. It manages creating the
+ * GL context and a GrContext that uses it. The GL/Gr contexts persist until the
+ * factory is destroyed (though the caller can always grab a ref on the returned
+ * Gr and GL contexts to make them outlive the factory).
+ */
+class GrContextFactory : SkNoncopyable {
+public:
+ enum GLContextType {
+ kNative_GLContextType, //! OpenGL or OpenGL ES context.
+ kGL_GLContextType, //! OpenGL context.
+ kGLES_GLContextType, //! OpenGL ES context.
+#if SK_ANGLE
+#ifdef SK_BUILD_FOR_WIN
+ kANGLE_GLContextType, //! ANGLE on DirectX OpenGL ES context.
+#endif
+ kANGLE_GL_GLContextType, //! ANGLE on OpenGL OpenGL ES context.
+#endif
+#if SK_COMMAND_BUFFER
+ kCommandBuffer_GLContextType, //! Chromium command buffer OpenGL ES context.
+#endif
+#if SK_MESA
+ kMESA_GLContextType, //! MESA OpenGL context
+#endif
+ kNull_GLContextType, //! Non-rendering OpenGL mock context.
+ kDebug_GLContextType, //! Non-rendering, state verifying OpenGL context.
+ kLastGLContextType = kDebug_GLContextType
+ };
+
+ static const int kGLContextTypeCnt = kLastGLContextType + 1;
+
+ /**
+ * Options for GL context creation. For historical and testing reasons the options will default
+ * to not using GL_NV_path_rendering extension even when the driver supports it.
+ */
+ enum GLContextOptions {
+ kNone_GLContextOptions = 0,
+ kEnableNVPR_GLContextOptions = 0x1,
+ };
+
+ static bool IsRenderingGLContext(GLContextType type) {
+ switch (type) {
+ case kNull_GLContextType:
+ case kDebug_GLContextType:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ static const char* GLContextTypeName(GLContextType type) {
+ switch (type) {
+ case kNative_GLContextType:
+ return "native";
+ case kGL_GLContextType:
+ return "gl";
+ case kGLES_GLContextType:
+ return "gles";
+#if SK_ANGLE
+#ifdef SK_BUILD_FOR_WIN
+ case kANGLE_GLContextType:
+ return "angle";
+#endif
+ case kANGLE_GL_GLContextType:
+ return "angle-gl";
+#endif
+#if SK_COMMAND_BUFFER
+ case kCommandBuffer_GLContextType:
+ return "commandbuffer";
+#endif
+#if SK_MESA
+ case kMESA_GLContextType:
+ return "mesa";
+#endif
+ case kNull_GLContextType:
+ return "null";
+ case kDebug_GLContextType:
+ return "debug";
+ default:
+ SkFAIL("Unknown GL Context type.");
+ }
+ }
+
+ explicit GrContextFactory(const GrContextOptions& opts);
+ GrContextFactory();
+
+ ~GrContextFactory();
+
+ void destroyContexts();
+ void abandonContexts();
+
+ struct ContextInfo {
+ ContextInfo()
+ : fGrContext(nullptr), fGLContext(nullptr) { }
+ ContextInfo(GrContext* grContext, GLContext* glContext)
+ : fGrContext(grContext), fGLContext(glContext) { }
+ GrContext* fGrContext;
+ GLContext* fGLContext; //! Valid until the factory destroys it via abandonContexts() or
+ //! destroyContexts().
+ };
+
+ /**
+ * Get a context initialized with a type of GL context. It also makes the GL context current.
+ */
+ ContextInfo getContextInfo(GLContextType type,
+ GLContextOptions options = kNone_GLContextOptions);
+ /**
+ * Get a GrContext initialized with a type of GL context. It also makes the GL context current.
+ */
+ GrContext* get(GLContextType type,
+ GLContextOptions options = kNone_GLContextOptions) {
+ return this->getContextInfo(type, options).fGrContext;
+ }
+ const GrContextOptions& getGlobalOptions() const { return fGlobalOptions; }
+
+private:
+ struct Context {
+ GLContextType fType;
+ GLContextOptions fOptions;
+ GLContext* fGLContext;
+ GrContext* fGrContext;
+ };
+ SkTArray<Context, true> fContexts;
+ const GrContextOptions fGlobalOptions;
+};
+} // namespace sk_gpu_test
+#endif
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
new file mode 100644
index 0000000000..2b6463d853
--- /dev/null
+++ b/tools/gpu/GrTest.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrTest.h"
+
+#include "GrBatchAtlas.h"
+#include "GrContextOptions.h"
+#include "GrDrawContextPriv.h"
+#include "GrDrawingManager.h"
+#include "GrGpuResourceCacheAccess.h"
+#include "GrResourceCache.h"
+
+#include "SkGpuDevice.h"
+#include "SkGrPriv.h"
+#include "SkString.h"
+
+#include "text/GrBatchFontCache.h"
+#include "text/GrTextBlobCache.h"
+
+namespace GrTest {
+void SetupAlwaysEvictAtlas(GrContext* context) {
+ // These sizes were selected because they allow each atlas to hold a single plot and will thus
+ // stress the atlas
+ int dim = GrBatchAtlas::kGlyphMaxDim;
+ GrBatchAtlasConfig configs[3];
+ configs[kA8_GrMaskFormat].fWidth = dim;
+ configs[kA8_GrMaskFormat].fHeight = dim;
+ configs[kA8_GrMaskFormat].fLog2Width = SkNextLog2(dim);
+ configs[kA8_GrMaskFormat].fLog2Height = SkNextLog2(dim);
+ configs[kA8_GrMaskFormat].fPlotWidth = dim;
+ configs[kA8_GrMaskFormat].fPlotHeight = dim;
+
+ configs[kA565_GrMaskFormat].fWidth = dim;
+ configs[kA565_GrMaskFormat].fHeight = dim;
+ configs[kA565_GrMaskFormat].fLog2Width = SkNextLog2(dim);
+ configs[kA565_GrMaskFormat].fLog2Height = SkNextLog2(dim);
+ configs[kA565_GrMaskFormat].fPlotWidth = dim;
+ configs[kA565_GrMaskFormat].fPlotHeight = dim;
+
+ configs[kARGB_GrMaskFormat].fWidth = dim;
+ configs[kARGB_GrMaskFormat].fHeight = dim;
+ configs[kARGB_GrMaskFormat].fLog2Width = SkNextLog2(dim);
+ configs[kARGB_GrMaskFormat].fLog2Height = SkNextLog2(dim);
+ configs[kARGB_GrMaskFormat].fPlotWidth = dim;
+ configs[kARGB_GrMaskFormat].fPlotHeight = dim;
+
+ context->setTextContextAtlasSizes_ForTesting(configs);
+}
+};
+
+void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target, GrRenderTarget* rt) {
+ SkASSERT(!fContext);
+
+ fContext.reset(SkRef(ctx));
+ fDrawTarget.reset(SkRef(target));
+ fRenderTarget.reset(SkRef(rt));
+}
+
+void GrContext::getTestTarget(GrTestTarget* tar, GrRenderTarget* rt) {
+ this->flush();
+ // We could create a proxy GrDrawTarget that passes through to fGpu until ~GrTextTarget() and
+ // then disconnects. This would help prevent test writers from mixing using the returned
+ // GrDrawTarget and regular drawing. We could also assert or fail in GrContext drawing methods
+ // until ~GrTestTarget().
+ if (!rt) {
+ GrSurfaceDesc desc;
+ desc.fFlags = kRenderTarget_GrSurfaceFlag;
+ desc.fWidth = 32;
+ desc.fHeight = 32;
+ desc.fConfig = kRGBA_8888_GrPixelConfig;
+ desc.fSampleCnt = 0;
+
+ SkAutoTUnref<GrTexture> texture(this->textureProvider()->createTexture(
+ desc, SkBudgeted::kNo, nullptr, 0));
+ if (nullptr == texture) {
+ return;
+ }
+ SkASSERT(nullptr != texture->asRenderTarget());
+ rt = texture->asRenderTarget();
+ }
+
+ SkAutoTUnref<GrDrawTarget> dt(fDrawingManager->newDrawTarget(rt));
+ tar->init(this, dt, rt);
+}
+
+void GrContext::setTextBlobCacheLimit_ForTesting(size_t bytes) {
+ fTextBlobCache->setBudget(bytes);
+}
+
+void GrContext::setTextContextAtlasSizes_ForTesting(const GrBatchAtlasConfig* configs) {
+ fBatchFontCache->setAtlasSizes_ForTesting(configs);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrContext::purgeAllUnlockedResources() {
+ fResourceCache->purgeAllUnlocked();
+}
+
+void GrContext::resetGpuStats() const {
+#if GR_GPU_STATS
+ fGpu->stats()->reset();
+#endif
+}
+
+void GrContext::dumpCacheStats(SkString* out) const {
+#if GR_CACHE_STATS
+ fResourceCache->dumpStats(out);
+#endif
+}
+
+void GrContext::dumpCacheStatsKeyValuePairs(SkTArray<SkString>* keys,
+ SkTArray<double>* values) const {
+#if GR_CACHE_STATS
+ fResourceCache->dumpStatsKeyValuePairs(keys, values);
+#endif
+}
+
+void GrContext::printCacheStats() const {
+ SkString out;
+ this->dumpCacheStats(&out);
+ SkDebugf("%s", out.c_str());
+}
+
+void GrContext::dumpGpuStats(SkString* out) const {
+#if GR_GPU_STATS
+ return fGpu->stats()->dump(out);
+#endif
+}
+
+void GrContext::dumpGpuStatsKeyValuePairs(SkTArray<SkString>* keys,
+ SkTArray<double>* values) const {
+#if GR_GPU_STATS
+ return fGpu->stats()->dumpKeyValuePairs(keys, values);
+#endif
+}
+
+void GrContext::printGpuStats() const {
+ SkString out;
+ this->dumpGpuStats(&out);
+ SkDebugf("%s", out.c_str());
+}
+
+GrTexture* GrContext::getFontAtlasTexture(GrMaskFormat format) {
+ GrBatchFontCache* cache = this->getBatchFontCache();
+
+ return cache->getTexture(format);
+}
+
+void SkGpuDevice::drawTexture(GrTexture* tex, const SkRect& dst, const SkPaint& paint) {
+ GrPaint grPaint;
+ SkMatrix mat;
+ mat.reset();
+ if (!SkPaintToGrPaint(this->context(), paint, mat, &grPaint)) {
+ return;
+ }
+ SkMatrix textureMat;
+ textureMat.reset();
+ textureMat[SkMatrix::kMScaleX] = 1.0f/dst.width();
+ textureMat[SkMatrix::kMScaleY] = 1.0f/dst.height();
+ textureMat[SkMatrix::kMTransX] = -dst.fLeft/dst.width();
+ textureMat[SkMatrix::kMTransY] = -dst.fTop/dst.height();
+
+ grPaint.addColorTextureProcessor(tex, textureMat);
+
+ GrClip clip;
+ fDrawContext->drawRect(clip, grPaint, mat, dst);
+}
+
+
+#if GR_GPU_STATS
+void GrGpu::Stats::dump(SkString* out) {
+ out->appendf("Render Target Binds: %d\n", fRenderTargetBinds);
+ out->appendf("Shader Compilations: %d\n", fShaderCompilations);
+ out->appendf("Textures Created: %d\n", fTextureCreates);
+ out->appendf("Texture Uploads: %d\n", fTextureUploads);
+ out->appendf("Transfers to Texture: %d\n", fTransfersToTexture);
+ out->appendf("Stencil Buffer Creates: %d\n", fStencilAttachmentCreates);
+ out->appendf("Number of draws: %d\n", fNumDraws);
+}
+
+void GrGpu::Stats::dumpKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* values) {
+ keys->push_back(SkString("render_target_binds")); values->push_back(fRenderTargetBinds);
+ keys->push_back(SkString("shader_compilations")); values->push_back(fShaderCompilations);
+ keys->push_back(SkString("texture_uploads")); values->push_back(fTextureUploads);
+ keys->push_back(SkString("number_of_draws")); values->push_back(fNumDraws);
+ keys->push_back(SkString("number_of_failed_draws")); values->push_back(fNumFailedDraws);
+}
+
+#endif
+
+#if GR_CACHE_STATS
+void GrResourceCache::getStats(Stats* stats) const {
+ stats->reset();
+
+ stats->fTotal = this->getResourceCount();
+ stats->fNumNonPurgeable = fNonpurgeableResources.count();
+ stats->fNumPurgeable = fPurgeableQueue.count();
+
+ for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
+ stats->update(fNonpurgeableResources[i]);
+ }
+ for (int i = 0; i < fPurgeableQueue.count(); ++i) {
+ stats->update(fPurgeableQueue.at(i));
+ }
+}
+
+void GrResourceCache::dumpStats(SkString* out) const {
+ this->validate();
+
+ Stats stats;
+
+ this->getStats(&stats);
+
+ float countUtilization = (100.f * fBudgetedCount) / fMaxCount;
+ float byteUtilization = (100.f * fBudgetedBytes) / fMaxBytes;
+
+ out->appendf("Budget: %d items %d bytes\n", fMaxCount, (int)fMaxBytes);
+ out->appendf("\t\tEntry Count: current %d"
+ " (%d budgeted, %d external(%d borrowed, %d adopted), %d locked, %d scratch %.2g%% full), high %d\n",
+ stats.fTotal, fBudgetedCount, stats.fExternal, stats.fBorrowed,
+ stats.fAdopted, stats.fNumNonPurgeable, stats.fScratch, countUtilization,
+ fHighWaterCount);
+ out->appendf("\t\tEntry Bytes: current %d (budgeted %d, %.2g%% full, %d unbudgeted) high %d\n",
+ SkToInt(fBytes), SkToInt(fBudgetedBytes), byteUtilization,
+ SkToInt(stats.fUnbudgetedSize), SkToInt(fHighWaterBytes));
+}
+
+void GrResourceCache::dumpStatsKeyValuePairs(SkTArray<SkString>* keys,
+ SkTArray<double>* values) const {
+ this->validate();
+
+ Stats stats;
+ this->getStats(&stats);
+
+ keys->push_back(SkString("gpu_cache_purgable_entries")); values->push_back(stats.fNumPurgeable);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrResourceCache::changeTimestamp(uint32_t newTimestamp) { fTimestamp = newTimestamp; }
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSERT_SINGLE_OWNER \
+ SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fDrawContext->fSingleOwner);)
+#define RETURN_IF_ABANDONED if (fDrawContext->fDrawingManager->abandoned()) { return; }
+
+void GrDrawContextPriv::testingOnly_drawBatch(const GrPipelineBuilder& pipelineBuilder,
+ GrDrawBatch* batch) {
+ ASSERT_SINGLE_OWNER
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(fDrawContext->validate();)
+ GR_AUDIT_TRAIL_AUTO_FRAME(fDrawContext->fAuditTrail, "GrDrawContext::testingOnly_drawBatch");
+
+ fDrawContext->getDrawTarget()->drawBatch(pipelineBuilder, batch);
+}
+
+#undef ASSERT_SINGLE_OWNER
+#undef RETURN_IF_ABANDONED
+
+///////////////////////////////////////////////////////////////////////////////
+// Code for the mock context. It's built on a mock GrGpu class that does nothing.
+////
+
+#include "GrGpu.h"
+
+class GrPipeline;
+
+class MockCaps : public GrCaps {
+public:
+ explicit MockCaps(const GrContextOptions& options) : INHERITED(options) {}
+ bool isConfigTexturable(GrPixelConfig config) const override { return false; }
+ bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const override { return false; }
+private:
+ typedef GrCaps INHERITED;
+};
+
+class MockGpu : public GrGpu {
+public:
+ MockGpu(GrContext* context, const GrContextOptions& options) : INHERITED(context) {
+ fCaps.reset(new MockCaps(options));
+ }
+ ~MockGpu() override {}
+
+ bool onGetReadPixelsInfo(GrSurface* srcSurface, int readWidth, int readHeight, size_t rowBytes,
+ GrPixelConfig readConfig, DrawPreference*,
+ ReadPixelTempDrawInfo*) override { return false; }
+
+ bool onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height,
+ GrPixelConfig srcConfig, DrawPreference*,
+ WritePixelTempDrawInfo*) override { return false; }
+
+ void discard(GrRenderTarget*) override {}
+
+ bool onCopySurface(GrSurface* dst,
+ GrSurface* src,
+ const SkIRect& srcRect,
+ const SkIPoint& dstPoint) override { return false; };
+
+ void onGetMultisampleSpecs(GrRenderTarget* rt,
+ const GrStencilSettings&,
+ int* effectiveSampleCnt,
+ SkAutoTDeleteArray<SkPoint>*) override {
+ *effectiveSampleCnt = rt->desc().fSampleCnt;
+ }
+
+ bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) const override {
+ return false;
+ }
+
+ void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {};
+
+private:
+ void onResetContext(uint32_t resetBits) override {}
+
+ void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
+
+ GrTexture* onCreateTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle lifeCycle,
+ const SkTArray<GrMipLevel>& texels) override {
+ return nullptr;
+ }
+
+ GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle,
+ const SkTArray<GrMipLevel>& texels) override {
+ return nullptr;
+ }
+
+ GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&,
+ GrWrapOwnership) override { return nullptr; }
+
+ GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&,
+ GrWrapOwnership) override {
+ return nullptr;
+ }
+
+ GrRenderTarget* onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&,
+ GrWrapOwnership) override {
+ return nullptr;
+ }
+
+ GrBuffer* onCreateBuffer(GrBufferType, size_t, GrAccessPattern) override { return nullptr; }
+
+ void onClear(GrRenderTarget*, const SkIRect& rect, GrColor color) override {}
+
+ void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) override {}
+
+ void onDraw(const GrPipeline&,
+ const GrPrimitiveProcessor&,
+ const GrMesh*,
+ int meshCount) override {}
+
+ bool onReadPixels(GrSurface* surface,
+ int left, int top, int width, int height,
+ GrPixelConfig,
+ void* buffer,
+ size_t rowBytes) override {
+ return false;
+ }
+
+ bool onWritePixels(GrSurface* surface,
+ int left, int top, int width, int height,
+ GrPixelConfig config, const SkTArray<GrMipLevel>& texels) override {
+ return false;
+ }
+
+ bool onTransferPixels(GrSurface* surface,
+ int left, int top, int width, int height,
+ GrPixelConfig config, GrBuffer* transferBuffer,
+ size_t offset, size_t rowBytes) override {
+ return false;
+ }
+
+ void onResolveRenderTarget(GrRenderTarget* target) override { return; }
+
+ GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*,
+ int width,
+ int height) override {
+ return nullptr;
+ }
+
+ void clearStencil(GrRenderTarget* target) override {}
+
+ GrBackendObject createTestingOnlyBackendTexture(void* pixels, int w, int h,
+ GrPixelConfig config) override {
+ return 0;
+ }
+ bool isTestingOnlyBackendTexture(GrBackendObject ) const override { return false; }
+ void deleteTestingOnlyBackendTexture(GrBackendObject, bool abandonTexture) override {}
+
+ typedef GrGpu INHERITED;
+};
+
+GrContext* GrContext::CreateMockContext() {
+ GrContext* context = new GrContext;
+
+ context->initMockContext();
+ return context;
+}
+
+void GrContext::initMockContext() {
+ GrContextOptions options;
+ options.fBufferMapThreshold = 0;
+ SkASSERT(nullptr == fGpu);
+ fGpu = new MockGpu(this, options);
+ SkASSERT(fGpu);
+ this->initCommon(options);
+
+ // We delete these because we want to test the cache starting with zero resources. Also, none of
+ // these objects are required for any of tests that use this context. TODO: make stop allocating
+ // resources in the buffer pools.
+ fDrawingManager->abandon();
+}
diff --git a/tools/gpu/GrTest.h b/tools/gpu/GrTest.h
new file mode 100644
index 0000000000..53aaac34e4
--- /dev/null
+++ b/tools/gpu/GrTest.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTest_DEFINED
+#define GrTest_DEFINED
+
+#include "GrContext.h"
+#include "GrDrawTarget.h"
+#include "gl/GrGLContext.h"
+
+namespace GrTest {
+ /**
+ * Forces the GrContext to use a small atlas which only has room for one plot and will thus
+ * constantly be evicting entries
+ */
+ void SetupAlwaysEvictAtlas(GrContext*);
+};
+
+/** TODO Please do not use this if you can avoid it. We are in the process of deleting it.
+ Allows a test to temporarily draw to a GrDrawTarget owned by a GrContext. Tests that use this
+ should be careful not to mix using the GrDrawTarget directly and drawing via SkCanvas or
+ GrContext. In the future this object may provide some guards to prevent this. */
+class GrTestTarget {
+public:
+ GrTestTarget() {};
+
+ void init(GrContext*, GrDrawTarget*, GrRenderTarget*);
+
+ GrDrawTarget* target() { return fDrawTarget.get(); }
+ GrResourceProvider* resourceProvider() { return fContext->resourceProvider(); }
+
+private:
+ SkAutoTUnref<GrContext> fContext;
+ SkAutoTUnref<GrDrawTarget> fDrawTarget;
+ SkAutoTUnref<GrRenderTarget> fRenderTarget;
+};
+
+#endif
diff --git a/tools/gpu/gl/GLContext.cpp b/tools/gpu/gl/GLContext.cpp
new file mode 100644
index 0000000000..ac0e310014
--- /dev/null
+++ b/tools/gpu/gl/GLContext.cpp
@@ -0,0 +1,188 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "GLContext.h"
+#include "gl/GrGLUtil.h"
+#include "SkGpuFenceSync.h"
+
+namespace sk_gpu_test {
+class GLContext::GLFenceSync : public SkGpuFenceSync {
+public:
+ static GLFenceSync* CreateIfSupported(const GLContext*);
+
+ SkPlatformGpuFence SK_WARN_UNUSED_RESULT insertFence() const override;
+ bool waitFence(SkPlatformGpuFence fence, bool flush) const override;
+ void deleteFence(SkPlatformGpuFence fence) const override;
+
+private:
+ GLFenceSync() {}
+
+ static const GrGLenum GL_SYNC_GPU_COMMANDS_COMPLETE = 0x9117;
+ static const GrGLenum GL_WAIT_FAILED = 0x911d;
+ static const GrGLbitfield GL_SYNC_FLUSH_COMMANDS_BIT = 0x00000001;
+
+ typedef struct __GLsync *GLsync;
+
+ typedef GLsync (GR_GL_FUNCTION_TYPE* GLFenceSyncProc) (GrGLenum, GrGLbitfield);
+ typedef GrGLenum (GR_GL_FUNCTION_TYPE* GLClientWaitSyncProc) (GLsync, GrGLbitfield, GrGLuint64);
+ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GLDeleteSyncProc) (GLsync);
+
+ GLFenceSyncProc fGLFenceSync;
+ GLClientWaitSyncProc fGLClientWaitSync;
+ GLDeleteSyncProc fGLDeleteSync;
+
+ typedef SkGpuFenceSync INHERITED;
+};
+
+GLContext::GLContext()
+ : fCurrentFenceIdx(0) {
+ memset(fFrameFences, 0, sizeof(fFrameFences));
+}
+
+GLContext::~GLContext() {
+ // Subclass should call teardown.
+#ifdef SK_DEBUG
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fFrameFences); i++) {
+ SkASSERT(0 == fFrameFences[i]);
+ }
+#endif
+ SkASSERT(nullptr == fGL.get());
+ SkASSERT(nullptr == fFenceSync.get());
+}
+
+void GLContext::init(const GrGLInterface* gl, SkGpuFenceSync* fenceSync) {
+ SkASSERT(!fGL.get());
+ fGL.reset(gl);
+ fFenceSync.reset(fenceSync ? fenceSync : GLFenceSync::CreateIfSupported(this));
+}
+
+void GLContext::teardown() {
+ if (fFenceSync) {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fFrameFences); i++) {
+ if (fFrameFences[i]) {
+ fFenceSync->deleteFence(fFrameFences[i]);
+ fFrameFences[i] = 0;
+ }
+ }
+ fFenceSync.reset(nullptr);
+ }
+
+ fGL.reset(nullptr);
+}
+
+void GLContext::makeCurrent() const {
+ this->onPlatformMakeCurrent();
+}
+
+void GLContext::swapBuffers() {
+ this->onPlatformSwapBuffers();
+}
+
+void GLContext::waitOnSyncOrSwap() {
+ if (!fFenceSync) {
+ // Fallback on the platform SwapBuffers method for synchronization. This may have no effect.
+ this->swapBuffers();
+ return;
+ }
+
+ if (fFrameFences[fCurrentFenceIdx]) {
+ if (!fFenceSync->waitFence(fFrameFences[fCurrentFenceIdx], true)) {
+ SkDebugf("WARNING: Wait failed for fence sync. Timings might not be accurate.\n");
+ }
+ fFenceSync->deleteFence(fFrameFences[fCurrentFenceIdx]);
+ }
+
+ fFrameFences[fCurrentFenceIdx] = fFenceSync->insertFence();
+ fCurrentFenceIdx = (fCurrentFenceIdx + 1) % SK_ARRAY_COUNT(fFrameFences);
+}
+
+void GLContext::testAbandon() {
+ if (fGL) {
+ fGL->abandon();
+ }
+ if (fFenceSync) {
+ memset(fFrameFences, 0, sizeof(fFrameFences));
+ }
+}
+
+GLContext::GLFenceSync* GLContext::GLFenceSync::CreateIfSupported(const GLContext* ctx) {
+ SkAutoTDelete<GLFenceSync> ret(new GLFenceSync);
+
+ if (kGL_GrGLStandard == ctx->gl()->fStandard) {
+ const GrGLubyte* versionStr;
+ GR_GL_CALL_RET(ctx->gl(), versionStr, GetString(GR_GL_VERSION));
+ GrGLVersion version = GrGLGetVersionFromString(reinterpret_cast<const char*>(versionStr));
+ if (version < GR_GL_VER(3,2) && !ctx->gl()->hasExtension("GL_ARB_sync")) {
+ return nullptr;
+ }
+ ret->fGLFenceSync = reinterpret_cast<GLFenceSyncProc>(
+ ctx->onPlatformGetProcAddress("glFenceSync"));
+ ret->fGLClientWaitSync = reinterpret_cast<GLClientWaitSyncProc>(
+ ctx->onPlatformGetProcAddress("glClientWaitSync"));
+ ret->fGLDeleteSync = reinterpret_cast<GLDeleteSyncProc>(
+ ctx->onPlatformGetProcAddress("glDeleteSync"));
+ } else {
+ if (!ctx->gl()->hasExtension("GL_APPLE_sync")) {
+ return nullptr;
+ }
+ ret->fGLFenceSync = reinterpret_cast<GLFenceSyncProc>(
+ ctx->onPlatformGetProcAddress("glFenceSyncAPPLE"));
+ ret->fGLClientWaitSync = reinterpret_cast<GLClientWaitSyncProc>(
+ ctx->onPlatformGetProcAddress("glClientWaitSyncAPPLE"));
+ ret->fGLDeleteSync = reinterpret_cast<GLDeleteSyncProc>(
+ ctx->onPlatformGetProcAddress("glDeleteSyncAPPLE"));
+ }
+
+ if (!ret->fGLFenceSync || !ret->fGLClientWaitSync || !ret->fGLDeleteSync) {
+ return nullptr;
+ }
+
+ return ret.release();
+}
+
+SkPlatformGpuFence GLContext::GLFenceSync::insertFence() const {
+ return fGLFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+bool GLContext::GLFenceSync::waitFence(SkPlatformGpuFence fence, bool flush) const {
+ GLsync glsync = static_cast<GLsync>(fence);
+ return GL_WAIT_FAILED != fGLClientWaitSync(glsync, flush ? GL_SYNC_FLUSH_COMMANDS_BIT : 0, -1);
+}
+
+void GLContext::GLFenceSync::deleteFence(SkPlatformGpuFence fence) const {
+ GLsync glsync = static_cast<GLsync>(fence);
+ fGLDeleteSync(glsync);
+}
+
+GrGLint GLContext::createTextureRectangle(int width, int height, GrGLenum internalFormat,
+ GrGLenum externalFormat, GrGLenum externalType,
+ GrGLvoid* data) {
+ if (!(kGL_GrGLStandard == fGL->fStandard && GrGLGetVersion(fGL) >= GR_GL_VER(3, 1)) &&
+ !fGL->fExtensions.has("GL_ARB_texture_rectangle")) {
+ return 0;
+ }
+
+ if (GrGLGetGLSLVersion(fGL) < GR_GLSL_VER(1, 40)) {
+ return 0;
+ }
+
+ GrGLuint id;
+ GR_GL_CALL(fGL, GenTextures(1, &id));
+ GR_GL_CALL(fGL, BindTexture(GR_GL_TEXTURE_RECTANGLE, id));
+ GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_MAG_FILTER,
+ GR_GL_NEAREST));
+ GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_MIN_FILTER,
+ GR_GL_NEAREST));
+ GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_WRAP_S,
+ GR_GL_CLAMP_TO_EDGE));
+ GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_WRAP_T,
+ GR_GL_CLAMP_TO_EDGE));
+ GR_GL_CALL(fGL, TexImage2D(GR_GL_TEXTURE_RECTANGLE, 0, internalFormat, width, height, 0,
+ externalFormat, externalType, data));
+ return id;
+}
+} // namespace sk_gpu_test
diff --git a/tools/gpu/gl/GLContext.h b/tools/gpu/gl/GLContext.h
new file mode 100644
index 0000000000..3f47613722
--- /dev/null
+++ b/tools/gpu/gl/GLContext.h
@@ -0,0 +1,141 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef GLContext_DEFINED
+#define GLContext_DEFINED
+
+#include "gl/GrGLInterface.h"
+#include "../private/SkGpuFenceSync.h"
+
+
+namespace sk_gpu_test {
+/**
+ * Create an offscreen Oppengl context. Provides a GrGLInterface struct of function pointers for
+ * the context. This class is intended for Skia's internal testing needs and not for general use.
+ */
+class GLContext : public SkNoncopyable {
+public:
+ virtual ~GLContext();
+
+ bool isValid() const { return NULL != gl(); }
+
+ const GrGLInterface *gl() const { return fGL.get(); }
+
+ bool fenceSyncSupport() const { return fFenceSync != nullptr; }
+
+ bool getMaxGpuFrameLag(int *maxFrameLag) const {
+ if (!fFenceSync) {
+ return false;
+ }
+ *maxFrameLag = kMaxFrameLag;
+ return true;
+ }
+
+ void makeCurrent() const;
+
+ /** Used for testing EGLImage integration. Take a GL_TEXTURE_2D and wraps it in an EGL Image */
+ virtual GrEGLImage texture2DToEGLImage(GrGLuint /*texID*/) const { return 0; }
+
+ virtual void destroyEGLImage(GrEGLImage) const { }
+
+ /** Used for testing GL_TEXTURE_RECTANGLE integration. */
+ GrGLint createTextureRectangle(int width, int height, GrGLenum internalFormat,
+ GrGLenum externalFormat, GrGLenum externalType,
+ GrGLvoid *data);
+
+ /**
+ * Used for testing EGLImage integration. Takes a EGLImage and wraps it in a
+ * GL_TEXTURE_EXTERNAL_OES.
+ */
+ virtual GrGLuint eglImageToExternalTexture(GrEGLImage) const { return 0; }
+
+ void swapBuffers();
+
+ /**
+ * The only purpose of this function it to provide a means of scheduling
+ * work on the GPU (since all of the subclasses create primary buffers for
+ * testing that are small and not meant to be rendered to the screen).
+ *
+ * If the platform supports fence sync (OpenGL 3.2+ or EGL_KHR_fence_sync),
+ * this will not swap any buffers, but rather emulate triple buffer
+ * synchronization using fences.
+ *
+ * Otherwise it will call the platform SwapBuffers method. This may or may
+ * not perform some sort of synchronization, depending on whether the
+ * drawing surface provided by the platform is double buffered.
+ */
+ void waitOnSyncOrSwap();
+
+ /**
+ * This notifies the context that we are deliberately testing abandoning
+ * the context. It is useful for debugging contexts that would otherwise
+ * test that GPU resources are properly deleted. It also allows a debugging
+ * context to test that further GL calls are not made by Skia GPU code.
+ */
+ void testAbandon();
+
+ /**
+ * Creates a new GL context of the same type and makes the returned context current
+ * (if not null).
+ */
+ virtual GLContext *createNew() const { return nullptr; }
+
+ class GLFenceSync; // SkGpuFenceSync implementation that uses the OpenGL functionality.
+
+ /*
+ * returns the fencesync object owned by this GLContext
+ */
+ SkGpuFenceSync *fenceSync() { return fFenceSync.get(); }
+
+protected:
+ GLContext();
+
+ /*
+ * Methods that sublcasses must call from their constructors and destructors.
+ */
+ void init(const GrGLInterface *, SkGpuFenceSync * = NULL);
+
+ void teardown();
+
+ /*
+ * Operations that have a platform-dependent implementation.
+ */
+ virtual void onPlatformMakeCurrent() const = 0;
+
+ virtual void onPlatformSwapBuffers() const = 0;
+
+ virtual GrGLFuncPtr onPlatformGetProcAddress(const char *) const = 0;
+
+private:
+ enum {
+ kMaxFrameLag = 3
+ };
+
+ SkAutoTDelete <SkGpuFenceSync> fFenceSync;
+ SkPlatformGpuFence fFrameFences[kMaxFrameLag - 1];
+ int fCurrentFenceIdx;
+
+ /** Subclass provides the gl interface object if construction was
+ * successful. */
+ SkAutoTUnref<const GrGLInterface> fGL;
+
+ friend class GLFenceSync; // For onPlatformGetProcAddress.
+};
+
+
+/** Creates platform-dependent GL context object. The shareContext parameter is in an optional
+ * context with which to share display lists. This should be a pointer to an GLContext created
+ * with SkCreatePlatformGLContext. NULL indicates that no sharing is to take place. Returns a valid
+ * gl context object or NULL if such can not be created.
+ * Note: If Skia embedder needs a custom GL context that sets up the GL interface, this function
+ * should be implemented by the embedder. Otherwise, the default implementation for the platform
+ * should be compiled in the library.
+ */
+GLContext* CreatePlatformGLContext(GrGLStandard forcedGpuAPI, GLContext *shareContext = nullptr);
+
+} // namespace sk_gpu_test
+#endif
diff --git a/tools/gpu/gl/angle/GLContext_angle.cpp b/tools/gpu/gl/angle/GLContext_angle.cpp
new file mode 100644
index 0000000000..0d7dfdb389
--- /dev/null
+++ b/tools/gpu/gl/angle/GLContext_angle.cpp
@@ -0,0 +1,320 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GLContext_angle.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "gl/GrGLDefines.h"
+#include "gl/GrGLUtil.h"
+
+#include "gl/GrGLInterface.h"
+#include "gl/GrGLAssembleInterface.h"
+#include "../ports/SkOSLibrary.h"
+
+#include <EGL/egl.h>
+
+#define EGL_PLATFORM_ANGLE_ANGLE 0x3202
+#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203
+#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207
+#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
+#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D
+
+namespace {
+struct Libs {
+ void* fGLLib;
+ void* fEGLLib;
+};
+
+static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) {
+ const Libs* libs = reinterpret_cast<const Libs*>(ctx);
+ GrGLFuncPtr proc = (GrGLFuncPtr) GetProcedureAddress(libs->fGLLib, name);
+ if (proc) {
+ return proc;
+ }
+ proc = (GrGLFuncPtr) GetProcedureAddress(libs->fEGLLib, name);
+ if (proc) {
+ return proc;
+ }
+ return eglGetProcAddress(name);
+}
+
+void* get_angle_egl_display(void* nativeDisplay, bool useGLBackend) {
+ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
+ eglGetPlatformDisplayEXT =
+ (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+
+ // We expect ANGLE to support this extension
+ if (!eglGetPlatformDisplayEXT) {
+ return EGL_NO_DISPLAY;
+ }
+
+ EGLDisplay display = EGL_NO_DISPLAY;
+ if (useGLBackend) {
+ EGLint attribs[3] = {
+ EGL_PLATFORM_ANGLE_TYPE_ANGLE,
+ EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE,
+ EGL_NONE
+ };
+ display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, nativeDisplay, attribs);
+ } else {
+ // Try for an ANGLE D3D11 context, fall back to D3D9.
+ EGLint attribs[3][3] = {
+ {
+ EGL_PLATFORM_ANGLE_TYPE_ANGLE,
+ EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
+ EGL_NONE
+ },
+ {
+ EGL_PLATFORM_ANGLE_TYPE_ANGLE,
+ EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
+ EGL_NONE
+ },
+ };
+ for (int i = 0; i < 3 && display == EGL_NO_DISPLAY; ++i) {
+ display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,nativeDisplay, attribs[i]);
+ }
+ }
+ return display;
+}
+
+class ANGLEGLContext : public sk_gpu_test::GLContext {
+public:
+ ANGLEGLContext(bool preferGLBackend);
+ ~ANGLEGLContext() override;
+
+ GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
+ void destroyEGLImage(GrEGLImage) const override;
+ GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
+ sk_gpu_test::GLContext* createNew() const override;
+
+private:
+ void destroyGLContext();
+
+ void onPlatformMakeCurrent() const override;
+ void onPlatformSwapBuffers() const override;
+ GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override;
+
+ void* fContext;
+ void* fDisplay;
+ void* fSurface;
+ bool fIsGLBackend;
+};
+
+ANGLEGLContext::ANGLEGLContext(bool useGLBackend)
+ : fContext(EGL_NO_CONTEXT)
+ , fDisplay(EGL_NO_DISPLAY)
+ , fSurface(EGL_NO_SURFACE) {
+
+ EGLint numConfigs;
+ static const EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_NONE
+ };
+
+ fIsGLBackend = useGLBackend;
+ fDisplay = get_angle_egl_display(EGL_DEFAULT_DISPLAY, useGLBackend);
+ if (EGL_NO_DISPLAY == fDisplay) {
+ SkDebugf("Could not create EGL display!");
+ return;
+ }
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ eglInitialize(fDisplay, &majorVersion, &minorVersion);
+
+ EGLConfig surfaceConfig;
+ eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs);
+
+ static const EGLint contextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ fContext = eglCreateContext(fDisplay, surfaceConfig, nullptr, contextAttribs);
+
+
+ static const EGLint surfaceAttribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+
+ fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs);
+
+ eglMakeCurrent(fDisplay, fSurface, fSurface, fContext);
+
+ SkAutoTUnref<const GrGLInterface> gl(sk_gpu_test::CreateANGLEGLInterface());
+ if (nullptr == gl.get()) {
+ SkDebugf("Could not create ANGLE GL interface!\n");
+ this->destroyGLContext();
+ return;
+ }
+ if (!gl->validate()) {
+ SkDebugf("Could not validate ANGLE GL interface!\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ this->init(gl.release());
+}
+
+ANGLEGLContext::~ANGLEGLContext() {
+ this->teardown();
+ this->destroyGLContext();
+}
+
+GrEGLImage ANGLEGLContext::texture2DToEGLImage(GrGLuint texID) const {
+ if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
+ return GR_EGL_NO_IMAGE;
+ }
+ GrEGLImage img;
+ GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0,
+ GR_EGL_IMAGE_PRESERVED, GR_EGL_TRUE,
+ GR_EGL_NONE };
+ // 64 bit cast is to shut Visual C++ up about casting 32 bit value to a pointer.
+ GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>((uint64_t)texID);
+ GR_GL_CALL_RET(this->gl(), img,
+ EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer,
+ attribs));
+ return img;
+}
+
+void ANGLEGLContext::destroyEGLImage(GrEGLImage image) const {
+ GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image));
+}
+
+GrGLuint ANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const {
+ GrGLClearErr(this->gl());
+ if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
+ return 0;
+ }
+ typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage);
+ EGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
+ (EGLImageTargetTexture2DProc)eglGetProcAddress("glEGLImageTargetTexture2DOES");
+ if (!glEGLImageTargetTexture2D) {
+ return 0;
+ }
+ GrGLuint texID;
+ GR_GL_CALL(this->gl(), GenTextures(1, &texID));
+ if (!texID) {
+ return 0;
+ }
+ GR_GL_CALL(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
+ if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
+ GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
+ return 0;
+ }
+ glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
+ if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
+ GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
+ return 0;
+ }
+ return texID;
+}
+
+sk_gpu_test::GLContext* ANGLEGLContext::createNew() const {
+#ifdef SK_BUILD_FOR_WIN
+ sk_gpu_test::GLContext* ctx = fIsGLBackend ? sk_gpu_test::CreateANGLEOpenGLGLContext()
+ : sk_gpu_test::CreateANGLEDirect3DGLContext();
+#else
+ sk_gpu_test::GLContext* ctx = sk_gpu_test::CreateANGLEOpenGLGLContext();
+#endif
+ if (ctx) {
+ ctx->makeCurrent();
+ }
+ return ctx;
+}
+
+void ANGLEGLContext::destroyGLContext() {
+ if (fDisplay) {
+ eglMakeCurrent(fDisplay, 0, 0, 0);
+
+ if (fContext) {
+ eglDestroyContext(fDisplay, fContext);
+ fContext = EGL_NO_CONTEXT;
+ }
+
+ if (fSurface) {
+ eglDestroySurface(fDisplay, fSurface);
+ fSurface = EGL_NO_SURFACE;
+ }
+
+ //TODO should we close the display?
+ fDisplay = EGL_NO_DISPLAY;
+ }
+}
+
+void ANGLEGLContext::onPlatformMakeCurrent() const {
+ if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+ SkDebugf("Could not set the context.\n");
+ }
+}
+
+void ANGLEGLContext::onPlatformSwapBuffers() const {
+ if (!eglSwapBuffers(fDisplay, fSurface)) {
+ SkDebugf("Could not complete eglSwapBuffers.\n");
+ }
+}
+
+GrGLFuncPtr ANGLEGLContext::onPlatformGetProcAddress(const char* name) const {
+ return eglGetProcAddress(name);
+}
+} // anonymous namespace
+
+namespace sk_gpu_test {
+const GrGLInterface* CreateANGLEGLInterface() {
+ static Libs gLibs = { nullptr, nullptr };
+
+ if (nullptr == gLibs.fGLLib) {
+ // We load the ANGLE library and never let it go
+#if defined _WIN32
+ gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dll");
+ gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dll");
+#elif defined SK_BUILD_FOR_MAC
+ gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dylib");
+ gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dylib");
+#else
+ gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.so");
+ gLibs.fEGLLib = DynamicLoadLibrary("libEGL.so");
+#endif
+ }
+
+ if (nullptr == gLibs.fGLLib || nullptr == gLibs.fEGLLib) {
+ // We can't setup the interface correctly w/o the so
+ return nullptr;
+ }
+
+ return GrGLAssembleGLESInterface(&gLibs, angle_get_gl_proc);
+}
+
+#ifdef SK_BUILD_FOR_WIN
+GLContext* CreateANGLEDirect3DGLContext() {
+ SkANGLEGLContext* ctx = new SkANGLEGLContext(false);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return NULL;
+ }
+ return ctx;
+ }
+#endif
+
+GLContext* CreateANGLEOpenGLGLContext() {
+ ANGLEGLContext* ctx = new ANGLEGLContext(true);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return NULL;
+ }
+ return ctx;
+}
+} // namespace sk_gpu_test
diff --git a/tools/gpu/gl/angle/GLContext_angle.h b/tools/gpu/gl/angle/GLContext_angle.h
new file mode 100644
index 0000000000..519ea6b5a3
--- /dev/null
+++ b/tools/gpu/gl/angle/GLContext_angle.h
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef GLContext_angle_DEFINED
+#define GLContext_angle_DEFINED
+
+#include "gl/GLContext.h"
+
+namespace sk_gpu_test {
+
+/**
+ * Creates a GrGLInterface for the currently ANGLE GL context currently bound in ANGLE's EGL
+ * implementation.
+ */
+const GrGLInterface* CreateANGLEGLInterface();
+
+#ifdef SK_BUILD_FOR_WIN
+/** Creates a GLContext backed by ANGLE's Direct3D backend. */
+GLContext* CreateANGLEDirect3DGLContext();
+#endif
+
+/** Creates a GLContext backed by ANGLE's OpenGL backend. */
+GLContext* CreateANGLEOpenGLGLContext();
+
+} // namespace sk_gpu_test
+#endif
diff --git a/tools/gpu/gl/command_buffer/GLContext_command_buffer.cpp b/tools/gpu/gl/command_buffer/GLContext_command_buffer.cpp
new file mode 100644
index 0000000000..b878cb4aa9
--- /dev/null
+++ b/tools/gpu/gl/command_buffer/GLContext_command_buffer.cpp
@@ -0,0 +1,345 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkOnce.h"
+#include "gl/GrGLInterface.h"
+#include "gl/GrGLAssembleInterface.h"
+#include "gl/command_buffer/GLContext_command_buffer.h"
+#include "../ports/SkOSEnvironment.h"
+#include "../ports/SkOSLibrary.h"
+
+#if defined SK_BUILD_FOR_MAC
+
+// EGL doesn't exist on the mac, so expose what we need to get the command buffer's EGL running.
+typedef void *EGLDisplay;
+typedef unsigned int EGLBoolean;
+typedef void *EGLConfig;
+typedef void *EGLSurface;
+typedef void *EGLContext;
+typedef int32_t EGLint;
+typedef void* EGLNativeDisplayType;
+typedef void* EGLNativeWindowType;
+typedef void (*__eglMustCastToProperFunctionPointerType)(void);
+#define EGL_FALSE 0
+#define EGL_OPENGL_ES2_BIT 0x0004
+#define EGL_CONTEXT_CLIENT_VERSION 0x3098
+#define EGL_NO_SURFACE ((EGLSurface)0)
+#define EGL_NO_DISPLAY ((EGLDisplay)0)
+#define EGL_NO_CONTEXT ((EGLContext)0)
+#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
+#define EGL_SURFACE_TYPE 0x3033
+#define EGL_PBUFFER_BIT 0x0001
+#define EGL_RENDERABLE_TYPE 0x3040
+#define EGL_RED_SIZE 0x3024
+#define EGL_GREEN_SIZE 0x3023
+#define EGL_BLUE_SIZE 0x3022
+#define EGL_ALPHA_SIZE 0x3021
+#define EGL_DEPTH_SIZE 0x3025
+#define EGL_STENCIL_SIZE 0x3025
+#define EGL_SAMPLES 0x3031
+#define EGL_SAMPLE_BUFFERS 0x3032
+#define EGL_NONE 0x3038
+#define EGL_WIDTH 0x3057
+#define EGL_HEIGHT 0x3056
+
+#else
+
+#include <EGL/egl.h>
+
+#endif
+
+typedef EGLDisplay (*GetDisplayProc)(EGLNativeDisplayType display_id);
+typedef EGLBoolean (*InitializeProc)(EGLDisplay dpy, EGLint *major, EGLint *minor);
+typedef EGLBoolean (*TerminateProc)(EGLDisplay dpy);
+typedef EGLBoolean (*ChooseConfigProc)(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config);
+typedef EGLBoolean (*GetConfigAttrib)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value);
+typedef EGLSurface (*CreateWindowSurfaceProc)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint* attrib_list);
+typedef EGLSurface (*CreatePbufferSurfaceProc)(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list);
+typedef EGLBoolean (*DestroySurfaceProc)(EGLDisplay dpy, EGLSurface surface);
+typedef EGLContext (*CreateContextProc)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list);
+typedef EGLBoolean (*DestroyContextProc)(EGLDisplay dpy, EGLContext ctx);
+typedef EGLBoolean (*MakeCurrentProc)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+typedef EGLBoolean (*SwapBuffersProc)(EGLDisplay dpy, EGLSurface surface);
+typedef __eglMustCastToProperFunctionPointerType (*GetProcAddressProc)(const char* procname);
+
+static GetDisplayProc gfGetDisplay = nullptr;
+static InitializeProc gfInitialize = nullptr;
+static TerminateProc gfTerminate = nullptr;
+static ChooseConfigProc gfChooseConfig = nullptr;
+static GetConfigAttrib gfGetConfigAttrib = nullptr;
+static CreateWindowSurfaceProc gfCreateWindowSurface = nullptr;
+static CreatePbufferSurfaceProc gfCreatePbufferSurface = nullptr;
+static DestroySurfaceProc gfDestroySurface = nullptr;
+static CreateContextProc gfCreateContext = nullptr;
+static DestroyContextProc gfDestroyContext = nullptr;
+static MakeCurrentProc gfMakeCurrent = nullptr;
+static SwapBuffersProc gfSwapBuffers = nullptr;
+static GetProcAddressProc gfGetProcAddress = nullptr;
+
+static void* gLibrary = nullptr;
+static bool gfFunctionsLoadedSuccessfully = false;
+
+namespace {
+static void load_command_buffer_functions() {
+ if (!gLibrary) {
+#if defined _WIN32
+ gLibrary = DynamicLoadLibrary("command_buffer_gles2.dll");
+#elif defined SK_BUILD_FOR_MAC
+ gLibrary = DynamicLoadLibrary("libcommand_buffer_gles2.dylib");
+#else
+ gLibrary = DynamicLoadLibrary("libcommand_buffer_gles2.so");
+#endif // defined _WIN32
+ if (gLibrary) {
+ gfGetDisplay = (GetDisplayProc)GetProcedureAddress(gLibrary, "eglGetDisplay");
+ gfInitialize = (InitializeProc)GetProcedureAddress(gLibrary, "eglInitialize");
+ gfTerminate = (TerminateProc)GetProcedureAddress(gLibrary, "eglTerminate");
+ gfChooseConfig = (ChooseConfigProc)GetProcedureAddress(gLibrary, "eglChooseConfig");
+ gfGetConfigAttrib = (GetConfigAttrib)GetProcedureAddress(gLibrary, "eglGetConfigAttrib");
+ gfCreateWindowSurface = (CreateWindowSurfaceProc)GetProcedureAddress(gLibrary, "eglCreateWindowSurface");
+ gfCreatePbufferSurface = (CreatePbufferSurfaceProc)GetProcedureAddress(gLibrary, "eglCreatePbufferSurface");
+ gfDestroySurface = (DestroySurfaceProc)GetProcedureAddress(gLibrary, "eglDestroySurface");
+ gfCreateContext = (CreateContextProc)GetProcedureAddress(gLibrary, "eglCreateContext");
+ gfDestroyContext = (DestroyContextProc)GetProcedureAddress(gLibrary, "eglDestroyContext");
+ gfMakeCurrent = (MakeCurrentProc)GetProcedureAddress(gLibrary, "eglMakeCurrent");
+ gfSwapBuffers = (SwapBuffersProc)GetProcedureAddress(gLibrary, "eglSwapBuffers");
+ gfGetProcAddress = (GetProcAddressProc)GetProcedureAddress(gLibrary, "eglGetProcAddress");
+
+ gfFunctionsLoadedSuccessfully = gfGetDisplay && gfInitialize && gfTerminate &&
+ gfChooseConfig && gfCreateWindowSurface &&
+ gfCreatePbufferSurface && gfDestroySurface &&
+ gfCreateContext && gfDestroyContext && gfMakeCurrent &&
+ gfSwapBuffers && gfGetProcAddress;
+
+ }
+ }
+}
+
+static GrGLFuncPtr command_buffer_get_gl_proc(void* ctx, const char name[]) {
+ if (!gfFunctionsLoadedSuccessfully) {
+ return nullptr;
+ }
+ return gfGetProcAddress(name);
+}
+
+SK_DECLARE_STATIC_ONCE(loadCommandBufferOnce);
+static void load_command_buffer_once() {
+ SkOnce(&loadCommandBufferOnce, load_command_buffer_functions);
+}
+
+static const GrGLInterface* create_command_buffer_interface() {
+ load_command_buffer_once();
+ if (!gfFunctionsLoadedSuccessfully) {
+ return nullptr;
+ }
+ return GrGLAssembleGLESInterface(gLibrary, command_buffer_get_gl_proc);
+}
+
+} // anonymous namespace
+
+namespace sk_gpu_test {
+
+CommandBufferGLContext::CommandBufferGLContext()
+ : fContext(EGL_NO_CONTEXT), fDisplay(EGL_NO_DISPLAY), fSurface(EGL_NO_SURFACE) {
+
+ static const EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_NONE
+ };
+
+ static const EGLint surfaceAttribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+
+ initializeGLContext(nullptr, configAttribs, surfaceAttribs);
+}
+
+CommandBufferGLContext::CommandBufferGLContext(void *nativeWindow, int msaaSampleCount) {
+ static const EGLint surfaceAttribs[] = {EGL_NONE};
+
+ EGLint configAttribs[] = {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 8,
+ EGL_STENCIL_SIZE, 8,
+ EGL_SAMPLE_BUFFERS, 1,
+ EGL_SAMPLES, msaaSampleCount,
+ EGL_NONE
+ };
+ if (msaaSampleCount == 0) {
+ configAttribs[12] = EGL_NONE;
+ }
+
+ initializeGLContext(nativeWindow, configAttribs, surfaceAttribs);
+}
+
+void CommandBufferGLContext::initializeGLContext(void *nativeWindow, const int *configAttribs,
+ const int *surfaceAttribs) {
+ load_command_buffer_once();
+ if (!gfFunctionsLoadedSuccessfully) {
+ SkDebugf("Command Buffer: Could not load EGL functions.\n");
+ return;
+ }
+
+ // Make sure CHROMIUM_path_rendering is enabled for NVPR support.
+ sk_setenv("CHROME_COMMAND_BUFFER_GLES2_ARGS", "--enable-gl-path-rendering");
+ fDisplay = gfGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (EGL_NO_DISPLAY == fDisplay) {
+ SkDebugf("Command Buffer: Could not create EGL display.\n");
+ return;
+ }
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ if (!gfInitialize(fDisplay, &majorVersion, &minorVersion)) {
+ SkDebugf("Command Buffer: Could not initialize EGL display.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ EGLint numConfigs;
+ if (!gfChooseConfig(fDisplay, configAttribs, static_cast<EGLConfig *>(&fConfig), 1,
+ &numConfigs) || numConfigs != 1) {
+ SkDebugf("Command Buffer: Could not choose EGL config.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ if (nativeWindow) {
+ fSurface = gfCreateWindowSurface(fDisplay,
+ static_cast<EGLConfig>(fConfig),
+ (EGLNativeWindowType) nativeWindow,
+ surfaceAttribs);
+ } else {
+ fSurface = gfCreatePbufferSurface(fDisplay,
+ static_cast<EGLConfig>(fConfig),
+ surfaceAttribs);
+ }
+ if (EGL_NO_SURFACE == fSurface) {
+ SkDebugf("Command Buffer: Could not create EGL surface.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ static const EGLint contextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ fContext = gfCreateContext(fDisplay, static_cast<EGLConfig>(fConfig), nullptr, contextAttribs);
+ if (EGL_NO_CONTEXT == fContext) {
+ SkDebugf("Command Buffer: Could not create EGL context.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ if (!gfMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+ SkDebugf("Command Buffer: Could not make EGL context current.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ SkAutoTUnref<const GrGLInterface> gl(create_command_buffer_interface());
+ if (nullptr == gl.get()) {
+ SkDebugf("Command Buffer: Could not create CommandBuffer GL interface.\n");
+ this->destroyGLContext();
+ return;
+ }
+ if (!gl->validate()) {
+ SkDebugf("Command Buffer: Could not validate CommandBuffer GL interface.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ this->init(gl.release());
+}
+
+CommandBufferGLContext::~CommandBufferGLContext() {
+ this->teardown();
+ this->destroyGLContext();
+}
+
+void CommandBufferGLContext::destroyGLContext() {
+ if (!gfFunctionsLoadedSuccessfully) {
+ return;
+ }
+ if (fDisplay) {
+ gfMakeCurrent(fDisplay, 0, 0, 0);
+
+ if (fContext) {
+ gfDestroyContext(fDisplay, fContext);
+ fContext = EGL_NO_CONTEXT;
+ }
+
+ if (fSurface) {
+ gfDestroySurface(fDisplay, fSurface);
+ fSurface = EGL_NO_SURFACE;
+ }
+
+ gfTerminate(fDisplay);
+ fDisplay = EGL_NO_DISPLAY;
+ }
+}
+
+void CommandBufferGLContext::onPlatformMakeCurrent() const {
+ if (!gfFunctionsLoadedSuccessfully) {
+ return;
+ }
+ if (!gfMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+ SkDebugf("Command Buffer: Could not make EGL context current.\n");
+ }
+}
+
+void CommandBufferGLContext::onPlatformSwapBuffers() const {
+ if (!gfFunctionsLoadedSuccessfully) {
+ return;
+ }
+ if (!gfSwapBuffers(fDisplay, fSurface)) {
+ SkDebugf("Command Buffer: Could not complete gfSwapBuffers.\n");
+ }
+}
+
+GrGLFuncPtr CommandBufferGLContext::onPlatformGetProcAddress(const char *name) const {
+ if (!gfFunctionsLoadedSuccessfully) {
+ return nullptr;
+ }
+ return gfGetProcAddress(name);
+}
+
+void CommandBufferGLContext::presentCommandBuffer() {
+ if (this->gl()) {
+ this->gl()->fFunctions.fFlush();
+ }
+
+ this->onPlatformSwapBuffers();
+}
+
+bool CommandBufferGLContext::makeCurrent() {
+ return gfMakeCurrent(fDisplay, fSurface, fSurface, fContext) != EGL_FALSE;
+}
+
+int CommandBufferGLContext::getStencilBits() {
+ EGLint result = 0;
+ gfGetConfigAttrib(fDisplay, static_cast<EGLConfig>(fConfig), EGL_STENCIL_SIZE, &result);
+ return result;
+}
+
+int CommandBufferGLContext::getSampleCount() {
+ EGLint result = 0;
+ gfGetConfigAttrib(fDisplay, static_cast<EGLConfig>(fConfig), EGL_SAMPLES, &result);
+ return result;
+}
+
+} // namespace sk_gpu_test
diff --git a/tools/gpu/gl/command_buffer/GLContext_command_buffer.h b/tools/gpu/gl/command_buffer/GLContext_command_buffer.h
new file mode 100644
index 0000000000..73f02e2c78
--- /dev/null
+++ b/tools/gpu/gl/command_buffer/GLContext_command_buffer.h
@@ -0,0 +1,68 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GLContext_command_buffer_DEFINED
+#define GLContext_command_buffer_DEFINED
+
+#include "gl/GLContext.h"
+
+namespace sk_gpu_test {
+class CommandBufferGLContext : public GLContext {
+public:
+ ~CommandBufferGLContext() override;
+
+ static CommandBufferGLContext *Create() {
+ CommandBufferGLContext *ctx = new CommandBufferGLContext;
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+ }
+
+ static CommandBufferGLContext *Create(void *nativeWindow, int msaaSampleCount) {
+ CommandBufferGLContext *ctx = new CommandBufferGLContext(nativeWindow, msaaSampleCount);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+ }
+
+ void presentCommandBuffer();
+
+ bool makeCurrent();
+
+ int getStencilBits();
+
+ int getSampleCount();
+
+private:
+ CommandBufferGLContext();
+
+ CommandBufferGLContext(void *nativeWindow, int msaaSampleCount);
+
+ void initializeGLContext(void *nativeWindow, const int *configAttribs,
+ const int *surfaceAttribs);
+
+ void destroyGLContext();
+
+ void onPlatformMakeCurrent() const override;
+
+ void onPlatformSwapBuffers() const override;
+
+ GrGLFuncPtr onPlatformGetProcAddress(const char *name) const override;
+
+ void *fContext;
+ void *fDisplay;
+ void *fSurface;
+ void *fConfig;
+};
+} // namespace sk_gpu_test
+
+#endif
diff --git a/tools/gpu/gl/debug/DebugGLContext.cpp b/tools/gpu/gl/debug/DebugGLContext.cpp
new file mode 100644
index 0000000000..f4cbbea680
--- /dev/null
+++ b/tools/gpu/gl/debug/DebugGLContext.cpp
@@ -0,0 +1,1256 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "DebugGLContext.h"
+
+#include "GrBufferObj.h"
+#include "GrFrameBufferObj.h"
+#include "GrProgramObj.h"
+#include "GrRenderBufferObj.h"
+#include "GrShaderObj.h"
+#include "GrTextureObj.h"
+#include "GrTextureUnitObj.h"
+#include "GrVertexArrayObj.h"
+#include "gl/GrGLTestInterface.h"
+
+#include "SkMutex.h"
+
+namespace {
+
+// Helper macro to make creating an object (where you need to get back a derived type) easier
+#define CREATE(className, classEnum) \
+ reinterpret_cast<className *>(this->createObj(classEnum))
+
+// Helper macro to make creating an object (where you need to get back a derived type) easier
+#define FIND(id, className, classEnum) \
+ reinterpret_cast<className *>(this->findObject(id, classEnum))
+
+class DebugInterface : public GrGLTestInterface {
+public:
+ DebugInterface()
+ : fCurrGenericID(0)
+ , fCurrTextureUnit(0)
+ , fArrayBuffer(nullptr)
+ , fElementArrayBuffer(nullptr)
+ , fVertexArray(nullptr)
+ , fPackRowLength(0)
+ , fUnpackRowLength(0)
+ , fPackAlignment(4)
+ , fFrameBuffer(nullptr)
+ , fRenderBuffer(nullptr)
+ , fProgram(nullptr)
+ , fAbandoned(false) {
+ for (int i = 0; i < kDefaultMaxTextureUnits; ++i) {
+ fTextureUnits[i] =
+ reinterpret_cast<GrTextureUnitObj*>(this->createObj(kTextureUnit_ObjTypes));
+ fTextureUnits[i]->ref();
+ fTextureUnits[i]->setNumber(i);
+ }
+ this->init(kGL_GrGLStandard);
+ }
+
+ ~DebugInterface() override {
+ // unref & delete the texture units first so they don't show up on the leak report
+ for (int i = 0; i < kDefaultMaxTextureUnits; ++i) {
+ fTextureUnits[i]->unref();
+ fTextureUnits[i]->deleteAction();
+ }
+ for (int i = 0; i < fObjects.count(); ++i) {
+ delete fObjects[i];
+ }
+ fObjects.reset();
+
+ fArrayBuffer = nullptr;
+ fElementArrayBuffer = nullptr;
+ fVertexArray = nullptr;
+
+ this->report();
+ }
+
+ void abandon() const override { fAbandoned = true; }
+
+ GrGLvoid activeTexture(GrGLenum texture) override {
+ // Ganesh offsets the texture unit indices
+ texture -= GR_GL_TEXTURE0;
+ GrAlwaysAssert(texture < kDefaultMaxTextureUnits);
+ fCurrTextureUnit = texture;
+ }
+
+ GrGLvoid attachShader(GrGLuint programID, GrGLuint shaderID) override {
+
+ GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes);
+ GrAlwaysAssert(program);
+
+ GrShaderObj *shader = FIND(shaderID, GrShaderObj, kShader_ObjTypes);
+ GrAlwaysAssert(shader);
+
+ program->AttachShader(shader);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ GrGLvoid bindTexture(GrGLenum target, GrGLuint textureID) override {
+ GrAlwaysAssert(target == GR_GL_TEXTURE_2D ||
+ target == GR_GL_TEXTURE_RECTANGLE ||
+ target == GR_GL_TEXTURE_EXTERNAL);
+
+ // a textureID of 0 is acceptable - it binds to the default texture target
+ GrTextureObj *texture = FIND(textureID, GrTextureObj, kTexture_ObjTypes);
+
+ this->setTexture(texture);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data,
+ GrGLenum usage) override {
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+ GR_GL_ELEMENT_ARRAY_BUFFER == target);
+ GrAlwaysAssert(size >= 0);
+ GrAlwaysAssert(GR_GL_STREAM_DRAW == usage ||
+ GR_GL_STATIC_DRAW == usage ||
+ GR_GL_DYNAMIC_DRAW == usage);
+
+ GrBufferObj *buffer = nullptr;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buffer = this->getArrayBuffer();
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buffer = this->getElementArrayBuffer();
+ break;
+ default:
+ SkFAIL("Unexpected target to glBufferData");
+ break;
+ }
+
+ GrAlwaysAssert(buffer);
+ GrAlwaysAssert(buffer->getBound());
+
+ buffer->allocate(size, reinterpret_cast<const GrGLchar *>(data));
+ buffer->setUsage(usage);
+ }
+
+
+ GrGLvoid pixelStorei(GrGLenum pname, GrGLint param) override {
+
+ switch (pname) {
+ case GR_GL_UNPACK_ROW_LENGTH:
+ fUnpackRowLength = param;
+ break;
+ case GR_GL_PACK_ROW_LENGTH:
+ fPackRowLength = param;
+ break;
+ case GR_GL_UNPACK_ALIGNMENT:
+ break;
+ case GR_GL_PACK_ALIGNMENT:
+ fPackAlignment = param;
+ break;
+ default:
+ GrAlwaysAssert(false);
+ break;
+ }
+ }
+
+ GrGLvoid readPixels(GrGLint x,
+ GrGLint y,
+ GrGLsizei width,
+ GrGLsizei height,
+ GrGLenum format,
+ GrGLenum type,
+ GrGLvoid* pixels) override {
+
+ GrGLint pixelsInRow = width;
+ if (fPackRowLength > 0) {
+ pixelsInRow = fPackRowLength;
+ }
+
+ GrGLint componentsPerPixel = 0;
+
+ switch (format) {
+ case GR_GL_RGBA:
+ // fallthrough
+ case GR_GL_BGRA:
+ componentsPerPixel = 4;
+ break;
+ case GR_GL_RGB:
+ componentsPerPixel = 3;
+ break;
+ case GR_GL_RED:
+ componentsPerPixel = 1;
+ break;
+ default:
+ GrAlwaysAssert(false);
+ break;
+ }
+
+ GrGLint alignment = fPackAlignment;
+
+ GrGLint componentSize = 0; // size (in bytes) of a single component
+
+ switch (type) {
+ case GR_GL_UNSIGNED_BYTE:
+ componentSize = 1;
+ break;
+ default:
+ GrAlwaysAssert(false);
+ break;
+ }
+
+ GrGLint rowStride = 0; // number of components (not bytes) to skip
+ if (componentSize >= alignment) {
+ rowStride = componentsPerPixel * pixelsInRow;
+ } else {
+ float fTemp =
+ sk_float_ceil(componentSize * componentsPerPixel * pixelsInRow /
+ static_cast<float>(alignment));
+ rowStride = static_cast<GrGLint>(alignment * fTemp / componentSize);
+ }
+
+ GrGLchar *scanline = static_cast<GrGLchar *>(pixels);
+ for (int y = 0; y < height; ++y) {
+ memset(scanline, 0, componentsPerPixel * componentSize * width);
+ scanline += rowStride;
+ }
+ }
+
+ GrGLvoid useProgram(GrGLuint programID) override {
+
+ // A programID of 0 is legal
+ GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes);
+
+ this->useProgram(program);
+ }
+
+ GrGLvoid bindFramebuffer(GrGLenum target, GrGLuint frameBufferID) override {
+
+ GrAlwaysAssert(GR_GL_FRAMEBUFFER == target ||
+ GR_GL_READ_FRAMEBUFFER == target ||
+ GR_GL_DRAW_FRAMEBUFFER);
+
+ // a frameBufferID of 0 is acceptable - it binds to the default
+ // frame buffer
+ GrFrameBufferObj *frameBuffer = FIND(frameBufferID, GrFrameBufferObj,
+ kFrameBuffer_ObjTypes);
+
+ this->setFrameBuffer(frameBuffer);
+ }
+
+ GrGLvoid bindRenderbuffer(GrGLenum target, GrGLuint renderBufferID) override {
+
+ GrAlwaysAssert(GR_GL_RENDERBUFFER == target);
+
+ // a renderBufferID of 0 is acceptable - it unbinds the bound render buffer
+ GrRenderBufferObj *renderBuffer = FIND(renderBufferID, GrRenderBufferObj,
+ kRenderBuffer_ObjTypes);
+
+ this->setRenderBuffer(renderBuffer);
+ }
+
+ GrGLvoid deleteTextures(GrGLsizei n, const GrGLuint* textures) override {
+ // first potentially unbind the texture
+ for (unsigned int i = 0; i < kDefaultMaxTextureUnits; ++i) {
+ GrTextureUnitObj *pTU = this->getTextureUnit(i);
+
+ if (pTU->getTexture()) {
+ for (int j = 0; j < n; ++j) {
+
+ if (textures[j] == pTU->getTexture()->getID()) {
+ // this ID is the current texture - revert the binding to 0
+ pTU->setTexture(nullptr);
+ }
+ }
+ }
+ }
+
+ // TODO: fuse the following block with DeleteRenderBuffers?
+ // Open GL will remove a deleted render buffer from the active
+ // frame buffer but not from any other frame buffer
+ if (this->getFrameBuffer()) {
+
+ GrFrameBufferObj *frameBuffer = this->getFrameBuffer();
+
+ for (int i = 0; i < n; ++i) {
+
+ if (frameBuffer->getColor() &&
+ textures[i] == frameBuffer->getColor()->getID()) {
+ frameBuffer->setColor(nullptr);
+ }
+ if (frameBuffer->getDepth() &&
+ textures[i] == frameBuffer->getDepth()->getID()) {
+ frameBuffer->setDepth(nullptr);
+ }
+ if (frameBuffer->getStencil() &&
+ textures[i] == frameBuffer->getStencil()->getID()) {
+ frameBuffer->setStencil(nullptr);
+ }
+ }
+ }
+
+ // then actually "delete" the buffers
+ for (int i = 0; i < n; ++i) {
+ GrTextureObj *buffer = FIND(textures[i], GrTextureObj, kTexture_ObjTypes);
+ GrAlwaysAssert(buffer);
+
+ // OpenGL gives no guarantees if a texture is deleted while attached to
+ // something other than the currently bound frame buffer
+ GrAlwaysAssert(!buffer->getBound());
+
+ GrAlwaysAssert(!buffer->getDeleted());
+ buffer->deleteAction();
+ }
+
+ }
+
+ GrGLvoid deleteFramebuffers(GrGLsizei n, const GrGLuint *frameBuffers) override {
+
+ // first potentially unbind the buffers
+ if (this->getFrameBuffer()) {
+ for (int i = 0; i < n; ++i) {
+
+ if (frameBuffers[i] ==
+ this->getFrameBuffer()->getID()) {
+ // this ID is the current frame buffer - rebind to the default
+ this->setFrameBuffer(nullptr);
+ }
+ }
+ }
+
+ // then actually "delete" the buffers
+ for (int i = 0; i < n; ++i) {
+ GrFrameBufferObj *buffer = FIND(frameBuffers[i], GrFrameBufferObj,
+ kFrameBuffer_ObjTypes);
+ GrAlwaysAssert(buffer);
+
+ GrAlwaysAssert(!buffer->getDeleted());
+ buffer->deleteAction();
+ }
+ }
+
+ GrGLvoid deleteRenderbuffers(GrGLsizei n,const GrGLuint *renderBuffers) override {
+
+ // first potentially unbind the buffers
+ if (this->getRenderBuffer()) {
+ for (int i = 0; i < n; ++i) {
+
+ if (renderBuffers[i] ==
+ this->getRenderBuffer()->getID()) {
+ // this ID is the current render buffer - make no
+ // render buffer be bound
+ this->setRenderBuffer(nullptr);
+ }
+ }
+ }
+
+ // TODO: fuse the following block with DeleteTextures?
+ // Open GL will remove a deleted render buffer from the active frame
+ // buffer but not from any other frame buffer
+ if (this->getFrameBuffer()) {
+
+ GrFrameBufferObj *frameBuffer = this->getFrameBuffer();
+
+ for (int i = 0; i < n; ++i) {
+
+ if (frameBuffer->getColor() &&
+ renderBuffers[i] == frameBuffer->getColor()->getID()) {
+ frameBuffer->setColor(nullptr);
+ }
+ if (frameBuffer->getDepth() &&
+ renderBuffers[i] == frameBuffer->getDepth()->getID()) {
+ frameBuffer->setDepth(nullptr);
+ }
+ if (frameBuffer->getStencil() &&
+ renderBuffers[i] == frameBuffer->getStencil()->getID()) {
+ frameBuffer->setStencil(nullptr);
+ }
+ }
+ }
+
+ // then actually "delete" the buffers
+ for (int i = 0; i < n; ++i) {
+ GrRenderBufferObj *buffer = FIND(renderBuffers[i], GrRenderBufferObj,
+ kRenderBuffer_ObjTypes);
+ GrAlwaysAssert(buffer);
+
+ // OpenGL gives no guarantees if a render buffer is deleted
+ // while attached to something other than the currently
+ // bound frame buffer
+ GrAlwaysAssert(!buffer->getColorBound());
+ GrAlwaysAssert(!buffer->getDepthBound());
+ // However, at GrContext destroy time we release all GrRsources and so stencil buffers
+ // may get deleted before FBOs that refer to them.
+ //GrAlwaysAssert(!buffer->getStencilBound());
+
+ GrAlwaysAssert(!buffer->getDeleted());
+ buffer->deleteAction();
+ }
+ }
+
+ GrGLvoid framebufferRenderbuffer(GrGLenum target,
+ GrGLenum attachment,
+ GrGLenum renderbuffertarget,
+ GrGLuint renderBufferID) override {
+
+ GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
+ GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment ||
+ GR_GL_DEPTH_ATTACHMENT == attachment ||
+ GR_GL_STENCIL_ATTACHMENT == attachment);
+ GrAlwaysAssert(GR_GL_RENDERBUFFER == renderbuffertarget);
+
+ GrFrameBufferObj *framebuffer = this->getFrameBuffer();
+ // A render buffer cannot be attached to the default framebuffer
+ GrAlwaysAssert(framebuffer);
+
+ // a renderBufferID of 0 is acceptable - it unbinds the current
+ // render buffer
+ GrRenderBufferObj *renderbuffer = FIND(renderBufferID, GrRenderBufferObj,
+ kRenderBuffer_ObjTypes);
+
+ switch (attachment) {
+ case GR_GL_COLOR_ATTACHMENT0:
+ framebuffer->setColor(renderbuffer);
+ break;
+ case GR_GL_DEPTH_ATTACHMENT:
+ framebuffer->setDepth(renderbuffer);
+ break;
+ case GR_GL_STENCIL_ATTACHMENT:
+ framebuffer->setStencil(renderbuffer);
+ break;
+ default:
+ GrAlwaysAssert(false);
+ break;
+ };
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ GrGLvoid framebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget,
+ GrGLuint textureID, GrGLint level) override {
+
+ GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
+ GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment ||
+ GR_GL_DEPTH_ATTACHMENT == attachment ||
+ GR_GL_STENCIL_ATTACHMENT == attachment);
+ GrAlwaysAssert(GR_GL_TEXTURE_2D == textarget);
+
+ GrFrameBufferObj *framebuffer = this->getFrameBuffer();
+ // A texture cannot be attached to the default framebuffer
+ GrAlwaysAssert(framebuffer);
+
+ // A textureID of 0 is allowed - it unbinds the currently bound texture
+ GrTextureObj *texture = FIND(textureID, GrTextureObj, kTexture_ObjTypes);
+ if (texture) {
+ // The texture shouldn't be bound to a texture unit - this
+ // could lead to a feedback loop
+ GrAlwaysAssert(!texture->getBound());
+ }
+
+ GrAlwaysAssert(0 == level);
+
+ switch (attachment) {
+ case GR_GL_COLOR_ATTACHMENT0:
+ framebuffer->setColor(texture);
+ break;
+ case GR_GL_DEPTH_ATTACHMENT:
+ framebuffer->setDepth(texture);
+ break;
+ case GR_GL_STENCIL_ATTACHMENT:
+ framebuffer->setStencil(texture);
+ break;
+ default:
+ GrAlwaysAssert(false);
+ break;
+ };
+ }
+
+ GrGLuint createProgram() override {
+
+ GrProgramObj *program = CREATE(GrProgramObj, kProgram_ObjTypes);
+
+ return program->getID();
+ }
+
+ GrGLuint createShader(GrGLenum type) override {
+
+ GrAlwaysAssert(GR_GL_VERTEX_SHADER == type ||
+ GR_GL_FRAGMENT_SHADER == type);
+
+ GrShaderObj *shader = CREATE(GrShaderObj, kShader_ObjTypes);
+ shader->setType(type);
+
+ return shader->getID();
+ }
+
+ GrGLenum checkFramebufferStatus(GrGLenum target) override { return GR_GL_FRAMEBUFFER_COMPLETE; }
+
+ GrGLvoid deleteProgram(GrGLuint programID) override {
+
+ GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes);
+ GrAlwaysAssert(program);
+
+ if (program->getRefCount()) {
+ // someone is still using this program so we can't delete it here
+ program->setMarkedForDeletion();
+ } else {
+ program->deleteAction();
+ }
+ }
+
+ GrGLvoid deleteShader(GrGLuint shaderID) override {
+
+ GrShaderObj *shader = FIND(shaderID, GrShaderObj, kShader_ObjTypes);
+ GrAlwaysAssert(shader);
+
+ if (shader->getRefCount()) {
+ // someone is still using this shader so we can't delete it here
+ shader->setMarkedForDeletion();
+ } else {
+ shader->deleteAction();
+ }
+ }
+
+ GrGLvoid genBuffers(GrGLsizei n, GrGLuint* ids) override {
+ this->genObjs(kBuffer_ObjTypes, n, ids);
+ }
+
+ GrGLvoid genFramebuffers(GrGLsizei n, GrGLuint* ids) override {
+ this->genObjs(kFrameBuffer_ObjTypes, n, ids);
+ }
+
+ GrGLvoid genRenderbuffers(GrGLsizei n, GrGLuint* ids) override {
+ this->genObjs(kRenderBuffer_ObjTypes, n, ids);
+ }
+
+ GrGLvoid genTextures(GrGLsizei n, GrGLuint* ids) override {
+ this->genObjs(kTexture_ObjTypes, n, ids);
+ }
+
+ GrGLvoid genVertexArrays(GrGLsizei n, GrGLuint* ids) override {
+ this->genObjs(kVertexArray_ObjTypes, n, ids);
+ }
+
+ GrGLvoid genQueries(GrGLsizei n, GrGLuint *ids) override { this->genGenericIds(n, ids); }
+
+ 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 getMultisamplefv(GrGLenum pname, GrGLuint index, GrGLfloat* val) override {
+ val[0] = val[1] = 0.5f;
+ }
+
+ 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 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 Debug GL";
+ case GR_GL_SHADING_LANGUAGE_VERSION:
+ return (const GrGLubyte*)"4.20.8 Debug GLSL";
+ case GR_GL_VENDOR:
+ return (const GrGLubyte*)"Debug Vendor";
+ case GR_GL_RENDERER:
+ return (const GrGLubyte*)"The Debug (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;
+ }
+ }
+
+ GrGLvoid getTexLevelParameteriv(GrGLenum target, GrGLint level, GrGLenum pname,
+ GrGLint* params) override {
+ // we used to use this to query stuff about externally created textures,
+ // now we just require clients to tell us everything about the texture.
+ SkFAIL("Should never query texture parameters.");
+ }
+
+ GrGLvoid deleteVertexArrays(GrGLsizei n, const GrGLuint* ids) override {
+ for (GrGLsizei i = 0; i < n; ++i) {
+ GrVertexArrayObj* array = FIND(ids[i], GrVertexArrayObj, kVertexArray_ObjTypes);
+ GrAlwaysAssert(array);
+
+ // Deleting the current vertex array binds object 0
+ if (this->getVertexArray() == array) {
+ this->setVertexArray(nullptr);
+ }
+
+ if (array->getRefCount()) {
+ // someone is still using this vertex array so we can't delete it here
+ array->setMarkedForDeletion();
+ } else {
+ array->deleteAction();
+ }
+ }
+ }
+
+ GrGLvoid bindVertexArray(GrGLuint id) override {
+ GrVertexArrayObj* array = FIND(id, GrVertexArrayObj, kVertexArray_ObjTypes);
+ GrAlwaysAssert((0 == id) || array);
+ this->setVertexArray(array);
+ }
+
+ GrGLvoid bindBuffer(GrGLenum target, GrGLuint bufferID) override {
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || GR_GL_ELEMENT_ARRAY_BUFFER == target);
+
+ GrBufferObj *buffer = FIND(bufferID, GrBufferObj, kBuffer_ObjTypes);
+ // 0 is a permissible bufferID - it unbinds the current buffer
+
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ this->setArrayBuffer(buffer);
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ this->setElementArrayBuffer(buffer);
+ break;
+ default:
+ SkFAIL("Unexpected target to glBindBuffer");
+ break;
+ }
+ }
+
+ // deleting a bound buffer has the side effect of binding 0
+ GrGLvoid deleteBuffers(GrGLsizei n, const GrGLuint* ids) override {
+ // first potentially unbind the buffers
+ for (int i = 0; i < n; ++i) {
+
+ if (this->getArrayBuffer() &&
+ ids[i] == this->getArrayBuffer()->getID()) {
+ // this ID is the current array buffer
+ this->setArrayBuffer(nullptr);
+ }
+ if (this->getElementArrayBuffer() &&
+ ids[i] == this->getElementArrayBuffer()->getID()) {
+ // this ID is the current element array buffer
+ this->setElementArrayBuffer(nullptr);
+ }
+ }
+
+ // then actually "delete" the buffers
+ for (int i = 0; i < n; ++i) {
+ GrBufferObj *buffer = FIND(ids[i], GrBufferObj, kBuffer_ObjTypes);
+ GrAlwaysAssert(buffer);
+
+ GrAlwaysAssert(!buffer->getDeleted());
+ buffer->deleteAction();
+ }
+ }
+
+ // map a buffer to the caller's address space
+ GrGLvoid* mapBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length,
+ GrGLbitfield access) override {
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+ GR_GL_ELEMENT_ARRAY_BUFFER == target);
+
+ // We only expect read access and we expect that the buffer or range is always invalidated.
+ GrAlwaysAssert(!SkToBool(GR_GL_MAP_READ_BIT & access));
+ GrAlwaysAssert((GR_GL_MAP_INVALIDATE_BUFFER_BIT | GR_GL_MAP_INVALIDATE_RANGE_BIT) & access);
+
+ GrBufferObj *buffer = nullptr;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buffer = this->getArrayBuffer();
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buffer = this->getElementArrayBuffer();
+ break;
+ default:
+ SkFAIL("Unexpected target to glMapBufferRange");
+ break;
+ }
+
+ if (buffer) {
+ GrAlwaysAssert(offset >= 0 && offset + length <= buffer->getSize());
+ GrAlwaysAssert(!buffer->getMapped());
+ buffer->setMapped(offset, length);
+ return buffer->getDataPtr() + offset;
+ }
+
+ GrAlwaysAssert(false);
+ return nullptr; // no buffer bound to the target
+ }
+
+ GrGLvoid* mapBuffer(GrGLenum target, GrGLenum access) override {
+ GrAlwaysAssert(GR_GL_WRITE_ONLY == access);
+
+ GrBufferObj *buffer = nullptr;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buffer = this->getArrayBuffer();
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buffer = this->getElementArrayBuffer();
+ break;
+ default:
+ SkFAIL("Unexpected target to glMapBuffer");
+ break;
+ }
+
+ return this->mapBufferRange(target, 0, buffer->getSize(),
+ GR_GL_MAP_WRITE_BIT | GR_GL_MAP_INVALIDATE_BUFFER_BIT);
+ }
+
+ // remove a buffer from the caller's address space
+ // TODO: check if the "access" method from "glMapBuffer" was honored
+ GrGLboolean unmapBuffer(GrGLenum target) override {
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+ GR_GL_ELEMENT_ARRAY_BUFFER == target);
+
+ GrBufferObj *buffer = nullptr;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buffer = this->getArrayBuffer();
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buffer = this->getElementArrayBuffer();
+ break;
+ default:
+ SkFAIL("Unexpected target to glUnmapBuffer");
+ break;
+ }
+
+ if (buffer) {
+ GrAlwaysAssert(buffer->getMapped());
+ buffer->resetMapped();
+ return GR_GL_TRUE;
+ }
+
+ GrAlwaysAssert(false);
+ return GR_GL_FALSE; // GR_GL_INVALID_OPERATION;
+ }
+
+ GrGLvoid flushMappedBufferRange(GrGLenum target, GrGLintptr offset,
+ GrGLsizeiptr length) override {
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+ GR_GL_ELEMENT_ARRAY_BUFFER == target);
+
+ GrBufferObj *buffer = nullptr;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buffer = this->getArrayBuffer();
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buffer = this->getElementArrayBuffer();
+ break;
+ default:
+ SkFAIL("Unexpected target to glUnmapBuffer");
+ break;
+ }
+
+ if (buffer) {
+ GrAlwaysAssert(buffer->getMapped());
+ GrAlwaysAssert(offset >= 0 && (offset + length) <= buffer->getMappedLength());
+ } else {
+ GrAlwaysAssert(false);
+ }
+ }
+
+ GrGLvoid getBufferParameteriv(GrGLenum target, GrGLenum value, GrGLint* params) override {
+
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+ GR_GL_ELEMENT_ARRAY_BUFFER == target);
+ GrAlwaysAssert(GR_GL_BUFFER_SIZE == value ||
+ GR_GL_BUFFER_USAGE == value);
+
+ GrBufferObj *buffer = nullptr;
+ switch (target) {
+ case GR_GL_ARRAY_BUFFER:
+ buffer = this->getArrayBuffer();
+ break;
+ case GR_GL_ELEMENT_ARRAY_BUFFER:
+ buffer = this->getElementArrayBuffer();
+ break;
+ }
+
+ GrAlwaysAssert(buffer);
+
+ switch (value) {
+ case GR_GL_BUFFER_MAPPED:
+ *params = GR_GL_FALSE;
+ if (buffer)
+ *params = buffer->getMapped() ? GR_GL_TRUE : GR_GL_FALSE;
+ break;
+ case GR_GL_BUFFER_SIZE:
+ *params = 0;
+ if (buffer)
+ *params = SkToInt(buffer->getSize());
+ break;
+ case GR_GL_BUFFER_USAGE:
+ *params = GR_GL_STATIC_DRAW;
+ if (buffer)
+ *params = buffer->getUsage();
+ break;
+ default:
+ SkFAIL("Unexpected value to glGetBufferParamateriv");
+ break;
+ }
+ }
+
+private:
+ // 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;
+
+ // the OpenGLES 2.0 spec says this must be >= 2
+ static const GrGLint kDefaultMaxTextureUnits = 8;
+
+ static const char* kExtensions[];
+
+ GrGLuint fCurrGenericID;
+ GrGLuint fCurrTextureUnit;
+ GrTextureUnitObj* fTextureUnits[kDefaultMaxTextureUnits];
+ GrBufferObj* fArrayBuffer;
+ GrBufferObj* fElementArrayBuffer;
+ GrVertexArrayObj* fVertexArray;
+ GrGLint fPackRowLength;
+ GrGLint fUnpackRowLength;
+ GrGLint fPackAlignment;
+ GrFrameBufferObj* fFrameBuffer;
+ GrRenderBufferObj* fRenderBuffer;
+ GrProgramObj* fProgram;
+ mutable bool fAbandoned;
+ // global store of all objects
+ SkTArray<GrFakeRefObj *> fObjects;
+
+ 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;
+ }
+ }
+
+ enum ObjTypes {
+ kTexture_ObjTypes = 0,
+ kBuffer_ObjTypes,
+ kRenderBuffer_ObjTypes,
+ kFrameBuffer_ObjTypes,
+ kShader_ObjTypes,
+ kProgram_ObjTypes,
+ kTextureUnit_ObjTypes,
+ kVertexArray_ObjTypes,
+ kObjTypeCount
+ };
+
+ typedef GrFakeRefObj *(*Create)();
+
+ static Create gFactoryFunc[kObjTypeCount];
+
+ GrGLvoid genObjs(ObjTypes type, GrGLsizei n, GrGLuint* ids) {
+ for (int i = 0; i < n; ++i) {
+ GrAlwaysAssert(ids[i] == 0);
+ GrFakeRefObj *obj = this->createObj(type);
+ GrAlwaysAssert(obj);
+ ids[i] = obj->getID();
+ }
+ }
+
+ GrFakeRefObj* createObj(ObjTypes type) {
+ GrFakeRefObj *temp = (*gFactoryFunc[type])();
+
+ fObjects.push_back(temp);
+
+ return temp;
+ }
+
+ GrFakeRefObj* findObject(GrGLuint ID, ObjTypes type) {
+ for (int i = 0; i < fObjects.count(); ++i) {
+ if (fObjects[i]->getID() == ID) { // && fObjects[i]->getType() == type) {
+ // The application shouldn't be accessing objects
+ // that (as far as OpenGL knows) were already deleted
+ GrAlwaysAssert(!fObjects[i]->getDeleted());
+ GrAlwaysAssert(!fObjects[i]->getMarkedForDeletion());
+ return fObjects[i];
+ }
+ }
+ return nullptr;
+ }
+
+ GrTextureUnitObj* getTextureUnit(int unit) {
+ GrAlwaysAssert(0 <= unit && kDefaultMaxTextureUnits > unit);
+
+ return fTextureUnits[unit];
+ }
+
+ void setArrayBuffer(GrBufferObj *arrayBuffer) {
+ if (fArrayBuffer) {
+ // automatically break the binding of the old buffer
+ GrAlwaysAssert(fArrayBuffer->getBound());
+ fArrayBuffer->resetBound();
+
+ GrAlwaysAssert(!fArrayBuffer->getDeleted());
+ fArrayBuffer->unref();
+ }
+
+ fArrayBuffer = arrayBuffer;
+
+ if (fArrayBuffer) {
+ GrAlwaysAssert(!fArrayBuffer->getDeleted());
+ fArrayBuffer->ref();
+
+ GrAlwaysAssert(!fArrayBuffer->getBound());
+ fArrayBuffer->setBound();
+ }
+ }
+
+ GrBufferObj* getArrayBuffer() { return fArrayBuffer; }
+ void setElementArrayBuffer(GrBufferObj *elementArrayBuffer) {
+ if (fElementArrayBuffer) {
+ // automatically break the binding of the old buffer
+ GrAlwaysAssert(fElementArrayBuffer->getBound());
+ fElementArrayBuffer->resetBound();
+
+ GrAlwaysAssert(!fElementArrayBuffer->getDeleted());
+ fElementArrayBuffer->unref();
+ }
+
+ fElementArrayBuffer = elementArrayBuffer;
+
+ if (fElementArrayBuffer) {
+ GrAlwaysAssert(!fElementArrayBuffer->getDeleted());
+ fElementArrayBuffer->ref();
+
+ GrAlwaysAssert(!fElementArrayBuffer->getBound());
+ fElementArrayBuffer->setBound();
+ }
+ }
+
+ GrBufferObj *getElementArrayBuffer() { return fElementArrayBuffer; }
+
+ void setVertexArray(GrVertexArrayObj* vertexArray) {
+ if (vertexArray) {
+ SkASSERT(!vertexArray->getDeleted());
+ }
+ SkRefCnt_SafeAssign(fVertexArray, vertexArray);
+ }
+
+ GrVertexArrayObj* getVertexArray() { return fVertexArray; }
+
+ void setTexture(GrTextureObj *texture) {
+ fTextureUnits[fCurrTextureUnit]->setTexture(texture);
+ }
+
+ void setFrameBuffer(GrFrameBufferObj *frameBuffer) {
+ if (fFrameBuffer) {
+ GrAlwaysAssert(fFrameBuffer->getBound());
+ fFrameBuffer->resetBound();
+
+ GrAlwaysAssert(!fFrameBuffer->getDeleted());
+ fFrameBuffer->unref();
+ }
+
+ fFrameBuffer = frameBuffer;
+
+ if (fFrameBuffer) {
+ GrAlwaysAssert(!fFrameBuffer->getDeleted());
+ fFrameBuffer->ref();
+
+ GrAlwaysAssert(!fFrameBuffer->getBound());
+ fFrameBuffer->setBound();
+ }
+ }
+
+ GrFrameBufferObj *getFrameBuffer() { return fFrameBuffer; }
+
+ void setRenderBuffer(GrRenderBufferObj *renderBuffer) {
+ if (fRenderBuffer) {
+ GrAlwaysAssert(fRenderBuffer->getBound());
+ fRenderBuffer->resetBound();
+
+ GrAlwaysAssert(!fRenderBuffer->getDeleted());
+ fRenderBuffer->unref();
+ }
+
+ fRenderBuffer = renderBuffer;
+
+ if (fRenderBuffer) {
+ GrAlwaysAssert(!fRenderBuffer->getDeleted());
+ fRenderBuffer->ref();
+
+ GrAlwaysAssert(!fRenderBuffer->getBound());
+ fRenderBuffer->setBound();
+ }
+ }
+ GrRenderBufferObj *getRenderBuffer() { return fRenderBuffer; }
+
+ void useProgram(GrProgramObj *program) {
+ if (fProgram) {
+ GrAlwaysAssert(fProgram->getInUse());
+ fProgram->resetInUse();
+
+ GrAlwaysAssert(!fProgram->getDeleted());
+ fProgram->unref();
+ }
+
+ fProgram = program;
+
+ if (fProgram) {
+ GrAlwaysAssert(!fProgram->getDeleted());
+ fProgram->ref();
+
+ GrAlwaysAssert(!fProgram->getInUse());
+ fProgram->setInUse();
+ }
+ }
+
+ void report() const {
+ for (int i = 0; i < fObjects.count(); ++i) {
+ if (!fAbandoned) {
+ GrAlwaysAssert(0 == fObjects[i]->getRefCount());
+ GrAlwaysAssert(fObjects[i]->getDeleted());
+ }
+ }
+ }
+
+ typedef GrGLTestInterface INHERITED;
+};
+
+#undef CREATE
+#undef FIND
+
+DebugInterface::Create DebugInterface::gFactoryFunc[kObjTypeCount] = {
+ GrTextureObj::createGrTextureObj,
+ GrBufferObj::createGrBufferObj,
+ GrRenderBufferObj::createGrRenderBufferObj,
+ GrFrameBufferObj::createGrFrameBufferObj,
+ GrShaderObj::createGrShaderObj,
+ GrProgramObj::createGrProgramObj,
+ GrTextureUnitObj::createGrTextureUnitObj,
+ GrVertexArrayObj::createGrVertexArrayObj,
+};
+
+const char* DebugInterface::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 DebugGLContext : public sk_gpu_test::GLContext {
+public:
+ DebugGLContext() {
+ this->init(new DebugInterface());
+ }
+
+ ~DebugGLContext() 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* CreateDebugGLContext() {
+ GLContext* ctx = new DebugGLContext();
+ if (ctx->isValid()) {
+ return ctx;
+ }
+ delete ctx;
+ return nullptr;
+}
+}
diff --git a/tools/gpu/gl/debug/DebugGLContext.h b/tools/gpu/gl/debug/DebugGLContext.h
new file mode 100644
index 0000000000..0ac505b632
--- /dev/null
+++ b/tools/gpu/gl/debug/DebugGLContext.h
@@ -0,0 +1,17 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef DebugGLContext_DEFINED
+#define DebugGLContext_DEFINED
+
+#include "gl/GLContext.h"
+
+namespace sk_gpu_test {
+GLContext* CreateDebugGLContext();
+} // namespace sk_gpu_test
+
+#endif
diff --git a/tools/gpu/gl/debug/GrBufferObj.cpp b/tools/gpu/gl/debug/GrBufferObj.cpp
new file mode 100644
index 0000000000..37d4438ef6
--- /dev/null
+++ b/tools/gpu/gl/debug/GrBufferObj.cpp
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrBufferObj.h"
+
+void GrBufferObj::allocate(GrGLsizeiptr size, const GrGLchar *dataPtr) {
+ GrAlwaysAssert(size >= 0);
+
+ // delete pre-existing data
+ delete[] fDataPtr;
+
+ fSize = size;
+ fDataPtr = new GrGLchar[size];
+ if (dataPtr) {
+ memcpy(fDataPtr, dataPtr, fSize);
+ }
+ // TODO: w/ no dataPtr the data is unitialized - this could be tracked
+}
+
+void GrBufferObj::deleteAction() {
+
+ // buffers are automatically unmapped when deleted
+ this->resetMapped();
+
+ this->INHERITED::deleteAction();
+}
diff --git a/tools/gpu/gl/debug/GrBufferObj.h b/tools/gpu/gl/debug/GrBufferObj.h
new file mode 100644
index 0000000000..96aef6ed0a
--- /dev/null
+++ b/tools/gpu/gl/debug/GrBufferObj.h
@@ -0,0 +1,76 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrBufferObj_DEFINED
+#define GrBufferObj_DEFINED
+
+#include "GrFakeRefObj.h"
+#include "gl/GrGLDefines.h"
+
+////////////////////////////////////////////////////////////////////////////////
+class GrBufferObj : public GrFakeRefObj {
+ GR_DEFINE_CREATOR(GrBufferObj);
+
+public:
+ GrBufferObj()
+ : GrFakeRefObj()
+ , fDataPtr(nullptr)
+ , fMapped(false)
+ , fBound(false)
+ , fSize(0)
+ , fUsage(GR_GL_STATIC_DRAW) {
+ }
+ virtual ~GrBufferObj() {
+ delete[] fDataPtr;
+ }
+
+ void access() {
+ // cannot access the buffer if it is currently mapped
+ GrAlwaysAssert(!fMapped);
+ }
+
+ void setMapped(GrGLintptr offset, GrGLsizeiptr length) {
+ fMapped = true;
+ fMappedOffset = offset;
+ fMappedLength = length;
+ }
+ void resetMapped() { fMapped = false; }
+ bool getMapped() const { return fMapped; }
+ GrGLintptr getMappedOffset() const { return fMappedOffset; }
+ GrGLsizeiptr getMappedLength() const { return fMappedLength; }
+
+ void setBound() { fBound = true; }
+ void resetBound() { fBound = false; }
+ bool getBound() const { return fBound; }
+
+ void allocate(GrGLsizeiptr size, const GrGLchar *dataPtr);
+ GrGLsizeiptr getSize() const { return fSize; }
+ GrGLchar *getDataPtr() { return fDataPtr; }
+
+ void setUsage(GrGLint usage) { fUsage = usage; }
+ GrGLint getUsage() const { return fUsage; }
+
+ void deleteAction() override;
+
+protected:
+private:
+
+ GrGLchar* fDataPtr;
+ bool fMapped; // is the buffer object mapped via "glMapBuffer[Range]"?
+ GrGLintptr fMappedOffset; // the offset of the buffer range that is mapped
+ GrGLsizeiptr fMappedLength; // the size of the buffer range that is mapped
+ bool fBound; // is the buffer object bound via "glBindBuffer"?
+ GrGLsizeiptr fSize; // size in bytes
+ GrGLint fUsage; // one of: GL_STREAM_DRAW,
+ // GL_STATIC_DRAW,
+ // GL_DYNAMIC_DRAW
+
+ typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrBufferObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrFBBindableObj.h b/tools/gpu/gl/debug/GrFBBindableObj.h
new file mode 100644
index 0000000000..e2b43a6a1b
--- /dev/null
+++ b/tools/gpu/gl/debug/GrFBBindableObj.h
@@ -0,0 +1,88 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrFBBindableObj_DEFINED
+#define GrFBBindableObj_DEFINED
+
+#include "SkTDArray.h"
+#include "GrFakeRefObj.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// A common base class for render buffers and textures
+class GrFBBindableObj : public GrFakeRefObj {
+
+public:
+ GrFBBindableObj()
+ : GrFakeRefObj() {
+ }
+
+ virtual ~GrFBBindableObj() {
+ GrAlwaysAssert(0 == fColorReferees.count());
+ GrAlwaysAssert(0 == fDepthReferees.count());
+ GrAlwaysAssert(0 == fStencilReferees.count());
+ }
+
+ void setColorBound(GrFakeRefObj *referee) {
+ fColorReferees.append(1, &referee);
+ }
+ void resetColorBound(GrFakeRefObj *referee) {
+ int index = fColorReferees.find(referee);
+ GrAlwaysAssert(0 <= index);
+ fColorReferees.removeShuffle(index);
+ }
+ bool getColorBound(GrFakeRefObj *referee) const {
+ int index = fColorReferees.find(referee);
+ return 0 <= index;
+ }
+ bool getColorBound() const {
+ return 0 != fColorReferees.count();
+ }
+
+ void setDepthBound(GrFakeRefObj *referee) {
+ fDepthReferees.append(1, &referee);
+ }
+ void resetDepthBound(GrFakeRefObj *referee) {
+ int index = fDepthReferees.find(referee);
+ GrAlwaysAssert(0 <= index);
+ fDepthReferees.removeShuffle(index);
+ }
+ bool getDepthBound(GrFakeRefObj *referee) const {
+ int index = fDepthReferees.find(referee);
+ return 0 <= index;
+ }
+ bool getDepthBound() const {
+ return 0 != fDepthReferees.count();
+ }
+
+ void setStencilBound(GrFakeRefObj *referee) {
+ fStencilReferees.append(1, &referee);
+ }
+ void resetStencilBound(GrFakeRefObj *referee) {
+ int index = fStencilReferees.find(referee);
+ GrAlwaysAssert(0 <= index);
+ fStencilReferees.removeShuffle(index);
+ }
+ bool getStencilBound(GrFakeRefObj *referee) const {
+ int index = fStencilReferees.find(referee);
+ return 0 <= index;
+ }
+ bool getStencilBound() const {
+ return 0 != fStencilReferees.count();
+ }
+
+
+protected:
+private:
+ SkTDArray<GrFakeRefObj *> fColorReferees; // frame buffers that use this as a color buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D")
+ SkTDArray<GrFakeRefObj *> fDepthReferees; // frame buffers that use this as a depth buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D")
+ SkTDArray<GrFakeRefObj *> fStencilReferees; // frame buffers that use this as a stencil buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D")
+
+ typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrFBBindableObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrFakeRefObj.h b/tools/gpu/gl/debug/GrFakeRefObj.h
new file mode 100644
index 0000000000..30580516fa
--- /dev/null
+++ b/tools/gpu/gl/debug/GrFakeRefObj.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrFakeRefObj_DEFINED
+#define GrFakeRefObj_DEFINED
+
+#include "SkTypes.h"
+#include "gl/GrGLInterface.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// This object is used to track the OpenGL objects. We don't use real
+// reference counting (i.e., we don't free the objects when their ref count
+// goes to 0) so that we can detect invalid memory accesses. The refs we
+// are tracking in this class are actually OpenGL's references to the objects
+// not "ours"
+// Each object also gets a unique globally identifying ID
+class GrFakeRefObj : SkNoncopyable {
+public:
+ GrFakeRefObj()
+ : fRef(0)
+ , fMarkedForDeletion(false)
+ , fDeleted(false) {
+
+ // source for globally unique IDs - 0 is reserved!
+ static int fNextID = 0;
+
+ fID = ++fNextID;
+ }
+ virtual ~GrFakeRefObj() {};
+
+ void ref() {
+ fRef++;
+ }
+ void unref() {
+ fRef--;
+ GrAlwaysAssert(fRef >= 0);
+
+ // often in OpenGL a given object may still be in use when the
+ // delete call is made. In these cases the object is marked
+ // for deletion and then freed when it is no longer in use
+ if (0 == fRef && fMarkedForDeletion) {
+ this->deleteAction();
+ }
+ }
+ int getRefCount() const { return fRef; }
+
+ GrGLuint getID() const { return fID; }
+
+ void setMarkedForDeletion() { fMarkedForDeletion = true; }
+ bool getMarkedForDeletion() const { return fMarkedForDeletion; }
+
+ bool getDeleted() const { return fDeleted; }
+
+ // The deleteAction fires if the object has been marked for deletion but
+ // couldn't be deleted earlier due to refs
+ virtual void deleteAction() {
+ this->setDeleted();
+ }
+
+protected:
+private:
+ int fRef; // ref count
+ GrGLuint fID; // globally unique ID
+ bool fMarkedForDeletion;
+ // The deleted flag is only set when OpenGL thinks the object is deleted
+ // It is obviously still allocated w/in this framework
+ bool fDeleted;
+
+ // setDeleted should only ever appear in the deleteAction method!
+ void setDeleted() { fDeleted = true; }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Each class derived from GrFakeRefObj should use this macro to add a
+// factory creation entry point. This entry point is used by the GrGLDebug
+// object to instantiate the various objects
+// all globally unique IDs
+#define GR_DEFINE_CREATOR(className) \
+public: \
+ static GrFakeRefObj *create##className() { return new className; }
+
+#endif // GrFakeRefObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrFrameBufferObj.cpp b/tools/gpu/gl/debug/GrFrameBufferObj.cpp
new file mode 100644
index 0000000000..7dc12acafb
--- /dev/null
+++ b/tools/gpu/gl/debug/GrFrameBufferObj.cpp
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrFrameBufferObj.h"
+#include "GrFBBindableObj.h"
+
+void GrFrameBufferObj::setColor(GrFBBindableObj *buffer) {
+ if (fColorBuffer) {
+ // automatically break the binding of the old buffer
+ GrAlwaysAssert(fColorBuffer->getColorBound(this));
+ fColorBuffer->resetColorBound(this);
+
+ GrAlwaysAssert(!fColorBuffer->getDeleted());
+ fColorBuffer->unref();
+ }
+ fColorBuffer = buffer;
+ if (fColorBuffer) {
+ GrAlwaysAssert(!fColorBuffer->getDeleted());
+ fColorBuffer->ref();
+
+ GrAlwaysAssert(!fColorBuffer->getColorBound(this));
+ fColorBuffer->setColorBound(this);
+ }
+}
+
+void GrFrameBufferObj::setDepth(GrFBBindableObj *buffer) {
+ if (fDepthBuffer) {
+ // automatically break the binding of the old buffer
+ GrAlwaysAssert(fDepthBuffer->getDepthBound(this));
+ fDepthBuffer->resetDepthBound(this);
+
+ GrAlwaysAssert(!fDepthBuffer->getDeleted());
+ fDepthBuffer->unref();
+ }
+ fDepthBuffer = buffer;
+ if (fDepthBuffer) {
+ GrAlwaysAssert(!fDepthBuffer->getDeleted());
+ fDepthBuffer->ref();
+
+ GrAlwaysAssert(!fDepthBuffer->getDepthBound(this));
+ fDepthBuffer->setDepthBound(this);
+ }
+}
+
+void GrFrameBufferObj::setStencil(GrFBBindableObj *buffer) {
+ if (fStencilBuffer) {
+ // automatically break the binding of the old buffer
+ GrAlwaysAssert(fStencilBuffer->getStencilBound(this));
+ fStencilBuffer->resetStencilBound(this);
+
+ //GrAlwaysAssert(!fStencilBuffer->getDeleted());
+ fStencilBuffer->unref();
+ }
+ fStencilBuffer = buffer;
+ if (fStencilBuffer) {
+ GrAlwaysAssert(!fStencilBuffer->getDeleted());
+ fStencilBuffer->ref();
+
+ GrAlwaysAssert(!fStencilBuffer->getStencilBound(this));
+ fStencilBuffer->setStencilBound(this);
+ }
+}
diff --git a/tools/gpu/gl/debug/GrFrameBufferObj.h b/tools/gpu/gl/debug/GrFrameBufferObj.h
new file mode 100644
index 0000000000..42a0effe07
--- /dev/null
+++ b/tools/gpu/gl/debug/GrFrameBufferObj.h
@@ -0,0 +1,68 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrFrameBufferObj_DEFINED
+#define GrFrameBufferObj_DEFINED
+
+#include "GrFakeRefObj.h"
+class GrFBBindableObj;
+
+////////////////////////////////////////////////////////////////////////////////
+// TODO: when a framebuffer obj is bound the GL_SAMPLES query must return 0
+// TODO: GL_STENCIL_BITS must also be redirected to the framebuffer
+class GrFrameBufferObj : public GrFakeRefObj {
+ GR_DEFINE_CREATOR(GrFrameBufferObj);
+
+public:
+ GrFrameBufferObj()
+ : GrFakeRefObj()
+ , fBound(false)
+ , fColorBuffer(nullptr)
+ , fDepthBuffer(nullptr)
+ , fStencilBuffer(nullptr) {
+ }
+
+ virtual ~GrFrameBufferObj() {
+ fColorBuffer = nullptr;
+ fDepthBuffer = nullptr;
+ fStencilBuffer = nullptr;
+ }
+
+ void setBound() { fBound = true; }
+ void resetBound() { fBound = false; }
+ bool getBound() const { return fBound; }
+
+ void setColor(GrFBBindableObj *buffer);
+ GrFBBindableObj *getColor() { return fColorBuffer; }
+
+ void setDepth(GrFBBindableObj *buffer);
+ GrFBBindableObj *getDepth() { return fDepthBuffer; }
+
+ void setStencil(GrFBBindableObj *buffer);
+ GrFBBindableObj *getStencil() { return fStencilBuffer; }
+
+ void deleteAction() override {
+
+ setColor(nullptr);
+ setDepth(nullptr);
+ setStencil(nullptr);
+
+ this->INHERITED::deleteAction();
+ }
+
+protected:
+private:
+ bool fBound; // is this frame buffer currently bound via "glBindFramebuffer"?
+ GrFBBindableObj * fColorBuffer;
+ GrFBBindableObj * fDepthBuffer;
+ GrFBBindableObj * fStencilBuffer;
+
+ typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrFrameBufferObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrProgramObj.cpp b/tools/gpu/gl/debug/GrProgramObj.cpp
new file mode 100644
index 0000000000..d6cc36bd74
--- /dev/null
+++ b/tools/gpu/gl/debug/GrProgramObj.cpp
@@ -0,0 +1,27 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrProgramObj.h"
+#include "GrShaderObj.h"
+
+void GrProgramObj::AttachShader(GrShaderObj *shader) {
+ shader->ref();
+ fShaders.push_back(shader);
+}
+
+void GrProgramObj::deleteAction() {
+
+ // shaders are automatically detached from a deleted program. They will only be
+ // deleted if they were marked for deletion by a prior call to glDeleteShader
+ for (int i = 0; i < fShaders.count(); ++i) {
+ fShaders[i]->unref();
+ }
+ fShaders.reset();
+
+ this->INHERITED::deleteAction();
+}
diff --git a/tools/gpu/gl/debug/GrProgramObj.h b/tools/gpu/gl/debug/GrProgramObj.h
new file mode 100644
index 0000000000..a25341a215
--- /dev/null
+++ b/tools/gpu/gl/debug/GrProgramObj.h
@@ -0,0 +1,43 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrProgramObj_DEFINED
+#define GrProgramObj_DEFINED
+
+#include "SkTArray.h"
+#include "GrFakeRefObj.h"
+class GrShaderObj;
+
+////////////////////////////////////////////////////////////////////////////////
+class GrProgramObj : public GrFakeRefObj {
+ GR_DEFINE_CREATOR(GrProgramObj);
+
+public:
+ GrProgramObj()
+ : GrFakeRefObj()
+ , fInUse(false) {}
+
+ void AttachShader(GrShaderObj *shader);
+
+ void deleteAction() override;
+
+ // TODO: this flag system won't work w/ multiple contexts!
+ void setInUse() { fInUse = true; }
+ void resetInUse() { fInUse = false; }
+ bool getInUse() const { return fInUse; }
+
+protected:
+
+private:
+ SkTArray<GrShaderObj *> fShaders;
+ bool fInUse; // has this program been activated by a glUseProgram call?
+
+ typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrProgramObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrRenderBufferObj.h b/tools/gpu/gl/debug/GrRenderBufferObj.h
new file mode 100644
index 0000000000..8231ef58df
--- /dev/null
+++ b/tools/gpu/gl/debug/GrRenderBufferObj.h
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrRenderBufferObj_DEFINED
+#define GrRenderBufferObj_DEFINED
+
+#include "GrFBBindableObj.h"
+
+////////////////////////////////////////////////////////////////////////////////
+class GrRenderBufferObj : public GrFBBindableObj {
+ GR_DEFINE_CREATOR(GrRenderBufferObj);
+
+public:
+ GrRenderBufferObj()
+ : GrFBBindableObj()
+ , fBound(false) {
+ }
+
+ void setBound() { fBound = true; }
+ void resetBound() { fBound = false; }
+ bool getBound() const { return fBound; }
+
+ void deleteAction() override {
+
+ this->INHERITED::deleteAction();
+ }
+
+protected:
+private:
+ bool fBound; // is this render buffer currently bound via "glBindRenderbuffer"?
+
+ typedef GrFBBindableObj INHERITED;
+};
+
+#endif // GrRenderBufferObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrShaderObj.cpp b/tools/gpu/gl/debug/GrShaderObj.cpp
new file mode 100644
index 0000000000..8d3caa1e3f
--- /dev/null
+++ b/tools/gpu/gl/debug/GrShaderObj.cpp
@@ -0,0 +1,14 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrShaderObj.h"
+
+void GrShaderObj::deleteAction() {
+
+ this->INHERITED::deleteAction();
+}
diff --git a/tools/gpu/gl/debug/GrShaderObj.h b/tools/gpu/gl/debug/GrShaderObj.h
new file mode 100644
index 0000000000..327bd7f084
--- /dev/null
+++ b/tools/gpu/gl/debug/GrShaderObj.h
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrShaderObj_DEFINED
+#define GrShaderObj_DEFINED
+
+#include "GrFakeRefObj.h"
+#include "gl/GrGLDefines.h"
+
+////////////////////////////////////////////////////////////////////////////////
+class GrShaderObj : public GrFakeRefObj {
+ GR_DEFINE_CREATOR(GrShaderObj);
+
+public:
+ GrShaderObj()
+ : GrFakeRefObj()
+ , fType(GR_GL_VERTEX_SHADER) {}
+
+ void setType(GrGLenum type) { fType = type; }
+ GrGLenum getType() { return fType; }
+
+ void deleteAction() override;
+
+protected:
+private:
+ GrGLenum fType; // either GR_GL_VERTEX_SHADER or GR_GL_FRAGMENT_SHADER
+
+ typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrShaderObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrTextureObj.cpp b/tools/gpu/gl/debug/GrTextureObj.cpp
new file mode 100644
index 0000000000..86063fbc99
--- /dev/null
+++ b/tools/gpu/gl/debug/GrTextureObj.cpp
@@ -0,0 +1,14 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrTextureObj.h"
+
+void GrTextureObj::deleteAction() {
+
+ this->INHERITED::deleteAction();
+}
diff --git a/tools/gpu/gl/debug/GrTextureObj.h b/tools/gpu/gl/debug/GrTextureObj.h
new file mode 100644
index 0000000000..fcf851db86
--- /dev/null
+++ b/tools/gpu/gl/debug/GrTextureObj.h
@@ -0,0 +1,57 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTextureObj_DEFINED
+#define GrTextureObj_DEFINED
+
+#include "GrFBBindableObj.h"
+
+class GrTextureUnitObj;
+
+////////////////////////////////////////////////////////////////////////////////
+class GrTextureObj : public GrFBBindableObj {
+ GR_DEFINE_CREATOR(GrTextureObj);
+
+public:
+ GrTextureObj()
+ : GrFBBindableObj() {
+ }
+
+ virtual ~GrTextureObj() {
+ GrAlwaysAssert(0 == fTextureUnitReferees.count());
+ }
+
+ void setBound(GrTextureUnitObj *referee) {
+ fTextureUnitReferees.append(1, &referee);
+ }
+
+ void resetBound(GrTextureUnitObj *referee) {
+ int index = fTextureUnitReferees.find(referee);
+ GrAlwaysAssert(0 <= index);
+ fTextureUnitReferees.removeShuffle(index);
+ }
+ bool getBound(GrTextureUnitObj *referee) const {
+ int index = fTextureUnitReferees.find(referee);
+ return 0 <= index;
+ }
+ bool getBound() const {
+ return 0 != fTextureUnitReferees.count();
+ }
+
+ void deleteAction() override;
+
+protected:
+
+private:
+ // texture units that bind this texture (via "glBindTexture")
+ SkTDArray<GrTextureUnitObj *> fTextureUnitReferees;
+
+ typedef GrFBBindableObj INHERITED;
+};
+
+#endif // GrTextureObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrTextureUnitObj.cpp b/tools/gpu/gl/debug/GrTextureUnitObj.cpp
new file mode 100644
index 0000000000..316dcecd93
--- /dev/null
+++ b/tools/gpu/gl/debug/GrTextureUnitObj.cpp
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrTextureUnitObj.h"
+#include "GrTextureObj.h"
+
+void GrTextureUnitObj::setTexture(GrTextureObj *texture) {
+
+ if (fTexture) {
+ GrAlwaysAssert(fTexture->getBound(this));
+ fTexture->resetBound(this);
+
+ GrAlwaysAssert(!fTexture->getDeleted());
+ fTexture->unref();
+ }
+
+ fTexture = texture;
+
+ if (fTexture) {
+ GrAlwaysAssert(!fTexture->getDeleted());
+ fTexture->ref();
+
+ GrAlwaysAssert(!fTexture->getBound(this));
+ fTexture->setBound(this);
+ }
+}
diff --git a/tools/gpu/gl/debug/GrTextureUnitObj.h b/tools/gpu/gl/debug/GrTextureUnitObj.h
new file mode 100644
index 0000000000..5c7a03980b
--- /dev/null
+++ b/tools/gpu/gl/debug/GrTextureUnitObj.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTextureUnitObj_DEFINED
+#define GrTextureUnitObj_DEFINED
+
+#include "GrFakeRefObj.h"
+class GrTextureObj;
+
+////////////////////////////////////////////////////////////////////////////////
+// Although texture unit objects are allocated & deallocated like the other
+// GL emulation objects they are derived from GrFakeRefObj to provide some
+// uniformity in how the debug interface class manages resources
+class GrTextureUnitObj : public GrFakeRefObj {
+ GR_DEFINE_CREATOR(GrTextureUnitObj);
+
+public:
+ GrTextureUnitObj()
+ : GrFakeRefObj()
+ , fNumber(0)
+ , fTexture(nullptr) {
+ }
+
+ void setNumber(GrGLenum number) {
+ fNumber = number;
+ }
+ GrGLenum getNumber() const { return fNumber; }
+
+ void setTexture(GrTextureObj *texture);
+ GrTextureObj *getTexture() { return fTexture; }
+
+protected:
+private:
+ GrGLenum fNumber;
+ GrTextureObj *fTexture;
+
+ typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrTextureUnitObj_DEFINED
diff --git a/tools/gpu/gl/debug/GrVertexArrayObj.h b/tools/gpu/gl/debug/GrVertexArrayObj.h
new file mode 100644
index 0000000000..989c610924
--- /dev/null
+++ b/tools/gpu/gl/debug/GrVertexArrayObj.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrVertexArrayObj_DEFINED
+#define GrVertexArrayObj_DEFINED
+
+#include "GrFakeRefObj.h"
+
+class GrVertexArrayObj : public GrFakeRefObj {
+ GR_DEFINE_CREATOR(GrVertexArrayObj);
+
+public:
+ GrVertexArrayObj() : GrFakeRefObj() {}
+
+ typedef GrFakeRefObj INHERITED;
+};
+#endif
diff --git a/tools/gpu/gl/egl/CreatePlatformGLContext_egl.cpp b/tools/gpu/gl/egl/CreatePlatformGLContext_egl.cpp
new file mode 100644
index 0000000000..ac2e7ca7cf
--- /dev/null
+++ b/tools/gpu/gl/egl/CreatePlatformGLContext_egl.cpp
@@ -0,0 +1,337 @@
+
+/*
+ * 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 "gl/GLContext.h"
+
+#include <GLES2/gl2.h>
+
+#define EGL_EGLEXT_PROTOTYPES
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "gl/GrGLDefines.h"
+#include "gl/GrGLUtil.h"
+
+namespace {
+
+// TODO: Share this class with ANGLE if/when it gets support for EGL_KHR_fence_sync.
+class SkEGLFenceSync : public SkGpuFenceSync {
+public:
+ static SkEGLFenceSync* CreateIfSupported(EGLDisplay);
+
+ SkPlatformGpuFence SK_WARN_UNUSED_RESULT insertFence() const override;
+ bool waitFence(SkPlatformGpuFence fence, bool flush) const override;
+ void deleteFence(SkPlatformGpuFence fence) const override;
+
+private:
+ SkEGLFenceSync(EGLDisplay display) : fDisplay(display) {}
+
+ EGLDisplay fDisplay;
+
+ typedef SkGpuFenceSync INHERITED;
+};
+
+class EGLGLContext : public sk_gpu_test::GLContext {
+public:
+ EGLGLContext(GrGLStandard forcedGpuAPI);
+ ~EGLGLContext() override;
+
+ GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
+ void destroyEGLImage(GrEGLImage) const override;
+ GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
+ sk_gpu_test::GLContext* createNew() const override;
+
+private:
+ void destroyGLContext();
+
+ void onPlatformMakeCurrent() const override;
+ void onPlatformSwapBuffers() const override;
+ GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
+
+ EGLContext fContext;
+ EGLDisplay fDisplay;
+ EGLSurface fSurface;
+};
+
+EGLGLContext::EGLGLContext(GrGLStandard forcedGpuAPI)
+ : fContext(EGL_NO_CONTEXT)
+ , fDisplay(EGL_NO_DISPLAY)
+ , fSurface(EGL_NO_SURFACE) {
+ static const EGLint kEGLContextAttribsForOpenGL[] = {
+ EGL_NONE
+ };
+
+ static const EGLint kEGLContextAttribsForOpenGLES[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ static const struct {
+ const EGLint* fContextAttribs;
+ EGLenum fAPI;
+ EGLint fRenderableTypeBit;
+ GrGLStandard fStandard;
+ } kAPIs[] = {
+ { // OpenGL
+ kEGLContextAttribsForOpenGL,
+ EGL_OPENGL_API,
+ EGL_OPENGL_BIT,
+ kGL_GrGLStandard
+ },
+ { // OpenGL ES. This seems to work for both ES2 and 3 (when available).
+ kEGLContextAttribsForOpenGLES,
+ EGL_OPENGL_ES_API,
+ EGL_OPENGL_ES2_BIT,
+ kGLES_GrGLStandard
+ },
+ };
+
+ size_t apiLimit = SK_ARRAY_COUNT(kAPIs);
+ size_t api = 0;
+ if (forcedGpuAPI == kGL_GrGLStandard) {
+ apiLimit = 1;
+ } else if (forcedGpuAPI == kGLES_GrGLStandard) {
+ api = 1;
+ }
+ SkASSERT(forcedGpuAPI == kNone_GrGLStandard || kAPIs[api].fStandard == forcedGpuAPI);
+
+ SkAutoTUnref<const GrGLInterface> gl;
+
+ for (; nullptr == gl.get() && api < apiLimit; ++api) {
+ fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ eglInitialize(fDisplay, &majorVersion, &minorVersion);
+
+#if 0
+ SkDebugf("VENDOR: %s\n", eglQueryString(fDisplay, EGL_VENDOR));
+ SkDebugf("APIS: %s\n", eglQueryString(fDisplay, EGL_CLIENT_APIS));
+ SkDebugf("VERSION: %s\n", eglQueryString(fDisplay, EGL_VERSION));
+ SkDebugf("EXTENSIONS %s\n", eglQueryString(fDisplay, EGL_EXTENSIONS));
+#endif
+
+ if (!eglBindAPI(kAPIs[api].fAPI)) {
+ continue;
+ }
+
+ EGLint numConfigs = 0;
+ const EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, kAPIs[api].fRenderableTypeBit,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_NONE
+ };
+
+ EGLConfig surfaceConfig;
+ if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) {
+ SkDebugf("eglChooseConfig failed. EGL Error: 0x%08x\n", eglGetError());
+ continue;
+ }
+
+ if (0 == numConfigs) {
+ SkDebugf("No suitable EGL config found.\n");
+ continue;
+ }
+
+ fContext = eglCreateContext(fDisplay, surfaceConfig, nullptr, kAPIs[api].fContextAttribs);
+ if (EGL_NO_CONTEXT == fContext) {
+ SkDebugf("eglCreateContext failed. EGL Error: 0x%08x\n", eglGetError());
+ continue;
+ }
+
+ static const EGLint kSurfaceAttribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+
+ fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, kSurfaceAttribs);
+ if (EGL_NO_SURFACE == fSurface) {
+ SkDebugf("eglCreatePbufferSurface failed. EGL Error: 0x%08x\n", eglGetError());
+ this->destroyGLContext();
+ continue;
+ }
+
+ if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+ SkDebugf("eglMakeCurrent failed. EGL Error: 0x%08x\n", eglGetError());
+ this->destroyGLContext();
+ continue;
+ }
+
+ gl.reset(GrGLCreateNativeInterface());
+ if (nullptr == gl.get()) {
+ SkDebugf("Failed to create gl interface.\n");
+ this->destroyGLContext();
+ continue;
+ }
+
+ if (!gl->validate()) {
+ SkDebugf("Failed to validate gl interface.\n");
+ this->destroyGLContext();
+ continue;
+ }
+
+ this->init(gl.release(), SkEGLFenceSync::CreateIfSupported(fDisplay));
+ break;
+ }
+}
+
+EGLGLContext::~EGLGLContext() {
+ this->teardown();
+ this->destroyGLContext();
+}
+
+void EGLGLContext::destroyGLContext() {
+ if (fDisplay) {
+ eglMakeCurrent(fDisplay, 0, 0, 0);
+
+ if (fContext) {
+ eglDestroyContext(fDisplay, fContext);
+ fContext = EGL_NO_CONTEXT;
+ }
+
+ if (fSurface) {
+ eglDestroySurface(fDisplay, fSurface);
+ fSurface = EGL_NO_SURFACE;
+ }
+
+ //TODO should we close the display?
+ fDisplay = EGL_NO_DISPLAY;
+ }
+}
+
+GrEGLImage EGLGLContext::texture2DToEGLImage(GrGLuint texID) const {
+ if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
+ return GR_EGL_NO_IMAGE;
+ }
+ GrEGLImage img;
+ GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0, GR_EGL_NONE };
+ GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>(texID);
+ GR_GL_CALL_RET(this->gl(), img,
+ EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, attribs));
+ return img;
+}
+
+void EGLGLContext::destroyEGLImage(GrEGLImage image) const {
+ GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image));
+}
+
+GrGLuint EGLGLContext::eglImageToExternalTexture(GrEGLImage image) const {
+ GrGLClearErr(this->gl());
+ if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
+ return 0;
+ }
+ typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage);
+
+ EGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
+ (EGLImageTargetTexture2DProc) eglGetProcAddress("glEGLImageTargetTexture2DOES");
+ if (!glEGLImageTargetTexture2D) {
+ return 0;
+ }
+ GrGLuint texID;
+ glGenTextures(1, &texID);
+ if (!texID) {
+ return 0;
+ }
+ glBindTexture(GR_GL_TEXTURE_EXTERNAL, texID);
+ if (glGetError() != GR_GL_NO_ERROR) {
+ glDeleteTextures(1, &texID);
+ return 0;
+ }
+ glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
+ if (glGetError() != GR_GL_NO_ERROR) {
+ glDeleteTextures(1, &texID);
+ return 0;
+ }
+ return texID;
+}
+
+sk_gpu_test::GLContext* EGLGLContext::createNew() const {
+ sk_gpu_test::GLContext* ctx = new EGLGLContext(this->gl()->fStandard);
+ if (ctx) {
+ ctx->makeCurrent();
+ }
+ return ctx;
+}
+
+void EGLGLContext::onPlatformMakeCurrent() const {
+ if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+ SkDebugf("Could not set the context.\n");
+ }
+}
+
+void EGLGLContext::onPlatformSwapBuffers() const {
+ if (!eglSwapBuffers(fDisplay, fSurface)) {
+ SkDebugf("Could not complete eglSwapBuffers.\n");
+ }
+}
+
+GrGLFuncPtr EGLGLContext::onPlatformGetProcAddress(const char* procName) const {
+ return eglGetProcAddress(procName);
+}
+
+static bool supports_egl_extension(EGLDisplay display, const char* extension) {
+ size_t extensionLength = strlen(extension);
+ const char* extensionsStr = eglQueryString(display, EGL_EXTENSIONS);
+ while (const char* match = strstr(extensionsStr, extension)) {
+ // Ensure the string we found is its own extension, not a substring of a larger extension
+ // (e.g. GL_ARB_occlusion_query / GL_ARB_occlusion_query2).
+ if ((match == extensionsStr || match[-1] == ' ') &&
+ (match[extensionLength] == ' ' || match[extensionLength] == '\0')) {
+ return true;
+ }
+ extensionsStr = match + extensionLength;
+ }
+ return false;
+}
+
+SkEGLFenceSync* SkEGLFenceSync::CreateIfSupported(EGLDisplay display) {
+ if (!display || !supports_egl_extension(display, "EGL_KHR_fence_sync")) {
+ return nullptr;
+ }
+ return new SkEGLFenceSync(display);
+}
+
+SkPlatformGpuFence SkEGLFenceSync::insertFence() const {
+ return eglCreateSyncKHR(fDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+}
+
+bool SkEGLFenceSync::waitFence(SkPlatformGpuFence platformFence, bool flush) const {
+ EGLSyncKHR eglsync = static_cast<EGLSyncKHR>(platformFence);
+ return EGL_CONDITION_SATISFIED_KHR ==
+ eglClientWaitSyncKHR(fDisplay,
+ eglsync,
+ flush ? EGL_SYNC_FLUSH_COMMANDS_BIT_KHR : 0,
+ EGL_FOREVER_KHR);
+}
+
+void SkEGLFenceSync::deleteFence(SkPlatformGpuFence platformFence) const {
+ EGLSyncKHR eglsync = static_cast<EGLSyncKHR>(platformFence);
+ eglDestroySyncKHR(fDisplay, eglsync);
+}
+
+} // anonymous namespace
+
+namespace sk_gpu_test {
+GLContext *CreatePlatformGLContext(GrGLStandard forcedGpuAPI, GLContext *shareContext) {
+ SkASSERT(!shareContext);
+ if (shareContext) {
+ return nullptr;
+ }
+ EGLGLContext *ctx = new EGLGLContext(forcedGpuAPI);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+} // namespace sk_gpu_test
+
diff --git a/tools/gpu/gl/glx/CreatePlatformGLContext_glx.cpp b/tools/gpu/gl/glx/CreatePlatformGLContext_glx.cpp
new file mode 100644
index 0000000000..b2168e3c5a
--- /dev/null
+++ b/tools/gpu/gl/glx/CreatePlatformGLContext_glx.cpp
@@ -0,0 +1,346 @@
+
+/*
+ * 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 "gl/GLContext.h"
+
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+#include <GL/glu.h>
+
+namespace {
+
+/* Note: Skia requires glx 1.3 or newer */
+
+/* This struct is taken from a mesa demo. Please update as required */
+static const struct { int major, minor; } gl_versions[] = {
+ {1, 0},
+ {1, 1},
+ {1, 2},
+ {1, 3},
+ {1, 4},
+ {1, 5},
+ {2, 0},
+ {2, 1},
+ {3, 0},
+ {3, 1},
+ {3, 2},
+ {3, 3},
+ {4, 0},
+ {4, 1},
+ {4, 2},
+ {4, 3},
+ {4, 4},
+ {0, 0} /* end of list */
+};
+#define NUM_GL_VERSIONS SK_ARRAY_COUNT(gl_versions)
+
+static bool ctxErrorOccurred = false;
+static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
+ ctxErrorOccurred = true;
+ return 0;
+}
+
+class GLXGLContext : public sk_gpu_test::GLContext {
+public:
+ GLXGLContext(GrGLStandard forcedGpuAPI, GLXGLContext* shareList);
+ ~GLXGLContext() override;
+
+private:
+ void destroyGLContext();
+
+ void onPlatformMakeCurrent() const override;
+ void onPlatformSwapBuffers() const override;
+ GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
+
+ GLXContext fContext;
+ Display* fDisplay;
+ Pixmap fPixmap;
+ GLXPixmap fGlxPixmap;
+};
+
+GLXGLContext::GLXGLContext(GrGLStandard forcedGpuAPI, GLXGLContext* shareContext)
+ : fContext(nullptr)
+ , fDisplay(nullptr)
+ , fPixmap(0)
+ , fGlxPixmap(0) {
+ fDisplay = XOpenDisplay(0);
+
+ GLXContext glxShareContext = shareContext ? shareContext->fContext : nullptr;
+
+ if (!fDisplay) {
+ SkDebugf("Failed to open X display.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ // Get a matching FB config
+ static int visual_attribs[] = {
+ GLX_X_RENDERABLE , True,
+ GLX_DRAWABLE_TYPE , GLX_PIXMAP_BIT,
+ None
+ };
+
+ int glx_major, glx_minor;
+
+ // FBConfigs were added in GLX version 1.3.
+ if (!glXQueryVersion(fDisplay, &glx_major, &glx_minor) ||
+ ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) {
+ SkDebugf("GLX version 1.3 or higher required.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ //SkDebugf("Getting matching framebuffer configs.\n");
+ int fbcount;
+ GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay),
+ visual_attribs, &fbcount);
+ if (!fbc) {
+ SkDebugf("Failed to retrieve a framebuffer config.\n");
+ this->destroyGLContext();
+ return;
+ }
+ //SkDebugf("Found %d matching FB configs.\n", fbcount);
+
+ // Pick the FB config/visual with the most samples per pixel
+ //SkDebugf("Getting XVisualInfos.\n");
+ int best_fbc = -1, best_num_samp = -1;
+
+ int i;
+ for (i = 0; i < fbcount; ++i) {
+ XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]);
+ if (vi) {
+ int samp_buf, samples;
+ glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+ glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples);
+
+ //SkDebugf(" Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
+ // " SAMPLES = %d\n",
+ // i, (unsigned int)vi->visualid, samp_buf, samples);
+
+ if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
+ best_fbc = i, best_num_samp = samples;
+ }
+ XFree(vi);
+ }
+
+ GLXFBConfig bestFbc = fbc[best_fbc];
+
+ // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
+ XFree(fbc);
+
+ // Get a visual
+ XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc);
+ //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
+
+ fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth);
+
+ if (!fPixmap) {
+ SkDebugf("Failed to create pixmap.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap);
+
+ // Done with the visual info data
+ XFree(vi);
+
+ // Create the context
+
+ // Install an X error handler so the application won't exit if GL 3.0
+ // context allocation fails.
+ //
+ // Note this error handler is global.
+ // All display connections in all threads of a process use the same
+ // error handler, so be sure to guard against other threads issuing
+ // X commands while this code is running.
+ ctxErrorOccurred = false;
+ int (*oldHandler)(Display*, XErrorEvent*) =
+ XSetErrorHandler(&ctxErrorHandler);
+
+ // Get the default screen's GLX extension list
+ const char *glxExts = glXQueryExtensionsString(
+ fDisplay, DefaultScreen(fDisplay)
+ );
+
+
+ // Check for the GLX_ARB_create_context extension string and the function.
+ // If either is not present, use GLX 1.3 context creation method.
+ if (!gluCheckExtension(reinterpret_cast<const GLubyte*>("GLX_ARB_create_context"),
+ reinterpret_cast<const GLubyte*>(glxExts))) {
+ if (kGLES_GrGLStandard != forcedGpuAPI) {
+ fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True);
+ }
+ } else {
+ //SkDebugf("Creating context.\n");
+ PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB =
+ (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
+
+ if (kGLES_GrGLStandard == forcedGpuAPI) {
+ if (gluCheckExtension(
+ reinterpret_cast<const GLubyte*>("GLX_EXT_create_context_es2_profile"),
+ reinterpret_cast<const GLubyte*>(glxExts))) {
+ static const int context_attribs_gles[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 0,
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT,
+ None
+ };
+ fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True,
+ context_attribs_gles);
+ }
+ } else {
+ // Well, unfortunately GLX will not just give us the highest context so instead we have
+ // to do this nastiness
+ for (i = NUM_GL_VERSIONS - 2; i > 0 ; i--) {
+ /* don't bother below GL 3.0 */
+ if (gl_versions[i].major == 3 && gl_versions[i].minor == 0) {
+ break;
+ }
+ // On Nvidia GPUs, to use Nv Path rendering we need a compatibility profile for the
+ // time being.
+ // TODO when Nvidia implements NVPR on Core profiles, we should start requesting
+ // core here
+ static const int context_attribs_gl[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, gl_versions[i].major,
+ GLX_CONTEXT_MINOR_VERSION_ARB, gl_versions[i].minor,
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
+ None
+ };
+ fContext =
+ glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True,
+ context_attribs_gl);
+
+ // Sync to ensure any errors generated are processed.
+ XSync(fDisplay, False);
+
+ if (!ctxErrorOccurred && fContext) {
+ break;
+ }
+ // try again
+ ctxErrorOccurred = false;
+ }
+
+ // Couldn't create GL 3.0 context.
+ // Fall back to old-style 2.x context.
+ // When a context version below 3.0 is requested,
+ // implementations will return the newest context version
+ // compatible with OpenGL versions less than version 3.0.
+ if (ctxErrorOccurred || !fContext) {
+ static const int context_attribs_gl_fallback[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 1,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 0,
+ None
+ };
+
+ ctxErrorOccurred = false;
+
+ fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True,
+ context_attribs_gl_fallback);
+ }
+ }
+ }
+
+ // Sync to ensure any errors generated are processed.
+ XSync(fDisplay, False);
+
+ // Restore the original error handler
+ XSetErrorHandler(oldHandler);
+
+ if (ctxErrorOccurred || !fContext) {
+ SkDebugf("Failed to create an OpenGL context.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ // Verify that context is a direct context
+ if (!glXIsDirect(fDisplay, fContext)) {
+ //SkDebugf("Indirect GLX rendering context obtained.\n");
+ } else {
+ //SkDebugf("Direct GLX rendering context obtained.\n");
+ }
+
+ //SkDebugf("Making context current.\n");
+ if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
+ SkDebugf("Could not set the context.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
+ if (nullptr == gl.get()) {
+ SkDebugf("Failed to create gl interface");
+ this->destroyGLContext();
+ return;
+ }
+
+ if (!gl->validate()) {
+ SkDebugf("Failed to validate gl interface");
+ this->destroyGLContext();
+ return;
+ }
+
+ this->init(gl.release());
+}
+
+
+GLXGLContext::~GLXGLContext() {
+ this->teardown();
+ this->destroyGLContext();
+}
+
+void GLXGLContext::destroyGLContext() {
+ if (fDisplay) {
+ glXMakeCurrent(fDisplay, 0, 0);
+
+ if (fContext) {
+ glXDestroyContext(fDisplay, fContext);
+ fContext = nullptr;
+ }
+
+ if (fGlxPixmap) {
+ glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
+ fGlxPixmap = 0;
+ }
+
+ if (fPixmap) {
+ XFreePixmap(fDisplay, fPixmap);
+ fPixmap = 0;
+ }
+
+ XCloseDisplay(fDisplay);
+ fDisplay = nullptr;
+ }
+}
+
+void GLXGLContext::onPlatformMakeCurrent() const {
+ if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
+ SkDebugf("Could not set the context.\n");
+ }
+}
+
+void GLXGLContext::onPlatformSwapBuffers() const {
+ glXSwapBuffers(fDisplay, fGlxPixmap);
+}
+
+GrGLFuncPtr GLXGLContext::onPlatformGetProcAddress(const char* procName) const {
+ return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName));
+}
+
+} // anonymous namespace
+
+namespace sk_gpu_test {
+GLContext *CreatePlatformGLContext(GrGLStandard forcedGpuAPI, GLContext *shareContext) {
+ GLXGLContext *glxShareContext = reinterpret_cast<GLXGLContext *>(shareContext);
+ GLXGLContext *ctx = new GLXGLContext(forcedGpuAPI, glxShareContext);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+} // namespace sk_gpu_test
diff --git a/tools/gpu/gl/iOS/CreatePlatformGLContext_iOS.mm b/tools/gpu/gl/iOS/CreatePlatformGLContext_iOS.mm
new file mode 100644
index 0000000000..d6507f280f
--- /dev/null
+++ b/tools/gpu/gl/iOS/CreatePlatformGLContext_iOS.mm
@@ -0,0 +1,108 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GLContext.h"
+#import <OpenGLES/EAGL.h>
+#include <dlfcn.h>
+
+#define EAGLCTX ((EAGLContext*)(fEAGLContext))
+
+namespace {
+
+class IOSGLContext : public sk_gpu_test::GLContext {
+public:
+ IOSGLContext();
+ ~IOSGLContext() override;
+
+private:
+ void destroyGLContext();
+
+ void onPlatformMakeCurrent() const override;
+ void onPlatformSwapBuffers() const override;
+ GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
+
+ void* fEAGLContext;
+ void* fGLLibrary;
+};
+
+IOSGLContext::IOSGLContext()
+ : fEAGLContext(NULL)
+ , fGLLibrary(RTLD_DEFAULT) {
+
+ fEAGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+ [EAGLContext setCurrentContext:EAGLCTX];
+
+ SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
+ if (NULL == gl.get()) {
+ SkDebugf("Failed to create gl interface");
+ this->destroyGLContext();
+ return;
+ }
+ if (!gl->validate()) {
+ SkDebugf("Failed to validate gl interface");
+ this->destroyGLContext();
+ return;
+ }
+
+ fGLLibrary = dlopen(
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib",
+ RTLD_LAZY);
+
+ this->init(gl.release());
+}
+
+IOSGLContext::~IOSGLContext() {
+ this->teardown();
+ this->destroyGLContext();
+}
+
+void IOSGLContext::destroyGLContext() {
+ if (fEAGLContext) {
+ if ([EAGLContext currentContext] == EAGLCTX) {
+ [EAGLContext setCurrentContext:nil];
+ }
+ [EAGLCTX release];
+ fEAGLContext = NULL;
+ }
+ if (RTLD_DEFAULT != fGLLibrary) {
+ dlclose(fGLLibrary);
+ }
+}
+
+
+void IOSGLContext::onPlatformMakeCurrent() const {
+ if (![EAGLContext setCurrentContext:EAGLCTX]) {
+ SkDebugf("Could not set the context.\n");
+ }
+}
+
+void IOSGLContext::onPlatformSwapBuffers() const { }
+
+GrGLFuncPtr IOSGLContext::onPlatformGetProcAddress(const char* procName) const {
+ return reinterpret_cast<GrGLFuncPtr>(dlsym(fGLLibrary, procName));
+}
+
+} // anonymous namespace
+
+namespace sk_gpu_test {
+GLContext *CreatePlatformGLContext(GrGLStandard forcedGpuAPI, GLContext *shareContext) {
+ SkASSERT(!shareContext);
+ if (shareContext) {
+ return NULL;
+ }
+ if (kGL_GrGLStandard == forcedGpuAPI) {
+ return NULL;
+ }
+ IOSGLContext *ctx = new IOSGLContext;
+ if (!ctx->isValid()) {
+ delete ctx;
+ return NULL;
+ }
+ return ctx;
+}
+}
diff --git a/tools/gpu/gl/mac/CreatePlatformGLContext_mac.cpp b/tools/gpu/gl/mac/CreatePlatformGLContext_mac.cpp
new file mode 100644
index 0000000000..7da99d7eb5
--- /dev/null
+++ b/tools/gpu/gl/mac/CreatePlatformGLContext_mac.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 "SkTypes.h"
+
+#include "gl/GLContext.h"
+#include "AvailabilityMacros.h"
+
+#include <OpenGL/OpenGL.h>
+#include <dlfcn.h>
+
+namespace {
+class MacGLContext : public sk_gpu_test::GLContext {
+public:
+ MacGLContext();
+ ~MacGLContext() override;
+
+private:
+ void destroyGLContext();
+
+ void onPlatformMakeCurrent() const override;
+ void onPlatformSwapBuffers() const override;
+ GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
+
+ CGLContextObj fContext;
+ void* fGLLibrary;
+};
+
+MacGLContext::MacGLContext()
+ : fContext(nullptr)
+ , fGLLibrary(RTLD_DEFAULT) {
+ CGLPixelFormatAttribute attributes[] = {
+#if MAC_OS_X_VERSION_10_7
+ kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core,
+#endif
+ kCGLPFADoubleBuffer,
+ (CGLPixelFormatAttribute)0
+ };
+ CGLPixelFormatObj pixFormat;
+ GLint npix;
+
+ CGLChoosePixelFormat(attributes, &pixFormat, &npix);
+
+ if (nullptr == pixFormat) {
+ SkDebugf("CGLChoosePixelFormat failed.");
+ return;
+ }
+
+ CGLCreateContext(pixFormat, nullptr, &fContext);
+ CGLReleasePixelFormat(pixFormat);
+
+ if (nullptr == fContext) {
+ SkDebugf("CGLCreateContext failed.");
+ return;
+ }
+
+ CGLSetCurrentContext(fContext);
+
+ SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
+ if (nullptr == gl.get()) {
+ SkDebugf("Context could not create GL interface.\n");
+ this->destroyGLContext();
+ return;
+ }
+ if (!gl->validate()) {
+ SkDebugf("Context could not validate GL interface.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ fGLLibrary = dlopen(
+ "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib",
+ RTLD_LAZY);
+
+ this->init(gl.release());
+}
+
+MacGLContext::~MacGLContext() {
+ this->teardown();
+ this->destroyGLContext();
+}
+
+void MacGLContext::destroyGLContext() {
+ if (fContext) {
+ CGLReleaseContext(fContext);
+ fContext = nullptr;
+ }
+ if (RTLD_DEFAULT != fGLLibrary) {
+ dlclose(fGLLibrary);
+ }
+}
+
+void MacGLContext::onPlatformMakeCurrent() const {
+ CGLSetCurrentContext(fContext);
+}
+
+void MacGLContext::onPlatformSwapBuffers() const {
+ CGLFlushDrawable(fContext);
+}
+
+GrGLFuncPtr MacGLContext::onPlatformGetProcAddress(const char* procName) const {
+ return reinterpret_cast<GrGLFuncPtr>(dlsym(fGLLibrary, procName));
+}
+
+} // anonymous namespace
+
+namespace sk_gpu_test {
+GLContext* CreatePlatformGLContext(GrGLStandard forcedGpuAPI, GLContext* shareContext) {
+ SkASSERT(!shareContext);
+ if (shareContext) {
+ return nullptr;
+ }
+
+ if (kGLES_GrGLStandard == forcedGpuAPI) {
+ return nullptr;
+ }
+ MacGLContext* ctx = new MacGLContext;
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+} // namespace sk_gpu_test
diff --git a/tools/gpu/gl/mesa/GLContext_mesa.cpp b/tools/gpu/gl/mesa/GLContext_mesa.cpp
new file mode 100644
index 0000000000..e6cc7c7f4b
--- /dev/null
+++ b/tools/gpu/gl/mesa/GLContext_mesa.cpp
@@ -0,0 +1,151 @@
+
+/*
+ * 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 <GL/osmesa.h>
+
+#include "gl/mesa/GLContext_mesa.h"
+#include "gl/GrGLDefines.h"
+
+#include "gl/GrGLAssembleInterface.h"
+#include "gl/GrGLUtil.h"
+#include "osmesa_wrapper.h"
+
+namespace {
+
+static GrGLFuncPtr osmesa_get(void* ctx, const char name[]) {
+ SkASSERT(nullptr == ctx);
+ SkASSERT(OSMesaGetCurrentContext());
+ return OSMesaGetProcAddress(name);
+}
+
+static const GrGLInterface* create_mesa_interface() {
+ if (nullptr == OSMesaGetCurrentContext()) {
+ return nullptr;
+ }
+ return GrGLAssembleInterface(nullptr, osmesa_get);
+}
+
+static const GrGLint gBOGUS_SIZE = 16;
+
+class MesaGLContext : public sk_gpu_test::GLContext {
+private:
+ typedef intptr_t Context;
+
+public:
+ MesaGLContext();
+ ~MesaGLContext() override;
+
+private:
+ void destroyGLContext();
+
+ void onPlatformMakeCurrent() const override;
+
+ void onPlatformSwapBuffers() const override;
+
+ GrGLFuncPtr onPlatformGetProcAddress(const char *) const override;
+
+ Context fContext;
+ GrGLubyte *fImage;
+};
+
+MesaGLContext::MesaGLContext() : fContext(static_cast<Context>(0)), fImage(nullptr) {
+ GR_STATIC_ASSERT(sizeof(Context) == sizeof(OSMesaContext));
+
+ /* Create an RGBA-mode context */
+#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
+ /* specify Z, stencil, accum sizes */
+ fContext = (Context)OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, nullptr);
+#else
+ fContext = (Context) OSMesaCreateContext(OSMESA_BGRA, nullptr);
+#endif
+ if (!fContext) {
+ SkDebugf("OSMesaCreateContext failed!\n");
+ this->destroyGLContext();
+ return;
+ }
+ // Allocate the image buffer
+ fImage = (GrGLubyte *) sk_malloc_throw(gBOGUS_SIZE * gBOGUS_SIZE *
+ 4 * sizeof(GrGLubyte));
+ if (!fImage) {
+ SkDebugf("Alloc image buffer failed!\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ // Bind the buffer to the context and make it current
+ if (!OSMesaMakeCurrent((OSMesaContext) fContext,
+ fImage,
+ GR_GL_UNSIGNED_BYTE,
+ gBOGUS_SIZE,
+ gBOGUS_SIZE)) {
+ SkDebugf("OSMesaMakeCurrent failed!\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ SkAutoTUnref<const GrGLInterface> gl(create_mesa_interface());
+ if (nullptr == gl.get()) {
+ SkDebugf("Could not create GL interface!\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ if (!gl->validate()) {
+ SkDebugf("Could not validate GL interface!\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ this->init(gl.release());
+}
+
+MesaGLContext::~MesaGLContext() {
+ this->teardown();
+ this->destroyGLContext();
+}
+
+void MesaGLContext::destroyGLContext() {
+ if (fImage) {
+ sk_free(fImage);
+ fImage = nullptr;
+ }
+
+ if (fContext) {
+ OSMesaDestroyContext((OSMesaContext) fContext);
+ fContext = static_cast<Context>(0);
+ }
+}
+
+
+void MesaGLContext::onPlatformMakeCurrent() const {
+ if (fContext) {
+ if (!OSMesaMakeCurrent((OSMesaContext) fContext, fImage,
+ GR_GL_UNSIGNED_BYTE, gBOGUS_SIZE, gBOGUS_SIZE)) {
+ SkDebugf("Could not make MESA context current.");
+ }
+ }
+}
+
+void MesaGLContext::onPlatformSwapBuffers() const { }
+
+GrGLFuncPtr MesaGLContext::onPlatformGetProcAddress(const char *procName) const {
+ return OSMesaGetProcAddress(procName);
+}
+} // anonymous namespace
+
+
+namespace sk_gpu_test {
+GLContext *CreateMesaGLContext() {
+ MesaGLContext *ctx = new MesaGLContext;
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+} // sk_gpu_test
diff --git a/tools/gpu/gl/mesa/GLContext_mesa.h b/tools/gpu/gl/mesa/GLContext_mesa.h
new file mode 100644
index 0000000000..0d6ee4dc10
--- /dev/null
+++ b/tools/gpu/gl/mesa/GLContext_mesa.h
@@ -0,0 +1,17 @@
+
+/*
+ * 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 GLContext_mesa_DEFINED
+#define GLContext_mesa_DEFINED
+
+#include "gl/GLContext.h"
+
+namespace sk_gpu_test {
+GLContext* CreateMesaGLContext();
+} // namespace sk_gpu_test
+
+#endif
diff --git a/tools/gpu/gl/mesa/osmesa_wrapper.h b/tools/gpu/gl/mesa/osmesa_wrapper.h
new file mode 100644
index 0000000000..70de99376d
--- /dev/null
+++ b/tools/gpu/gl/mesa/osmesa_wrapper.h
@@ -0,0 +1,16 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Older versions of XQuartz have a bug where a header included by osmesa.h
+// defines GL_GLEXT_PROTOTYPES. This will cause a redefinition warning if
+// the file that includes osmesa.h already defined it. XCode 3 uses a version
+// of gcc (4.2.1) that does not support the diagnostic pragma to disable a
+// warning (added in 4.2.4). So we use the system_header pragma to shut GCC
+// up about warnings in osmesa.h
+#pragma GCC system_header
+#include <GL/osmesa.h>
diff --git a/tools/gpu/gl/null/NullGLContext.cpp b/tools/gpu/gl/null/NullGLContext.cpp
new file mode 100644
index 0000000000..4781c90e4f
--- /dev/null
+++ b/tools/gpu/gl/null/NullGLContext.cpp
@@ -0,0 +1,630 @@
+
+/*
+ * 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
+
diff --git a/tools/gpu/gl/null/NullGLContext.h b/tools/gpu/gl/null/NullGLContext.h
new file mode 100644
index 0000000000..16fb9fd1df
--- /dev/null
+++ b/tools/gpu/gl/null/NullGLContext.h
@@ -0,0 +1,17 @@
+
+/*
+ * 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 NullGLContext_DEFINED
+#define NullGLContext_DEFINED
+
+#include "gl/GLContext.h"
+
+namespace sk_gpu_test {
+GLContext* CreateNullGLContext();
+} // namespace sk_gpu_test
+
+#endif
diff --git a/tools/gpu/gl/win/CreatePlatformGLContext_win.cpp b/tools/gpu/gl/win/CreatePlatformGLContext_win.cpp
new file mode 100644
index 0000000000..efee28b74b
--- /dev/null
+++ b/tools/gpu/gl/win/CreatePlatformGLContext_win.cpp
@@ -0,0 +1,204 @@
+
+/*
+ * 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 "gl/GLContext.h"
+
+#include <windows.h>
+#include <GL/GL.h>
+#include "win/SkWGL.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace {
+
+class WinGLContext : public sk_gpu_test::GLContext {
+public:
+ WinGLContext(GrGLStandard forcedGpuAPI);
+ ~WinGLContext() override;
+
+private:
+ void destroyGLContext();
+
+ void onPlatformMakeCurrent() const override;
+ void onPlatformSwapBuffers() const override;
+ GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override;
+
+ HWND fWindow;
+ HDC fDeviceContext;
+ HGLRC fGlRenderContext;
+ static ATOM gWC;
+ SkWGLPbufferContext* fPbufferContext;
+};
+
+ATOM WinGLContext::gWC = 0;
+
+WinGLContext::WinGLContext(GrGLStandard forcedGpuAPI)
+ : fWindow(nullptr)
+ , fDeviceContext(nullptr)
+ , fGlRenderContext(0)
+ , fPbufferContext(nullptr) {
+ HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(nullptr);
+
+ if (!gWC) {
+ WNDCLASS wc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hbrBackground = nullptr;
+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
+ wc.hInstance = hInstance;
+ wc.lpfnWndProc = (WNDPROC) DefWindowProc;
+ wc.lpszClassName = TEXT("Griffin");
+ wc.lpszMenuName = nullptr;
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+
+ gWC = RegisterClass(&wc);
+ if (!gWC) {
+ SkDebugf("Could not register window class.\n");
+ return;
+ }
+ }
+
+ if (!(fWindow = CreateWindow(TEXT("Griffin"),
+ TEXT("The Invisible Man"),
+ WS_OVERLAPPEDWINDOW,
+ 0, 0, 1, 1,
+ nullptr, nullptr,
+ hInstance, nullptr))) {
+ SkDebugf("Could not create window.\n");
+ return;
+ }
+
+ if (!(fDeviceContext = GetDC(fWindow))) {
+ SkDebugf("Could not get device context.\n");
+ this->destroyGLContext();
+ return;
+ }
+ // Requesting a Core profile would bar us from using NVPR. So we request
+ // compatibility profile or GL ES.
+ SkWGLContextRequest contextType =
+ kGLES_GrGLStandard == forcedGpuAPI ?
+ kGLES_SkWGLContextRequest : kGLPreferCompatibilityProfile_SkWGLContextRequest;
+
+ fPbufferContext = SkWGLPbufferContext::Create(fDeviceContext, 0, contextType);
+
+ HDC dc;
+ HGLRC glrc;
+
+ if (nullptr == fPbufferContext) {
+ if (!(fGlRenderContext = SkCreateWGLContext(fDeviceContext, 0, contextType))) {
+ SkDebugf("Could not create rendering context.\n");
+ this->destroyGLContext();
+ return;
+ }
+ dc = fDeviceContext;
+ glrc = fGlRenderContext;
+ } else {
+ ReleaseDC(fWindow, fDeviceContext);
+ fDeviceContext = 0;
+ DestroyWindow(fWindow);
+ fWindow = 0;
+
+ dc = fPbufferContext->getDC();
+ glrc = fPbufferContext->getGLRC();
+ }
+
+ if (!(wglMakeCurrent(dc, glrc))) {
+ SkDebugf("Could not set the context.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
+ if (nullptr == gl.get()) {
+ SkDebugf("Could not create GL interface.\n");
+ this->destroyGLContext();
+ return;
+ }
+ if (!gl->validate()) {
+ SkDebugf("Could not validate GL interface.\n");
+ this->destroyGLContext();
+ return;
+ }
+
+ this->init(gl.release());
+}
+
+WinGLContext::~WinGLContext() {
+ this->teardown();
+ this->destroyGLContext();
+}
+
+void WinGLContext::destroyGLContext() {
+ SkSafeSetNull(fPbufferContext);
+ if (fGlRenderContext) {
+ wglDeleteContext(fGlRenderContext);
+ fGlRenderContext = 0;
+ }
+ if (fWindow && fDeviceContext) {
+ ReleaseDC(fWindow, fDeviceContext);
+ fDeviceContext = 0;
+ }
+ if (fWindow) {
+ DestroyWindow(fWindow);
+ fWindow = 0;
+ }
+}
+
+void WinGLContext::onPlatformMakeCurrent() const {
+ HDC dc;
+ HGLRC glrc;
+
+ if (nullptr == fPbufferContext) {
+ dc = fDeviceContext;
+ glrc = fGlRenderContext;
+ } else {
+ dc = fPbufferContext->getDC();
+ glrc = fPbufferContext->getGLRC();
+ }
+
+ if (!wglMakeCurrent(dc, glrc)) {
+ SkDebugf("Could not create rendering context.\n");
+ }
+}
+
+void WinGLContext::onPlatformSwapBuffers() const {
+ HDC dc;
+
+ if (nullptr == fPbufferContext) {
+ dc = fDeviceContext;
+ } else {
+ dc = fPbufferContext->getDC();
+ }
+ if (!SwapBuffers(dc)) {
+ SkDebugf("Could not complete SwapBuffers.\n");
+ }
+}
+
+GrGLFuncPtr WinGLContext::onPlatformGetProcAddress(const char* name) const {
+ return reinterpret_cast<GrGLFuncPtr>(wglGetProcAddress(name));
+}
+
+} // anonymous namespace
+
+namespace sk_gpu_test {
+GLContext* CreatePlatformGLContext(GrGLStandard forcedGpuAPI, GLContext *shareContext) {
+ SkASSERT(!shareContext);
+ if (shareContext) {
+ return nullptr;
+ }
+ WinGLContext *ctx = new WinGLContext(forcedGpuAPI);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+} // namespace sk_gpu_test
+
diff --git a/tools/kilobench/kilobench.cpp b/tools/kilobench/kilobench.cpp
index c0422d81eb..81238353e2 100644
--- a/tools/kilobench/kilobench.cpp
+++ b/tools/kilobench/kilobench.cpp
@@ -20,6 +20,7 @@
#include "Timer.h"
#include "VisualSKPBench.h"
#include "gl/GrGLDefines.h"
+#include "gl/GrGLUtil.h"
#include "../private/SkMutex.h"
#include "../private/SkSemaphore.h"
#include "../private/SkGpuFenceSync.h"
@@ -29,6 +30,8 @@
#include <sys/types.h>
#include <sys/wait.h>
+using namespace sk_gpu_test;
+
/*
* This is an experimental GPU only benchmarking program. The initial implementation will only
* support SKPs.
@@ -144,14 +147,14 @@ struct GPUTarget {
void setup() {
fGL->makeCurrent();
// Make sure we're done with whatever came before.
- SK_GL(*fGL, Finish());
+ GR_GL_CALL(fGL->gl(), Finish());
}
SkCanvas* beginTiming(SkCanvas* canvas) { return canvas; }
void endTiming(bool usePlatformSwapBuffers) {
if (fGL) {
- SK_GL(*fGL, Flush());
+ GR_GL_CALL(fGL->gl(), Flush());
if (usePlatformSwapBuffers) {
fGL->swapBuffers();
} else {
@@ -160,7 +163,7 @@ struct GPUTarget {
}
}
void finish() {
- SK_GL(*fGL, Finish());
+ GR_GL_CALL(fGL->gl(), Finish());
}
bool needsFrameTiming(int* maxFrameLag) const {
@@ -215,10 +218,10 @@ struct GPUTarget {
return true;
}
- SkGLContext* gl() { return fGL; }
+ GLContext* gl() { return fGL; }
private:
- SkGLContext* fGL;
+ GLContext* fGL;
SkAutoTDelete<SkSurface> fSurface;
};
@@ -279,7 +282,7 @@ static int clamp_loops(int loops) {
static double now_ms() { return SkTime::GetNSecs() * 1e-6; }
struct TimingThread {
- TimingThread(SkGLContext* mainContext)
+ TimingThread(GLContext* mainContext)
: fFenceSync(mainContext->fenceSync())
, fMainContext(mainContext)
, fDone(false) {}
@@ -305,8 +308,8 @@ struct TimingThread {
void timingLoop() {
// Create a context which shares display lists with the main thread
- SkAutoTDelete<SkGLContext> glContext(SkCreatePlatformGLContext(kNone_GrGLStandard,
- fMainContext));
+ SkAutoTDelete<GLContext> glContext(CreatePlatformGLContext(kNone_GrGLStandard,
+ fMainContext));
glContext->makeCurrent();
// Basic timing methodology is:
@@ -402,7 +405,7 @@ private:
SyncQueue fFrameEndSyncs;
SkTArray<double> fTimings;
SkMutex fDoneMutex;
- SkGLContext* fMainContext;
+ GLContext* fMainContext;
bool fDone;
};
diff --git a/tools/skiaserve/Request.cpp b/tools/skiaserve/Request.cpp
index 1d946abaa0..d9e1bdaa33 100644
--- a/tools/skiaserve/Request.cpp
+++ b/tools/skiaserve/Request.cpp
@@ -10,6 +10,8 @@
#include "SkPictureRecorder.h"
#include "SkPixelSerializer.h"
+using namespace sk_gpu_test;
+
static int kDefaultWidth = 1920;
static int kDefaultHeight = 1080;
@@ -63,8 +65,8 @@ SkData* Request::writeCanvasToPng(SkCanvas* canvas) {
SkCanvas* Request::getCanvas() {
#if SK_SUPPORT_GPU
GrContextFactory* factory = fContextFactory;
- SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
- GrContextFactory::kNone_GLContextOptions).fGLContext;
+ GLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
+ GrContextFactory::kNone_GLContextOptions).fGLContext;
gl->makeCurrent();
#endif
SkASSERT(fDebugCanvas);
diff --git a/tools/skiaserve/Request.h b/tools/skiaserve/Request.h
index e3bc373841..d19c2ba801 100644
--- a/tools/skiaserve/Request.h
+++ b/tools/skiaserve/Request.h
@@ -19,7 +19,9 @@
#include "UrlDataManager.h"
+namespace sk_gpu_test {
class GrContextFactory;
+}
struct MHD_Connection;
struct MHD_PostProcessor;
@@ -69,7 +71,7 @@ private:
GrContext* getContext();
sk_sp<SkPicture> fPicture;
- GrContextFactory* fContextFactory;
+ sk_gpu_test::GrContextFactory* fContextFactory;
SkAutoTUnref<SkSurface> fSurface;
bool fGPUEnabled;
};