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