Skip to content

Commit 9ecbb76

Browse files
bderodnfield
authored andcommitted
Add an ImGui backend targetting Impeller to the playground (flutter#20)
1 parent cb9997a commit 9ecbb76

File tree

7 files changed

+326
-1
lines changed

7 files changed

+326
-1
lines changed

impeller/playground/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ impeller_component("playground") {
1717
"../entity:entity_shaders",
1818
"../fixtures:shader_fixtures",
1919
"../renderer",
20+
"imgui:imgui_impeller_backend",
2021
"//flutter/testing",
2122
"//third_party/glfw",
23+
"//third_party/imgui:imgui_glfw",
2224
]
2325

2426
public_configs = [ ":playground_config" ]

impeller/playground/imgui/BUILD.gn

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import("//flutter/impeller/tools/impeller.gni")
2+
3+
impeller_shaders("imgui_shaders") {
4+
name = "imgui_shaders"
5+
shaders = [
6+
"imgui_raster.vert",
7+
"imgui_raster.frag",
8+
]
9+
}
10+
11+
source_set("imgui_impeller_backend") {
12+
testonly = true
13+
14+
public_deps = [
15+
":imgui_shaders",
16+
"//third_party/imgui",
17+
]
18+
19+
deps = [ "//flutter/impeller/renderer" ]
20+
21+
sources = [
22+
"imgui_impl_impeller.cc",
23+
"imgui_impl_impeller.h",
24+
]
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#include "imgui_impl_impeller.h"
2+
3+
#include <algorithm>
4+
#include <climits>
5+
#include <memory>
6+
#include <vector>
7+
8+
#include "imgui_raster.frag.h"
9+
#include "imgui_raster.vert.h"
10+
#include "third_party/imgui/imgui.h"
11+
12+
#include "impeller/geometry/matrix.h"
13+
#include "impeller/geometry/point.h"
14+
#include "impeller/geometry/rect.h"
15+
#include "impeller/geometry/size.h"
16+
#include "impeller/renderer/allocator.h"
17+
#include "impeller/renderer/command.h"
18+
#include "impeller/renderer/context.h"
19+
#include "impeller/renderer/formats.h"
20+
#include "impeller/renderer/pipeline_builder.h"
21+
#include "impeller/renderer/pipeline_library.h"
22+
#include "impeller/renderer/range.h"
23+
#include "impeller/renderer/render_pass.h"
24+
#include "impeller/renderer/sampler.h"
25+
#include "impeller/renderer/sampler_library.h"
26+
#include "impeller/renderer/texture.h"
27+
#include "impeller/renderer/texture_descriptor.h"
28+
#include "impeller/renderer/vertex_buffer.h"
29+
30+
struct ImGui_ImplImpeller_Data {
31+
std::shared_ptr<impeller::Context> context;
32+
std::shared_ptr<impeller::Texture> font_texture;
33+
std::shared_ptr<impeller::Pipeline> pipeline;
34+
std::shared_ptr<const impeller::Sampler> sampler;
35+
};
36+
37+
static ImGui_ImplImpeller_Data* ImGui_ImplImpeller_GetBackendData() {
38+
return ImGui::GetCurrentContext()
39+
? static_cast<ImGui_ImplImpeller_Data*>(
40+
ImGui::GetIO().BackendRendererUserData)
41+
: nullptr;
42+
}
43+
44+
bool ImGui_ImplImpeller_Init(std::shared_ptr<impeller::Context> context) {
45+
ImGuiIO& io = ImGui::GetIO();
46+
IM_ASSERT(io.BackendRendererUserData == nullptr &&
47+
"Already initialized a renderer backend!");
48+
49+
// Setup backend capabilities flags
50+
auto* bd = new ImGui_ImplImpeller_Data();
51+
io.BackendRendererUserData = reinterpret_cast<void*>(bd);
52+
io.BackendRendererName = "imgui_impl_impeller";
53+
io.BackendFlags |=
54+
ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the
55+
// ImDrawCmd::VtxOffset field,
56+
// allowing for large meshes.
57+
58+
bd->context = context;
59+
60+
// Generate/upload the font atlas.
61+
{
62+
unsigned char* pixels;
63+
int width, height;
64+
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
65+
66+
auto texture_descriptor = impeller::TextureDescriptor{};
67+
texture_descriptor.format = impeller::PixelFormat::kR8G8B8A8UNormInt;
68+
texture_descriptor.size = {width, height};
69+
texture_descriptor.mip_count = 1u;
70+
71+
bd->font_texture = context->GetPermanentsAllocator()->CreateTexture(
72+
impeller::StorageMode::kHostVisible, texture_descriptor);
73+
IM_ASSERT(bd->font_texture != nullptr &&
74+
"Could not allocate ImGui font texture.");
75+
76+
bool uploaded = bd->font_texture->SetContents(pixels, width * height * 4);
77+
IM_ASSERT(uploaded &&
78+
"Could not upload ImGui font texture to device memory.");
79+
}
80+
81+
// Build the raster pipeline.
82+
{
83+
auto desc = impeller::PipelineBuilder<impeller::ImguiRasterVertexShader,
84+
impeller::ImguiRasterFragmentShader>::
85+
MakeDefaultPipelineDescriptor(*context);
86+
desc->SetSampleCount(impeller::SampleCount::kCount4);
87+
bd->pipeline =
88+
context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get();
89+
IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui pipeline.");
90+
91+
bd->sampler = context->GetSamplerLibrary()->GetSampler({});
92+
IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui sampler.");
93+
}
94+
95+
return true;
96+
}
97+
98+
void ImGui_ImplImpeller_Shutdown() {
99+
auto* bd = ImGui_ImplImpeller_GetBackendData();
100+
IM_ASSERT(bd != nullptr &&
101+
"No renderer backend to shutdown, or already shutdown?");
102+
delete bd;
103+
}
104+
105+
void ImGui_ImplImpeller_RenderDrawData(ImDrawData* draw_data,
106+
impeller::RenderPass& render_pass) {
107+
if (draw_data->CmdListsCount == 0) {
108+
return; // Nothing to render.
109+
}
110+
111+
using VS = impeller::ImguiRasterVertexShader;
112+
using FS = impeller::ImguiRasterFragmentShader;
113+
114+
auto* bd = ImGui_ImplImpeller_GetBackendData();
115+
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplImpeller_Init()?");
116+
117+
size_t total_vtx_bytes = draw_data->TotalVtxCount * sizeof(ImDrawVert);
118+
size_t total_idx_bytes = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
119+
if (!total_vtx_bytes || !total_idx_bytes) {
120+
return; // Nothing to render.
121+
}
122+
123+
// Allocate buffer for vertices + indices.
124+
auto buffer = bd->context->GetTransientsAllocator()->CreateBuffer(
125+
impeller::StorageMode::kHostVisible, total_vtx_bytes + total_idx_bytes);
126+
buffer->SetLabel(impeller::SPrintF("ImGui vertex+index buffer"));
127+
128+
VS::UniformBuffer uniforms;
129+
uniforms.mvp = impeller::Matrix::MakeOrthographic(
130+
impeller::Size(draw_data->DisplaySize.x, draw_data->DisplaySize.y));
131+
uniforms.mvp = uniforms.mvp.Translate(
132+
-impeller::Vector3(draw_data->DisplayPos.x, draw_data->DisplayPos.y));
133+
134+
size_t vertex_buffer_offset = 0;
135+
size_t index_buffer_offset = total_vtx_bytes;
136+
137+
for (int draw_list_i = 0; draw_list_i < draw_data->CmdListsCount;
138+
draw_list_i++) {
139+
const ImDrawList* cmd_list = draw_data->CmdLists[draw_list_i];
140+
141+
auto draw_list_vtx_bytes =
142+
static_cast<size_t>(cmd_list->VtxBuffer.size_in_bytes());
143+
auto draw_list_idx_bytes =
144+
static_cast<size_t>(cmd_list->IdxBuffer.size_in_bytes());
145+
146+
if (!buffer->CopyHostBuffer(
147+
reinterpret_cast<uint8_t*>(cmd_list->VtxBuffer.Data),
148+
impeller::Range{0, draw_list_vtx_bytes}, vertex_buffer_offset)) {
149+
IM_ASSERT(false && "Could not copy vertices to buffer.");
150+
}
151+
if (!buffer->CopyHostBuffer(
152+
reinterpret_cast<uint8_t*>(cmd_list->IdxBuffer.Data),
153+
impeller::Range{0, draw_list_idx_bytes}, index_buffer_offset)) {
154+
IM_ASSERT(false && "Could not copy indices to buffer.");
155+
}
156+
157+
auto viewport = impeller::Viewport{
158+
.rect =
159+
impeller::Rect(draw_data->DisplayPos.x, draw_data->DisplayPos.y,
160+
draw_data->DisplaySize.x, draw_data->DisplaySize.y)};
161+
162+
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
163+
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
164+
165+
if (pcmd->UserCallback) {
166+
pcmd->UserCallback(cmd_list, pcmd);
167+
} else {
168+
// Project scissor/clipping rectangles into framebuffer space.
169+
impeller::IPoint clip_min(pcmd->ClipRect.x - draw_data->DisplayPos.x,
170+
pcmd->ClipRect.y - draw_data->DisplayPos.y);
171+
impeller::IPoint clip_max(pcmd->ClipRect.z - draw_data->DisplayPos.x,
172+
pcmd->ClipRect.w - draw_data->DisplayPos.y);
173+
// Ensure the scissor never goes out of bounds.
174+
clip_min.x = std::clamp(
175+
clip_min.x, 0ll,
176+
static_cast<decltype(clip_min.x)>(draw_data->DisplaySize.x));
177+
clip_min.y = std::clamp(
178+
clip_min.y, 0ll,
179+
static_cast<decltype(clip_min.y)>(draw_data->DisplaySize.y));
180+
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) {
181+
continue; // Nothing to render.
182+
}
183+
184+
impeller::Command cmd;
185+
cmd.label = impeller::SPrintF("ImGui draw list %d (command %d)",
186+
draw_list_i, cmd_i);
187+
188+
cmd.viewport = viewport;
189+
cmd.scissor = impeller::IRect::MakeLTRB(
190+
std::max(0ll, clip_min.x), std::max(0ll, clip_min.y),
191+
std::min(render_pass.GetRenderTargetSize().width, clip_max.x),
192+
std::min(render_pass.GetRenderTargetSize().height, clip_max.y));
193+
194+
cmd.winding = impeller::WindingOrder::kClockwise;
195+
cmd.pipeline = bd->pipeline;
196+
VS::BindUniformBuffer(
197+
cmd, render_pass.GetTransientsBuffer().EmplaceUniform(uniforms));
198+
FS::BindTex(cmd, bd->font_texture, bd->sampler);
199+
200+
size_t vb_start =
201+
vertex_buffer_offset + pcmd->VtxOffset * sizeof(ImDrawVert);
202+
203+
impeller::VertexBuffer vertex_buffer;
204+
vertex_buffer.vertex_buffer = {
205+
.buffer = buffer,
206+
.range = impeller::Range(vb_start, draw_list_vtx_bytes - vb_start)};
207+
vertex_buffer.index_buffer = {
208+
.buffer = buffer,
209+
.range = impeller::Range(
210+
index_buffer_offset + pcmd->IdxOffset * sizeof(ImDrawIdx),
211+
pcmd->ElemCount * sizeof(ImDrawIdx))};
212+
vertex_buffer.index_count = pcmd->ElemCount;
213+
vertex_buffer.index_type = impeller::IndexType::k16bit;
214+
cmd.BindVertices(vertex_buffer);
215+
cmd.base_vertex = pcmd->VtxOffset;
216+
cmd.primitive_type = impeller::PrimitiveType::kTriangle;
217+
218+
render_pass.AddCommand(std::move(cmd));
219+
}
220+
}
221+
222+
vertex_buffer_offset += draw_list_vtx_bytes;
223+
index_buffer_offset += draw_list_idx_bytes;
224+
}
225+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
3+
#include <memory>
4+
5+
#include "third_party/imgui/imgui.h"
6+
7+
namespace impeller {
8+
class Context;
9+
class RenderPass;
10+
} // namespace impeller
11+
12+
IMGUI_IMPL_API bool ImGui_ImplImpeller_Init(
13+
std::shared_ptr<impeller::Context> context);
14+
15+
IMGUI_IMPL_API void ImGui_ImplImpeller_Shutdown();
16+
17+
IMGUI_IMPL_API void ImGui_ImplImpeller_RenderDrawData(
18+
ImDrawData* draw_data,
19+
impeller::RenderPass& renderpass);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
in vec2 frag_texture_coordinates;
2+
in vec4 frag_vertex_color;
3+
4+
out vec4 frag_color;
5+
6+
uniform sampler2D tex;
7+
8+
void main() {
9+
frag_color = frag_vertex_color * texture(tex, frag_texture_coordinates.st);
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
uniform UniformBuffer {
2+
mat4 mvp;
3+
}
4+
uniforms;
5+
6+
in vec2 vertex_position;
7+
in vec2 texture_coordinates;
8+
in uint vertex_color;
9+
10+
out vec2 frag_texture_coordinates;
11+
out vec4 frag_vertex_color;
12+
13+
void main() {
14+
gl_Position = uniforms.mvp * vec4(vertex_position.xy, 0.0, 1.0);
15+
frag_texture_coordinates = texture_coordinates;
16+
frag_vertex_color = unpackUnorm4x8(vertex_color);
17+
}

impeller/playground/playground.mm

+28-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "flutter/testing/testing.h"
1111
#include "impeller/base/validation.h"
1212
#include "impeller/image/compressed_image.h"
13+
#include "impeller/playground/imgui/imgui_impl_impeller.h"
14+
#include "impeller/playground/imgui/imgui_shaders.h"
1315
#include "impeller/playground/playground.h"
1416
#include "impeller/renderer/allocator.h"
1517
#include "impeller/renderer/backend/metal/context_mtl.h"
@@ -20,6 +22,8 @@
2022
#include "impeller/renderer/formats.h"
2123
#include "impeller/renderer/render_pass.h"
2224
#include "impeller/renderer/renderer.h"
25+
#include "third_party/imgui/backends/imgui_impl_glfw.h"
26+
#include "third_party/imgui/imgui.h"
2327

2428
#define GLFW_INCLUDE_NONE
2529
#import "third_party/glfw/include/GLFW/glfw3.h"
@@ -38,6 +42,8 @@
3842
impeller_entity_shaders_length),
3943
std::make_shared<fml::NonOwnedMapping>(impeller_shader_fixtures_data,
4044
impeller_shader_fixtures_length),
45+
std::make_shared<fml::NonOwnedMapping>(impeller_imgui_shaders_data,
46+
impeller_imgui_shaders_length),
4147

4248
};
4349
}
@@ -93,6 +99,13 @@ static void PlaygroundKeyCallback(GLFWwindow* window,
9399
return false;
94100
}
95101

102+
IMGUI_CHECKVERSION();
103+
ImGui::CreateContext();
104+
fml::ScopedCleanupClosure destroy_imgui_context(
105+
[]() { ImGui::DestroyContext(); });
106+
ImGui::StyleColorsDark();
107+
ImGui::GetIO().IniFilename = nullptr;
108+
96109
if (::glfwInit() != GLFW_TRUE) {
97110
return false;
98111
}
@@ -129,6 +142,13 @@ static void PlaygroundKeyCallback(GLFWwindow* window,
129142
fml::ScopedCleanupClosure close_window(
130143
[window]() { ::glfwDestroyWindow(window); });
131144

145+
ImGui_ImplGlfw_InitForOther(window, true);
146+
fml::ScopedCleanupClosure shutdown_imgui([]() { ImGui_ImplGlfw_Shutdown(); });
147+
148+
ImGui_ImplImpeller_Init(renderer_.GetContext());
149+
fml::ScopedCleanupClosure shutdown_imgui_impeller(
150+
[]() { ImGui_ImplImpeller_Shutdown(); });
151+
132152
NSWindow* cocoa_window = ::glfwGetCocoaWindow(window);
133153
CAMetalLayer* layer = [CAMetalLayer layer];
134154
layer.device = ContextMTL::Cast(*renderer_.GetContext()).GetMTLDevice();
@@ -144,14 +164,21 @@ static void PlaygroundKeyCallback(GLFWwindow* window,
144164
return true;
145165
}
146166

167+
ImGui_ImplGlfw_NewFrame();
168+
147169
const auto layer_size = layer.bounds.size;
148170
const auto layer_scale = layer.contentsScale;
149171
layer.drawableSize = CGSizeMake(layer_size.width * layer_scale,
150172
layer_size.height * layer_scale);
151173

152174
Renderer::RenderCallback wrapped_callback = [render_callback](auto& pass) {
153175
pass.SetLabel("Playground Main Render Pass");
154-
return render_callback(pass);
176+
177+
ImGui::NewFrame();
178+
bool result = render_callback(pass);
179+
ImGui::Render();
180+
ImGui_ImplImpeller_RenderDrawData(ImGui::GetDrawData(), pass);
181+
return result;
155182
};
156183

157184
if (!renderer_.Render(SurfaceMTL::WrapCurrentMetalLayerDrawable(

0 commit comments

Comments
 (0)