mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This moves the Metal `GrContext` creation utilities from `GPUSurfaceMetal` into a separate `IOSContext` object subclass. An analogue of this object was used in the GL regime for the management of onscreen and offscreen contexts that were not tied to the lifecycle of the `GPUSurface`. This pattern has now been generalized for use with all backends that need a resource context (`IOSContextGL` and `IOContextMetal`). The platform views controller management in the `ExternalViewEmbedder` interface implementation was repeated three times for [Metal][metal], [OpenGL](opengl) and [Software](software) rendering. This repetition has been removed and a single implementation present in the base `IOSSurface` and used on all platforms. Addition of new client rendering APIs should not affect how the engine renders into the platform view interleaving levels. All rendering API selection logic has been moved into a single set of utilities in `rendering_api_selection.h`. This enables the removal of a lot of code blocks guarded by `FLUTTER_SHELL_ENABLE_METAL`. The remaining uses of this will be removed when unified builds are enabled. The Metal backend now also adds traces similar to the GL backend. The `IOGLContext` has been renamed to `IOContextGL` to be more in line with the convention used in this library. Fixes https://github.com/flutter/flutter/issues/41827 Adds https://github.com/flutter/flutter/issues/52150 [metal]:1194ba2b21/shell/platform/darwin/ios/ios_surface_metal.mm (L55)[opengl]:1194ba2b21/shell/platform/darwin/ios/ios_surface_gl.mm (L95)[software]:1194ba2b21/shell/platform/darwin/ios/ios_surface_software.mm (L146)
158 lines
5.4 KiB
Plaintext
158 lines
5.4 KiB
Plaintext
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "flutter/shell/gpu/gpu_surface_metal.h"
|
|
|
|
#include <QuartzCore/CAMetalLayer.h>
|
|
|
|
#include "flutter/fml/trace_event.h"
|
|
#include "third_party/skia/include/core/SkSurface.h"
|
|
#include "third_party/skia/include/gpu/GrBackendSurface.h"
|
|
#include "third_party/skia/include/ports/SkCFObject.h"
|
|
|
|
static_assert(!__has_feature(objc_arc), "ARC must be disabled.");
|
|
|
|
namespace flutter {
|
|
|
|
GPUSurfaceMetal::GPUSurfaceMetal(GPUSurfaceDelegate* delegate,
|
|
fml::scoped_nsobject<CAMetalLayer> layer,
|
|
sk_sp<GrContext> context,
|
|
fml::scoped_nsprotocol<id<MTLCommandQueue>> command_queue)
|
|
: delegate_(delegate),
|
|
layer_(std::move(layer)),
|
|
context_(std::move(context)),
|
|
command_queue_(std::move(command_queue)) {
|
|
layer_.get().pixelFormat = MTLPixelFormatBGRA8Unorm;
|
|
// Flutter needs to read from the color attachment in cases where there are effects such as
|
|
// backdrop filters.
|
|
layer_.get().framebufferOnly = NO;
|
|
}
|
|
|
|
GPUSurfaceMetal::~GPUSurfaceMetal() {
|
|
ReleaseUnusedDrawableIfNecessary();
|
|
}
|
|
|
|
// |Surface|
|
|
bool GPUSurfaceMetal::IsValid() {
|
|
return layer_ && context_ && command_queue_;
|
|
}
|
|
|
|
// |Surface|
|
|
std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& size) {
|
|
if (!IsValid()) {
|
|
FML_LOG(ERROR) << "Metal surface was invalid.";
|
|
return nullptr;
|
|
}
|
|
|
|
if (size.isEmpty()) {
|
|
FML_LOG(ERROR) << "Metal surface was asked for an empty frame.";
|
|
return nullptr;
|
|
}
|
|
|
|
const auto bounds = layer_.get().bounds.size;
|
|
const auto scale = layer_.get().contentsScale;
|
|
if (bounds.width <= 0.0 || bounds.height <= 0.0 || scale <= 0.0) {
|
|
FML_LOG(ERROR) << "Metal layer bounds were invalid.";
|
|
return nullptr;
|
|
}
|
|
|
|
const auto scaled_bounds = CGSizeMake(bounds.width * scale, bounds.height * scale);
|
|
|
|
ReleaseUnusedDrawableIfNecessary();
|
|
|
|
if (!CGSizeEqualToSize(scaled_bounds, layer_.get().drawableSize)) {
|
|
layer_.get().drawableSize = scaled_bounds;
|
|
}
|
|
|
|
auto surface = SkSurface::MakeFromCAMetalLayer(context_.get(), // context
|
|
layer_.get(), // layer
|
|
kTopLeft_GrSurfaceOrigin, // origin
|
|
1, // sample count
|
|
kBGRA_8888_SkColorType, // color type
|
|
nullptr, // colorspace
|
|
nullptr, // surface properties
|
|
&next_drawable_ // drawable (transfer out)
|
|
);
|
|
|
|
if (!surface) {
|
|
FML_LOG(ERROR) << "Could not create the SkSurface from the metal texture.";
|
|
return nullptr;
|
|
}
|
|
|
|
auto submit_callback = [this](const SurfaceFrame& surface_frame, SkCanvas* canvas) -> bool {
|
|
TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit");
|
|
canvas->flush();
|
|
|
|
if (next_drawable_ == nullptr) {
|
|
FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface.";
|
|
return false;
|
|
}
|
|
|
|
const auto has_external_view_embedder = delegate_->GetExternalViewEmbedder() != nullptr;
|
|
|
|
auto command_buffer =
|
|
fml::scoped_nsprotocol<id<MTLCommandBuffer>>([[command_queue_.get() commandBuffer] retain]);
|
|
|
|
fml::scoped_nsprotocol<id<CAMetalDrawable>> drawable(
|
|
reinterpret_cast<id<CAMetalDrawable>>(next_drawable_));
|
|
next_drawable_ = nullptr;
|
|
|
|
// External views need to present with transaction. When presenting with
|
|
// transaction, we have to block, otherwise we risk presenting the drawable
|
|
// after the CATransaction has completed.
|
|
// See:
|
|
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction
|
|
// TODO(dnfield): only do this if transactions are actually being used.
|
|
// https://github.com/flutter/flutter/issues/24133
|
|
if (!has_external_view_embedder) {
|
|
[command_buffer.get() presentDrawable:drawable.get()];
|
|
[command_buffer.get() commit];
|
|
} else {
|
|
[command_buffer.get() commit];
|
|
[command_buffer.get() waitUntilScheduled];
|
|
[drawable.get() present];
|
|
}
|
|
return true;
|
|
};
|
|
|
|
return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
|
|
}
|
|
|
|
// |Surface|
|
|
SkMatrix GPUSurfaceMetal::GetRootTransformation() const {
|
|
// This backend does not currently support root surface transformations. Just
|
|
// return identity.
|
|
return {};
|
|
}
|
|
|
|
// |Surface|
|
|
GrContext* GPUSurfaceMetal::GetContext() {
|
|
return context_.get();
|
|
}
|
|
|
|
// |Surface|
|
|
flutter::ExternalViewEmbedder* GPUSurfaceMetal::GetExternalViewEmbedder() {
|
|
return delegate_->GetExternalViewEmbedder();
|
|
}
|
|
|
|
// |Surface|
|
|
bool GPUSurfaceMetal::MakeRenderContextCurrent() {
|
|
// This backend has no such concept.
|
|
return true;
|
|
}
|
|
|
|
void GPUSurfaceMetal::ReleaseUnusedDrawableIfNecessary() {
|
|
// If the previous surface frame was not submitted before a new one is acquired, the old drawable
|
|
// needs to be released. An RAII wrapper may not be used because this needs to interoperate with
|
|
// Skia APIs.
|
|
if (next_drawable_ == nullptr) {
|
|
return;
|
|
}
|
|
|
|
CFRelease(next_drawable_);
|
|
next_drawable_ = nullptr;
|
|
}
|
|
|
|
} // namespace flutter
|