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 // The dimensions of `texture` at `mip_level`, clamped to a minimum of 1x1.
20 // Rendering into a mip level uses that level's size, not the base size.
21 static ISize SizeForMipLevel(const std::shared_ptr<Texture>& texture,
22  uint32_t mip_level) {
23  ISize size = texture->GetSize();
24  // Any real texture reaches 1x1 well before this; clamping the shift also
25  // avoids undefined behavior for absurd mip levels.
26  uint32_t shift = std::min(mip_level, 31u);
27  return ISize{std::max<int64_t>(1, size.width >> shift),
28  std::max<int64_t>(1, size.height >> shift)};
29 }
30 
31 RenderTarget::RenderTarget() = default;
32 
33 RenderTarget::~RenderTarget() = default;
34 
35 bool RenderTarget::IsValid() const {
36  // Validate that there is a color attachment at zero index.
37  if (!HasColorAttachment(0u)) {
39  << "Render target does not have color attachment at index 0.";
40  return false;
41  }
42 
43 #ifndef NDEBUG
44  // Validate that all attachments are of the same size.
45  {
46  std::optional<ISize> size;
47  bool sizes_are_same = true;
48  auto iterator = [&](const Attachment& attachment) -> bool {
49  ISize attachment_size =
50  SizeForMipLevel(attachment.texture, attachment.mip_level);
51  if (!size.has_value()) {
52  size = attachment_size;
53  }
54  if (size != attachment_size) {
55  sizes_are_same = false;
56  return false;
57  }
58  return true;
59  };
60  IterateAllAttachments(iterator);
61  if (!sizes_are_same) {
63  << "Sizes of all render target attachments are not the same.";
64  return false;
65  }
66  }
67 
68  // Validate that all attachments are of the same type and sample counts.
69  {
70  std::optional<TextureType> texture_type;
71  std::optional<SampleCount> sample_count;
72  bool passes_type_validation = true;
73  auto iterator = [&](const Attachment& attachment) -> bool {
74  if (!texture_type.has_value() || !sample_count.has_value()) {
75  texture_type = attachment.texture->GetTextureDescriptor().type;
76  sample_count = attachment.texture->GetTextureDescriptor().sample_count;
77  }
78 
79  if (texture_type != attachment.texture->GetTextureDescriptor().type) {
80  passes_type_validation = false;
81  VALIDATION_LOG << "Render target has incompatible texture types: "
82  << TextureTypeToString(texture_type.value()) << " != "
84  attachment.texture->GetTextureDescriptor().type)
85  << " on target " << ToString();
86  return false;
87  }
88 
89  if (sample_count !=
90  attachment.texture->GetTextureDescriptor().sample_count) {
91  passes_type_validation = false;
92  VALIDATION_LOG << "Render target (" << ToString()
93  << ") has incompatible sample counts.";
94 
95  return false;
96  }
97 
98  return true;
99  };
100  IterateAllAttachments(iterator);
101  if (!passes_type_validation) {
102  return false;
103  }
104  }
105 #endif // NDEBUG
106 
107  return true;
108 }
109 
111  const std::function<bool(size_t index, const ColorAttachment& attachment)>&
112  iterator) const {
113  if (color0_.has_value()) {
114  if (!iterator(0, color0_.value())) {
115  return false;
116  }
117  }
118  for (const auto& [index, attachment] : colors_) {
119  if (!iterator(index, attachment)) {
120  return false;
121  }
122  }
123  return true;
124 }
125 
127  const std::function<bool(const Attachment& attachment)>& iterator) const {
128  if (color0_.has_value()) {
129  if (!iterator(color0_.value())) {
130  return;
131  }
132  }
133  for (const auto& color : colors_) {
134  if (!iterator(color.second)) {
135  return;
136  }
137  }
138 
139  if (depth_.has_value()) {
140  if (!iterator(depth_.value())) {
141  return;
142  }
143  }
144 
145  if (stencil_.has_value()) {
146  if (!iterator(stencil_.value())) {
147  return;
148  }
149  }
150 }
151 
153  if (color0_.has_value()) {
154  return color0_.value().texture->GetTextureDescriptor().sample_count;
155  }
156  return SampleCount::kCount1;
157 }
158 
159 bool RenderTarget::HasColorAttachment(size_t index) const {
160  if (index == 0u) {
161  return color0_.has_value();
162  }
163  if (auto found = colors_.find(index); found != colors_.end()) {
164  return true;
165  }
166  return false;
167 }
168 
169 std::optional<ISize> RenderTarget::GetColorAttachmentSize(size_t index) const {
170  if (index == 0u) {
171  if (color0_.has_value()) {
172  return SizeForMipLevel(color0_.value().texture,
173  color0_.value().mip_level);
174  }
175  return std::nullopt;
176  }
177  auto found = colors_.find(index);
178 
179  if (found == colors_.end()) {
180  return std::nullopt;
181  }
182 
183  return SizeForMipLevel(found->second.texture, found->second.mip_level);
184 }
185 
187  auto size = GetColorAttachmentSize(0u);
188  return size.has_value() ? size.value() : ISize{};
189 }
190 
191 std::shared_ptr<Texture> RenderTarget::GetRenderTargetTexture() const {
192  if (!color0_.has_value()) {
193  return nullptr;
194  }
195  return color0_->resolve_texture ? color0_->resolve_texture : color0_->texture;
196 }
197 
199  if (auto texture = GetRenderTargetTexture(); texture != nullptr) {
200  return texture->GetTextureDescriptor().format;
201  }
202 
203  return PixelFormat::kUnknown;
204 }
205 
207  size_t max = 0;
208  for (const auto& color : colors_) {
209  max = std::max(color.first, max);
210  }
211  return max;
212 }
213 
215  const ColorAttachment& attachment,
216  size_t index) {
217  if (!attachment.IsValid()) {
218  return *this;
219  }
220  if (index == 0u) {
221  color0_ = attachment;
222  } else {
223  colors_[index] = attachment;
224  }
225  return *this;
226 }
227 
229  std::optional<DepthAttachment> attachment) {
230  if (!attachment.has_value()) {
231  depth_ = std::nullopt;
232  } else if (attachment->IsValid()) {
233  depth_ = std::move(attachment);
234  }
235  return *this;
236 }
237 
239  std::optional<StencilAttachment> attachment) {
240  if (!attachment.has_value()) {
241  stencil_ = std::nullopt;
242  } else if (attachment->IsValid()) {
243  stencil_ = std::move(attachment);
244  }
245  return *this;
246 }
247 
249  if (index == 0) {
250  if (color0_.has_value()) {
251  return color0_.value();
252  }
253  return ColorAttachment{};
254  }
255  std::map<size_t, ColorAttachment>::const_iterator it = colors_.find(index);
256  if (it != colors_.end()) {
257  return it->second;
258  }
259  return ColorAttachment{};
260 }
261 
262 const std::optional<DepthAttachment>& RenderTarget::GetDepthAttachment() const {
263  return depth_;
264 }
265 
266 const std::optional<StencilAttachment>& RenderTarget::GetStencilAttachment()
267  const {
268  return stencil_;
269 }
270 
272  size_t count = 0u;
273  for (const auto& [_, color] : colors_) {
274  if (color.texture) {
275  count++;
276  }
277  if (color.resolve_texture) {
278  count++;
279  }
280  }
281  if (color0_.has_value()) {
282  count++;
283  }
284  if (depth_.has_value()) {
285  count++;
286  }
287  if (stencil_.has_value()) {
288  count++;
289  }
290  return count;
291 }
292 
293 std::string RenderTarget::ToString() const {
294  std::stringstream stream;
295 
296  if (color0_.has_value()) {
297  stream << std::format("Color[{}]=({})", 0,
298  ColorAttachmentToString(color0_.value()));
299  }
300  for (const auto& [index, color] : colors_) {
301  stream << std::format("Color[{}]=({})", index,
302  ColorAttachmentToString(color));
303  }
304  if (depth_) {
305  stream << ",";
306  stream << std::format("Depth=({})",
307  DepthAttachmentToString(depth_.value()));
308  }
309  if (stencil_) {
310  stream << ",";
311  stream << std::format("Stencil=({})",
312  StencilAttachmentToString(stencil_.value()));
313  }
314  return stream.str();
315 }
316 
318  if (!color0_.has_value()) {
319  return RenderTargetConfig{};
320  }
321  const auto& color_attachment = color0_.value();
322  return RenderTargetConfig{
323  .size = color_attachment.texture->GetSize(),
324  .mip_count = color_attachment.texture->GetMipCount(),
325  .has_msaa = color_attachment.resolve_texture != nullptr,
326  .has_depth_stencil = depth_.has_value() && stencil_.has_value()};
327 }
328 
330  std::shared_ptr<Allocator> allocator)
331  : allocator_(std::move(allocator)) {}
332 
334 
336 
338  const Context& context,
339  ISize size,
340  int mip_count,
341  std::string_view label,
342  RenderTarget::AttachmentConfig color_attachment_config,
343  std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config,
344  const std::shared_ptr<Texture>& existing_color_texture,
345  const std::shared_ptr<Texture>& existing_depth_stencil_texture,
346  std::optional<PixelFormat> target_pixel_format) {
347  if (size.IsEmpty()) {
348  return {};
349  }
350 
351  RenderTarget target;
352 
353  std::shared_ptr<Texture> color0_tex;
354  if (existing_color_texture) {
355  color0_tex = existing_color_texture;
356  } else {
357  TextureDescriptor color0_tex_desc;
358  color0_tex_desc.storage_mode = color_attachment_config.storage_mode;
359  color0_tex_desc.format =
360  target_pixel_format.has_value()
361  ? target_pixel_format.value()
362  : context.GetCapabilities()->GetDefaultColorFormat();
363  color0_tex_desc.size = size;
364  color0_tex_desc.mip_count = mip_count;
365  color0_tex_desc.usage =
367  color0_tex = allocator_->CreateTexture(color0_tex_desc);
368  if (!color0_tex) {
369  return {};
370  }
371  }
372  color0_tex->SetLabel(label, "Color Texture");
373 
374  ColorAttachment color0;
375  color0.clear_color = color_attachment_config.clear_color;
376  color0.load_action = color_attachment_config.load_action;
377  color0.store_action = color_attachment_config.store_action;
378  color0.texture = color0_tex;
379  target.SetColorAttachment(color0, 0u);
380 
381  if (stencil_attachment_config.has_value()) {
383  context, *allocator_, size, false, label,
384  stencil_attachment_config.value(), existing_depth_stencil_texture);
385  } else {
386  target.SetStencilAttachment(std::nullopt);
387  target.SetDepthAttachment(std::nullopt);
388  }
389 
390  return target;
391 }
392 
394  const Context& context,
395  ISize size,
396  int mip_count,
397  std::string_view label,
398  RenderTarget::AttachmentConfigMSAA color_attachment_config,
399  std::optional<RenderTarget::AttachmentConfig> stencil_attachment_config,
400  const std::shared_ptr<Texture>& existing_color_msaa_texture,
401  const std::shared_ptr<Texture>& existing_color_resolve_texture,
402  const std::shared_ptr<Texture>& existing_depth_stencil_texture,
403  std::optional<PixelFormat> target_pixel_format) {
404  if (size.IsEmpty()) {
405  return {};
406  }
407 
408  RenderTarget target;
409  PixelFormat pixel_format =
410  target_pixel_format.has_value()
411  ? target_pixel_format.value()
412  : context.GetCapabilities()->GetDefaultColorFormat();
413 
414  // Create MSAA color texture.
415  std::shared_ptr<Texture> color0_msaa_tex;
416  if (existing_color_msaa_texture) {
417  color0_msaa_tex = existing_color_msaa_texture;
418  } else {
419  TextureDescriptor color0_tex_desc;
420  color0_tex_desc.storage_mode = color_attachment_config.storage_mode;
421  color0_tex_desc.type = TextureType::kTexture2DMultisample;
422  color0_tex_desc.sample_count = SampleCount::kCount4;
423  color0_tex_desc.format = pixel_format;
424  color0_tex_desc.size = size;
425  color0_tex_desc.usage = TextureUsage::kRenderTarget;
426  if (context.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
427  // See below ("SupportsImplicitResolvingMSAA") for more details.
428  color0_tex_desc.storage_mode = StorageMode::kDevicePrivate;
429  }
430  color0_msaa_tex = allocator_->CreateTexture(color0_tex_desc);
431  if (!color0_msaa_tex) {
432  VALIDATION_LOG << "Could not create multisample color texture.";
433  return {};
434  }
435  }
436  color0_msaa_tex->SetLabel(label, "Color Texture (Multisample)");
437 
438  // Create color resolve texture.
439  std::shared_ptr<Texture> color0_resolve_tex;
440  if (existing_color_resolve_texture) {
441  color0_resolve_tex = existing_color_resolve_texture;
442  } else {
443  TextureDescriptor color0_resolve_tex_desc;
444  color0_resolve_tex_desc.storage_mode =
445  color_attachment_config.resolve_storage_mode;
446  color0_resolve_tex_desc.format = pixel_format;
447  color0_resolve_tex_desc.size = size;
448  color0_resolve_tex_desc.compression_type = CompressionType::kLossy;
449  color0_resolve_tex_desc.usage =
451  color0_resolve_tex_desc.mip_count = mip_count;
452  color0_resolve_tex = allocator_->CreateTexture(color0_resolve_tex_desc);
453  if (!color0_resolve_tex) {
454  VALIDATION_LOG << "Could not create color texture.";
455  return {};
456  }
457  }
458  color0_resolve_tex->SetLabel(label, "Color Texture");
459 
460  // Color attachment.
461 
462  ColorAttachment color0;
463  color0.clear_color = color_attachment_config.clear_color;
464  color0.load_action = color_attachment_config.load_action;
465  color0.store_action = color_attachment_config.store_action;
466  color0.texture = color0_msaa_tex;
467  color0.resolve_texture = color0_resolve_tex;
468 
469  if (context.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
470  // If implicit MSAA is supported, then the resolve texture is not needed
471  // because the multisample texture is automatically resolved. We instead
472  // provide a view of the multisample texture as the resolve texture (because
473  // the HAL does expect a resolve texture).
474  //
475  // In practice, this is used for GLES 2.0 EXT_multisampled_render_to_texture
476  // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multisampled_render_to_texture.txt
477  color0.resolve_texture = color0_msaa_tex;
478  }
479 
480  target.SetColorAttachment(color0, 0u);
481 
482  // Create MSAA stencil texture.
483 
484  if (stencil_attachment_config.has_value()) {
485  target.SetupDepthStencilAttachments(context, *allocator_, size, true, label,
486  stencil_attachment_config.value(),
487  existing_depth_stencil_texture);
488  } else {
489  target.SetDepthAttachment(std::nullopt);
490  target.SetStencilAttachment(std::nullopt);
491  }
492 
493  return target;
494 }
495 
497  const Context& context,
498  Allocator& allocator,
499  ISize size,
500  bool msaa,
501  std::string_view label,
502  RenderTarget::AttachmentConfig stencil_attachment_config,
503  const std::shared_ptr<Texture>& existing_depth_stencil_texture) {
504  std::shared_ptr<Texture> depth_stencil_texture;
505  if (existing_depth_stencil_texture) {
506  depth_stencil_texture = existing_depth_stencil_texture;
507  } else {
508  TextureDescriptor depth_stencil_texture_desc;
509  depth_stencil_texture_desc.storage_mode =
510  stencil_attachment_config.storage_mode;
511  if (msaa) {
512  depth_stencil_texture_desc.type = TextureType::kTexture2DMultisample;
513  depth_stencil_texture_desc.sample_count = SampleCount::kCount4;
514  }
515  depth_stencil_texture_desc.format =
516  context.GetCapabilities()->GetDefaultDepthStencilFormat();
517  depth_stencil_texture_desc.size = size;
518  depth_stencil_texture_desc.usage = TextureUsage::kRenderTarget;
519  depth_stencil_texture = allocator.CreateTexture(depth_stencil_texture_desc);
520  if (!depth_stencil_texture) {
521  return; // Error messages are handled by `Allocator::CreateTexture`.
522  }
523  }
524 
525  DepthAttachment depth0;
526  depth0.load_action = stencil_attachment_config.load_action;
527  depth0.store_action = stencil_attachment_config.store_action;
528  depth0.clear_depth = 0u;
529  depth0.texture = depth_stencil_texture;
530 
531  StencilAttachment stencil0;
532  stencil0.load_action = stencil_attachment_config.load_action;
533  stencil0.store_action = stencil_attachment_config.store_action;
534  stencil0.clear_stencil = 0u;
535  stencil0.texture = std::move(depth_stencil_texture);
536  stencil0.texture->SetLabel(label, "Depth+Stencil Texture");
537 
538  SetDepthAttachment(std::move(depth0));
539  SetStencilAttachment(std::move(stencil0));
540 }
541 
542 } // 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:69
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
std::string ToString() const
RenderTarget & SetDepthAttachment(std::optional< DepthAttachment > attachment)
size_t GetMaxColorAttachmentBindIndex() const
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:144
std::string ColorAttachmentToString(const ColorAttachment &color)
Definition: formats.cc:137
std::string StencilAttachmentToString(const StencilAttachment &stencil)
Definition: formats.cc:151
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:457
static ISize SizeForMipLevel(const std::shared_ptr< Texture > &texture, uint32_t mip_level)
SampleCount
Definition: formats.h:483
Definition: comparable.h:93
std::shared_ptr< ContextGLES > context
std::shared_ptr< Texture > resolve_texture
Definition: formats.h:910
bool IsValid() const
Definition: formats.cc:26
LoadAction load_action
Definition: formats.h:911
std::shared_ptr< Texture > texture
Definition: formats.h:909
StoreAction store_action
Definition: formats.h:912
Type height
Definition: size.h:29
Type width
Definition: size.h:28
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