7#define IMPELLER_PLAYGROUND_SUPPORTS_ANGLE FML_OS_MACOSX
9#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
13#define GLFW_INCLUDE_NONE
14#include "third_party/glfw/include/GLFW/glfw3.h"
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"
35class GLESPlaygroundEnvironment :
public ::testing::Environment {
37 std::unique_ptr<impeller::PlaygroundImplGLES::ShareableContext>
39 std::unique_ptr<impeller::PlaygroundImplGLES::ShareableContext>
43GLESPlaygroundEnvironment* g_gles_playground_env =
44 static_cast<GLESPlaygroundEnvironment*
>(
45 ::testing::AddGlobalTestEnvironment(
new GLESPlaygroundEnvironment()));
59 auto found = reactions_allowed_.find(std::this_thread::get_id());
60 if (found == reactions_allowed_.end()) {
68 reactions_allowed_[std::this_thread::get_id()] = allowed;
72 mutable RWMutex mutex_;
73 std::map<std::thread::id, bool> reactions_allowed_
IPLR_GUARDED_BY(mutex_);
83 std::shared_ptr<ReactorWorker>
worker,
84 std::shared_ptr<ContextGLES>
context,
93 ::glfwMakeContextCurrent(
window.get());
105 UniqueHandle
window = {
nullptr, &DestroyWindowHandle};
112void PlaygroundImplGLES::DestroyWindowHandle(
WindowHandle handle) {
116 ::glfwDestroyWindow(
reinterpret_cast<GLFWwindow*
>(handle));
119static std::vector<std::shared_ptr<fml::Mapping>>
124 handle_(nullptr, &DestroyWindowHandle),
125 use_angle_(switches.use_angle) {
127#if IMPELLER_PLAYGROUND_SUPPORTS_ANGLE
128 angle_glesv2_ = dlopen(
"libGLESv2.dylib", RTLD_LAZY);
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.";
143 context_ = shared_context->context;
148 shared_context->context->GetGPUTracer()->Reset();
151std::unique_ptr<PlaygroundImplGLES::ShareableContext>&
152PlaygroundImplGLES::GetShareableContext() {
155 return unique_context_;
158 FML_CHECK(g_gles_playground_env !=
nullptr);
159 std::unique_ptr<PlaygroundImplGLES::ShareableContext>& shared_context =
161 : g_gles_playground_env->shared_context;
165 if (shared_context && (shared_context->switches !=
switches_)) {
166 shared_context.reset();
171 return shared_context;
174GLFWwindow* PlaygroundImplGLES::CreateGLWindow(
175 const PlaygroundSwitches& switches,
176 GLFWwindow* share_window) {
177 ::glfwDefaultWindowHints();
180 FML_CHECK(switches.use_angle) <<
"Must use Angle on macOS for OpenGL ES.";
181 ::glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
186 ::glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
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);
196 ::glfwWindowHint(GLFW_STENCIL_BITS, 8);
197 ::glfwWindowHint(GLFW_SAMPLES, 4);
201 ::glfwWindowHint(GLFW_CONTEXT_DEBUG,
GLFW_TRUE);
204 auto window = ::glfwCreateWindow(1, 1,
"Test",
nullptr, share_window);
205 ::glfwMakeContextCurrent(
window);
209std::unique_ptr<PlaygroundImplGLES::ShareableContext>
210PlaygroundImplGLES::MakeShareableContext(
const PlaygroundSwitches& switches) {
212 UniqueHandle(CreateGLWindow(switches,
nullptr), &DestroyWindowHandle);
214 FML_LOG(ERROR) <<
"Could not create GLES window.";
219 std::make_unique<ProcTableGLES>(CreateGLProcAddressResolver(switches));
220 if (!gl->IsValid()) {
221 FML_LOG(ERROR) <<
"Proc table when creating a playground was invalid.";
225 if (gl->GetDescription()->HasDebugExtension()) {
226 gl->DebugMessageCallbackKHR(
227 +[](GLenum , GLenum message_type, GLuint ,
228 GLenum , GLsizei ,
const GLchar*
message,
230 switch (message_type) {
231 case GL_DEBUG_TYPE_ERROR_KHR:
241 gl->Enable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
244 bool is_gles3 = gl->GetDescription()->GetGlVersion().IsAtLeast(Version(3));
249 FML_LOG(ERROR) <<
"Could not create context.";
253 auto worker = std::make_shared<ReactorWorker>();
254 worker->SetReactionsAllowedOnCurrentThread(
true);
257 auto worker_id = context_gles->AddReactorWorker(worker);
258 if (!worker_id.has_value()) {
259 FML_LOG(ERROR) <<
"Could not add reactor worker.";
263 return std::make_unique<ShareableContext>(std::move(
window), worker,
264 context_gles, switches);
268 if (context_ && handle_) {
269 ::glfwMakeContextCurrent(handle_.get());
270 [[maybe_unused]]
auto result = context_->FlushCommandBuffers();
274static std::vector<std::shared_ptr<fml::Mapping>>
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),
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),
320std::shared_ptr<Context> PlaygroundImplGLES::GetContext()
const {
326PlaygroundImplGLES::CreateGLProcAddressResolver()
const {
327 return CreateGLProcAddressResolver(
switches_);
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);
342 : [](
const char*
name) ->
void* {
343 return reinterpret_cast<void*
>(::glfwGetProcAddress(
name));
349 return handle_.get();
353std::unique_ptr<Surface> PlaygroundImplGLES::AcquireSurfaceFrame(
354 std::shared_ptr<Context>
context) {
355 auto window =
reinterpret_cast<GLFWwindow*
>(GetWindowHandle());
363 ::glfwSwapBuffers(
window);
375 const std::shared_ptr<Capabilities>& capabilities) {
378 "PlaygroundImplGLES doesn't support setting the capabilities.");
383 std::make_unique<ProcTableGLES>(CreateGLProcAddressResolver());
384 if (!gl->IsValid()) {
385 FML_LOG(ERROR) <<
"Proc table was invalid. Assuming baseline OpenGL ES";
388 bool is_gles3 = gl->GetDescription()->GetGlVersion().IsAtLeast(Version(3));
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
bool CanReactorReactOnCurrentThreadNow(const ReactorGLES &reactor) const override
Determines the ability of the worker to service a reaction on the current thread. The OpenGL context ...
void SetReactionsAllowedOnCurrentThread(bool allowed)
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
#define FML_LOG(severity)
#define FML_CHECK(condition)
static std::vector< std::shared_ptr< fml::Mapping > > ShaderLibraryMappingsForPlayground()
std::shared_ptr< ReactorGLES > reactor
std::shared_ptr< ContextGLES > context
bool use_sdfs
Use SDFs for rendering.
PlaygroundSwitches switches
ShareableContext(UniqueHandle window, std::shared_ptr< ReactorWorker > worker, std::shared_ptr< ContextGLES > context, PlaygroundSwitches switches)
std::shared_ptr< ContextGLES > context
std::shared_ptr< ReactorWorker > worker
static constexpr TSize MakeWH(Type width, Type height)
#define IPLR_GUARDED_BY(x)