| Flutter Impeller |

imgui_impl_impeller.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
5 #include "imgui_impl_impeller.h"
6
7 #include <algorithm>
8 #include <climits>
9 #include <memory>
10 #include <vector>
11
12 #include "impeller/geometry/scalar.h"
13 #include "impeller/geometry/vector.h"
14 #include "impeller/playground/imgui/imgui_raster.frag.h"
15 #include "impeller/playground/imgui/imgui_raster.vert.h"
16 #include "third_party/imgui/imgui.h"
17
18 #include "impeller/core/allocator.h"
19 #include "impeller/core/formats.h"
20 #include "impeller/core/range.h"
21 #include "impeller/core/sampler.h"
22 #include "impeller/core/texture.h"
23 #include "impeller/core/texture_descriptor.h"
24 #include "impeller/core/vertex_buffer.h"
25 #include "impeller/geometry/matrix.h"
26 #include "impeller/geometry/point.h"
27 #include "impeller/geometry/rect.h"
28 #include "impeller/geometry/size.h"
29 #include "impeller/renderer/command.h"
30 #include "impeller/renderer/context.h"
31 #include "impeller/renderer/pipeline_builder.h"
32 #include "impeller/renderer/pipeline_descriptor.h"
33 #include "impeller/renderer/pipeline_library.h"
34 #include "impeller/renderer/render_pass.h"
35 #include "impeller/renderer/sampler_library.h"
36
37 struct ImGui_ImplImpeller_Data {
38 std::shared_ptr<impeller::Context> context;
39 std::shared_ptr<impeller::Texture> font_texture;
40 std::shared_ptr<impeller::Pipeline<impeller::PipelineDescriptor>> pipeline;
41 std::shared_ptr<const impeller::Sampler> sampler;
42 };
43
44 static ImGui_ImplImpeller_Data* ImGui_ImplImpeller_GetBackendData() {
45 return ImGui::GetCurrentContext()
46 ? static_cast<ImGui_ImplImpeller_Data*>(
47 ImGui::GetIO().BackendRendererUserData)
48 : nullptr;
49 }
50
51 bool ImGui_ImplImpeller_Init(
52 const std::shared_ptr<impeller::Context>& context) {
53 ImGuiIO& io = ImGui::GetIO();
54 IM_ASSERT(io.BackendRendererUserData == nullptr &&
55 "Already initialized a renderer backend!");
56
57 // Setup backend capabilities flags
58 auto* bd = new ImGui_ImplImpeller_Data();
59 io.BackendRendererUserData = reinterpret_cast<void*>(bd);
60 io.BackendRendererName = "imgui_impl_impeller";
61 io.BackendFlags |=
62 ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the
63 // ImDrawCmd::VtxOffset field,
64 // allowing for large meshes.
65
66 bd->context = context;
67
68 // Generate/upload the font atlas.
69 {
70 unsigned char* pixels;
71 int width, height;
72 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
73
74 auto texture_descriptor = impeller::TextureDescriptor{};
75 texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible;
76 texture_descriptor.format = impeller::PixelFormat::kR8G8B8A8UNormInt;
77 texture_descriptor.size = {width, height};
78 texture_descriptor.mip_count = 1u;
79
80 bd->font_texture =
81 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
82 IM_ASSERT(bd->font_texture != nullptr &&
83 "Could not allocate ImGui font texture.");
84 bd->font_texture->SetLabel("ImGui Font Texture");
85
86 [[maybe_unused]] bool uploaded = bd->font_texture->SetContents(
87 pixels, texture_descriptor.GetByteSizeOfBaseMipLevel());
88 IM_ASSERT(uploaded &&
89 "Could not upload ImGui font texture to device memory.");
90 }
91
92 // Build the raster pipeline.
93 {
94 auto desc = impeller::PipelineBuilder<impeller::ImguiRasterVertexShader,
95 impeller::ImguiRasterFragmentShader>::
96 MakeDefaultPipelineDescriptor(*context);
97 IM_ASSERT(desc.has_value() && "Could not create Impeller pipeline");
98 if (desc.has_value()) { // Needed to silence clang-tidy check
99 // bugprone-unchecked-optional-access.
100 desc->ClearStencilAttachments();
101 desc->ClearDepthAttachment();
102 }
103
104 bd->pipeline =
105 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
106 IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui pipeline.");
107
108 bd->sampler = context->GetSamplerLibrary()->GetSampler({});
109 IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui sampler.");
110 }
111
112 return true;
113 }
114
115 void ImGui_ImplImpeller_Shutdown() {
116 auto* bd = ImGui_ImplImpeller_GetBackendData();
117 IM_ASSERT(bd != nullptr &&
118 "No renderer backend to shutdown, or already shutdown?");
119 delete bd;
120 }
121
122 void ImGui_ImplImpeller_RenderDrawData(ImDrawData* draw_data,
123 impeller::RenderPass& render_pass) {
124 if (draw_data->CmdListsCount == 0) {
125 return; // Nothing to render.
126 }
127
128 using VS = impeller::ImguiRasterVertexShader;
129 using FS = impeller::ImguiRasterFragmentShader;
130
131 auto* bd = ImGui_ImplImpeller_GetBackendData();
132 IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplImpeller_Init()?");
133
134 size_t total_vtx_bytes = draw_data->TotalVtxCount * sizeof(VS::PerVertexData);
135 size_t total_idx_bytes = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
136 if (!total_vtx_bytes || !total_idx_bytes) {
137 return; // Nothing to render.
138 }
139
140 // Allocate buffer for vertices + indices.
141 impeller::DeviceBufferDescriptor buffer_desc;
142 buffer_desc.size = total_vtx_bytes + total_idx_bytes;
143 buffer_desc.storage_mode = impeller::StorageMode::kHostVisible;
144
145 auto buffer = bd->context->GetResourceAllocator()->CreateBuffer(buffer_desc);
146 buffer->SetLabel(impeller::SPrintF("ImGui vertex+index buffer"));
147
148 auto display_rect =
149 impeller::Rect(draw_data->DisplayPos.x, draw_data->DisplayPos.y,
150 draw_data->DisplaySize.x, draw_data->DisplaySize.y);
151
152 auto viewport = impeller::Viewport{
153 .rect = impeller::Rect(
154 display_rect.origin.x * draw_data->FramebufferScale.x,
155 display_rect.origin.y * draw_data->FramebufferScale.y,
156 display_rect.size.width * draw_data->FramebufferScale.x,
157 display_rect.size.height * draw_data->FramebufferScale.y)};
158
159 // Allocate vertex shader uniform buffer.
160 VS::UniformBuffer uniforms;
161 uniforms.mvp = impeller::Matrix::MakeOrthographic(display_rect.size)
162 .Translate(-display_rect.origin);
163 auto vtx_uniforms =
164 render_pass.GetTransientsBuffer().EmplaceUniform(uniforms);
165
166 size_t vertex_buffer_offset = 0;
167 size_t index_buffer_offset = total_vtx_bytes;
168
169 for (int draw_list_i = 0; draw_list_i < draw_data->CmdListsCount;
170 draw_list_i++) {
171 const ImDrawList* cmd_list = draw_data->CmdLists[draw_list_i];
172
173 // Convert ImGui's per-vertex data (`ImDrawVert`) into the per-vertex data
174 // required by the shader (`VS::PerVectexData`). The only difference is that
175 // `ImDrawVert` uses an `int` for the color and the impeller shader uses 4
176 // floats.
177
178 // TODO(102778): Remove the need for this by adding support for attribute
179 // mapping of uint32s host-side to vec4s shader-side in
180 // impellerc.
181 std::vector<VS::PerVertexData> vtx_data;
182 vtx_data.reserve(cmd_list->VtxBuffer.size());
183 for (const auto& v : cmd_list->VtxBuffer) {
184 ImVec4 color = ImGui::ColorConvertU32ToFloat4(v.col);
185 vtx_data.push_back({{v.pos.x, v.pos.y}, //
186 {v.uv.x, v.uv.y}, //
187 {color.x, color.y, color.z, color.w}});
188 }
189
190 auto draw_list_vtx_bytes =
191 static_cast<size_t>(vtx_data.size() * sizeof(VS::PerVertexData));
192 auto draw_list_idx_bytes =
193 static_cast<size_t>(cmd_list->IdxBuffer.size_in_bytes());
194 if (!buffer->CopyHostBuffer(reinterpret_cast<uint8_t*>(vtx_data.data()),
195 impeller::Range{0, draw_list_vtx_bytes},
196 vertex_buffer_offset)) {
197 IM_ASSERT(false && "Could not copy vertices to buffer.");
198 }
199 if (!buffer->CopyHostBuffer(
200 reinterpret_cast<uint8_t*>(cmd_list->IdxBuffer.Data),
201 impeller::Range{0, draw_list_idx_bytes}, index_buffer_offset)) {
202 IM_ASSERT(false && "Could not copy indices to buffer.");
203 }
204
205 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
206 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
207
208 if (pcmd->UserCallback) {
209 pcmd->UserCallback(cmd_list, pcmd);
210 } else {
211 // Make the clip rect relative to the viewport.
212 auto clip_rect = impeller::Rect::MakeLTRB(
213 (pcmd->ClipRect.x - draw_data->DisplayPos.x) *
214 draw_data->FramebufferScale.x,
215 (pcmd->ClipRect.y - draw_data->DisplayPos.y) *
216 draw_data->FramebufferScale.y,
217 (pcmd->ClipRect.z - draw_data->DisplayPos.x) *
218 draw_data->FramebufferScale.x,
219 (pcmd->ClipRect.w - draw_data->DisplayPos.y) *
220 draw_data->FramebufferScale.y);
221 {
222 // Clamp the clip to the viewport bounds.
223 auto visible_clip = clip_rect.Intersection(viewport.rect);
224 if (!visible_clip.has_value()) {
225 continue; // Nothing to render.
226 }
227 clip_rect = visible_clip.value();
228 }
229 {
230 // Clamp the clip to ensure it never goes outside of the render
231 // target.
232 auto visible_clip = clip_rect.Intersection(
233 impeller::Rect::MakeSize(render_pass.GetRenderTargetSize()));
234 if (!visible_clip.has_value()) {
235 continue; // Nothing to render.
236 }
237 clip_rect = visible_clip.value();
238 }
239
240 impeller::Command cmd;
241 DEBUG_COMMAND_INFO(cmd,
242 impeller::SPrintF("ImGui draw list %d (command %d)",
243 draw_list_i, cmd_i));
244
245 cmd.viewport = viewport;
246 cmd.scissor = impeller::IRect(clip_rect);
247
248 cmd.pipeline = bd->pipeline;
249 VS::BindUniformBuffer(cmd, vtx_uniforms);
250 FS::BindTex(cmd, bd->font_texture, bd->sampler);
251
252 size_t vb_start =
253 vertex_buffer_offset + pcmd->VtxOffset * sizeof(ImDrawVert);
254
255 impeller::VertexBuffer vertex_buffer;
256 vertex_buffer.vertex_buffer = {
257 .buffer = buffer,
258 .range = impeller::Range(vb_start, draw_list_vtx_bytes - vb_start)};
259 vertex_buffer.index_buffer = {
260 .buffer = buffer,
261 .range = impeller::Range(
262 index_buffer_offset + pcmd->IdxOffset * sizeof(ImDrawIdx),
263 pcmd->ElemCount * sizeof(ImDrawIdx))};
264 vertex_buffer.vertex_count = pcmd->ElemCount;
265 vertex_buffer.index_type = impeller::IndexType::k16bit;
266 cmd.BindVertices(vertex_buffer);
267 cmd.base_vertex = pcmd->VtxOffset;
268
269 render_pass.AddCommand(std::move(cmd));
270 }
271 }
272
273 vertex_buffer_offset += draw_list_vtx_bytes;
274 index_buffer_offset += draw_list_idx_bytes;
275 }
276 }
ImGui_ImplImpeller_Data::context
std::shared_ptr< impeller::Context > context
Definition: imgui_impl_impeller.cc:38
impeller::Command
An object used to specify work to the GPU along with references to resources the GPU will used when d...
Definition: command.h:99
impeller::VertexBuffer::index_type
IndexType index_type
Definition: vertex_buffer.h:18
DEBUG_COMMAND_INFO
#define DEBUG_COMMAND_INFO(obj, arg)
Definition: command.h:31
impeller::Command::scissor
std::optional< IRect > scissor
Definition: command.h:169
point.h
impeller::IndexType::k16bit
@ k16bit
impeller::Viewport::rect
Rect rect
Definition: formats.h:353
sampler_library.h
vertex_buffer.h
impeller::DeviceBufferDescriptor
Definition: device_buffer_descriptor.h:13
impeller::VertexBuffer
Definition: vertex_buffer.h:12
texture_descriptor.h
formats.h
impeller::Command::viewport
std::optional< Viewport > viewport
Definition: command.h:163
impeller::DeviceBufferDescriptor::size
size_t size
Definition: device_buffer_descriptor.h:15
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
ImGui_ImplImpeller_GetBackendData
static ImGui_ImplImpeller_Data * ImGui_ImplImpeller_GetBackendData()
Definition: imgui_impl_impeller.cc:44
impeller::VertexBuffer::vertex_buffer
BufferView vertex_buffer
Definition: vertex_buffer.h:13
impeller::StorageMode::kHostVisible
@ kHostVisible
pipeline_builder.h
impeller::VertexBuffer::vertex_count
size_t vertex_count
Definition: vertex_buffer.h:17
sampler.h
impeller::Command::base_vertex
uint64_t base_vertex
Definition: command.h:156
matrix.h
command.h
ImGui_ImplImpeller_Data::sampler
std::shared_ptr< const impeller::Sampler > sampler
Definition: imgui_impl_impeller.cc:41
impeller::RenderPass::GetRenderTargetSize
ISize GetRenderTargetSize() const
Definition: render_pass.cc:30
ImGui_ImplImpeller_Data::font_texture
std::shared_ptr< impeller::Texture > font_texture
Definition: imgui_impl_impeller.cc:39
render_pass.h
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::BufferView::buffer
std::shared_ptr< const Buffer > buffer
Definition: buffer_view.h:14
impeller::DeviceBufferDescriptor::storage_mode
StorageMode storage_mode
Definition: device_buffer_descriptor.h:14
impeller::Matrix::Translate
constexpr Matrix Translate(const Vector3 &t) const
Definition: matrix.h:233
range.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
impeller::IRect
TRect< int64_t > IRect
Definition: rect.h:307
pipeline_library.h
scalar.h
ImGui_ImplImpeller_Shutdown
void ImGui_ImplImpeller_Shutdown()
Definition: imgui_impl_impeller.cc:115
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:27
impeller::Viewport
Definition: formats.h:352
impeller::VertexBuffer::index_buffer
BufferView index_buffer
Definition: vertex_buffer.h:14
allocator.h
ImGui_ImplImpeller_RenderDrawData
void ImGui_ImplImpeller_RenderDrawData(ImDrawData *draw_data, impeller::RenderPass &render_pass)
Definition: imgui_impl_impeller.cc:122
impeller::Range
Definition: range.h:13
vector.h
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:52
ImGui_ImplImpeller_Data::pipeline
std::shared_ptr< impeller::Pipeline< impeller::PipelineDescriptor > > pipeline
Definition: imgui_impl_impeller.cc:40
rect.h
impeller::HostBuffer::EmplaceUniform
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition: host_buffer.h:43
texture.h
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:448
context.h
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:40
ImGui_ImplImpeller_Init
bool ImGui_ImplImpeller_Init(const std::shared_ptr< impeller::Context > &context)
Definition: imgui_impl_impeller.cc:51
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:40
pipeline_descriptor.h
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:39
impeller::Command::BindVertices
bool BindVertices(const VertexBuffer &buffer)
Specify the vertex and index buffer to use for this command.
Definition: command.cc:15
impeller::Command::pipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > pipeline
Definition: command.h:103
impeller::PipelineBuilder
An optional (but highly recommended) utility for creating pipelines from reflected shader information...
Definition: pipeline_builder.h:31
ImGui_ImplImpeller_Data
Definition: imgui_impl_impeller.cc:37
impeller::RenderPass::AddCommand
bool AddCommand(Command &&command)
Record a command for subsequent encoding to the underlying command buffer. No work is encoded into th...
Definition: render_pass.cc:46
imgui_impl_impeller.h
impeller::RenderPass::GetTransientsBuffer
HostBuffer & GetTransientsBuffer()
Definition: render_pass.cc:34
size.h