From 3724e574a744491b7cfb8187ac865a70ef3d4528 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Wed, 30 Mar 2016 18:56:19 -0700 Subject: 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 Committed: https://skia.googlesource.com/skia/+/fe3456cb006110d045b26ff3f8681b893a757b58 Review URL: https://codereview.chromium.org/1815823002 --- tools/gpu/gl/GLContext.cpp | 188 +++ tools/gpu/gl/GLContext.h | 141 +++ tools/gpu/gl/angle/GLContext_angle.cpp | 320 +++++ tools/gpu/gl/angle/GLContext_angle.h | 30 + .../gl/command_buffer/GLContext_command_buffer.cpp | 345 ++++++ .../gl/command_buffer/GLContext_command_buffer.h | 68 ++ tools/gpu/gl/debug/DebugGLContext.cpp | 1256 ++++++++++++++++++++ tools/gpu/gl/debug/DebugGLContext.h | 17 + tools/gpu/gl/debug/GrBufferObj.cpp | 31 + tools/gpu/gl/debug/GrBufferObj.h | 76 ++ tools/gpu/gl/debug/GrFBBindableObj.h | 88 ++ tools/gpu/gl/debug/GrFakeRefObj.h | 86 ++ tools/gpu/gl/debug/GrFrameBufferObj.cpp | 67 ++ tools/gpu/gl/debug/GrFrameBufferObj.h | 68 ++ tools/gpu/gl/debug/GrProgramObj.cpp | 27 + tools/gpu/gl/debug/GrProgramObj.h | 43 + tools/gpu/gl/debug/GrRenderBufferObj.h | 40 + tools/gpu/gl/debug/GrShaderObj.cpp | 14 + tools/gpu/gl/debug/GrShaderObj.h | 36 + tools/gpu/gl/debug/GrTextureObj.cpp | 14 + tools/gpu/gl/debug/GrTextureObj.h | 57 + tools/gpu/gl/debug/GrTextureUnitObj.cpp | 31 + tools/gpu/gl/debug/GrTextureUnitObj.h | 44 + tools/gpu/gl/debug/GrVertexArrayObj.h | 21 + tools/gpu/gl/egl/CreatePlatformGLContext_egl.cpp | 337 ++++++ tools/gpu/gl/glx/CreatePlatformGLContext_glx.cpp | 346 ++++++ tools/gpu/gl/iOS/CreatePlatformGLContext_iOS.mm | 108 ++ tools/gpu/gl/mac/CreatePlatformGLContext_mac.cpp | 128 ++ tools/gpu/gl/mesa/GLContext_mesa.cpp | 151 +++ tools/gpu/gl/mesa/GLContext_mesa.h | 17 + tools/gpu/gl/mesa/osmesa_wrapper.h | 16 + tools/gpu/gl/null/NullGLContext.cpp | 630 ++++++++++ tools/gpu/gl/null/NullGLContext.h | 17 + tools/gpu/gl/win/CreatePlatformGLContext_win.cpp | 204 ++++ 34 files changed, 5062 insertions(+) create mode 100644 tools/gpu/gl/GLContext.cpp create mode 100644 tools/gpu/gl/GLContext.h create mode 100644 tools/gpu/gl/angle/GLContext_angle.cpp create mode 100644 tools/gpu/gl/angle/GLContext_angle.h create mode 100644 tools/gpu/gl/command_buffer/GLContext_command_buffer.cpp create mode 100644 tools/gpu/gl/command_buffer/GLContext_command_buffer.h create mode 100644 tools/gpu/gl/debug/DebugGLContext.cpp create mode 100644 tools/gpu/gl/debug/DebugGLContext.h create mode 100644 tools/gpu/gl/debug/GrBufferObj.cpp create mode 100644 tools/gpu/gl/debug/GrBufferObj.h create mode 100644 tools/gpu/gl/debug/GrFBBindableObj.h create mode 100644 tools/gpu/gl/debug/GrFakeRefObj.h create mode 100644 tools/gpu/gl/debug/GrFrameBufferObj.cpp create mode 100644 tools/gpu/gl/debug/GrFrameBufferObj.h create mode 100644 tools/gpu/gl/debug/GrProgramObj.cpp create mode 100644 tools/gpu/gl/debug/GrProgramObj.h create mode 100644 tools/gpu/gl/debug/GrRenderBufferObj.h create mode 100644 tools/gpu/gl/debug/GrShaderObj.cpp create mode 100644 tools/gpu/gl/debug/GrShaderObj.h create mode 100644 tools/gpu/gl/debug/GrTextureObj.cpp create mode 100644 tools/gpu/gl/debug/GrTextureObj.h create mode 100644 tools/gpu/gl/debug/GrTextureUnitObj.cpp create mode 100644 tools/gpu/gl/debug/GrTextureUnitObj.h create mode 100644 tools/gpu/gl/debug/GrVertexArrayObj.h create mode 100644 tools/gpu/gl/egl/CreatePlatformGLContext_egl.cpp create mode 100644 tools/gpu/gl/glx/CreatePlatformGLContext_glx.cpp create mode 100644 tools/gpu/gl/iOS/CreatePlatformGLContext_iOS.mm create mode 100644 tools/gpu/gl/mac/CreatePlatformGLContext_mac.cpp create mode 100644 tools/gpu/gl/mesa/GLContext_mesa.cpp create mode 100644 tools/gpu/gl/mesa/GLContext_mesa.h create mode 100644 tools/gpu/gl/mesa/osmesa_wrapper.h create mode 100644 tools/gpu/gl/null/NullGLContext.cpp create mode 100644 tools/gpu/gl/null/NullGLContext.h create mode 100644 tools/gpu/gl/win/CreatePlatformGLContext_win.cpp (limited to 'tools/gpu/gl') 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 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(versionStr)); + if (version < GR_GL_VER(3,2) && !ctx->gl()->hasExtension("GL_ARB_sync")) { + return nullptr; + } + ret->fGLFenceSync = reinterpret_cast( + ctx->onPlatformGetProcAddress("glFenceSync")); + ret->fGLClientWaitSync = reinterpret_cast( + ctx->onPlatformGetProcAddress("glClientWaitSync")); + ret->fGLDeleteSync = reinterpret_cast( + ctx->onPlatformGetProcAddress("glDeleteSync")); + } else { + if (!ctx->gl()->hasExtension("GL_APPLE_sync")) { + return nullptr; + } + ret->fGLFenceSync = reinterpret_cast( + ctx->onPlatformGetProcAddress("glFenceSyncAPPLE")); + ret->fGLClientWaitSync = reinterpret_cast( + ctx->onPlatformGetProcAddress("glClientWaitSyncAPPLE")); + ret->fGLDeleteSync = reinterpret_cast( + 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(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(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 fFenceSync; + SkPlatformGpuFence fFrameFences[kMaxFrameLag - 1]; + int fCurrentFenceIdx; + + /** Subclass provides the gl interface object if construction was + * successful. */ + SkAutoTUnref 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..f1e8aad509 --- /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 +#include + +#include "gl/GrGLDefines.h" +#include "gl/GrGLUtil.h" + +#include "gl/GrGLInterface.h" +#include "gl/GrGLAssembleInterface.h" +#include "../ports/SkOSLibrary.h" + +#include + +#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(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 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((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() { + ANGLEGLContext* ctx = new ANGLEGLContext(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 + +#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(&fConfig), 1, + &numConfigs) || numConfigs != 1) { + SkDebugf("Command Buffer: Could not choose EGL config.\n"); + this->destroyGLContext(); + return; + } + + if (nativeWindow) { + fSurface = gfCreateWindowSurface(fDisplay, + static_cast(fConfig), + (EGLNativeWindowType) nativeWindow, + surfaceAttribs); + } else { + fSurface = gfCreatePbufferSurface(fDisplay, + static_cast(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(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 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(fConfig), EGL_STENCIL_SIZE, &result); + return result; +} + +int CommandBufferGLContext::getSampleCount() { + EGLint result = 0; + gfGetConfigAttrib(fDisplay, static_cast(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(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(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(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(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(alignment)); + rowStride = static_cast(alignment * fTemp / componentSize); + } + + GrGLchar *scanline = static_cast(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 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 + 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 fColorReferees; // frame buffers that use this as a color buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D") + SkTDArray fDepthReferees; // frame buffers that use this as a depth buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D") + SkTDArray 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 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 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 + +#define EGL_EGLEXT_PROTOTYPES +#include +#include + +#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 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(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(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(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 +#include +#include + +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("GLX_ARB_create_context"), + reinterpret_cast(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("GLX_EXT_create_context_es2_profile"), + reinterpret_cast(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 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(procName)); +} + +} // anonymous namespace + +namespace sk_gpu_test { +GLContext *CreatePlatformGLContext(GrGLStandard forcedGpuAPI, GLContext *shareContext) { + GLXGLContext *glxShareContext = reinterpret_cast(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 +#include + +#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 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(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 +#include + +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 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(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 + +#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(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 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(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 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(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(fFreeListHead); + fFreeListHead = reinterpret_cast(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(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 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 + 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 +#include +#include "win/SkWGL.h" + +#define WIN32_LEAN_AND_MEAN +#include + +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 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(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 + -- cgit v1.2.3