1 /*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "tools/gpu/gl/angle/GLTestContext_angle.h"
9
10 #include "include/core/SkTime.h"
11 #include "include/gpu/gl/GrGLAssembleInterface.h"
12 #include "include/gpu/gl/GrGLInterface.h"
13 #include "src/core/SkTraceEvent.h"
14 #include "src/gpu/gl/GrGLDefines.h"
15 #include "src/gpu/gl/GrGLUtil.h"
16 #include "src/ports/SkOSLibrary.h"
17 #include "third_party/externals/angle2/include/platform/Platform.h"
18
19 #include <vector>
20
21 #define EGL_EGL_PROTOTYPES 1
22 #include <EGL/egl.h>
23 #include <EGL/eglext.h>
24
25 #define EGL_PLATFORM_ANGLE_ANGLE 0x3202
26 #define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203
27 #define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207
28 #define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
29 #define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D
30
31 #define EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE 0x3483
32
33 using sk_gpu_test::ANGLEBackend;
34 using sk_gpu_test::ANGLEContextVersion;
35
36 namespace {
37 struct Libs {
38 void* fGLLib;
39 void* fEGLLib;
40 };
41
context_restorer()42 std::function<void()> context_restorer() {
43 auto display = eglGetCurrentDisplay();
44 auto dsurface = eglGetCurrentSurface(EGL_DRAW);
45 auto rsurface = eglGetCurrentSurface(EGL_READ);
46 auto context = eglGetCurrentContext();
47 return [display, dsurface, rsurface, context] {
48 eglMakeCurrent(display, dsurface, rsurface, context);
49 };
50 }
51
angle_get_gl_proc(void * ctx,const char name[])52 static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) {
53 const Libs* libs = reinterpret_cast<const Libs*>(ctx);
54 GrGLFuncPtr proc = (GrGLFuncPtr) SkGetProcedureAddress(libs->fGLLib, name);
55 if (proc) {
56 return proc;
57 }
58 proc = (GrGLFuncPtr) SkGetProcedureAddress(libs->fEGLLib, name);
59 if (proc) {
60 return proc;
61 }
62 return eglGetProcAddress(name);
63 }
64
get_angle_egl_display(void * nativeDisplay,ANGLEBackend type)65 void* get_angle_egl_display(void* nativeDisplay, ANGLEBackend type) {
66 PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
67 eglGetPlatformDisplayEXT =
68 (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
69
70 // We expect ANGLE to support this extension
71 if (!eglGetPlatformDisplayEXT) {
72 return EGL_NO_DISPLAY;
73 }
74
75 EGLint typeNum = 0;
76 switch (type) {
77 case ANGLEBackend::kD3D9:
78 typeNum = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
79 break;
80 case ANGLEBackend::kD3D11:
81 typeNum = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
82 break;
83 case ANGLEBackend::kOpenGL:
84 typeNum = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
85 break;
86 }
87 const EGLint attribs[] = { EGL_PLATFORM_ANGLE_TYPE_ANGLE, typeNum, EGL_NONE };
88 return eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, nativeDisplay, attribs);
89 }
90
91 class ANGLEGLContext : public sk_gpu_test::GLTestContext {
92 public:
93 ANGLEGLContext(ANGLEBackend, ANGLEContextVersion, ANGLEGLContext* shareContext, void* display);
94 ~ANGLEGLContext() override;
95
96 GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
97 void destroyEGLImage(GrEGLImage) const override;
98 GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
99 std::unique_ptr<sk_gpu_test::GLTestContext> makeNew() const override;
100
101 private:
102 void destroyGLContext();
103
104 void onPlatformMakeNotCurrent() const override;
105 void onPlatformMakeCurrent() const override;
106 std::function<void()> onPlatformGetAutoContextRestore() const override;
107 GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override;
108
109 void* fContext;
110 void* fDisplay;
111 void* fSurface;
112 ANGLEBackend fType;
113 ANGLEContextVersion fVersion;
114 bool fOwnsDisplay;
115
116 angle::ResetDisplayPlatformFunc fResetPlatform = nullptr;
117
118 PFNEGLCREATEIMAGEKHRPROC fCreateImage = nullptr;
119 PFNEGLDESTROYIMAGEKHRPROC fDestroyImage = nullptr;
120
121 #ifdef SK_BUILD_FOR_WIN
122 HWND fWindow;
123 HDC fDeviceContext;
124 static ATOM gWC;
125 #endif
126 };
127
128 #ifdef SK_BUILD_FOR_WIN
129 ATOM ANGLEGLContext::gWC = 0;
130
131 enum class IsWine { kUnknown, kNo, kYes };
132
is_wine()133 static IsWine is_wine() {
134 HMODULE ntdll = GetModuleHandle("ntdll.dll");
135 if (!ntdll) {
136 SkDebugf("No ntdll.dll on Windows?!\n");
137 return IsWine::kUnknown;
138 }
139 return GetProcAddress(ntdll, "wine_get_version") == nullptr ? IsWine::kNo : IsWine::kYes;
140 }
141
142 #endif
143
ANGLE_getTraceCategoryEnabledFlag(angle::PlatformMethods * platform,const char * category_group)144 static const unsigned char* ANGLE_getTraceCategoryEnabledFlag(angle::PlatformMethods* platform,
145 const char* category_group) {
146 return SkEventTracer::GetInstance()->getCategoryGroupEnabled(category_group);
147 }
148
ANGLE_addTraceEvent(angle::PlatformMethods * platform,char phase,const unsigned char * category_group_enabled,const char * name,unsigned long long id,double timestamp,int num_args,const char ** arg_names,const unsigned char * arg_types,const unsigned long long * arg_values,unsigned char flags)149 static angle::TraceEventHandle ANGLE_addTraceEvent(angle::PlatformMethods* platform,
150 char phase,
151 const unsigned char* category_group_enabled,
152 const char* name,
153 unsigned long long id,
154 double timestamp,
155 int num_args,
156 const char** arg_names,
157 const unsigned char* arg_types,
158 const unsigned long long* arg_values,
159 unsigned char flags) {
160 static_assert(sizeof(unsigned long long) == sizeof(uint64_t), "Non-64-bit trace event args!");
161 return SkEventTracer::GetInstance()->addTraceEvent(
162 phase, category_group_enabled, name, id, num_args, arg_names, arg_types,
163 reinterpret_cast<const uint64_t*>(arg_values), flags);
164 }
165
ANGLE_updateTraceEventDuration(angle::PlatformMethods * platform,const unsigned char * category_group_enabled,const char * name,angle::TraceEventHandle handle)166 static void ANGLE_updateTraceEventDuration(angle::PlatformMethods* platform,
167 const unsigned char* category_group_enabled,
168 const char* name,
169 angle::TraceEventHandle handle) {
170 SkEventTracer::GetInstance()->updateTraceEventDuration(category_group_enabled, name, handle);
171 }
172
ANGLE_monotonicallyIncreasingTime(angle::PlatformMethods * platform)173 static double ANGLE_monotonicallyIncreasingTime(angle::PlatformMethods* platform) {
174 return SkTime::GetSecs();
175 }
176
ANGLEGLContext(ANGLEBackend type,ANGLEContextVersion version,ANGLEGLContext * shareContext,void * display)177 ANGLEGLContext::ANGLEGLContext(ANGLEBackend type, ANGLEContextVersion version,
178 ANGLEGLContext* shareContext, void* display)
179 : fContext(EGL_NO_CONTEXT)
180 , fDisplay(display)
181 , fSurface(EGL_NO_SURFACE)
182 , fType(type)
183 , fVersion(version)
184 , fOwnsDisplay(false) {
185 #ifdef SK_BUILD_FOR_WIN
186 fWindow = nullptr;
187 fDeviceContext = nullptr;
188
189 static IsWine gIsWine = is_wine();
190 if (gIsWine == IsWine::kYes && type != ANGLEBackend::kOpenGL) {
191 // D3D backends of ANGLE don't really work well under Wine with our tests and are likely to
192 // crash. This makes it easier to test using the GL ANGLE backend under Wine on Linux
193 // without lots of spurious Wine debug spew and crashes.
194 return;
195 }
196
197 if (EGL_NO_DISPLAY == fDisplay) {
198 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(nullptr);
199
200 if (!gWC) {
201 WNDCLASS wc;
202 wc.cbClsExtra = 0;
203 wc.cbWndExtra = 0;
204 wc.hbrBackground = nullptr;
205 wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
206 wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
207 wc.hInstance = hInstance;
208 wc.lpfnWndProc = (WNDPROC) DefWindowProc;
209 wc.lpszClassName = TEXT("ANGLE-win");
210 wc.lpszMenuName = nullptr;
211 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
212
213 gWC = RegisterClass(&wc);
214 if (!gWC) {
215 SkDebugf("Could not register window class.\n");
216 return;
217 }
218 }
219 if (!(fWindow = CreateWindow(TEXT("ANGLE-win"),
220 TEXT("The Invisible Man"),
221 WS_OVERLAPPEDWINDOW,
222 0, 0, 1, 1,
223 nullptr, nullptr,
224 hInstance, nullptr))) {
225 SkDebugf("Could not create window.\n");
226 return;
227 }
228
229 if (!(fDeviceContext = GetDC(fWindow))) {
230 SkDebugf("Could not get device context.\n");
231 this->destroyGLContext();
232 return;
233 }
234
235 fDisplay = get_angle_egl_display(fDeviceContext, type);
236 fOwnsDisplay = true;
237 }
238 #else
239 SkASSERT(EGL_NO_DISPLAY == fDisplay);
240 fDisplay = get_angle_egl_display(EGL_DEFAULT_DISPLAY, type);
241 fOwnsDisplay = true;
242 #endif
243 if (EGL_NO_DISPLAY == fDisplay) {
244 SkDebugf("Could not create EGL display!");
245 return;
246 }
247
248 // Add ANGLE platform hooks to connect to Skia's tracing implementation
249 angle::GetDisplayPlatformFunc getPlatform = reinterpret_cast<angle::GetDisplayPlatformFunc>(
250 eglGetProcAddress("ANGLEGetDisplayPlatform"));
251 if (getPlatform) {
252 fResetPlatform = reinterpret_cast<angle::ResetDisplayPlatformFunc>(
253 eglGetProcAddress("ANGLEResetDisplayPlatform"));
254 SkASSERT(fResetPlatform);
255
256 angle::PlatformMethods* platformMethods = nullptr;
257 if (getPlatform(fDisplay, angle::g_PlatformMethodNames, angle::g_NumPlatformMethods,
258 nullptr, &platformMethods)) {
259 platformMethods->addTraceEvent = ANGLE_addTraceEvent;
260 platformMethods->getTraceCategoryEnabledFlag = ANGLE_getTraceCategoryEnabledFlag;
261 platformMethods->updateTraceEventDuration = ANGLE_updateTraceEventDuration;
262 platformMethods->monotonicallyIncreasingTime = ANGLE_monotonicallyIncreasingTime;
263 }
264 }
265
266 EGLint majorVersion;
267 EGLint minorVersion;
268 if (!eglInitialize(fDisplay, &majorVersion, &minorVersion)) {
269 SkDebugf("Could not initialize display!");
270 this->destroyGLContext();
271 return;
272 }
273
274 EGLint numConfigs;
275 static const EGLint configAttribs[] = {
276 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
277 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
278 EGL_RED_SIZE, 8,
279 EGL_GREEN_SIZE, 8,
280 EGL_BLUE_SIZE, 8,
281 EGL_ALPHA_SIZE, 8,
282 EGL_NONE
283 };
284
285 EGLConfig surfaceConfig;
286 if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) {
287 SkDebugf("Could not create choose config!");
288 this->destroyGLContext();
289 return;
290 }
291
292 int versionNum = ANGLEContextVersion::kES2 == version ? 2 : 3;
293 std::vector<EGLint> contextAttribs = {
294 EGL_CONTEXT_CLIENT_VERSION, versionNum,
295 };
296
297 const char* extensions = eglQueryString(fDisplay, EGL_EXTENSIONS);
298 if (strstr(extensions, "EGL_ANGLE_create_context_backwards_compatible")) {
299 contextAttribs.push_back(EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE);
300 contextAttribs.push_back(EGL_FALSE);
301 }
302
303 contextAttribs.push_back(EGL_NONE);
304
305 EGLContext eglShareContext = shareContext ? shareContext->fContext : nullptr;
306 fContext = eglCreateContext(fDisplay, surfaceConfig, eglShareContext, contextAttribs.data());
307 if (EGL_NO_CONTEXT == fContext) {
308 SkDebugf("Could not create context!");
309 this->destroyGLContext();
310 return;
311 }
312
313 static const EGLint surfaceAttribs[] = {
314 EGL_WIDTH, 1,
315 EGL_HEIGHT, 1,
316 EGL_NONE
317 };
318
319 fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs);
320
321 SkScopeExit restorer(context_restorer());
322 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
323 SkDebugf("Could not set the context.");
324 this->destroyGLContext();
325 return;
326 }
327
328 sk_sp<const GrGLInterface> gl = sk_gpu_test::CreateANGLEGLInterface();
329 if (nullptr == gl.get()) {
330 SkDebugf("Could not create ANGLE GL interface!\n");
331 this->destroyGLContext();
332 return;
333 }
334 if (!gl->validate()) {
335 SkDebugf("Could not validate ANGLE GL interface!\n");
336 this->destroyGLContext();
337 return;
338 }
339
340 #ifdef SK_DEBUG
341 // Verify that the interface we requested was actually returned to us
342 const GrGLubyte* rendererUByte;
343 GR_GL_CALL_RET(gl.get(), rendererUByte, GetString(GR_GL_RENDERER));
344 const char* renderer = reinterpret_cast<const char*>(rendererUByte);
345 switch (type) {
346 case ANGLEBackend::kD3D9:
347 SkASSERT(strstr(renderer, "Direct3D9"));
348 break;
349 case ANGLEBackend::kD3D11:
350 SkASSERT(strstr(renderer, "Direct3D11"));
351 break;
352 case ANGLEBackend::kOpenGL:
353 SkASSERT(strstr(renderer, "OpenGL"));
354 break;
355 }
356 #endif
357 if (strstr(extensions, "EGL_KHR_image")) {
358 fCreateImage = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
359 fDestroyImage = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
360 }
361
362 this->init(std::move(gl));
363 }
364
~ANGLEGLContext()365 ANGLEGLContext::~ANGLEGLContext() {
366 this->teardown();
367 this->destroyGLContext();
368 }
369
texture2DToEGLImage(GrGLuint texID) const370 GrEGLImage ANGLEGLContext::texture2DToEGLImage(GrGLuint texID) const {
371 if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
372 return GR_EGL_NO_IMAGE;
373 }
374 EGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0,
375 GR_EGL_IMAGE_PRESERVED, GR_EGL_TRUE,
376 GR_EGL_NONE };
377 // 64 bit cast is to shut Visual C++ up about casting 32 bit value to a pointer.
378 GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>((uint64_t)texID);
379 return fCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, attribs);
380 }
381
destroyEGLImage(GrEGLImage image) const382 void ANGLEGLContext::destroyEGLImage(GrEGLImage image) const { fDestroyImage(fDisplay, image); }
383
eglImageToExternalTexture(GrEGLImage image) const384 GrGLuint ANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const {
385 while (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {}
386 if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
387 return 0;
388 }
389 typedef GrGLvoid (EGLAPIENTRY *EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage);
390 EGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
391 (EGLImageTargetTexture2DProc)eglGetProcAddress("glEGLImageTargetTexture2DOES");
392 if (!glEGLImageTargetTexture2D) {
393 return 0;
394 }
395 GrGLuint texID;
396 GR_GL_CALL(this->gl(), GenTextures(1, &texID));
397 if (!texID) {
398 return 0;
399 }
400 GR_GL_CALL(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
401 if (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
402 GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
403 return 0;
404 }
405 glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
406 if (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
407 GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
408 return 0;
409 }
410 return texID;
411 }
412
makeNew() const413 std::unique_ptr<sk_gpu_test::GLTestContext> ANGLEGLContext::makeNew() const {
414 // For EGLImage sharing between contexts to work in ANGLE the two contexts
415 // need to share the same display
416 std::unique_ptr<sk_gpu_test::GLTestContext> ctx =
417 sk_gpu_test::MakeANGLETestContext(fType, fVersion, nullptr, fDisplay);
418 if (ctx) {
419 ctx->makeCurrent();
420 }
421 return ctx;
422 }
423
destroyGLContext()424 void ANGLEGLContext::destroyGLContext() {
425 if (EGL_NO_DISPLAY != fDisplay) {
426 if (eglGetCurrentContext() == fContext) {
427 // This will ensure that the context is immediately deleted.
428 eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
429 }
430
431 if (EGL_NO_CONTEXT != fContext) {
432 eglDestroyContext(fDisplay, fContext);
433 fContext = EGL_NO_CONTEXT;
434 }
435
436 if (EGL_NO_SURFACE != fSurface) {
437 eglDestroySurface(fDisplay, fSurface);
438 fSurface = EGL_NO_SURFACE;
439 }
440
441 if (fResetPlatform) {
442 fResetPlatform(fDisplay);
443 }
444
445 if (fOwnsDisplay) {
446 // Only terminate the display if we created it. If we were a context created by makeNew,
447 // the parent context might still have work to do on the display. If we terminate now,
448 // that context might be deleted once it no longer becomes current, and we may hit
449 // undefined behavior in this destructor when calling eglDestroy[Context|Surface] on a
450 // terminated display.
451 eglTerminate(fDisplay);
452 }
453 fDisplay = EGL_NO_DISPLAY;
454 fOwnsDisplay = false;
455 }
456
457 #ifdef SK_BUILD_FOR_WIN
458 if (fWindow) {
459 if (fDeviceContext) {
460 ReleaseDC(fWindow, fDeviceContext);
461 fDeviceContext = 0;
462 }
463
464 DestroyWindow(fWindow);
465 fWindow = 0;
466 }
467 #endif
468 }
469
onPlatformMakeNotCurrent() const470 void ANGLEGLContext::onPlatformMakeNotCurrent() const {
471 if (!eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
472 SkDebugf("Could not reset the context 0x%x.\n", eglGetError());
473 }
474 }
475
onPlatformMakeCurrent() const476 void ANGLEGLContext::onPlatformMakeCurrent() const {
477 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
478 SkDebugf("Could not set the context 0x%x.\n", eglGetError());
479 }
480 }
481
onPlatformGetAutoContextRestore() const482 std::function<void()> ANGLEGLContext::onPlatformGetAutoContextRestore() const {
483 if (eglGetCurrentContext() == fContext) {
484 return nullptr;
485 }
486 return context_restorer();
487 }
488
onPlatformGetProcAddress(const char * name) const489 GrGLFuncPtr ANGLEGLContext::onPlatformGetProcAddress(const char* name) const {
490 return eglGetProcAddress(name);
491 }
492 } // anonymous namespace
493
494 namespace sk_gpu_test {
CreateANGLEGLInterface()495 sk_sp<const GrGLInterface> CreateANGLEGLInterface() {
496 static Libs gLibs = { nullptr, nullptr };
497
498 if (nullptr == gLibs.fGLLib) {
499 // We load the ANGLE library and never let it go
500 #if defined _WIN32
501 gLibs.fGLLib = SkLoadDynamicLibrary("libGLESv2.dll");
502 gLibs.fEGLLib = SkLoadDynamicLibrary("libEGL.dll");
503 #elif defined SK_BUILD_FOR_MAC
504 gLibs.fGLLib = SkLoadDynamicLibrary("libGLESv2.dylib");
505 gLibs.fEGLLib = SkLoadDynamicLibrary("libEGL.dylib");
506 #else
507 gLibs.fGLLib = SkLoadDynamicLibrary("libGLESv2.so");
508 gLibs.fEGLLib = SkLoadDynamicLibrary("libEGL.so");
509 #endif
510 }
511
512 if (nullptr == gLibs.fGLLib || nullptr == gLibs.fEGLLib) {
513 // We can't setup the interface correctly w/o the so
514 return nullptr;
515 }
516
517 return GrGLMakeAssembledGLESInterface(&gLibs, angle_get_gl_proc);
518 }
519
MakeANGLETestContext(ANGLEBackend type,ANGLEContextVersion version,GLTestContext * shareContext,void * display)520 std::unique_ptr<GLTestContext> MakeANGLETestContext(ANGLEBackend type, ANGLEContextVersion version,
521 GLTestContext* shareContext, void* display) {
522 #if defined(SK_BUILD_FOR_WIN) && defined(_M_ARM64)
523 // Windows-on-ARM only has D3D11. This will fail correctly, but it produces huge amounts of
524 // debug output for every unit test from both ANGLE and our context factory.
525 if (ANGLEBackend::kD3D11 != type) {
526 return nullptr;
527 }
528 #endif
529
530 ANGLEGLContext* angleShareContext = reinterpret_cast<ANGLEGLContext*>(shareContext);
531 std::unique_ptr<GLTestContext> ctx(new ANGLEGLContext(type, version,
532 angleShareContext, display));
533 if (!ctx->isValid()) {
534 return nullptr;
535 }
536 return ctx;
537 }
538 } // namespace sk_gpu_test
539