1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "include/core/SkTypes.h"
9
10#include "tools/gpu/gl/GLTestContext.h"
11#include "AvailabilityMacros.h"
12
13#include <OpenGL/OpenGL.h>
14#include <dlfcn.h>
15
16namespace {
17
18std::function<void()> context_restorer() {
19    auto context = CGLGetCurrentContext();
20    return [context] { CGLSetCurrentContext(context); };
21}
22
23class MacGLTestContext : public sk_gpu_test::GLTestContext {
24public:
25    MacGLTestContext(MacGLTestContext* shareContext);
26    ~MacGLTestContext() override;
27
28private:
29    void destroyGLContext();
30
31    void onPlatformMakeNotCurrent() const override;
32    void onPlatformMakeCurrent() const override;
33    std::function<void()> onPlatformGetAutoContextRestore() const override;
34    GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
35
36    CGLContextObj fContext;
37    void* fGLLibrary;
38};
39
40MacGLTestContext::MacGLTestContext(MacGLTestContext* shareContext)
41    : fContext(nullptr)
42    , fGLLibrary(RTLD_DEFAULT) {
43    // We first try to request a Radeon eGPU if one is available.
44    // This will be a Radeon HD7000 and up, which includes all eGPU configs.
45    // If that fails, we try again with only the base parameters.
46    CGLPixelFormatAttribute attributes[] = {
47        // base parameters
48#if MAC_OS_X_VERSION_10_7
49        kCGLPFAOpenGLProfile,
50        (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core,
51#endif
52        kCGLPFADoubleBuffer,
53
54#if MAC_OS_X_VERSION_10_8
55        // eGPU parameters
56        kCGLPFAAllowOfflineRenderers,  // Enables e-GPU.
57        kCGLPFANoRecovery,  // Disallows software rendering.
58        kCGLPFARendererID, (CGLPixelFormatAttribute)kCGLRendererATIRadeonX4000ID, // Select Radeon
59#endif
60        (CGLPixelFormatAttribute)NULL
61    };
62#if MAC_OS_X_VERSION_10_8
63    static const int kFirstEGPUParameter = 3;
64    SkASSERT(kCGLPFAAllowOfflineRenderers == attributes[kFirstEGPUParameter]);
65#endif
66
67    CGLPixelFormatObj pixFormat;
68    GLint npix;
69    CGLChoosePixelFormat(attributes, &pixFormat, &npix);
70
71#if MAC_OS_X_VERSION_10_8
72    if (nullptr == pixFormat) {
73        // Move the NULL-termination up to remove the eGPU parameters and try again
74        attributes[kFirstEGPUParameter] = (CGLPixelFormatAttribute)NULL;
75        CGLChoosePixelFormat(attributes, &pixFormat, &npix);
76    }
77#endif
78    if (nullptr == pixFormat) {
79        SkDebugf("CGLChoosePixelFormat failed.");
80        return;
81    }
82
83    CGLCreateContext(pixFormat, shareContext ? shareContext->fContext : nullptr, &fContext);
84    CGLReleasePixelFormat(pixFormat);
85
86    if (nullptr == fContext) {
87        SkDebugf("CGLCreateContext failed.");
88        return;
89    }
90
91    SkScopeExit restorer(context_restorer());
92    CGLSetCurrentContext(fContext);
93
94    auto gl = GrGLMakeNativeInterface();
95    if (!gl) {
96        SkDebugf("Context could not create GL interface.\n");
97        this->destroyGLContext();
98        return;
99    }
100    if (!gl->validate()) {
101        SkDebugf("Context could not validate GL interface.\n");
102        this->destroyGLContext();
103        return;
104    }
105
106    fGLLibrary = dlopen(
107        "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib",
108        RTLD_LAZY);
109
110    this->init(std::move(gl));
111}
112
113MacGLTestContext::~MacGLTestContext() {
114    this->teardown();
115    this->destroyGLContext();
116}
117
118void MacGLTestContext::destroyGLContext() {
119    if (fContext) {
120        if (CGLGetCurrentContext() == fContext) {
121            // This will ensure that the context is immediately deleted.
122            CGLSetCurrentContext(nullptr);
123        }
124        CGLReleaseContext(fContext);
125        fContext = nullptr;
126    }
127    if (nullptr != fGLLibrary) {
128        dlclose(fGLLibrary);
129    }
130}
131
132void MacGLTestContext::onPlatformMakeNotCurrent() const {
133    CGLSetCurrentContext(nullptr);
134}
135
136void MacGLTestContext::onPlatformMakeCurrent() const {
137    CGLSetCurrentContext(fContext);
138}
139
140std::function<void()> MacGLTestContext::onPlatformGetAutoContextRestore() const {
141    if (CGLGetCurrentContext() == fContext) {
142        return nullptr;
143    }
144    return context_restorer();
145}
146
147GrGLFuncPtr MacGLTestContext::onPlatformGetProcAddress(const char* procName) const {
148    void* handle = (nullptr == fGLLibrary) ? RTLD_DEFAULT : fGLLibrary;
149    return reinterpret_cast<GrGLFuncPtr>(dlsym(handle, procName));
150}
151
152}  // anonymous namespace
153
154namespace sk_gpu_test {
155GLTestContext* CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
156                                           GLTestContext* shareContext) {
157    if (kGLES_GrGLStandard == forcedGpuAPI) {
158        return nullptr;
159    }
160    MacGLTestContext* macShareContext = reinterpret_cast<MacGLTestContext*>(shareContext);
161    MacGLTestContext* ctx = new MacGLTestContext(macShareContext);
162    if (!ctx->isValid()) {
163        delete ctx;
164        return nullptr;
165    }
166    return ctx;
167}
168}  // namespace sk_gpu_test
169