Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
playground_impl_gles.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#define IMPELLER_PLAYGROUND_SUPPORTS_ANGLE FML_OS_MACOSX
8
9#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
10#include <dlfcn.h>
11#endif
12
13#define GLFW_INCLUDE_NONE
14#include "third_party/glfw/include/GLFW/glfw3.h"
15
18#include "impeller/entity/gles/entity_shaders_gles.h"
19#include "impeller/entity/gles/framebuffer_blend_shaders_gles.h"
20#include "impeller/entity/gles/modern_shaders_gles.h"
21#include "impeller/entity/gles3/entity_shaders_gles.h"
22#include "impeller/entity/gles3/framebuffer_blend_shaders_gles.h"
23#include "impeller/entity/gles3/modern_shaders_gles.h"
24#include "impeller/fixtures/gles/fixtures_shaders_gles.h"
25#include "impeller/fixtures/gles/modern_fixtures_shaders_gles.h"
26#include "impeller/fixtures/gles3/fixtures_shaders_gles.h"
27#include "impeller/fixtures/gles3/modern_fixtures_shaders_gles.h"
28#include "impeller/playground/imgui/gles/imgui_shaders_gles.h"
29#include "impeller/playground/imgui/gles3/imgui_shaders_gles.h"
32
33namespace {
34
35class GLESPlaygroundEnvironment : public ::testing::Environment {
36 public:
37 std::unique_ptr<impeller::PlaygroundImplGLES::ShareableContext>
38 shared_context;
39 std::unique_ptr<impeller::PlaygroundImplGLES::ShareableContext>
40 shared_context_sdf;
41};
42
43GLESPlaygroundEnvironment* g_gles_playground_env =
44 static_cast<GLESPlaygroundEnvironment*>(
45 ::testing::AddGlobalTestEnvironment(new GLESPlaygroundEnvironment()));
46
47} // namespace
48
49namespace impeller {
50
52 public:
53 ReactorWorker() = default;
54
55 // |ReactorGLES::Worker|
57 const ReactorGLES& reactor) const override {
58 ReaderLock lock(mutex_);
59 auto found = reactions_allowed_.find(std::this_thread::get_id());
60 if (found == reactions_allowed_.end()) {
61 return false;
62 }
63 return found->second;
64 }
65
67 WriterLock lock(mutex_);
68 reactions_allowed_[std::this_thread::get_id()] = allowed;
69 }
70
71 private:
72 mutable RWMutex mutex_;
73 std::map<std::thread::id, bool> reactions_allowed_ IPLR_GUARDED_BY(mutex_);
74
75 ReactorWorker(const ReactorWorker&) = delete;
76
77 ReactorWorker& operator=(const ReactorWorker&) = delete;
78};
79
81 public:
83 std::shared_ptr<ReactorWorker> worker,
84 std::shared_ptr<ContextGLES> context,
86 : window(std::move(window)),
87 worker(std::move(worker)),
88 context(std::move(context)),
90
92 if (window) {
93 ::glfwMakeContextCurrent(window.get());
94 }
95 context.reset();
96 worker.reset();
97 window.reset();
98 }
99
100 // This is a placeholder/dummy window. It is not rendered to by playground
101 // tests. Instead, it is created so different playground tests can create
102 // windows that share the same context.
103 // See https://www.glfw.org/docs/latest/context_guide.html#context_sharing for
104 // details.
105 UniqueHandle window = {nullptr, &DestroyWindowHandle};
106
107 std::shared_ptr<ReactorWorker> worker;
108 std::shared_ptr<ContextGLES> context;
110};
111
112void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
113 if (!handle) {
114 return;
115 }
116 ::glfwDestroyWindow(reinterpret_cast<GLFWwindow*>(handle));
117}
118
119static std::vector<std::shared_ptr<fml::Mapping>>
121
123 : PlaygroundImpl(switches),
124 handle_(nullptr, &DestroyWindowHandle),
125 use_angle_(switches.use_angle) {
126 if (use_angle_) {
127#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
128 angle_glesv2_ = dlopen("libGLESv2.dylib", RTLD_LAZY);
129#endif
130 FML_CHECK(angle_glesv2_ != nullptr);
131 }
132
133 std::unique_ptr<PlaygroundImplGLES::ShareableContext>& shared_context =
134 GetShareableContext();
135 if (!shared_context) {
136 shared_context = MakeShareableContext(switches_);
137 if (!shared_context) {
138 FML_LOG(ERROR) << "Could not create GLES context.";
139 return;
140 }
141 }
142
143 context_ = shared_context->context;
144
145 auto window = CreateGLWindow(switches_, shared_context->window.get());
146 handle_.reset(window);
147
148 shared_context->context->GetGPUTracer()->Reset();
149}
150
151std::unique_ptr<PlaygroundImplGLES::ShareableContext>&
152PlaygroundImplGLES::GetShareableContext() {
154 // Caller will initialize the information in the private context field.
155 return unique_context_;
156 }
157
158 FML_CHECK(g_gles_playground_env != nullptr);
159 std::unique_ptr<PlaygroundImplGLES::ShareableContext>& shared_context =
160 switches_.flags.use_sdfs ? g_gles_playground_env->shared_context_sdf
161 : g_gles_playground_env->shared_context;
162
163 // If the switches have values that result in a different GLES context than
164 // the existing shared context, reset the shared context to create a new one.
165 if (shared_context && (shared_context->switches != switches_)) {
166 shared_context.reset();
167 }
168
169 // Caller will initialize the shared information in the global shared
170 // context field.
171 return shared_context;
172}
173
174GLFWwindow* PlaygroundImplGLES::CreateGLWindow(
175 const PlaygroundSwitches& switches,
176 GLFWwindow* share_window) {
177 ::glfwDefaultWindowHints();
178
179#if FML_OS_MACOSX
180 FML_CHECK(switches.use_angle) << "Must use Angle on macOS for OpenGL ES.";
181 ::glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
182#endif // FML_OS_MACOSX
183#if FML_OS_LINUX
184 // Use EGL even on X11 then the client can select the GLES implementation
185 // by defining __EGL_VENDOR_LIBRARY_FILENAMES
186 ::glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
187#endif
188 ::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
189 ::glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
190 ::glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
191 ::glfwWindowHint(GLFW_RED_BITS, 8);
192 ::glfwWindowHint(GLFW_GREEN_BITS, 8);
193 ::glfwWindowHint(GLFW_BLUE_BITS, 8);
194 ::glfwWindowHint(GLFW_ALPHA_BITS, 8);
195 ::glfwWindowHint(GLFW_DEPTH_BITS, 32); // 32 bit depth buffer
196 ::glfwWindowHint(GLFW_STENCIL_BITS, 8); // 8 bit stencil buffer
197 ::glfwWindowHint(GLFW_SAMPLES, 4); // 4xMSAA
198
199 ::glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
200#ifndef NDEBUG
201 ::glfwWindowHint(GLFW_CONTEXT_DEBUG, GLFW_TRUE);
202#endif
203
204 auto window = ::glfwCreateWindow(1, 1, "Test", nullptr, share_window);
205 ::glfwMakeContextCurrent(window);
206 return window;
207}
208
209std::unique_ptr<PlaygroundImplGLES::ShareableContext>
210PlaygroundImplGLES::MakeShareableContext(const PlaygroundSwitches& switches) {
211 auto window =
212 UniqueHandle(CreateGLWindow(switches, nullptr), &DestroyWindowHandle);
213 if (!window) {
214 FML_LOG(ERROR) << "Could not create GLES window.";
215 return nullptr;
216 }
217
218 auto gl =
219 std::make_unique<ProcTableGLES>(CreateGLProcAddressResolver(switches));
220 if (!gl->IsValid()) {
221 FML_LOG(ERROR) << "Proc table when creating a playground was invalid.";
222 return nullptr;
223 }
224
225 if (gl->GetDescription()->HasDebugExtension()) {
226 gl->DebugMessageCallbackKHR(
227 +[](GLenum /* source */, GLenum message_type, GLuint /* message_id */,
228 GLenum /* severity */, GLsizei /* length */, const GLchar* message,
229 const void* /* user_param */) {
230 switch (message_type) {
231 case GL_DEBUG_TYPE_ERROR_KHR:
232 FML_LOG(ERROR) << "GL Error: " << message;
233 return;
234 default:
235 return;
236 }
237 },
238 nullptr);
239
240#ifndef NDEBUG
241 gl->Enable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
242#endif
243 }
244 bool is_gles3 = gl->GetDescription()->GetGlVersion().IsAtLeast(Version(3));
245 auto context_gles =
246 ContextGLES::Create(switches.flags, std::move(gl),
247 ShaderLibraryMappingsForPlayground(is_gles3), true);
248 if (!context_gles) {
249 FML_LOG(ERROR) << "Could not create context.";
250 return nullptr;
251 }
252
253 auto worker = std::make_shared<ReactorWorker>();
254 worker->SetReactionsAllowedOnCurrentThread(true);
255
256 // REMIND: context stores only a weak pointer.
257 auto worker_id = context_gles->AddReactorWorker(worker);
258 if (!worker_id.has_value()) {
259 FML_LOG(ERROR) << "Could not add reactor worker.";
260 return nullptr;
261 }
262
263 return std::make_unique<ShareableContext>(std::move(window), worker,
264 context_gles, switches);
265}
266
268 if (context_ && handle_) {
269 ::glfwMakeContextCurrent(handle_.get());
270 [[maybe_unused]] auto result = context_->FlushCommandBuffers();
271 }
272}
273
274static std::vector<std::shared_ptr<fml::Mapping>>
276 if (is_gles3) {
277 return {
278 std::make_shared<fml::NonOwnedMapping>(
279 impeller_entity_shaders_gles3_data,
280 impeller_entity_shaders_gles3_length),
281 std::make_shared<fml::NonOwnedMapping>(
282 impeller_modern_shaders_gles3_data,
283 impeller_modern_shaders_gles3_length),
284 std::make_shared<fml::NonOwnedMapping>(
285 impeller_framebuffer_blend_shaders_gles3_data,
286 impeller_framebuffer_blend_shaders_gles3_length),
287 std::make_shared<fml::NonOwnedMapping>(
288 impeller_fixtures_shaders_gles3_data,
289 impeller_fixtures_shaders_gles3_length),
290 std::make_shared<fml::NonOwnedMapping>(
291 impeller_modern_fixtures_shaders_gles3_data,
292 impeller_modern_fixtures_shaders_gles3_length),
293 std::make_shared<fml::NonOwnedMapping>(
294 impeller_imgui_shaders_gles3_data,
295 impeller_imgui_shaders_gles3_length),
296 };
297 }
298 return {
299 std::make_shared<fml::NonOwnedMapping>(
300 impeller_entity_shaders_gles_data,
301 impeller_entity_shaders_gles_length),
302 std::make_shared<fml::NonOwnedMapping>(
303 impeller_modern_shaders_gles_data,
304 impeller_modern_shaders_gles_length),
305 std::make_shared<fml::NonOwnedMapping>(
306 impeller_framebuffer_blend_shaders_gles_data,
307 impeller_framebuffer_blend_shaders_gles_length),
308 std::make_shared<fml::NonOwnedMapping>(
309 impeller_fixtures_shaders_gles_data,
310 impeller_fixtures_shaders_gles_length),
311 std::make_shared<fml::NonOwnedMapping>(
312 impeller_modern_fixtures_shaders_gles_data,
313 impeller_modern_fixtures_shaders_gles_length),
314 std::make_shared<fml::NonOwnedMapping>(
315 impeller_imgui_shaders_gles_data, impeller_imgui_shaders_gles_length),
316 };
317}
318
319// |PlaygroundImpl|
320std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
321 return context_;
322}
323
324// |PlaygroundImpl|
326PlaygroundImplGLES::CreateGLProcAddressResolver() const {
327 return CreateGLProcAddressResolver(switches_);
328}
329
331PlaygroundImplGLES::CreateGLProcAddressResolver(
332 const PlaygroundSwitches& switches) {
333 return switches.use_angle ? [](const char* name) -> void* {
334 void* symbol = nullptr;
335#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
336 void* angle_glesv2 = dlopen("libGLESv2.dylib", RTLD_LAZY);
337 symbol = dlsym(angle_glesv2, name);
338#endif
339 FML_CHECK(symbol);
340 return symbol;
341 }
342 : [](const char* name) -> void* {
343 return reinterpret_cast<void*>(::glfwGetProcAddress(name));
344 };
345}
346
347// |PlaygroundImpl|
348PlaygroundImpl::WindowHandle PlaygroundImplGLES::GetWindowHandle() const {
349 return handle_.get();
350}
351
352// |PlaygroundImpl|
353std::unique_ptr<Surface> PlaygroundImplGLES::AcquireSurfaceFrame(
354 std::shared_ptr<Context> context) {
355 auto window = reinterpret_cast<GLFWwindow*>(GetWindowHandle());
356 int width = 0;
357 int height = 0;
358 ::glfwGetFramebufferSize(window, &width, &height);
359 if (width <= 0 || height <= 0) {
360 return nullptr;
361 }
362 SurfaceGLES::SwapCallback swap_callback = [window]() -> bool {
363 ::glfwSwapBuffers(window);
364 return true;
365 };
367 swap_callback, //
368 0u, //
371 );
372}
373
375 const std::shared_ptr<Capabilities>& capabilities) {
376 return fml::Status(
378 "PlaygroundImplGLES doesn't support setting the capabilities.");
379}
380
381RuntimeStageBackend PlaygroundImplGLES::GetRuntimeStageBackend() const {
382 const auto gl =
383 std::make_unique<ProcTableGLES>(CreateGLProcAddressResolver());
384 if (!gl->IsValid()) {
385 FML_LOG(ERROR) << "Proc table was invalid. Assuming baseline OpenGL ES";
387 }
388 bool is_gles3 = gl->GetDescription()->GetGlVersion().IsAtLeast(Version(3));
389 return is_gles3 ? RuntimeStageBackend::kOpenGLES3
391}
392
393} // namespace impeller
static std::shared_ptr< ContextGLES > Create(const Flags &flags, std::unique_ptr< ProcTableGLES > gl, const std::vector< std::shared_ptr< fml::Mapping > > &shader_libraries, bool enable_gpu_tracing)
std::function< void *(const char *proc_name)> GLProcAddressResolver
Definition playground.h:107
bool CanReactorReactOnCurrentThreadNow(const ReactorGLES &reactor) const override
Determines the ability of the worker to service a reaction on the current thread. The OpenGL context ...
PlaygroundImplGLES(PlaygroundSwitches switches)
fml::Status SetCapabilities(const std::shared_ptr< Capabilities > &capabilities) override
const PlaygroundSwitches switches_
A delegate implemented by a thread on which an OpenGL context is current. There may be multiple worke...
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
static std::unique_ptr< Surface > WrapFBO(const std::shared_ptr< Context > &context, SwapCallback swap_callback, GLuint fbo, PixelFormat color_format, ISize fbo_size)
std::function< bool(void)> SwapCallback
GLFWwindow * window
Definition main.cc:60
const char * message
#define GLFW_TRUE
#define GLFW_FALSE
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
const char * name
Definition fuchsia.cc:50
static std::vector< std::shared_ptr< fml::Mapping > > ShaderLibraryMappingsForPlayground()
Definition ref_ptr.h:261
fuchsia::ui::composition::ParentViewportWatcherHandle handle_
std::shared_ptr< ReactorGLES > reactor
std::shared_ptr< ContextGLES > context
int32_t height
int32_t width
bool use_sdfs
Use SDFs for rendering.
Definition flags.h:13
ShareableContext(UniqueHandle window, std::shared_ptr< ReactorWorker > worker, std::shared_ptr< ContextGLES > context, PlaygroundSwitches switches)
static constexpr TSize MakeWH(Type width, Type height)
Definition size.h:43
#define IPLR_GUARDED_BY(x)