| /* | |
| * 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 "gl/SkGLContext.h" | |
| #include "GrGLUtil.h" | |
| #include "SkGpuFenceSync.h" | |
| class SkGLContext::GLFenceSync : public SkGpuFenceSync { | |
| public: | |
| static GLFenceSync* CreateIfSupported(const SkGLContext*); | |
| SkPlatformGpuFence SK_WARN_UNUSED_RESULT insertFence() const override; | |
| bool flushAndWaitFence(SkPlatformGpuFence fence) 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; | |
| }; | |
| SkGLContext::SkGLContext() | |
| : fCurrentFenceIdx(0) { | |
| memset(fFrameFences, 0, sizeof(fFrameFences)); | |
| } | |
| SkGLContext::~SkGLContext() { | |
| // 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 SkGLContext::init(const GrGLInterface* gl, SkGpuFenceSync* fenceSync) { | |
| SkASSERT(!fGL.get()); | |
| fGL.reset(gl); | |
| fFenceSync.reset(fenceSync ? fenceSync : GLFenceSync::CreateIfSupported(this)); | |
| } | |
| void SkGLContext::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 SkGLContext::makeCurrent() const { | |
| this->onPlatformMakeCurrent(); | |
| } | |
| void SkGLContext::swapBuffers() { | |
| if (!fFenceSync) { | |
| // Fallback on the platform SwapBuffers method for synchronization. This may have no effect. | |
| this->onPlatformSwapBuffers(); | |
| return; | |
| } | |
| if (fFrameFences[fCurrentFenceIdx]) { | |
| if (!fFenceSync->flushAndWaitFence(fFrameFences[fCurrentFenceIdx])) { | |
| 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 SkGLContext::testAbandon() { | |
| if (fGL) { | |
| fGL->abandon(); | |
| } | |
| if (fFenceSync) { | |
| memset(fFrameFences, 0, sizeof(fFrameFences)); | |
| } | |
| } | |
| SkGLContext::GLFenceSync* SkGLContext::GLFenceSync::CreateIfSupported(const SkGLContext* ctx) { | |
| SkAutoTDelete<GLFenceSync> ret(new GLFenceSync); | |
| if (kGL_GrGLStandard == ctx->gl()->fStandard) { | |
| const GrGLubyte* versionStr; | |
| SK_GL_RET(*ctx, versionStr, GetString(GR_GL_VERSION)); | |
| GrGLVersion version = GrGLGetVersionFromString(reinterpret_cast<const char*>(versionStr)); | |
| if (version < GR_GL_VER(3,2) && !ctx->gl()->hasExtension("GL_ARB_sync")) { | |
| return nullptr; | |
| } | |
| ret->fGLFenceSync = reinterpret_cast<GLFenceSyncProc>( | |
| ctx->onPlatformGetProcAddress("glFenceSync")); | |
| ret->fGLClientWaitSync = reinterpret_cast<GLClientWaitSyncProc>( | |
| ctx->onPlatformGetProcAddress("glClientWaitSync")); | |
| ret->fGLDeleteSync = reinterpret_cast<GLDeleteSyncProc>( | |
| ctx->onPlatformGetProcAddress("glDeleteSync")); | |
| } else { | |
| if (!ctx->gl()->hasExtension("GL_APPLE_sync")) { | |
| return nullptr; | |
| } | |
| ret->fGLFenceSync = reinterpret_cast<GLFenceSyncProc>( | |
| ctx->onPlatformGetProcAddress("glFenceSyncAPPLE")); | |
| ret->fGLClientWaitSync = reinterpret_cast<GLClientWaitSyncProc>( | |
| ctx->onPlatformGetProcAddress("glClientWaitSyncAPPLE")); | |
| ret->fGLDeleteSync = reinterpret_cast<GLDeleteSyncProc>( | |
| ctx->onPlatformGetProcAddress("glDeleteSyncAPPLE")); | |
| } | |
| if (!ret->fGLFenceSync || !ret->fGLClientWaitSync || !ret->fGLDeleteSync) { | |
| return nullptr; | |
| } | |
| return ret.detach(); | |
| } | |
| SkPlatformGpuFence SkGLContext::GLFenceSync::insertFence() const { | |
| return fGLFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); | |
| } | |
| bool SkGLContext::GLFenceSync::flushAndWaitFence(SkPlatformGpuFence fence) const { | |
| GLsync glsync = static_cast<GLsync>(fence); | |
| return GL_WAIT_FAILED != fGLClientWaitSync(glsync, GL_SYNC_FLUSH_COMMANDS_BIT, -1); | |
| } | |
| void SkGLContext::GLFenceSync::deleteFence(SkPlatformGpuFence fence) const { | |
| GLsync glsync = static_cast<GLsync>(fence); | |
| fGLDeleteSync(glsync); | |
| } |