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