| Flutter Impeller |

surface_mtl.mm
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 "impeller/renderer/backend/metal/surface_mtl.h"
6
7 #include "flutter/fml/trace_event.h"
8 #include "flutter/impeller/renderer/command_buffer.h"
9 #include "impeller/base/validation.h"
10 #include "impeller/core/texture_descriptor.h"
11 #include "impeller/renderer/backend/metal/context_mtl.h"
12 #include "impeller/renderer/backend/metal/formats_mtl.h"
13 #include "impeller/renderer/backend/metal/texture_mtl.h"
14 #include "impeller/renderer/render_target.h"
15
16 namespace impeller {
17
18 #pragma GCC diagnostic push
19 #pragma GCC diagnostic ignored "-Wunguarded-availability-new"
20
21 id<CAMetalDrawable> SurfaceMTL::GetMetalDrawableAndValidate(
22 const std::shared_ptr<Context>& context,
23 CAMetalLayer* layer) {
24 TRACE_EVENT0("impeller", "SurfaceMTL::WrapCurrentMetalLayerDrawable");
25
26 if (context == nullptr || !context->IsValid() || layer == nil) {
27 return nullptr;
28 }
29
30 id<CAMetalDrawable> current_drawable = nil;
31 {
32 TRACE_EVENT0("impeller", "WaitForNextDrawable");
33 current_drawable = [layer nextDrawable];
34 }
35
36 if (!current_drawable) {
37 VALIDATION_LOG << "Could not acquire current drawable.";
38 return nullptr;
39 }
40 return current_drawable;
41 }
42
43 static std::optional<RenderTarget> WrapTextureWithRenderTarget(
44 Allocator& allocator,
45 id<MTLTexture> texture,
46 bool requires_blit,
47 std::optional<IRect> clip_rect) {
48 // compositor_context.cc will offset the rendering by the clip origin. Here we
49 // shrink to the size of the clip. This has the same effect as clipping the
50 // rendering but also creates smaller intermediate passes.
51 ISize root_size;
52 if (requires_blit) {
53 if (!clip_rect.has_value()) {
54 VALIDATION_LOG << "Missing clip rectangle.";
55 return std::nullopt;
56 }
57 root_size = ISize(clip_rect->size.width, clip_rect->size.height);
58 } else {
59 root_size = {static_cast<ISize::Type>(texture.width),
60 static_cast<ISize::Type>(texture.height)};
61 }
62
63 TextureDescriptor resolve_tex_desc;
64 resolve_tex_desc.format = FromMTLPixelFormat(texture.pixelFormat);
65 resolve_tex_desc.size = root_size;
66 resolve_tex_desc.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget) |
67 static_cast<uint64_t>(TextureUsage::kShaderRead);
68 resolve_tex_desc.sample_count = SampleCount::kCount1;
69 resolve_tex_desc.storage_mode = StorageMode::kDevicePrivate;
70
71 if (resolve_tex_desc.format == PixelFormat::kUnknown) {
72 VALIDATION_LOG << "Unknown drawable color format.";
73 return std::nullopt;
74 }
75
76 // Create color resolve texture.
77 std::shared_ptr<Texture> resolve_tex;
78 if (requires_blit) {
79 resolve_tex_desc.compression_type = CompressionType::kLossy;
80 resolve_tex = allocator.CreateTexture(resolve_tex_desc);
81 } else {
82 resolve_tex = std::make_shared<TextureMTL>(resolve_tex_desc, texture);
83 }
84
85 if (!resolve_tex) {
86 VALIDATION_LOG << "Could not wrap resolve texture.";
87 return std::nullopt;
88 }
89 resolve_tex->SetLabel("ImpellerOnscreenResolve");
90
91 TextureDescriptor msaa_tex_desc;
92 msaa_tex_desc.storage_mode = StorageMode::kDeviceTransient;
93 msaa_tex_desc.type = TextureType::kTexture2DMultisample;
94 msaa_tex_desc.sample_count = SampleCount::kCount4;
95 msaa_tex_desc.format = resolve_tex->GetTextureDescriptor().format;
96 msaa_tex_desc.size = resolve_tex->GetSize();
97 msaa_tex_desc.usage = static_cast<uint64_t>(TextureUsage::kRenderTarget);
98
99 auto msaa_tex = allocator.CreateTexture(msaa_tex_desc);
100 if (!msaa_tex) {
101 VALIDATION_LOG << "Could not allocate MSAA color texture.";
102 return std::nullopt;
103 }
104 msaa_tex->SetLabel("ImpellerOnscreenColorMSAA");
105
106 ColorAttachment color0;
107 color0.texture = msaa_tex;
108 color0.clear_color = Color::DarkSlateGray();
109 color0.load_action = LoadAction::kClear;
110 color0.store_action = StoreAction::kMultisampleResolve;
111 color0.resolve_texture = std::move(resolve_tex);
112
113 auto render_target_desc = std::make_optional<RenderTarget>();
114 render_target_desc->SetColorAttachment(color0, 0u);
115
116 return render_target_desc;
117 }
118
119 std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromMetalLayerDrawable(
120 const std::shared_ptr<Context>& context,
121 id<CAMetalDrawable> drawable,
122 std::optional<IRect> clip_rect) {
123 return SurfaceMTL::MakeFromTexture(context, drawable.texture, clip_rect,
124 drawable);
125 }
126
127 std::unique_ptr<SurfaceMTL> SurfaceMTL::MakeFromTexture(
128 const std::shared_ptr<Context>& context,
129 id<MTLTexture> texture,
130 std::optional<IRect> clip_rect,
131 id<CAMetalDrawable> drawable) {
132 bool partial_repaint_blit_required = ShouldPerformPartialRepaint(clip_rect);
133
134 // The returned render target is the texture that Impeller will render the
135 // root pass to. If partial repaint is in use, this may be a new texture which
136 // is smaller than the given MTLTexture.
137 auto render_target =
138 WrapTextureWithRenderTarget(*context->GetResourceAllocator(), texture,
139 partial_repaint_blit_required, clip_rect);
140 if (!render_target) {
141 return nullptr;
142 }
143
144 // If partial repainting, set a "source" texture. The presence of a source
145 // texture and clip rect instructs the surface to blit this texture to the
146 // destination texture.
147 auto source_texture = partial_repaint_blit_required
148 ? render_target->GetRenderTargetTexture()
149 : nullptr;
150
151 // The final "destination" texture is the texture that will be presented. In
152 // this case, it's always the given drawable.
153 std::shared_ptr<Texture> destination_texture;
154 if (partial_repaint_blit_required) {
155 // If blitting for partial repaint, we need to wrap the drawable. Simply
156 // reuse the texture descriptor that was already formed for the new render
157 // target, but override the size with the drawable's size.
158 auto destination_descriptor =
159 render_target->GetRenderTargetTexture()->GetTextureDescriptor();
160 destination_descriptor.size = {static_cast<ISize::Type>(texture.width),
161 static_cast<ISize::Type>(texture.height)};
162 destination_texture = TextureMTL::Wrapper(destination_descriptor, texture);
163 } else {
164 // When not partial repaint blit is needed, the render target texture _is_
165 // the drawable texture.
166 destination_texture = render_target->GetRenderTargetTexture();
167 }
168
169 return std::unique_ptr<SurfaceMTL>(new SurfaceMTL(
170 context, // context
171 *render_target, // target
172 render_target->GetRenderTargetTexture(), // resolve_texture
173 drawable, // drawable
174 source_texture, // source_texture
175 destination_texture, // destination_texture
176 partial_repaint_blit_required, // requires_blit
177 clip_rect // clip_rect
178 ));
179 }
180
181 SurfaceMTL::SurfaceMTL(const std::weak_ptr<Context>& context,
182 const RenderTarget& target,
183 std::shared_ptr<Texture> resolve_texture,
184 id<CAMetalDrawable> drawable,
185 std::shared_ptr<Texture> source_texture,
186 std::shared_ptr<Texture> destination_texture,
187 bool requires_blit,
188 std::optional<IRect> clip_rect)
189 : Surface(target),
190 context_(context),
191 resolve_texture_(std::move(resolve_texture)),
192 drawable_(drawable),
193 source_texture_(std::move(source_texture)),
194 destination_texture_(std::move(destination_texture)),
195 requires_blit_(requires_blit),
196 clip_rect_(clip_rect) {}
197
198 // |Surface|
199 SurfaceMTL::~SurfaceMTL() = default;
200
201 bool SurfaceMTL::ShouldPerformPartialRepaint(std::optional<IRect> damage_rect) {
202 // compositor_context.cc will conditionally disable partial repaint if the
203 // damage region is large. If that happened, then a nullopt damage rect
204 // will be provided here.
205 if (!damage_rect.has_value()) {
206 return false;
207 }
208 // If the damage rect is 0 in at least one dimension, partial repaint isn't
209 // performed as we skip right to present.
210 if (damage_rect->size.width <= 0 || damage_rect->size.height <= 0) {
211 return false;
212 }
213 return true;
214 }
215
216 // |Surface|
217 IRect SurfaceMTL::coverage() const {
218 return IRect::MakeSize(resolve_texture_->GetSize());
219 }
220
221 // |Surface|
222 bool SurfaceMTL::Present() const {
223 auto context = context_.lock();
224 if (!context) {
225 return false;
226 }
227
228 if (requires_blit_) {
229 if (!(source_texture_ && destination_texture_)) {
230 return false;
231 }
232
233 auto blit_command_buffer = context->CreateCommandBuffer();
234 if (!blit_command_buffer) {
235 return false;
236 }
237 auto blit_pass = blit_command_buffer->CreateBlitPass();
238 if (!clip_rect_.has_value()) {
239 VALIDATION_LOG << "Missing clip rectangle.";
240 return false;
241 }
242 blit_pass->AddCopy(source_texture_, destination_texture_, std::nullopt,
243 clip_rect_->origin);
244 blit_pass->EncodeCommands(context->GetResourceAllocator());
245 if (!blit_command_buffer->SubmitCommands()) {
246 return false;
247 }
248 }
249
250 if (drawable_) {
251 id<MTLCommandBuffer> command_buffer =
252 ContextMTL::Cast(context.get())
253 ->CreateMTLCommandBuffer("Present Waiter Command Buffer");
254 // If the threads have been merged, or there is a pending frame capture,
255 // then block on cmd buffer scheduling to ensure that the
256 // transaction/capture work correctly.
257 if ([[NSThread currentThread] isMainThread] ||
258 [[MTLCaptureManager sharedCaptureManager] isCapturing]) {
259 TRACE_EVENT0("flutter", "waitUntilScheduled");
260 [command_buffer commit];
261 [command_buffer waitUntilScheduled];
262 [drawable_ present];
263 } else {
264 [command_buffer presentDrawable:drawable_];
265 [command_buffer commit];
266 }
267 }
268
269 return true;
270 }
271 #pragma GCC diagnostic pop
272
273 } // namespace impeller
impeller::StoreAction::kMultisampleResolve
@ kMultisampleResolve
impeller::Attachment::store_action
StoreAction store_action
Definition: formats.h:594
impeller::SurfaceMTL
Definition: surface_mtl.h:17
context_mtl.h
impeller::ColorAttachment
Definition: formats.h:599
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:42
impeller::SurfaceMTL::coverage
IRect coverage() const
Definition: surface_mtl.mm:217
texture_descriptor.h
impeller::SampleCount::kCount1
@ kCount1
formats_mtl.h
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::Allocator::CreateTexture
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc)
Definition: allocator.cc:49
impeller::TextureDescriptor::sample_count
SampleCount sample_count
Definition: texture_descriptor.h:47
validation.h
impeller::TextureDescriptor::usage
TextureUsageMask usage
Definition: texture_descriptor.h:45
impeller::SampleCount::kCount4
@ kCount4
impeller::TSize< int64_t >::Type
int64_t Type
Definition: size.h:19
impeller::Surface
Definition: surface.h:17
impeller::WrapTextureWithRenderTarget
static std::optional< RenderTarget > WrapTextureWithRenderTarget(Allocator &allocator, id< MTLTexture > texture, bool requires_blit, std::optional< IRect > clip_rect)
Definition: surface_mtl.mm:43
impeller::TextureDescriptor::type
TextureType type
Definition: texture_descriptor.h:41
impeller::TextureType::kTexture2DMultisample
@ kTexture2DMultisample
impeller::Color::DarkSlateGray
static constexpr Color DarkSlateGray()
Definition: color.h:408
impeller::TSize
Definition: size.h:18
impeller::LoadAction::kClear
@ kClear
impeller::StorageMode::kDeviceTransient
@ kDeviceTransient
impeller::ColorAttachment::clear_color
Color clear_color
Definition: formats.h:600
impeller::SurfaceMTL::~SurfaceMTL
~SurfaceMTL() override
impeller::Attachment::texture
std::shared_ptr< Texture > texture
Definition: formats.h:591
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::TextureUsage::kShaderRead
@ kShaderRead
impeller::SurfaceMTL::MakeFromMetalLayerDrawable
static std::unique_ptr< SurfaceMTL > MakeFromMetalLayerDrawable(const std::shared_ptr< Context > &context, id< CAMetalDrawable > drawable, std::optional< IRect > clip_rect=std::nullopt)
Definition: surface_mtl.mm:119
impeller::Allocator
An object that allocates device memory.
Definition: allocator.h:25
impeller::PixelFormat::kUnknown
@ kUnknown
impeller::RenderTarget
Definition: render_target.h:48
impeller::SurfaceMTL::MakeFromTexture
static std::unique_ptr< SurfaceMTL > MakeFromTexture(const std::shared_ptr< Context > &context, id< MTLTexture > texture, std::optional< IRect > clip_rect, id< CAMetalDrawable > drawable=nil)
Definition: surface_mtl.mm:127
impeller::CompressionType::kLossy
@ kLossy
impeller::SurfaceMTL::drawable
id< MTLDrawable > drawable() const
Definition: surface_mtl.h:56
impeller::TextureMTL::Wrapper
static std::shared_ptr< TextureMTL > Wrapper(TextureDescriptor desc, id< MTLTexture > texture, std::function< void()> deletion_proc=nullptr)
Definition: texture_mtl.mm:38
surface_mtl.h
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:136
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:43
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:60
command_buffer.h
impeller::Attachment::resolve_texture
std::shared_ptr< Texture > resolve_texture
Definition: formats.h:592
impeller::TRect< int64_t >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:52
std
Definition: comparable.h:98
impeller::SurfaceMTL::GetMetalDrawableAndValidate
static id< CAMetalDrawable > GetMetalDrawableAndValidate(const std::shared_ptr< Context > &context, CAMetalLayer *layer)
Wraps the current drawable of the given Metal layer to create a surface Impeller can render to....
Definition: surface_mtl.mm:21
texture_mtl.h
impeller::BackendCast< ContextMTL, Context >::Cast
static ContextMTL & Cast(Context &base)
Definition: backend_cast.h:14
impeller::Attachment::load_action
LoadAction load_action
Definition: formats.h:593
impeller::ContextMTL::CreateMTLCommandBuffer
id< MTLCommandBuffer > CreateMTLCommandBuffer(const std::string &label) const
Definition: context_mtl.mm:370
impeller::FromMTLPixelFormat
constexpr PixelFormat FromMTLPixelFormat(MTLPixelFormat format)
Definition: formats_mtl.h:21
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:40
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:39
render_target.h
impeller::TextureDescriptor::compression_type
CompressionType compression_type
Definition: texture_descriptor.h:48
impeller
Definition: aiks_context.cc:10
impeller::TRect< int64_t >
impeller::SurfaceMTL::Present
bool Present() const override
Definition: surface_mtl.mm:222