Implement SkGLContext swapBuffers with fence syncs

Improves the GPU measuring accuracy of nanobench by using fence syncs.
Fence syncs are very widely supported and available on almost every
platform.

NO_MERGE_BUILDS
BUG=skia:

Review URL: https://codereview.chromium.org/1194783003
This commit is contained in:
cdalton 2015-06-23 13:23:44 -07:00 committed by Commit bot
parent b607767703
commit d416a5b10f
22 changed files with 448 additions and 90 deletions

View File

@ -79,7 +79,7 @@ DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
DEFINE_double(overheadGoal, 0.0001,
"Loop until timer overhead is at most this fraction of our measurments.");
DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag.");
DEFINE_int32(gpuFrameLag, 5, "If unknown, estimated maximum number of frames GPU allows to lag.");
DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling back to "
"software path rendering.");
@ -144,7 +144,13 @@ struct GPUTarget : public Target {
SK_GL(*this->gl, Finish());
}
bool needsFrameTiming() const override { return true; }
bool needsFrameTiming(int* maxFrameLag) const override {
if (!this->gl->getMaxGpuFrameLag(maxFrameLag)) {
// Frame lag is unknown.
*maxFrameLag = FLAGS_gpuFrameLag;
}
return true;
}
bool init(SkImageInfo info, Benchmark* bench) override {
uint32_t flags = this->config.useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
@ -155,6 +161,10 @@ struct GPUTarget : public Target {
if (!this->surface.get()) {
return false;
}
if (!this->gl->fenceSyncSupport()) {
SkDebugf("WARNING: GL context for config \"%s\" does not support fence sync. "
"Timings might not be accurate.\n", this->config.name);
}
return true;
}
void fillOptions(ResultsWriter* log) override {
@ -307,7 +317,8 @@ static int cpu_bench(const double overhead, Target* target, Benchmark* bench, do
static int gpu_bench(Target* target,
Benchmark* bench,
double* samples) {
double* samples,
int maxGpuFrameLag) {
// First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
int loops = FLAGS_loops;
if (kAutoTuneLoops == loops) {
@ -321,9 +332,8 @@ static int gpu_bench(Target* target,
}
loops *= 2;
// If the GPU lets frames lag at all, we need to make sure we're timing
// _this_ round, not still timing last round. We force this by looping
// more times than any reasonable GPU will allow frames to lag.
for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
// _this_ round, not still timing last round.
for (int i = 0; i < maxGpuFrameLag; i++) {
elapsed = time(loops, bench, target);
}
} while (elapsed < FLAGS_gpuMs);
@ -340,7 +350,7 @@ static int gpu_bench(Target* target,
// Pretty much the same deal as the calibration: do some warmup to make
// sure we're timing steady-state pipelined frames.
for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
for (int i = 0; i < maxGpuFrameLag - 1; i++) {
time(loops, bench, target);
}
@ -428,6 +438,9 @@ static void create_configs(SkTDArray<Config>* configs) {
GPU_CONFIG(nullgpu, kNull_GLContextType, 0, false)
#ifdef SK_ANGLE
GPU_CONFIG(angle, kANGLE_GLContextType, 0, false)
#endif
#if SK_MESA
GPU_CONFIG(mesa, kMESA_GLContextType, 0, false)
#endif
}
#endif
@ -1008,9 +1021,10 @@ int nanobench_main() {
targets[j]->setup();
bench->perCanvasPreDraw(canvas);
int frameLag;
const int loops =
targets[j]->needsFrameTiming()
? gpu_bench(targets[j], bench.get(), samples.get())
targets[j]->needsFrameTiming(&frameLag)
? gpu_bench(targets[j], bench.get(), samples.get(), frameLag)
: cpu_bench(overhead, targets[j], bench.get(), samples.get());
bench->perCanvasPostDraw(canvas);

View File

@ -63,7 +63,7 @@ struct Target {
/** CPU-like targets can just be timed, but GPU-like
targets need to pay attention to frame boundaries
or other similar details. */
virtual bool needsFrameTiming() const { return false; }
virtual bool needsFrameTiming(int* frameLag) const { return false; }
/** Called once per target, during program initialization.
Returns false if initialization fails. */

View File

@ -42,7 +42,9 @@ void HWUITarget::fence() {
this->renderer.proxy->fence();
}
bool HWUITarget::needsFrameTiming() const {
bool HWUITarget::needsFrameTiming(int* frameLag) const {
extern int FLAGS_gpuFrameLag;
*frameLag = FLAGS_gpuFrameLag;
return true;
}

View File

@ -23,7 +23,7 @@ struct HWUITarget : public Target {
SkCanvas* beginTiming(SkCanvas* canvas) override;
void endTiming() override;
void fence() override;
bool needsFrameTiming() const override;
bool needsFrameTiming(int* frameLag) const override;
bool init(SkImageInfo info, Benchmark* bench) override;
bool capturePixels(SkBitmap* bmp) override;

View File

@ -30,6 +30,8 @@
* comments in GrGLConfig.h
*/
typedef void(*GrGLFuncPtr)();
struct GrGLInterface;
const GrGLInterface* GrGLDefaultInterface();

View File

@ -9,6 +9,7 @@
#define SkGLContext_DEFINED
#include "GrGLInterface.h"
#include "../../src/gpu/SkGpuFenceSync.h"
/**
* Create an offscreen opengl context with an RGBA8 / 8bit stencil FBO.
@ -25,19 +26,32 @@ public:
const GrGLInterface* gl() const { return fGL.get(); }
virtual void makeCurrent() const = 0;
bool fenceSyncSupport() const { return SkToBool(fFenceSync); }
bool getMaxGpuFrameLag(int* maxFrameLag) const {
if (!fFenceSync) {
return false;
}
*maxFrameLag = kMaxFrameLag;
return true;
}
void makeCurrent() const;
/**
* The primary purpose of this function it to provide a means of scheduling
* 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 drawing surface provided by the platform is double buffered this
* call will cause the platform to swap which buffer is currently being
* targeted. If the current surface does not include a back buffer, this
* call has no effect.
* 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.
*/
virtual void swapBuffers() const = 0;
void swapBuffers();
/**
* This notifies the context that we are deliberately testing abandoning
@ -47,13 +61,37 @@ public:
*/
void testAbandon();
class GLFenceSync; // SkGpuFenceSync implementation that uses the OpenGL functionality.
protected:
SkGLContext();
/*
* Methods that sublcasses must call from their constructors and destructors.
*/
void init(const GrGLInterface*, SkGpuFenceSync* = NULL);
void teardown();
/*
* Operations that have a platform-dependent implementation.
*/
virtual void onPlatformMakeCurrent() const = 0;
virtual void onPlatformSwapBuffers() const = 0;
virtual GrGLFuncPtr onPlatformGetProcAddress(const char*) const = 0;
private:
enum { kMaxFrameLag = 3 };
SkAutoTDelete<SkGpuFenceSync> fFenceSync;
SkPlatformGpuFence fFrameFences[kMaxFrameLag - 1];
int fCurrentFenceIdx;
/** Subclass provides the gl interface object if construction was
* successful. */
SkAutoTUnref<const GrGLInterface> fGL;
friend class GLFenceSync; // For onPlatformGetProcAddress.
typedef SkRefCnt INHERITED;
};

View File

@ -13,8 +13,6 @@
class SK_API SkNullGLContext : public SkGLContext {
public:
~SkNullGLContext() override;
void makeCurrent() const override;
void swapBuffers() const override {};
static SkNullGLContext* Create(GrGLStandard);
@ -23,6 +21,10 @@ public:
private:
SkNullGLContext();
void onPlatformMakeCurrent() const override;
void onPlatformSwapBuffers() const override {}
GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return NULL; }
ContextState* fState;
};

View File

@ -15,8 +15,6 @@
class SkANGLEGLContext : public SkGLContext {
public:
~SkANGLEGLContext() override;
void makeCurrent() const override;
void swapBuffers() const override;
static SkANGLEGLContext* Create(GrGLStandard forcedGpuAPI) {
if (kGL_GrGLStandard == forcedGpuAPI) {
@ -37,6 +35,10 @@ private:
SkANGLEGLContext();
void destroyGLContext();
void onPlatformMakeCurrent() const override;
void onPlatformSwapBuffers() const override;
GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override;
void* fContext;
void* fDisplay;
void* fSurface;

29
src/gpu/SkGpuFenceSync.h Normal file
View File

@ -0,0 +1,29 @@
/*
* 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 SkGpuFenceSync_DEFINED
#define SkGpuFenceSync_DEFINED
#include "SkTypes.h"
typedef void* SkPlatformGpuFence;
/*
* This class provides an interface to interact with fence syncs. A fence sync is an object that the
* client can insert into the GPU command stream, and then at any future time, wait until all
* commands that were issued before the fence have completed.
*/
class SkGpuFenceSync {
public:
virtual SkPlatformGpuFence SK_WARN_UNUSED_RESULT insertFence() const = 0;
virtual bool flushAndWaitFence(SkPlatformGpuFence) const = 0;
virtual void deleteFence(SkPlatformGpuFence) const = 0;
virtual ~SkGpuFenceSync() {}
};
#endif

View File

@ -8,7 +8,6 @@
#include "gl/GrGLInterface.h"
typedef void(*GrGLFuncPtr)();
typedef GrGLFuncPtr (*GrGLGetProc)(void* ctx, const char name[]);

View File

@ -7,16 +7,148 @@
*/
#include "gl/SkGLContext.h"
#include "GrGLUtil.h"
#include "SkGpuFenceSync.h"
SkGLContext::SkGLContext() {
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() {
SkASSERT(NULL == fGL.get()); // Subclass should destroy the interface.
// Subclass should call teardown.
#ifdef SK_DEBUG
for (size_t i = 0; i < SK_ARRAY_COUNT(fFrameFences); i++) {
SkASSERT(0 == fFrameFences[i]);
}
#endif
SkASSERT(NULL == fGL.get());
SkASSERT(NULL == 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(NULL);
}
fGL.reset(NULL);
}
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(SkNEW(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 NULL;
}
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 NULL;
}
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 NULL;
}
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);
}

View File

@ -547,7 +547,7 @@ SkNullGLContext* SkNullGLContext::Create(GrGLStandard forcedGpuAPI) {
SkNullGLContext::SkNullGLContext() {
fState = SkNEW(ContextState);
GrGLInterface* interface = create_null_interface(fState);
fGL.reset(interface);
this->init(interface);
#if GR_GL_PER_GL_FUNC_CALLBACK
interface->fCallback = set_current_context_from_interface;
interface->fCallbackData = reinterpret_cast<GrGLInterfaceCallbackData>(fState);
@ -555,8 +555,8 @@ SkNullGLContext::SkNullGLContext() {
}
SkNullGLContext::~SkNullGLContext() {
fGL.reset(NULL);
this->teardown();
fState->unref();
}
void SkNullGLContext::makeCurrent() const { set_current_context(fState); }
void SkNullGLContext::onPlatformMakeCurrent() const { set_current_context(fState); }

View File

@ -96,25 +96,27 @@ SkANGLEGLContext::SkANGLEGLContext()
eglMakeCurrent(fDisplay, fSurface, fSurface, fContext);
fGL.reset(GrGLCreateANGLEInterface());
if (NULL == fGL.get()) {
SkAutoTUnref<const GrGLInterface> gl(GrGLCreateANGLEInterface());
if (NULL == gl.get()) {
SkDebugf("Could not create ANGLE GL interface!\n");
this->destroyGLContext();
return;
}
if (!fGL->validate()) {
if (!gl->validate()) {
SkDebugf("Could not validate ANGLE GL interface!\n");
this->destroyGLContext();
return;
}
this->init(gl.detach());
}
SkANGLEGLContext::~SkANGLEGLContext() {
this->teardown();
this->destroyGLContext();
}
void SkANGLEGLContext::destroyGLContext() {
fGL.reset(NULL);
if (fDisplay) {
eglMakeCurrent(fDisplay, 0, 0, 0);
@ -133,14 +135,18 @@ void SkANGLEGLContext::destroyGLContext() {
}
}
void SkANGLEGLContext::makeCurrent() const {
void SkANGLEGLContext::onPlatformMakeCurrent() const {
if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
SkDebugf("Could not set the context.\n");
}
}
void SkANGLEGLContext::swapBuffers() const {
void SkANGLEGLContext::onPlatformSwapBuffers() const {
if (!eglSwapBuffers(fDisplay, fSurface)) {
SkDebugf("Could not complete eglSwapBuffers.\n");
}
}
GrGLFuncPtr SkANGLEGLContext::onPlatformGetProcAddress(const char* name) const {
return eglGetProcAddress(name);
}

View File

@ -9,9 +9,9 @@
#include "gl/debug/SkDebugGLContext.h"
SkDebugGLContext::SkDebugGLContext() {
fGL.reset(GrGLCreateDebugInterface());
this->init(GrGLCreateDebugInterface());
}
SkDebugGLContext::~SkDebugGLContext() {
fGL.reset(NULL);
this->teardown();
}

View File

@ -13,8 +13,6 @@
class SkDebugGLContext : public SkGLContext {
public:
~SkDebugGLContext() override;
void makeCurrent() const override {}
void swapBuffers() const override {}
static SkDebugGLContext* Create(GrGLStandard forcedGpuAPI) {
if (kGLES_GrGLStandard == forcedGpuAPI) {
@ -23,6 +21,10 @@ public:
return SkNEW(SkDebugGLContext);
}
private:
void onPlatformMakeCurrent() const override {}
void onPlatformSwapBuffers() const override {}
GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return NULL; }
SkDebugGLContext();
};

View File

@ -8,20 +8,42 @@
#include "gl/SkGLContext.h"
#include <GLES2/gl2.h>
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/egl.h>
#include <EGL/eglext.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 flushAndWaitFence(SkPlatformGpuFence fence) const override;
void deleteFence(SkPlatformGpuFence fence) const override;
private:
SkEGLFenceSync(EGLDisplay display) : fDisplay(display) {}
EGLDisplay fDisplay;
typedef SkGpuFenceSync INHERITED;
};
class EGLGLContext : public SkGLContext {
public:
EGLGLContext(GrGLStandard forcedGpuAPI);
~EGLGLContext() override;
void makeCurrent() const override;
void swapBuffers() 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;
@ -69,7 +91,9 @@ EGLGLContext::EGLGLContext(GrGLStandard forcedGpuAPI)
}
SkASSERT(forcedGpuAPI == kNone_GrGLStandard || kAPIs[api].fStandard == forcedGpuAPI);
for (; NULL == fGL.get() && api < apiLimit; ++api) {
SkAutoTUnref<const GrGLInterface> gl;
for (; NULL == gl.get() && api < apiLimit; ++api) {
fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGLint majorVersion;
@ -134,27 +158,30 @@ EGLGLContext::EGLGLContext(GrGLStandard forcedGpuAPI)
continue;
}
fGL.reset(GrGLCreateNativeInterface());
if (NULL == fGL.get()) {
gl.reset(GrGLCreateNativeInterface());
if (NULL == gl.get()) {
SkDebugf("Failed to create gl interface.\n");
this->destroyGLContext();
continue;
}
if (!fGL->validate()) {
if (!gl->validate()) {
SkDebugf("Failed to validate gl interface.\n");
this->destroyGLContext();
continue;
}
this->init(gl.detach(), SkEGLFenceSync::CreateIfSupported(fDisplay));
break;
}
}
EGLGLContext::~EGLGLContext() {
this->teardown();
this->destroyGLContext();
}
void EGLGLContext::destroyGLContext() {
fGL.reset(NULL);
if (fDisplay) {
eglMakeCurrent(fDisplay, 0, 0, 0);
@ -174,18 +201,61 @@ void EGLGLContext::destroyGLContext() {
}
void EGLGLContext::makeCurrent() const {
void EGLGLContext::onPlatformMakeCurrent() const {
if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
SkDebugf("Could not set the context.\n");
}
}
void EGLGLContext::swapBuffers() const {
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) {
int 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 NULL;
}
return SkNEW_ARGS(SkEGLFenceSync, (display));
}
SkPlatformGpuFence SkEGLFenceSync::insertFence() const {
return eglCreateSyncKHR(fDisplay, EGL_SYNC_FENCE_KHR, NULL);
}
bool SkEGLFenceSync::flushAndWaitFence(SkPlatformGpuFence platformFence) const {
EGLSyncKHR eglsync = static_cast<EGLSyncKHR>(platformFence);
return EGL_CONDITION_SATISFIED_KHR == eglClientWaitSyncKHR(fDisplay,
eglsync,
EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
EGL_FOREVER_KHR);
}
void SkEGLFenceSync::deleteFence(SkPlatformGpuFence platformFence) const {
EGLSyncKHR eglsync = static_cast<EGLSyncKHR>(platformFence);
eglDestroySyncKHR(fDisplay, eglsync);
}
} // anonymous namespace
SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI) {

View File

@ -48,12 +48,14 @@ class GLXGLContext : public SkGLContext {
public:
GLXGLContext(GrGLStandard forcedGpuAPI);
~GLXGLContext() override;
void makeCurrent() const override;
void swapBuffers() const override;
private:
void destroyGLContext();
void onPlatformMakeCurrent() const override;
void onPlatformSwapBuffers() const override;
GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
GLXContext fContext;
Display* fDisplay;
Pixmap fPixmap;
@ -267,27 +269,29 @@ GLXGLContext::GLXGLContext(GrGLStandard forcedGpuAPI)
return;
}
fGL.reset(GrGLCreateNativeInterface());
if (NULL == fGL.get()) {
SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
if (NULL == gl.get()) {
SkDebugf("Failed to create gl interface");
this->destroyGLContext();
return;
}
if (!fGL->validate()) {
if (!gl->validate()) {
SkDebugf("Failed to validate gl interface");
this->destroyGLContext();
return;
}
this->init(gl.detach());
}
GLXGLContext::~GLXGLContext() {
this->teardown();
this->destroyGLContext();
}
void GLXGLContext::destroyGLContext() {
fGL.reset(NULL);
if (fDisplay) {
glXMakeCurrent(fDisplay, 0, 0);
@ -311,16 +315,20 @@ void GLXGLContext::destroyGLContext() {
}
}
void GLXGLContext::makeCurrent() const {
void GLXGLContext::onPlatformMakeCurrent() const {
if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
SkDebugf("Could not set the context.\n");
}
}
void GLXGLContext::swapBuffers() const {
void GLXGLContext::onPlatformSwapBuffers() const {
glXSwapBuffers(fDisplay, fGlxPixmap);
}
GrGLFuncPtr GLXGLContext::onPlatformGetProcAddress(const char* procName) const {
return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName));
}
} // anonymous namespace
SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI) {

View File

@ -8,6 +8,7 @@
#include "gl/SkGLContext.h"
#import <OpenGLES/EAGL.h>
#include <dlfcn.h>
#define EAGLCTX ((EAGLContext*)(fEAGLContext))
@ -17,40 +18,50 @@ class IOSGLContext : public SkGLContext {
public:
IOSGLContext();
~IOSGLContext() override;
void makeCurrent() const override;
void swapBuffers() const 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) {
: fEAGLContext(NULL)
, fGLLibrary(RTLD_DEFAULT) {
fEAGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:EAGLCTX];
fGL.reset(GrGLCreateNativeInterface());
if (NULL == fGL.get()) {
SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
if (NULL == gl.get()) {
SkDebugf("Failed to create gl interface");
this->destroyGLContext();
return;
}
if (!fGL->validate()) {
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.detach());
}
IOSGLContext::~IOSGLContext() {
this->teardown();
this->destroyGLContext();
}
void IOSGLContext::destroyGLContext() {
fGL.reset(NULL);
if (fEAGLContext) {
if ([EAGLContext currentContext] == EAGLCTX) {
[EAGLContext setCurrentContext:nil];
@ -58,16 +69,23 @@ void IOSGLContext::destroyGLContext() {
[EAGLCTX release];
fEAGLContext = NULL;
}
if (RTLD_DEFAULT != fGLLibrary) {
dlclose(fGLLibrary);
}
}
void IOSGLContext::makeCurrent() const {
void IOSGLContext::onPlatformMakeCurrent() const {
if (![EAGLContext setCurrentContext:EAGLCTX]) {
SkDebugf("Could not set the context.\n");
}
}
void IOSGLContext::swapBuffers() const { }
void IOSGLContext::onPlatformSwapBuffers() const { }
GrGLFuncPtr IOSGLContext::onPlatformGetProcAddress(const char* procName) const {
return reinterpret_cast<GrGLFuncPtr>(dlsym(fGLLibrary, procName));
}
} // anonymous namespace

View File

@ -9,23 +9,28 @@
#include "AvailabilityMacros.h"
#include <OpenGL/OpenGL.h>
#include <dlfcn.h>
namespace {
class MacGLContext : public SkGLContext {
public:
MacGLContext();
~MacGLContext() override;
void makeCurrent() const override;
void swapBuffers() const 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(NULL) {
: fContext(NULL)
, fGLLibrary(RTLD_DEFAULT) {
CGLPixelFormatAttribute attributes[] = {
#if MAC_OS_X_VERSION_10_7
kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core,
@ -53,39 +58,52 @@ MacGLContext::MacGLContext()
CGLSetCurrentContext(fContext);
fGL.reset(GrGLCreateNativeInterface());
if (NULL == fGL.get()) {
SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
if (NULL == gl.get()) {
SkDebugf("Context could not create GL interface.\n");
this->destroyGLContext();
return;
}
if (!fGL->validate()) {
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.detach());
}
MacGLContext::~MacGLContext() {
this->teardown();
this->destroyGLContext();
}
void MacGLContext::destroyGLContext() {
fGL.reset(NULL);
if (fContext) {
CGLReleaseContext(fContext);
fContext = NULL;
}
if (RTLD_DEFAULT != fGLLibrary) {
dlclose(fGLLibrary);
}
}
void MacGLContext::makeCurrent() const {
void MacGLContext::onPlatformMakeCurrent() const {
CGLSetCurrentContext(fContext);
}
void MacGLContext::swapBuffers() const {
void MacGLContext::onPlatformSwapBuffers() const {
CGLFlushDrawable(fContext);
}
GrGLFuncPtr MacGLContext::onPlatformGetProcAddress(const char* procName) const {
return reinterpret_cast<GrGLFuncPtr>(dlsym(fGLLibrary, procName));
}
} // anonymous namespace
SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI) {

View File

@ -50,26 +50,28 @@ SkMesaGLContext::SkMesaGLContext()
return;
}
fGL.reset(GrGLCreateMesaInterface());
if (NULL == fGL.get()) {
SkAutoTUnref<const GrGLInterface> gl(GrGLCreateMesaInterface());
if (NULL == gl.get()) {
SkDebugf("Could not create GL interface!\n");
this->destroyGLContext();
return;
}
if (!fGL->validate()) {
if (!gl->validate()) {
SkDebugf("Could not validate GL interface!\n");
this->destroyGLContext();
return;
}
this->init(gl.detach());
}
SkMesaGLContext::~SkMesaGLContext() {
this->teardown();
this->destroyGLContext();
}
void SkMesaGLContext::destroyGLContext() {
fGL.reset(NULL);
if (fImage) {
sk_free(fImage);
fImage = NULL;
@ -83,7 +85,7 @@ void SkMesaGLContext::destroyGLContext() {
void SkMesaGLContext::makeCurrent() const {
void SkMesaGLContext::onPlatformMakeCurrent() const {
if (fContext) {
if (!OSMesaMakeCurrent((OSMesaContext)fContext, fImage,
GR_GL_UNSIGNED_BYTE, gBOGUS_SIZE, gBOGUS_SIZE)) {
@ -92,4 +94,8 @@ void SkMesaGLContext::makeCurrent() const {
}
}
void SkMesaGLContext::swapBuffers() const { }
void SkMesaGLContext::onPlatformSwapBuffers() const { }
GrGLFuncPtr SkMesaGLContext::onPlatformGetProcAddress(const char* procName) const {
return OSMesaGetProcAddress(procName);
}

View File

@ -18,8 +18,6 @@ private:
public:
~SkMesaGLContext() override;
void makeCurrent() const override;
void swapBuffers() const override;
static SkMesaGLContext* Create(GrGLStandard forcedGpuAPI) {
if (kGLES_GrGLStandard == forcedGpuAPI) {
@ -37,6 +35,10 @@ private:
SkMesaGLContext();
void destroyGLContext();
void onPlatformMakeCurrent() const override;
void onPlatformSwapBuffers() const override;
GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
Context fContext;
GrGLubyte *fImage;
};

View File

@ -21,12 +21,14 @@ class WinGLContext : public SkGLContext {
public:
WinGLContext(GrGLStandard forcedGpuAPI);
~WinGLContext() override;
void makeCurrent() const override;
void swapBuffers() const 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;
@ -113,25 +115,27 @@ WinGLContext::WinGLContext(GrGLStandard forcedGpuAPI)
return;
}
fGL.reset(GrGLCreateNativeInterface());
if (NULL == fGL.get()) {
SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
if (NULL == gl.get()) {
SkDebugf("Could not create GL interface.\n");
this->destroyGLContext();
return;
}
if (!fGL->validate()) {
if (!gl->validate()) {
SkDebugf("Could not validate GL interface.\n");
this->destroyGLContext();
return;
}
this->init(gl.detach());
}
WinGLContext::~WinGLContext() {
this->teardown();
this->destroyGLContext();
}
void WinGLContext::destroyGLContext() {
fGL.reset(NULL);
SkSafeSetNull(fPbufferContext);
if (fGlRenderContext) {
wglDeleteContext(fGlRenderContext);
@ -147,7 +151,7 @@ void WinGLContext::destroyGLContext() {
}
}
void WinGLContext::makeCurrent() const {
void WinGLContext::onPlatformMakeCurrent() const {
HDC dc;
HGLRC glrc;
@ -164,7 +168,7 @@ void WinGLContext::makeCurrent() const {
}
}
void WinGLContext::swapBuffers() const {
void WinGLContext::onPlatformSwapBuffers() const {
HDC dc;
if (NULL == fPbufferContext) {
@ -177,6 +181,10 @@ void WinGLContext::swapBuffers() const {
}
}
GrGLFuncPtr WinGLContext::onPlatformGetProcAddress(const char* name) const {
return reinterpret_cast<GrGLFuncPtr>(wglGetProcAddress(name));
}
} // anonymous namespace
SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI) {