blob: adb810a81d1ea9eb4439c8a2da8f0a2810b4c0ca [file] [log] [blame] [edit]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <array>
#include <chrono>
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <memory>
#include "fml/closure.h"
#include "fml/mapping.h"
#include "imgui/backends/imgui_impl_glfw.h"
#include "imgui/imgui.h"
#include "impeller/core/allocator.h"
#include "impeller/core/formats.h"
#include "impeller/core/texture_descriptor.h"
#include "impeller/playground/imgui/gles/imgui_shaders_gles.h"
#include "impeller/playground/imgui/imgui_impl_impeller.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/backend/gles/reactor_gles.h"
#include "impeller/renderer/backend/gles/surface_gles.h"
#include "impeller/renderer/renderer.h"
#define GLFW_INCLUDE_NONE
#include "third_party/glfw/include/GLFW/glfw3.h"
#include "examples/assets.h"
#include "examples/clock.h"
#include "examples/example_base.h"
#include "examples/mesh/mesh_example.h"
#include "examples/the_impeller/the_impeller_example.h"
#include "generated/shaders/gles/example_shaders_gles.h"
#include "generated/shaders/impeller.frag.h"
#include "generated/shaders/impeller.vert.h"
class ReactorWorker final : public impeller::ReactorGLES::Worker {
public:
ReactorWorker() = default;
// |ReactorGLES::Worker|
bool CanReactorReactOnCurrentThreadNow(
const impeller::ReactorGLES& reactor) const override {
impeller::ReaderLock lock(mutex_);
auto found = reactions_allowed_.find(std::this_thread::get_id());
if (found == reactions_allowed_.end()) {
return false;
}
return found->second;
}
void SetReactionsAllowedOnCurrentThread(bool allowed) {
impeller::WriterLock lock(mutex_);
reactions_allowed_[std::this_thread::get_id()] = allowed;
}
private:
mutable impeller::RWMutex mutex_;
std::map<std::thread::id, bool> reactions_allowed_ IPLR_GUARDED_BY(mutex_);
FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker);
};
int main() {
//----------------------------------------------------------------------------
/// Create a GLFW window.
///
if (::glfwInit() != GLFW_TRUE) {
std::cerr << "Couldn't initialize GLFW." << std::endl;
return EXIT_FAILURE;
}
::glfwSetErrorCallback([](int code, const char* description) {
std::cerr << "GLFW Error '" << description << "' (" << code << ").";
});
::glfwDefaultWindowHints();
#ifdef FML_OS_MACOSX
// ES Profiles are not supported on Mac.
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
#else // FML_OS_MACOSX
::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
::glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
::glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#endif // FML_OS_MACOSX
//::glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE);
::glfwWindowHint(GLFW_RED_BITS, 8);
::glfwWindowHint(GLFW_GREEN_BITS, 8);
::glfwWindowHint(GLFW_BLUE_BITS, 8);
::glfwWindowHint(GLFW_ALPHA_BITS, 8);
::glfwWindowHint(GLFW_DEPTH_BITS, 24);
::glfwWindowHint(GLFW_STENCIL_BITS, 8); // 8 bit stencil buffer
::glfwWindowHint(GLFW_SAMPLES, 4); // 4xMSAA
auto window =
::glfwCreateWindow(1024, 768, "Impeller example", nullptr, nullptr);
if (!window) {
std::cerr << "Couldn't create GLFW window.";
::glfwTerminate();
return EXIT_FAILURE;
}
::glfwMakeContextCurrent(window);
//----------------------------------------------------------------------------
/// Create an Impeller context.
///
auto resolver = [](const char* name) -> void* {
return reinterpret_cast<void*>(::glfwGetProcAddress(name));
};
auto gl = std::make_unique<impeller::ProcTableGLES>(resolver);
if (!gl->IsValid()) {
std::cerr << "Failed to create a valid GLES proc table.";
return EXIT_FAILURE;
}
auto context =
impeller::ContextGLES::Create(std::move(gl),
{std::make_shared<fml::NonOwnedMapping>(
impeller_imgui_shaders_gles_data,
impeller_imgui_shaders_gles_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_example_shaders_gles_data,
impeller_example_shaders_gles_length)},
false);
if (!context) {
std::cerr << "Failed to create Impeller context.";
return EXIT_FAILURE;
}
auto worker = std::make_shared<ReactorWorker>();
worker->SetReactionsAllowedOnCurrentThread(true);
auto worker_id = context->AddReactorWorker(worker);
if (!worker_id.has_value()) {
std::cerr << "Failed to register GLES reactor worker.";
return EXIT_FAILURE;
}
auto renderer = std::make_unique<impeller::Renderer>(context);
//----------------------------------------------------------------------------
/// Setup ImGui.
///
IMGUI_CHECKVERSION();
ImGui::CreateContext();
fml::ScopedCleanupClosure destroy_imgui_context(
[]() { ImGui::DestroyContext(); });
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
::ImGui_ImplGlfw_InitForOther(window, true);
::ImGui_ImplImpeller_Init(context);
//----------------------------------------------------------------------------
/// Setup examples.
///
std::vector<std::unique_ptr<example::ExampleBase>> examples;
examples.push_back(std::make_unique<example::TheImpellerExample>());
examples.push_back(std::make_unique<example::MeshExample>());
std::vector<const char*> example_names;
for (auto& example : examples) {
auto info = example->GetInfo();
example_names.push_back(info.name);
if (!example->Setup(*renderer->GetContext())) {
return EXIT_FAILURE;
}
}
//----------------------------------------------------------------------------
/// Render.
///
while (!::glfwWindowShouldClose(window)) {
::ImGui_ImplGlfw_NewFrame();
/// Get the next surface.
impeller::SurfaceGLES::SwapCallback swap_callback = [window]() -> bool {
::glfwSwapBuffers(window);
return true;
};
int width, height;
::glfwGetFramebufferSize(window, &width, &height);
auto surface = impeller::SurfaceGLES::WrapFBO(
context, swap_callback, 0u, impeller::PixelFormat::kB8G8R8A8UNormInt,
impeller::ISize::MakeWH(width, height));
/// Render to the surface.
ImGui::SetNextWindowPos({10, 10});
impeller::Renderer::RenderCallback render_callback =
[&context, &renderer, &examples,
&example_names](impeller::RenderTarget& render_target) -> bool {
static int selected_example_index = 1;
auto example = examples[selected_example_index].get();
auto example_info = example->GetInfo();
ImGui::NewFrame();
ImGui::Begin(example_info.name, nullptr,
ImGuiWindowFlags_AlwaysAutoResize);
{
if (ImGui::SmallButton("<")) {
selected_example_index -= 1;
}
ImGui::SameLine(200);
if (ImGui::SmallButton(">")) {
selected_example_index += 1;
}
while (selected_example_index < 0) {
selected_example_index += example_names.size();
}
while (selected_example_index >= example_names.size()) {
selected_example_index -= example_names.size();
}
ImGui::ListBox("##", &selected_example_index, example_names.data(),
example_names.size());
ImGui::TextWrapped("%s", example_info.description);
}
auto buffer = renderer->GetContext()->CreateCommandBuffer();
if (!buffer) {
return false;
}
buffer->SetLabel("Command Buffer");
/// Setup depth attachment.
{
impeller::TextureDescriptor depth_texture_desc;
depth_texture_desc.type = impeller::TextureType::kTexture2D;
depth_texture_desc.format =
impeller::PixelFormat::kD32FloatS8UInt; // DefaultColor;
depth_texture_desc.size = render_target.GetRenderTargetSize();
depth_texture_desc.usage = static_cast<impeller::TextureUsageMask>(
impeller::TextureUsage::kRenderTarget);
depth_texture_desc.sample_count = impeller::SampleCount::kCount1;
depth_texture_desc.storage_mode = impeller::StorageMode::kDevicePrivate;
impeller::DepthAttachment depth;
depth.load_action = impeller::LoadAction::kClear;
depth.store_action = impeller::StoreAction::kDontCare;
depth.clear_depth = 1.0;
depth.texture =
renderer->GetContext()->GetResourceAllocator()->CreateTexture(
depth_texture_desc);
render_target.SetDepthAttachment(depth);
}
// Render the selected example.
if (!example->Render(*renderer->GetContext(), render_target, *buffer)) {
return false;
}
ImGui::End();
ImGui::Render();
// Render ImGui overlay.
{
if (render_target.GetColorAttachments().empty()) {
return false;
}
auto color0 = render_target.GetColorAttachments().find(0)->second;
color0.load_action = impeller::LoadAction::kLoad;
render_target.SetColorAttachment(color0, 0);
auto pass = buffer->CreateRenderPass(render_target);
if (!pass) {
return false;
}
pass->SetLabel("ImGui Render Pass");
::ImGui_ImplImpeller_RenderDrawData(ImGui::GetDrawData(), *pass);
if (!pass->EncodeCommands()) {
return false;
}
}
// TODO(bdero): GetComandQueue shouldn't be private...
std::shared_ptr<impeller::Context>(context)->GetCommandQueue()->Submit(
{buffer});
return true;
};
renderer->Render(std::move(surface), render_callback);
::glfwPollEvents();
}
::ImGui_ImplImpeller_Shutdown();
::ImGui_ImplGlfw_Shutdown();
::glfwDestroyWindow(window);
::glfwTerminate();
return EXIT_SUCCESS;
}