10 #include "fml/time/time_point.h"
16 #define GLFW_INCLUDE_NONE
17 #include "third_party/glfw/include/GLFW/glfw3.h"
19 #include "flutter/fml/paths.h"
30 #include "third_party/imgui/backends/imgui_impl_glfw.h"
31 #include "third_party/imgui/imgui.h"
34 #include "fml/platform/darwin/scoped_nsautorelease_pool.h"
69 static std::once_flag sOnceInitializer;
70 std::call_once(sOnceInitializer, []() {
71 ::glfwSetErrorCallback([](
int code,
const char* description) {
72 FML_LOG(ERROR) <<
"GLFW Error '" << description <<
"' (" << code
75 FML_CHECK(::glfwInit() == GLFW_TRUE);
81 : switches_(switches),
93 #if IMPELLER_ENABLE_METAL
95 #else // IMPELLER_ENABLE_METAL
97 #endif // IMPELLER_ENABLE_METAL
99 #if IMPELLER_ENABLE_OPENGLES
101 #else // IMPELLER_ENABLE_OPENGLES
103 #endif // IMPELLER_ENABLE_OPENGLES
105 #if IMPELLER_ENABLE_VULKAN
107 #else // IMPELLER_ENABLE_VULKAN
109 #endif // IMPELLER_ENABLE_VULKAN
119 FML_LOG(WARNING) <<
"PlaygroundImpl::Create failed.";
123 context_ = impl_->GetContext();
128 FML_LOG(WARNING) <<
"Asked to set up a window with no context (call "
129 "SetupContext first).";
132 auto renderer = std::make_unique<Renderer>(context_);
133 if (!renderer->IsValid()) {
136 renderer_ = std::move(renderer);
138 start_time_ = fml::TimePoint::Now().ToEpochDelta();
143 context_->Shutdown();
161 if ((key == GLFW_KEY_ESCAPE) && action == GLFW_RELEASE) {
162 if (mods & (GLFW_MOD_CONTROL | GLFW_MOD_SUPER | GLFW_MOD_SHIFT)) {
165 ::glfwSetWindowShouldClose(window, GLFW_TRUE);
170 return cursor_position_;
178 return impl_->GetContentScale();
182 return (fml::TimePoint::Now().ToEpochDelta() - start_time_).ToSecondsF();
185 void Playground::SetCursorPosition(
Point pos) {
186 cursor_position_ = pos;
195 if (!render_callback) {
199 if (!renderer_ || !renderer_->IsValid()) {
203 IMGUI_CHECKVERSION();
204 ImGui::CreateContext();
205 fml::ScopedCleanupClosure destroy_imgui_context(
206 []() { ImGui::DestroyContext(); });
207 ImGui::StyleColorsDark();
209 auto& io = ImGui::GetIO();
210 io.IniFilename =
nullptr;
211 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
212 io.ConfigWindowsResizeFromEdges =
true;
214 auto window =
reinterpret_cast<GLFWwindow*
>(impl_->GetWindowHandle());
219 ::glfwSetWindowUserPointer(window,
this);
220 ::glfwSetWindowSizeCallback(
221 window, [](GLFWwindow* window,
int width,
int height) ->
void {
223 reinterpret_cast<Playground*
>(::glfwGetWindowUserPointer(window));
227 playground->SetWindowSize(
ISize{width, height}.
Max({}));
230 ::glfwSetCursorPosCallback(window, [](GLFWwindow* window,
double x,
232 reinterpret_cast<Playground*
>(::glfwGetWindowUserPointer(window))
233 ->SetCursorPosition({
static_cast<Scalar>(x),
static_cast<Scalar>(y)});
236 ImGui_ImplGlfw_InitForOther(window,
true);
237 fml::ScopedCleanupClosure shutdown_imgui([]() { ImGui_ImplGlfw_Shutdown(); });
240 fml::ScopedCleanupClosure shutdown_imgui_impeller(
243 ImGui::SetNextWindowPos({10, 10});
246 ::glfwSetWindowPos(window, 200, 100);
247 ::glfwShowWindow(window);
251 fml::ScopedNSAutoreleasePool pool;
255 if (::glfwWindowShouldClose(window)) {
259 ImGui_ImplGlfw_NewFrame();
263 &renderer = renderer_](
RenderTarget& render_target) ->
bool {
265 ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(),
266 ImGuiDockNodeFlags_PassthruCentralNode);
267 bool result = render_callback(render_target);
272 auto buffer = renderer->GetContext()->CreateCommandBuffer();
276 buffer->SetLabel(
"ImGui Command Buffer");
278 if (render_target.GetColorAttachments().empty()) {
282 auto color0 = render_target.GetColorAttachments().find(0)->second;
284 if (color0.resolve_texture) {
285 color0.texture = color0.resolve_texture;
286 color0.resolve_texture =
nullptr;
289 render_target.SetColorAttachment(color0, 0);
291 render_target.SetStencilAttachment(std::nullopt);
292 render_target.SetDepthAttachment(std::nullopt);
294 auto pass = buffer->CreateRenderPass(render_target);
298 pass->SetLabel(
"ImGui Render Pass");
302 pass->EncodeCommands();
303 if (!buffer->SubmitCommands()) {
311 if (!renderer_->Render(impl_->AcquireSurfaceFrame(renderer_->GetContext()),
322 ::glfwHideWindow(window);
330 auto buffer = context->CreateCommandBuffer();
334 buffer->SetLabel(
"Playground Command Buffer");
336 auto pass = buffer->CreateRenderPass(render_target);
340 pass->SetLabel(
"Playground Render Pass");
342 if (!pass_callback(*pass)) {
346 pass->EncodeCommands();
347 if (!buffer->SubmitCommands()) {
355 std::shared_ptr<fml::Mapping> mapping) {
357 if (!compressed_image) {
362 return compressed_image;
366 const std::shared_ptr<CompressedImage>& compressed) {
367 if (compressed ==
nullptr) {
375 auto image = compressed->Decode().ConvertToRGBA();
376 if (!image.IsValid()) {
385 const std::shared_ptr<Context>& context,
387 bool enable_mipmapping) {
390 if (context->GetCapabilities()->SupportsBufferToTextureBlits()) {
394 texture_descriptor.
size = decompressed_image.
GetSize();
399 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
401 FML_DLOG(ERROR) <<
"Could not create Impeller texture.";
405 auto buffer = context->GetResourceAllocator()->CreateBufferWithCopy(
408 dest_texture->SetLabel(
411 auto command_buffer = context->CreateCommandBuffer();
412 if (!command_buffer) {
414 <<
"Could not create command buffer for mipmap generation.";
417 command_buffer->SetLabel(
"Mipmap Command Buffer");
419 auto blit_pass = command_buffer->CreateBlitPass();
421 FML_DLOG(ERROR) <<
"Could not create blit pass for mipmap generation.";
424 blit_pass->SetLabel(
"Mipmap Blit Pass");
425 blit_pass->AddCopy(buffer->AsBufferView(), dest_texture);
426 if (enable_mipmapping) {
427 blit_pass->GenerateMipmap(dest_texture);
430 blit_pass->EncodeCommands(context->GetResourceAllocator());
431 if (!command_buffer->SubmitCommands()) {
432 FML_DLOG(ERROR) <<
"Failed to submit blit pass command buffer.";
440 texture_descriptor.size = decompressed_image.
GetSize();
441 texture_descriptor.mip_count =
445 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
451 auto uploaded = texture->SetContents(decompressed_image.
GetAllocation());
454 <<
"Could not upload texture to device memory for fixture.";
462 const std::shared_ptr<Context>& context,
463 std::shared_ptr<fml::Mapping> mapping,
464 bool enable_mipmapping) {
467 if (!image.has_value()) {
475 const char* fixture_name,
476 bool enable_mipmapping)
const {
480 if (texture ==
nullptr) {
483 texture->SetLabel(fixture_name);
488 std::array<const char*, 6> fixture_names)
const {
489 std::array<DecompressedImage, 6> images;
490 for (
size_t i = 0; i < fixture_names.size(); i++) {
493 if (!image.has_value()) {
496 images[i] = image.value();
503 texture_descriptor.size = images[0].GetSize();
504 texture_descriptor.mip_count = 1u;
506 auto texture = renderer_->GetContext()->GetResourceAllocator()->CreateTexture(
512 texture->SetLabel(
"Texture cube");
514 for (
size_t i = 0; i < fixture_names.size(); i++) {
516 texture->SetContents(images[i].GetAllocation()->GetMapping(),
517 images[i].GetAllocation()->GetSize(), i);
527 void Playground::SetWindowSize(
ISize size) {