Don’t depend on an implicit transaction when no external view embedder is present. (flutter/engine#17258)

All Metal layers have their presentsWithTransaction property set to true.
However, when an external view embedder is not present, there is no mechanism to
ensure that the command buffer commit is within transaction scope. This works in
most cases as there there is usually an implicit (possibly nested) transaction
in place during rendering. However, when there isn’t, rendering will look paused
at an incorrect size. This code now works similar to OpenGL but will be
refactored for ease of understanding and consistency between the various
backends.
This commit is contained in:
Chinmay Garde 2020-03-22 19:16:21 -07:00 committed by GitHub
parent 23b8e74799
commit 09be477a9f

View File

@ -39,32 +39,25 @@ bool GPUSurfaceMetal::IsValid() {
}
// |Surface|
std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& size) {
std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& frame_size) {
if (!IsValid()) {
FML_LOG(ERROR) << "Metal surface was invalid.";
return nullptr;
}
if (size.isEmpty()) {
if (frame_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 drawable_size = CGSizeMake(frame_size.width(), frame_size.height());
const auto scaled_bounds = CGSizeMake(bounds.width * scale, bounds.height * scale);
if (!CGSizeEqualToSize(drawable_size, layer_.get().drawableSize)) {
layer_.get().drawableSize = drawable_size;
}
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
@ -89,8 +82,6 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& size)
return false;
}
const auto has_external_view_embedder = delegate_->GetExternalViewEmbedder() != nullptr;
auto command_buffer =
fml::scoped_nsprotocol<id<MTLCommandBuffer>>([[command_queue_.get() commandBuffer] retain]);
@ -98,21 +89,10 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& size)
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];
}
[command_buffer.get() commit];
[command_buffer.get() waitUntilScheduled];
[drawable.get() present];
return true;
};