1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/lite/delegates/gpu/gl/egl_environment.h"
17
18 #include <memory>
19
20 #include "absl/memory/memory.h"
21 #include "tensorflow/lite/delegates/gpu/common/status.h"
22 #include "tensorflow/lite/delegates/gpu/gl/gl_call.h"
23 #include "tensorflow/lite/delegates/gpu/gl/request_gpu_info.h"
24
25 namespace tflite {
26 namespace gpu {
27 namespace gl {
28 namespace {
29
30 // TODO(akulik): detect power management event when all contexts are destroyed
31 // and OpenGL ES is reinitialized. See eglMakeCurrent
32
InitDisplay(EGLDisplay * egl_display)33 absl::Status InitDisplay(EGLDisplay* egl_display) {
34 RETURN_IF_ERROR(
35 TFLITE_GPU_CALL_EGL(eglGetDisplay, egl_display, EGL_DEFAULT_DISPLAY));
36 if (*egl_display == EGL_NO_DISPLAY) {
37 return absl::UnavailableError("eglGetDisplay returned nullptr");
38 }
39 bool is_initialized;
40 RETURN_IF_ERROR(TFLITE_GPU_CALL_EGL(eglInitialize, &is_initialized,
41 *egl_display, nullptr, nullptr));
42 if (!is_initialized) {
43 return absl::InternalError("No EGL error, but eglInitialize failed");
44 }
45 return absl::OkStatus();
46 }
47
48 } // namespace
49
NewEglEnvironment(std::unique_ptr<EglEnvironment> * egl_environment)50 absl::Status EglEnvironment::NewEglEnvironment(
51 std::unique_ptr<EglEnvironment>* egl_environment) {
52 *egl_environment = std::make_unique<EglEnvironment>();
53 RETURN_IF_ERROR((*egl_environment)->Init());
54 return absl::OkStatus();
55 }
56
~EglEnvironment()57 EglEnvironment::~EglEnvironment() {
58 if (dummy_framebuffer_ != GL_INVALID_INDEX) {
59 glDeleteFramebuffers(1, &dummy_framebuffer_);
60 }
61 if (dummy_texture_ != GL_INVALID_INDEX) {
62 glDeleteTextures(1, &dummy_texture_);
63 }
64 }
65
Init()66 absl::Status EglEnvironment::Init() {
67 bool is_bound;
68 RETURN_IF_ERROR(
69 TFLITE_GPU_CALL_EGL(eglBindAPI, &is_bound, EGL_OPENGL_ES_API));
70 if (!is_bound) {
71 return absl::InternalError("No EGL error, but eglBindAPI failed");
72 }
73
74 // Re-use context and display if it was created on this thread.
75 if (eglGetCurrentContext() != EGL_NO_CONTEXT) {
76 display_ = eglGetCurrentDisplay();
77 context_ =
78 EglContext(eglGetCurrentContext(), display_, EGL_NO_CONFIG_KHR, false);
79 } else {
80 RETURN_IF_ERROR(InitDisplay(&display_));
81
82 absl::Status status = InitConfiglessContext();
83 if (!status.ok()) {
84 status = InitSurfacelessContext();
85 }
86 if (!status.ok()) {
87 status = InitPBufferContext();
88 }
89 if (!status.ok()) {
90 return status;
91 }
92 }
93
94 if (gpu_info_.vendor == GpuVendor::kUnknown) {
95 RETURN_IF_ERROR(RequestGpuInfo(&gpu_info_));
96 }
97 // TODO(akulik): when do we need ForceSyncTurning?
98 ForceSyncTurning();
99 return absl::OkStatus();
100 }
101
InitConfiglessContext()102 absl::Status EglEnvironment::InitConfiglessContext() {
103 RETURN_IF_ERROR(CreateConfiglessContext(display_, EGL_NO_CONTEXT, &context_));
104 return context_.MakeCurrentSurfaceless();
105 }
106
InitSurfacelessContext()107 absl::Status EglEnvironment::InitSurfacelessContext() {
108 RETURN_IF_ERROR(
109 CreateSurfacelessContext(display_, EGL_NO_CONTEXT, &context_));
110 RETURN_IF_ERROR(context_.MakeCurrentSurfaceless());
111
112 // PowerVR support EGL_KHR_surfaceless_context, but glFenceSync crashes on
113 // PowerVR when it is surface-less.
114 RETURN_IF_ERROR(RequestGpuInfo(&gpu_info_));
115 if (gpu_info_.IsPowerVR()) {
116 return absl::UnavailableError(
117 "Surface-less context is not properly supported on powervr.");
118 }
119 return absl::OkStatus();
120 }
121
InitPBufferContext()122 absl::Status EglEnvironment::InitPBufferContext() {
123 RETURN_IF_ERROR(CreatePBufferContext(display_, EGL_NO_CONTEXT, &context_));
124 RETURN_IF_ERROR(CreatePbufferRGBSurface(context_.config(), display_, 1, 1,
125 &surface_read_));
126 RETURN_IF_ERROR(CreatePbufferRGBSurface(context_.config(), display_, 1, 1,
127 &surface_draw_));
128 return context_.MakeCurrent(surface_read_.surface(), surface_draw_.surface());
129 }
130
ForceSyncTurning()131 void EglEnvironment::ForceSyncTurning() {
132 glGenFramebuffers(1, &dummy_framebuffer_);
133 glBindFramebuffer(GL_FRAMEBUFFER, dummy_framebuffer_);
134
135 glGenTextures(1, &dummy_texture_);
136 glBindTexture(GL_TEXTURE_2D, dummy_texture_);
137 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4);
138 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
139 dummy_texture_, 0);
140
141 GLenum draw_buffers[1] = {GL_COLOR_ATTACHMENT0};
142 glDrawBuffers(1, draw_buffers);
143
144 glViewport(0, 0, 4, 4);
145 glClear(GL_COLOR_BUFFER_BIT);
146 }
147
148 } // namespace gl
149 } // namespace gpu
150 } // namespace tflite
151