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