Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
6
7#include <algorithm>
8#include <climits>
9#include <format>
10#include <memory>
11#include <vector>
12
13#include "fml/mapping.h"
18#include "impeller/playground/imgui/imgui_raster.frag.h"
19#include "impeller/playground/imgui/imgui_raster.vert.h"
20#include "third_party/imgui/imgui.h"
21
24#include "impeller/core/range.h"
38
43
44 std::shared_ptr<impeller::Context> context;
45 std::shared_ptr<impeller::Texture> font_texture;
46 std::shared_ptr<impeller::Pipeline<impeller::PipelineDescriptor>> pipeline;
48};
49
51 return ImGui::GetCurrentContext()
52 ? static_cast<ImGui_ImplImpeller_Data*>(
53 ImGui::GetIO().BackendRendererUserData)
54 : nullptr;
55}
56
58 const std::shared_ptr<impeller::Context>& context) {
59 ImGuiIO& io = ImGui::GetIO();
60 IM_ASSERT(io.BackendRendererUserData == nullptr &&
61 "Already initialized a renderer backend!");
62
63 // Setup backend capabilities flags
64 auto* bd =
65 new ImGui_ImplImpeller_Data(context->GetSamplerLibrary()->GetSampler({}));
66 io.BackendRendererUserData = reinterpret_cast<void*>(bd);
67 io.BackendRendererName = "imgui_impl_impeller";
68 io.BackendFlags |=
69 ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the
70 // ImDrawCmd::VtxOffset field,
71 // allowing for large meshes.
72
73 bd->context = context;
74
75 // Generate/upload the font atlas.
76 {
77 unsigned char* pixels;
78 int width, height;
79 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
80
81 impeller::TextureDescriptor texture_descriptor;
84 texture_descriptor.size = {width, height};
85 texture_descriptor.mip_count = 1u;
86
87 bd->font_texture =
88 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
89 IM_ASSERT(bd->font_texture != nullptr &&
90 "Could not allocate ImGui font texture.");
91 bd->font_texture->SetLabel("ImGui Font Texture");
92
93 auto command_buffer = context->CreateCommandBuffer();
94 auto blit_pass = command_buffer->CreateBlitPass();
95 auto mapping = std::make_shared<fml::NonOwnedMapping>(
96 reinterpret_cast<const uint8_t*>(pixels),
97 texture_descriptor.GetByteSizeOfBaseMipLevel());
98 auto device_buffer =
99 context->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
100
101 blit_pass->AddCopy(impeller::DeviceBuffer::AsBufferView(device_buffer),
102 bd->font_texture);
103 blit_pass->EncodeCommands();
104
105 [[maybe_unused]] bool uploaded =
106 context->GetCommandQueue()->Submit({command_buffer}).ok();
107 IM_ASSERT(uploaded &&
108 "Could not upload ImGui font texture to device memory.");
109 }
110
111 // Build the raster pipeline.
112 {
113 auto desc = impeller::PipelineBuilder<impeller::ImguiRasterVertexShader,
114 impeller::ImguiRasterFragmentShader>::
115 MakeDefaultPipelineDescriptor(*context);
116 IM_ASSERT(desc.has_value() && "Could not create Impeller pipeline");
117 if (desc.has_value()) { // Needed to silence clang-tidy check
118 // bugprone-unchecked-optional-access.
119 desc->ClearStencilAttachments();
120 desc->ClearDepthAttachment();
121 }
122
123 bd->pipeline =
124 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
125 IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui pipeline.");
126 IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui sampler.");
127 }
128
129 return true;
130}
131
134 IM_ASSERT(bd != nullptr &&
135 "No renderer backend to shutdown, or already shutdown?");
136 ImGuiIO& io = ImGui::GetIO();
137
138 io.BackendRendererName = nullptr;
139 io.BackendRendererUserData = nullptr;
140 io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
141 delete bd;
142}
143
144void ImGui_ImplImpeller_RenderDrawData(ImDrawData* draw_data,
145 impeller::RenderPass& render_pass,
146 impeller::HostBuffer& host_buffer) {
147 if (draw_data->CmdListsCount == 0) {
148 return; // Nothing to render.
149 }
150
151 using VS = impeller::ImguiRasterVertexShader;
152 using FS = impeller::ImguiRasterFragmentShader;
153
155 IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplImpeller_Init()?");
156
157 size_t total_vtx_bytes = draw_data->TotalVtxCount * sizeof(VS::PerVertexData);
158 size_t total_idx_bytes = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
159 if (!total_vtx_bytes || !total_idx_bytes) {
160 return; // Nothing to render.
161 }
162
163 // Allocate buffer for vertices + indices.
165 buffer_desc.size = total_vtx_bytes + total_idx_bytes;
167
168 auto buffer = bd->context->GetResourceAllocator()->CreateBuffer(buffer_desc);
169 buffer->SetLabel("ImGui vertex+index buffer");
170
171 auto display_rect = impeller::Rect::MakeXYWH(
172 draw_data->DisplayPos.x, draw_data->DisplayPos.y,
173 draw_data->DisplaySize.x, draw_data->DisplaySize.y);
174
175 auto viewport = impeller::Viewport{
176 .rect = display_rect.Scale(draw_data->FramebufferScale.x,
177 draw_data->FramebufferScale.y)};
178
179 // Allocate vertex shader uniform buffer.
180 VS::UniformBuffer uniforms;
181 uniforms.mvp = impeller::Matrix::MakeOrthographic(display_rect.GetSize())
182 .Translate(-display_rect.GetOrigin());
183 auto vtx_uniforms = host_buffer.EmplaceUniform(uniforms);
184
185 size_t vertex_buffer_offset = 0;
186 size_t index_buffer_offset = total_vtx_bytes;
187
188 for (int draw_list_i = 0; draw_list_i < draw_data->CmdListsCount;
189 draw_list_i++) {
190 const ImDrawList* cmd_list = draw_data->CmdLists[draw_list_i];
191
192 // Convert ImGui's per-vertex data (`ImDrawVert`) into the per-vertex data
193 // required by the shader (`VS::PerVectexData`). The only difference is that
194 // `ImDrawVert` uses an `int` for the color and the impeller shader uses 4
195 // floats.
196
197 // TODO(102778): Remove the need for this by adding support for attribute
198 // mapping of uint32s host-side to vec4s shader-side in
199 // impellerc.
200 std::vector<VS::PerVertexData> vtx_data;
201 vtx_data.reserve(cmd_list->VtxBuffer.size());
202 for (const auto& v : cmd_list->VtxBuffer) {
203 ImVec4 color = ImGui::ColorConvertU32ToFloat4(v.col);
204 vtx_data.push_back({{v.pos.x, v.pos.y}, //
205 {v.uv.x, v.uv.y}, //
206 {color.x, color.y, color.z, color.w}});
207 }
208
209 auto draw_list_vtx_bytes =
210 static_cast<size_t>(vtx_data.size() * sizeof(VS::PerVertexData));
211 auto draw_list_idx_bytes =
212 static_cast<size_t>(cmd_list->IdxBuffer.size_in_bytes());
213 if (!buffer->CopyHostBuffer(reinterpret_cast<uint8_t*>(vtx_data.data()),
214 impeller::Range{0, draw_list_vtx_bytes},
215 vertex_buffer_offset)) {
216 IM_ASSERT(false && "Could not copy vertices to buffer.");
217 }
218 if (!buffer->CopyHostBuffer(
219 reinterpret_cast<uint8_t*>(cmd_list->IdxBuffer.Data),
220 impeller::Range{0, draw_list_idx_bytes}, index_buffer_offset)) {
221 IM_ASSERT(false && "Could not copy indices to buffer.");
222 }
223
224 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
225 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
226
227 if (pcmd->UserCallback) {
228 pcmd->UserCallback(cmd_list, pcmd);
229 } else {
230 // Make the clip rect relative to the viewport.
231 auto clip_rect = impeller::Rect::MakeLTRB(
232 (pcmd->ClipRect.x - draw_data->DisplayPos.x) *
233 draw_data->FramebufferScale.x,
234 (pcmd->ClipRect.y - draw_data->DisplayPos.y) *
235 draw_data->FramebufferScale.y,
236 (pcmd->ClipRect.z - draw_data->DisplayPos.x) *
237 draw_data->FramebufferScale.x,
238 (pcmd->ClipRect.w - draw_data->DisplayPos.y) *
239 draw_data->FramebufferScale.y);
240 {
241 // Clamp the clip to the viewport bounds.
242 auto visible_clip = clip_rect.Intersection(viewport.rect);
243 if (!visible_clip.has_value()) {
244 continue; // Nothing to render.
245 }
246 clip_rect = visible_clip.value();
247 }
248 {
249 // Clamp the clip to ensure it never goes outside of the render
250 // target.
251 auto visible_clip = clip_rect.Intersection(
253 if (!visible_clip.has_value()) {
254 continue; // Nothing to render.
255 }
256 clip_rect = visible_clip.value();
257 }
258
259 render_pass.SetCommandLabel(
260 std::format("ImGui draw list {} (command {})", draw_list_i, cmd_i));
261 render_pass.SetViewport(viewport);
262 render_pass.SetScissor(impeller::IRect32::RoundOut(clip_rect));
263 render_pass.SetPipeline(bd->pipeline);
264 VS::BindUniformBuffer(render_pass, vtx_uniforms);
265 FS::BindTex(render_pass, bd->font_texture, bd->sampler);
266
267 size_t vb_start =
268 vertex_buffer_offset + pcmd->VtxOffset * sizeof(ImDrawVert);
269
270 impeller::VertexBuffer vertex_buffer;
271 vertex_buffer.vertex_buffer = impeller::BufferView(
272 buffer, impeller::Range(vb_start, draw_list_vtx_bytes - vb_start));
273 vertex_buffer.index_buffer = impeller::BufferView(
274 buffer, impeller::Range(index_buffer_offset +
275 pcmd->IdxOffset * sizeof(ImDrawIdx),
276 pcmd->ElemCount * sizeof(ImDrawIdx)));
277 vertex_buffer.vertex_count = pcmd->ElemCount;
279 render_pass.SetVertexBuffer(std::move(vertex_buffer));
280 render_pass.SetBaseVertex(pcmd->VtxOffset);
281
282 render_pass.Draw().ok();
283 }
284 }
285
286 vertex_buffer_offset += draw_list_vtx_bytes;
287 index_buffer_offset += draw_list_idx_bytes;
288 }
289 host_buffer.Reset();
290}
bool ok() const
Definition status.h:71
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition host_buffer.h:47
void Reset()
Resets the contents of the HostBuffer to nothing so it can be reused.
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition render_pass.h:30
virtual bool SetVertexBuffer(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
virtual void SetScissor(IRect32 scissor)
virtual void SetPipeline(PipelineRef pipeline)
The pipeline to use for this command.
ISize GetRenderTargetSize() const
virtual fml::Status Draw()
Record the currently pending command.
virtual void SetCommandLabel(std::string_view label)
The debugging label to use for the command.
virtual void SetBaseVertex(uint64_t value)
virtual void SetViewport(Viewport viewport)
A wrapper around a raw ptr that adds additional unopt mode only checks.
Definition raw_ptr.h:15
void ImGui_ImplImpeller_RenderDrawData(ImDrawData *draw_data, impeller::RenderPass &render_pass, impeller::HostBuffer &host_buffer)
bool ImGui_ImplImpeller_Init(const std::shared_ptr< impeller::Context > &context)
static ImGui_ImplImpeller_Data * ImGui_ImplImpeller_GetBackendData()
void ImGui_ImplImpeller_Shutdown()
int32_t height
int32_t width
std::shared_ptr< impeller::Context > context
std::shared_ptr< impeller::Pipeline< impeller::PipelineDescriptor > > pipeline
std::shared_ptr< impeller::Texture > font_texture
impeller::raw_ptr< const impeller::Sampler > sampler
ImGui_ImplImpeller_Data(impeller::raw_ptr< const impeller::Sampler > p_sampler)
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition matrix.h:641
constexpr Matrix Translate(const Vector3 &t) const
Definition matrix.h:263
An optional (but highly recommended) utility for creating pipelines from reflected shader information...
RoundOut(const TRect< U > &r)
Definition rect.h:713
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
constexpr TRect Scale(Type scale) const
Definition rect.h:226
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
constexpr size_t GetByteSizeOfBaseMipLevel() const
BufferView index_buffer
The index buffer binding used by the vertex shader stage.