Flutter Impeller
render_target.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 <format>
8 #include <sstream>
9 
12 #include "impeller/core/formats.h"
13 #include "impeller/core/texture.h"
16 
17 namespace impeller {
18 
19 RenderTarget::RenderTarget() = default;
20 
21 RenderTarget::~RenderTarget() = default;
22 
23 bool RenderTarget::IsValid() const {
24  // Validate that there is a color attachment at zero index.
25  if (!HasColorAttachment(0u)) {
27  << "Render target does not have color attachment at index 0.";
28  return false;
29  }
30 
31 #ifndef NDEBUG
32  // Validate that all attachments are of the same size.
33  {
34  std::optional<ISize> size;
35  bool sizes_are_same = true;
36  auto iterator = [&](const Attachment& attachment) -> bool {
37  if (!size.has_value()) {
38  size = attachment.texture->GetSize();
39  }
40  if (size != attachment.texture->GetSize()) {
41  sizes_are_same = false;
42  return false;
43  }
44  return true;
45  };
46  IterateAllAttachments(iterator);
47  if (!sizes_are_same) {
49  << "Sizes of all render target attachments are not the same.";
50  return false;
51  }
52  }
53 
54  // Validate that all attachments are of the same type and sample counts.
55  {
56  std::optional<TextureType> texture_type;
57  std::optional<SampleCount> sample_count;
58  bool passes_type_validation = true;
59  auto iterator = [&](const Attachment& attachment) -> bool {
60  if (!texture_type.has_value() || !sample_count.has_value()) {
61  texture_type = attachment.texture->GetTextureDescriptor().type;
62  sample_count = attachment.texture->GetTextureDescriptor().sample_count;
63  }
64 
65  if (texture_type != attachment.texture->GetTextureDescriptor().type) {
66  passes_type_validation = false;
67  VALIDATION_LOG << "Render target has incompatible texture types: "
68  << TextureTypeToString(texture_type.value()) << " != "
70  attachment.texture->GetTextureDescriptor().type)
71  << " on target " << ToString();
72  return false;
73  }
74 
75  if (sample_count !=
76  attachment.texture->GetTextureDescriptor().sample_count) {
77  passes_type_validation = false;
78  VALIDATION_LOG << "Render target (" << ToString()
79  << ") has incompatible sample counts.";
80 
81  return false;
82  }
83 
84  return true;
85  };
86  IterateAllAttachments(iterator);
87  if (!passes_type_validation) {
88  return false;
89  }
90  }
91 #endif // NDEBUG
92 
93  return true;
94 }
95 
97  const std::function<bool(size_t index, const ColorAttachment& attachment)>&
98  iterator) const {
99  if (color0_.has_value()) {
100  if (!iterator(0, color0_.value())) {
101  return false;
102  }
103  }
104  for (const auto& [index, attachment] : colors_) {
105  if (!iterator(index, attachment)) {
106  return false;
107  }
108  }
109  return true;
110 }
111 
113  const std::function<bool(const Attachment& attachment)>& iterator) const {
114  if (color0_.has_value()) {
115  if (!iterator(color0_.value())) {
116  return;
117  }
118  }
119  for (const auto& color : colors_) {
120  if (!iterator(color.second)) {
121  return;
122  }
123  }
124 
125  if (depth_.has_value()) {
126  if (!iterator(depth_.value())) {
127  return;
128  }
129  }
130 
131  if (stencil_.has_value()) {
132  if (!iterator(stencil_.value())) {
133  return;
134  }
135  }
136 }
137 
139  if (color0_.has_value()) {
140  return color0_.value().texture->GetTextureDescriptor().sample_count;
141  }
142  return SampleCount::kCount1;
143 }
144 
145 bool RenderTarget::HasColorAttachment(size_t index) const {
146  if (index == 0u) {
147  return color0_.has_value();
148  }
149  if (auto found = colors_.find(index); found != colors_.end()) {
150  return true;
151  }
152  return false;
153 }
154 
155 std::optional<ISize> RenderTarget::GetColorAttachmentSize(size_t index) const {
156  if (index == 0u) {
157  if (color0_.has_value()) {
158  return color0_.value().texture->GetSize();
159  }
160  return std::nullopt;
161  }
162  auto found = colors_.find(index);
163 
164  if (found == colors_.end()) {
165  return std::nullopt;
166  }
167 
168  return found->second.texture->GetSize();
169 }
170 
172  auto size = GetColorAttachmentSize(0u);
173  return size.has_value() ? size.value() : ISize{};
174 }
175 
176 std::shared_ptr<Texture> RenderTarget::GetRenderTargetTexture() const {
177  if (!color0_.has_value()) {
178  return nullptr;
179  }
180  return color0_->resolve_texture ? color0_->resolve_texture : color0_->texture;
181 }
182 
184  if (auto texture = GetRenderTargetTexture(); texture != nullptr) {
185  return texture->GetTextureDescriptor().format;
186  }
187 
188  return PixelFormat::kUnknown;
189 }
190 
192  size_t max = 0;
193  for (const auto& color : colors_) {
194  max = std::max(color.first, max);
195  }
196  return max;
197 }
198 
200  const ColorAttachment& attachment,
201  size_t index) {
202  if (!attachment.IsValid()) {
203  return *this;
204  }
205  if (index == 0u) {
206  color0_ = attachment;
207  } else {
208  colors_[index] = attachment;
209  }
210  return *this;
211 }
212 
214  std::optional<DepthAttachment> attachment) {
215  if (!attachment.has_value()) {
216  depth_ = std::nullopt;
217  } else if (attachment->IsValid()) {
218  depth_ = std::move(attachment);
219  }
220  return *this;
221 }
222 
224  std::optional<StencilAttachment> attachment) {
225  if (!attachment.has_value()) {
226  stencil_ = std::nullopt;
227  } else if (attachment->IsValid()) {
228  stencil_ = std::move(attachment);
229  }
230  return *this;
231 }
232 
234  if (index == 0) {
235  if (color0_.has_value()) {
236  return color0_.value();
237  }
238  return ColorAttachment{};
239  }
240  std::map<size_t, ColorAttachment>::const_iterator it = colors_.find(index);
241  if (it != colors_.end()) {
242  return it->second;
243  }
244  return ColorAttachment{};
245 }
246 
247 const std::optional<DepthAttachment>& RenderTarget::GetDepthAttachment() const {
248  return depth_;
249 }
250 
251 const std::optional<StencilAttachment>& RenderTarget::GetStencilAttachment()
252  const {
253  return stencil_;
254 }
255 
257  size_t count = 0u;
258  for (const auto& [_, color] : colors_) {
259  if (color.texture) {
260  count++;
261  }
262  if (color.resolve_texture) {
263  count++;
264  }
265  }
266  if (color0_.has_value()) {
267  count++;
268  }
269  if (depth_.has_value()) {
270  count++;
271  }
272  if (stencil_.has_value()) {
273  count++;
274  }
275  return count;
276 }
277 
278 std::string RenderTarget::ToString() const {
279  std::stringstream stream;
280 
281  if (color0_.has_value()) {
282  stream << std::format("Color[{}]=({})", 0,
283  ColorAttachmentToString(color0_.value()));
284  }
285  for (const auto& [index, color] : colors_) {
286  stream << std::format("Color[{}]=({})", index,
287  ColorAttachmentToString(color));
288  }
289  if (depth_) {
290  stream << ",";
291  stream << std::format("Depth=({})",
292  DepthAttachmentToString(depth_.value()));
293  }
294  if (stencil_) {
295  stream << ",";
296  stream << std::format("Stencil=({})",
297  StencilAttachmentToString(stencil_.value()));
298  }
299  return stream.str();
300 }
301 
303  if (!color0_.has_value()) {
304  return RenderTargetConfig{};
305  }
306  const auto& color_attachment = color0_.value();
307  return RenderTargetConfig{
308  .size = color_attachment.texture->GetSize(),
309  .mip_count = color_attachment.texture->GetMipCount(),
310  .has_msaa = color_attachment.resolve_texture != nullptr,
311  .has_depth_stencil = depth_.has_value() && stencil_.has_value()};
312 }
313 
315  std::shared_ptr<Allocator> allocator)
316  : allocator_(std::move(allocator)) {}
317 
319 
321 
323  const Context& context,
324  ISize size,
325  int mip_count,
326  std::string_view label,
327  RenderTarget::AttachmentConfig color_attachment_config,
328  std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config,
329  const std::shared_ptr<Texture>& existing_color_texture,
330  const std::shared_ptr<Texture>& existing_depth_stencil_texture,
331  std::optional<PixelFormat> target_pixel_format) {
332  if (size.IsEmpty()) {
333  return {};
334  }
335 
336  RenderTarget target;
337 
338  std::shared_ptr<Texture> color0_tex;
339  if (existing_color_texture) {
340  color0_tex = existing_color_texture;
341  } else {
342  TextureDescriptor color0_tex_desc;
343  color0_tex_desc.storage_mode = color_attachment_config.storage_mode;
344  color0_tex_desc.format =
345  target_pixel_format.has_value()
346  ? target_pixel_format.value()
347  : context.GetCapabilities()->GetDefaultColorFormat();
348  color0_tex_desc.size = size;
349  color0_tex_desc.mip_count = mip_count;
350  color0_tex_desc.usage =
352  color0_tex = allocator_->CreateTexture(color0_tex_desc);
353  if (!color0_tex) {
354  return {};
355  }
356  }
357  color0_tex->SetLabel(label, "Color Texture");
358 
359  ColorAttachment color0;
360  color0.clear_color = color_attachment_config.clear_color;
361  color0.load_action = color_attachment_config.load_action;
362  color0.store_action = color_attachment_config.store_action;
363  color0.texture = color0_tex;
364  target.SetColorAttachment(color0, 0u);
365 
366  if (stencil_attachment_config.has_value()) {
368  context, *allocator_, size, false, label,
369  stencil_attachment_config.value(), existing_depth_stencil_texture);
370  } else {
371  target.SetStencilAttachment(std::nullopt);
372  target.SetDepthAttachment(std::nullopt);
373  }
374 
375  return target;
376 }
377 
379  const Context& context,
380  ISize size,
381  int mip_count,
382  std::string_view label,
383  RenderTarget::AttachmentConfigMSAA color_attachment_config,
384  std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config,
385  const std::shared_ptr<Texture>& existing_color_msaa_texture,
386  const std::shared_ptr<Texture>& existing_color_resolve_texture,
387  const std::shared_ptr<Texture>& existing_depth_stencil_texture,
388  std::optional<PixelFormat> target_pixel_format) {
389  if (size.IsEmpty()) {
390  return {};
391  }
392 
393  RenderTarget target;
394  PixelFormat pixel_format =
395  target_pixel_format.has_value()
396  ? target_pixel_format.value()
397  : context.GetCapabilities()->GetDefaultColorFormat();
398 
399  // Create MSAA color texture.
400  std::shared_ptr<Texture> color0_msaa_tex;
401  if (existing_color_msaa_texture) {
402  color0_msaa_tex = existing_color_msaa_texture;
403  } else {
404  TextureDescriptor color0_tex_desc;
405  color0_tex_desc.storage_mode = color_attachment_config.storage_mode;
406  color0_tex_desc.type = TextureType::kTexture2DMultisample;
407  color0_tex_desc.sample_count = SampleCount::kCount4;
408  color0_tex_desc.format = pixel_format;
409  color0_tex_desc.size = size;
410  color0_tex_desc.usage = TextureUsage::kRenderTarget;
411  if (context.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
412  // See below ("SupportsImplicitResolvingMSAA") for more details.
413  color0_tex_desc.storage_mode = StorageMode::kDevicePrivate;
414  }
415  color0_msaa_tex = allocator_->CreateTexture(color0_tex_desc);
416  if (!color0_msaa_tex) {
417  VALIDATION_LOG << "Could not create multisample color texture.";
418  return {};
419  }
420  }
421  color0_msaa_tex->SetLabel(label, "Color Texture (Multisample)");
422 
423  // Create color resolve texture.
424  std::shared_ptr<Texture> color0_resolve_tex;
425  if (existing_color_resolve_texture) {
426  color0_resolve_tex = existing_color_resolve_texture;
427  } else {
428  TextureDescriptor color0_resolve_tex_desc;
429  color0_resolve_tex_desc.storage_mode =
430  color_attachment_config.resolve_storage_mode;
431  color0_resolve_tex_desc.format = pixel_format;
432  color0_resolve_tex_desc.size = size;
433  color0_resolve_tex_desc.compression_type = CompressionType::kLossy;
434  color0_resolve_tex_desc.usage =
436  color0_resolve_tex_desc.mip_count = mip_count;
437  color0_resolve_tex = allocator_->CreateTexture(color0_resolve_tex_desc);
438  if (!color0_resolve_tex) {
439  VALIDATION_LOG << "Could not create color texture.";
440  return {};
441  }
442  }
443  color0_resolve_tex->SetLabel(label, "Color Texture");
444 
445  // Color attachment.
446 
447  ColorAttachment color0;
448  color0.clear_color = color_attachment_config.clear_color;
449  color0.load_action = color_attachment_config.load_action;
450  color0.store_action = color_attachment_config.store_action;
451  color0.texture = color0_msaa_tex;
452  color0.resolve_texture = color0_resolve_tex;
453 
454  if (context.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
455  // If implicit MSAA is supported, then the resolve texture is not needed
456  // because the multisample texture is automatically resolved. We instead
457  // provide a view of the multisample texture as the resolve texture (because
458  // the HAL does expect a resolve texture).
459  //
460  // In practice, this is used for GLES 2.0 EXT_multisampled_render_to_texture
461  // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multisampled_render_to_texture.txt
462  color0.resolve_texture = color0_msaa_tex;
463  }
464 
465  target.SetColorAttachment(color0, 0u);
466 
467  // Create MSAA stencil texture.
468 
469  if (stencil_attachment_config.has_value()) {
470  target.SetupDepthStencilAttachments(context, *allocator_, size, true, label,
471  stencil_attachment_config.value(),
472  existing_depth_stencil_texture);
473  } else {
474  target.SetDepthAttachment(std::nullopt);
475  target.SetStencilAttachment(std::nullopt);
476  }
477 
478  return target;
479 }
480 
482  const Context& context,
483  Allocator& allocator,
484  ISize size,
485  bool msaa,
486  std::string_view label,
487  RenderTarget::AttachmentConfig stencil_attachment_config,
488  const std::shared_ptr<Texture>& existing_depth_stencil_texture) {
489  std::shared_ptr<Texture> depth_stencil_texture;
490  if (existing_depth_stencil_texture) {
491  depth_stencil_texture = existing_depth_stencil_texture;
492  } else {
493  TextureDescriptor depth_stencil_texture_desc;
494  depth_stencil_texture_desc.storage_mode =
495  stencil_attachment_config.storage_mode;
496  if (msaa) {
497  depth_stencil_texture_desc.type = TextureType::kTexture2DMultisample;
498  depth_stencil_texture_desc.sample_count = SampleCount::kCount4;
499  }
500  depth_stencil_texture_desc.format =
501  context.GetCapabilities()->GetDefaultDepthStencilFormat();
502  depth_stencil_texture_desc.size = size;
503  depth_stencil_texture_desc.usage = TextureUsage::kRenderTarget;
504  depth_stencil_texture = allocator.CreateTexture(depth_stencil_texture_desc);
505  if (!depth_stencil_texture) {
506  return; // Error messages are handled by `Allocator::CreateTexture`.
507  }
508  }
509 
510  DepthAttachment depth0;
511  depth0.load_action = stencil_attachment_config.load_action;
512  depth0.store_action = stencil_attachment_config.store_action;
513  depth0.clear_depth = 0u;
514  depth0.texture = depth_stencil_texture;
515 
516  StencilAttachment stencil0;
517  stencil0.load_action = stencil_attachment_config.load_action;
518  stencil0.store_action = stencil_attachment_config.store_action;
519  stencil0.clear_stencil = 0u;
520  stencil0.texture = std::move(depth_stencil_texture);
521  stencil0.texture->SetLabel(label, "Depth+Stencil Texture");
522 
523  SetDepthAttachment(std::move(depth0));
524  SetStencilAttachment(std::move(stencil0));
525 }
526 
527 } // namespace impeller
An object that allocates device memory.
Definition: allocator.h:24
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc, bool threadsafe=false)
Creates a new texture.
Definition: allocator.cc:49
To do anything rendering related with Impeller, you need a context.
Definition: context.h:65
virtual const std::shared_ptr< const Capabilities > & GetCapabilities() const =0
Get the capabilities of Impeller context. All optionally supported feature of the platform,...
RenderTargetAllocator(std::shared_ptr< Allocator > allocator)
virtual RenderTarget CreateOffscreenMSAA(const Context &context, ISize size, int mip_count, std::string_view label="Offscreen MSAA", RenderTarget::AttachmentConfigMSAA color_attachment_config=RenderTarget::kDefaultColorAttachmentConfigMSAA, std::optional< RenderTarget::AttachmentConfig > stencil_attachment_config=RenderTarget::kDefaultStencilAttachmentConfig, const std::shared_ptr< Texture > &existing_color_msaa_texture=nullptr, const std::shared_ptr< Texture > &existing_color_resolve_texture=nullptr, const std::shared_ptr< Texture > &existing_depth_stencil_texture=nullptr, std::optional< PixelFormat > target_pixel_format=std::nullopt)
virtual RenderTarget CreateOffscreen(const Context &context, ISize size, int mip_count, std::string_view label="Offscreen", RenderTarget::AttachmentConfig color_attachment_config=RenderTarget::kDefaultColorAttachmentConfig, std::optional< RenderTarget::AttachmentConfig > stencil_attachment_config=RenderTarget::kDefaultStencilAttachmentConfig, const std::shared_ptr< Texture > &existing_color_texture=nullptr, const std::shared_ptr< Texture > &existing_depth_stencil_texture=nullptr, std::optional< PixelFormat > target_pixel_format=std::nullopt)
virtual void Start()
Mark the beginning of a frame workload.
virtual void End()
Mark the end of a frame workload.
ColorAttachment GetColorAttachment(size_t index) const
Get the color attachment at [index].
std::shared_ptr< Texture > GetRenderTargetTexture() const
SampleCount GetSampleCount() const
void IterateAllAttachments(const std::function< bool(const Attachment &attachment)> &iterator) const
RenderTarget & SetColorAttachment(const ColorAttachment &attachment, size_t index)
bool HasColorAttachment(size_t index) const
size_t GetMaxColorAttacmentBindIndex() const
std::string ToString() const
RenderTarget & SetDepthAttachment(std::optional< DepthAttachment > attachment)
PixelFormat GetRenderTargetPixelFormat() const
size_t GetTotalAttachmentCount() const
ISize GetRenderTargetSize() const
RenderTarget & SetStencilAttachment(std::optional< StencilAttachment > attachment)
bool IterateAllColorAttachments(const std::function< bool(size_t index, const ColorAttachment &attachment)> &iterator) const
std::optional< ISize > GetColorAttachmentSize(size_t index) const
const std::optional< DepthAttachment > & GetDepthAttachment() const
const std::optional< StencilAttachment > & GetStencilAttachment() const
void SetupDepthStencilAttachments(const Context &context, Allocator &allocator, ISize size, bool msaa, std::string_view label="Offscreen", RenderTarget::AttachmentConfig stencil_attachment_config=RenderTarget::kDefaultStencilAttachmentConfig, const std::shared_ptr< Texture > &depth_stencil_texture=nullptr)
RenderTargetConfig ToConfig() const
std::string DepthAttachmentToString(const DepthAttachment &depth)
Definition: formats.cc:130
std::string ColorAttachmentToString(const ColorAttachment &color)
Definition: formats.cc:123
std::string StencilAttachmentToString(const StencilAttachment &stencil)
Definition: formats.cc:137
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
constexpr const char * TextureTypeToString(TextureType type)
Definition: formats.h:272
SampleCount
Definition: formats.h:298
Definition: comparable.h:95
std::shared_ptr< Texture > resolve_texture
Definition: formats.h:662
bool IsValid() const
Definition: formats.cc:26
LoadAction load_action
Definition: formats.h:663
std::shared_ptr< Texture > texture
Definition: formats.h:661
StoreAction store_action
Definition: formats.h:664
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: size.h:123
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define VALIDATION_LOG
Definition: validation.h:91