提交 ed30d77a 编写于 作者: C Chinmay Garde 提交者: Chinmay Garde

Setup a Metal test surface and add a new unit-test target that tests the testing utilities.

`//flutter/testing` now contains a lot of utilities used by other test targets.
This includes stuff like working with render targets that use either OpenGL or
Metal, fixtures for interacting with the Dart VM, test assertion predicates,
etc.. However, these utilities themselves are not tested as part of a standalone
test suite. Instead, only the test targets that include it exercise these
utilities. Since these are no longer trivial, a new test target has been added
that tests the testing utilities directly.
上级 591144df
......@@ -73,6 +73,7 @@ group("flutter") {
"$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper_unittests",
"$flutter_root/shell/platform/embedder:embedder_unittests",
"$flutter_root/shell/platform/glfw/client_wrapper:client_wrapper_glfw_unittests",
"$flutter_root/testing:testing_unittests",
"$flutter_root/third_party/txt:txt_unittests",
]
......
......@@ -2,6 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//flutter/shell/config.gni")
import("//flutter/testing/testing.gni")
source_set("testing_lib") {
testonly = true
......@@ -77,4 +80,55 @@ if (current_toolchain == host_toolchain) {
"//third_party/swiftshader_flutter:swiftshader",
]
}
# All targets on all platforms should be able to the Metal utilities. On
# platforms where Metal in not available, the tests must be skipped or
# implemented to use another available client rendering API. This is usually
# either OpenGL which is portably implemented via SwiftShader or the software
# backend. This way, all tests compile on all platforms but the Metal backend
# exercised on platforms where Metal itself is available.
source_set("metal") {
testonly = true
sources = [
"$flutter_root/testing/test_metal_surface.cc",
"$flutter_root/testing/test_metal_surface.h",
]
defines = []
if (shell_enable_metal) {
sources += [ "$flutter_root/testing/test_metal_surface_impl.mm" ]
defines += [ "TESTING_ENABLE_METAL" ]
}
deps = [
":skia",
"$flutter_root/fml",
]
}
}
test_fixtures("testing_fixtures") {
fixtures = []
}
# The //flutter/testing library provides utility methods to other test targets.
# This test target tests the testing utilities.
executable("testing_unittests") {
testonly = true
sources = [
"$flutter_root/testing/test_metal_surface_unittests.cc",
]
deps = [
":dart",
":metal",
":opengl",
":skia",
":testing",
":testing_fixtures",
":testing_lib",
]
}
......@@ -108,6 +108,8 @@ def RunCCTests(build_dir, filter):
RunEngineExecutable(build_dir, 'ui_unittests', filter, shuffle_flags)
RunEngineExecutable(build_dir, 'testing_unittests', filter, shuffle_flags)
# These unit-tests are Objective-C and can only run on Darwin.
if IsMac():
RunEngineExecutable(build_dir, 'flutter_channels_unittests', filter, shuffle_flags)
......
// 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/testing/test_metal_surface.h"
#if TESTING_ENABLE_METAL
#include "flutter/testing/test_metal_surface_impl.h"
#endif // TESTING_ENABLE_METAL
namespace flutter {
bool TestMetalSurface::PlatformSupportsMetal() {
#if TESTING_ENABLE_METAL
return true;
#else // TESTING_ENABLE_METAL
return false;
#endif // TESTING_ENABLE_METAL
}
std::unique_ptr<TestMetalSurface> TestMetalSurface::Create(
SkISize surface_size) {
#if TESTING_ENABLE_METAL
return std::make_unique<TestMetalSurfaceImpl>(surface_size);
#else // TESTING_ENABLE_METAL
return nullptr;
#endif // TESTING_ENABLE_METAL
}
TestMetalSurface::TestMetalSurface() = default;
TestMetalSurface::~TestMetalSurface() = default;
bool TestMetalSurface::IsValid() const {
return impl_ ? impl_->IsValid() : false;
}
sk_sp<GrContext> TestMetalSurface::GetGrContext() const {
return impl_ ? impl_->GetGrContext() : nullptr;
}
sk_sp<SkSurface> TestMetalSurface::GetSurface() const {
return impl_ ? impl_->GetSurface() : nullptr;
}
} // namespace flutter
// 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.
#ifndef FLUTTER_TESTING_TEST_METAL_SURFACE_H_
#define FLUTTER_TESTING_TEST_METAL_SURFACE_H_
#include "flutter/fml/macros.h"
#include "third_party/skia/include/core/SkSize.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
namespace flutter {
//------------------------------------------------------------------------------
/// @brief Creates a MTLTexture backed SkSurface and context that can be
/// used to render to in unit-tests.
///
class TestMetalSurface {
public:
static bool PlatformSupportsMetal();
static std::unique_ptr<TestMetalSurface> Create(
SkISize surface_size = SkISize::MakeEmpty());
virtual ~TestMetalSurface();
virtual bool IsValid() const;
virtual sk_sp<GrContext> GetGrContext() const;
virtual sk_sp<SkSurface> GetSurface() const;
protected:
TestMetalSurface();
private:
std::unique_ptr<TestMetalSurface> impl_;
TestMetalSurface(std::unique_ptr<TestMetalSurface> impl);
FML_DISALLOW_COPY_AND_ASSIGN(TestMetalSurface);
};
} // namespace flutter
#endif // FLUTTER_TESTING_TEST_METAL_SURFACE_H_
// 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.
#ifndef FLUTTER_TESTING_TEST_METAL_SURFACE_IMPL_H_
#define FLUTTER_TESTING_TEST_METAL_SURFACE_IMPL_H_
#include "flutter/fml/macros.h"
#include "flutter/testing/test_metal_surface.h"
namespace flutter {
class TestMetalSurfaceImpl : public TestMetalSurface {
public:
TestMetalSurfaceImpl(SkISize surface_size);
// |TestMetalSurface|
~TestMetalSurfaceImpl() override;
private:
bool is_valid_ = false;
sk_sp<GrContext> context_;
sk_sp<SkSurface> surface_;
// |TestMetalSurface|
bool IsValid() const override;
// |TestMetalSurface|
sk_sp<GrContext> GetGrContext() const override;
// |TestMetalSurface|
sk_sp<SkSurface> GetSurface() const override;
FML_DISALLOW_COPY_AND_ASSIGN(TestMetalSurfaceImpl);
};
} // namespace flutter
#endif // FLUTTER_TESTING_TEST_METAL_SURFACE_IMPL_H_
// 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/testing/test_metal_surface_impl.h"
#include <Metal/Metal.h>
#include "flutter/fml/logging.h"
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace flutter {
TestMetalSurfaceImpl::TestMetalSurfaceImpl(SkISize surface_size) {
if (surface_size.isEmpty()) {
FML_LOG(ERROR) << "Size of test Metal surface was empty.";
return;
}
auto device = fml::scoped_nsobject{[MTLCreateSystemDefaultDevice() retain]};
if (!device) {
FML_LOG(ERROR) << "Could not acquire Metal device.";
return;
}
auto command_queue = fml::scoped_nsobject{[device.get() newCommandQueue]};
if (!command_queue) {
FML_LOG(ERROR) << "Could not create the default command queue.";
return;
}
auto texture_descriptor = fml::scoped_nsobject{[[MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:surface_size.width()
height:surface_size.height()
mipmapped:NO] retain]};
// The most pessimistic option and disables all optimizations but allows tests
// the most flexible access to the surface. They may read and wrote to the
// surface from shaders or use as a pixel view.
texture_descriptor.get().usage = MTLTextureUsageUnknown;
if (!texture_descriptor) {
FML_LOG(ERROR) << "Invalid texture descriptor.";
return;
}
auto texture = fml::scoped_nsobject{
[device.get() newTextureWithDescriptor:texture_descriptor.get()]};
if (!texture) {
FML_LOG(ERROR) << "Could not create texture from texture descriptor.";
return;
}
auto skia_context = GrContext::MakeMetal(device.get(), command_queue.get());
if (skia_context) {
// Skia wants ownership of the device and queue. If a context was created,
// we now no longer own the argument. Release the arguments only on
// successful creation of the context.
FML_ALLOW_UNUSED_LOCAL(device.release());
FML_ALLOW_UNUSED_LOCAL(command_queue.release());
} else {
FML_LOG(ERROR) << "Could not create the GrContext from the Metal Device "
"and command queue.";
return;
}
GrMtlTextureInfo skia_texture_info;
skia_texture_info.fTexture = sk_cf_obj<const void*>{[texture.get() retain]};
auto backend_render_target = GrBackendRenderTarget{
surface_size.width(), // width
surface_size.height(), // height
1, // sample count
skia_texture_info // texture info
};
auto surface = SkSurface::MakeFromBackendRenderTarget(
skia_context.get(), // context
backend_render_target, // backend render target
kTopLeft_GrSurfaceOrigin, // surface origin
kBGRA_8888_SkColorType, // color type
nullptr, // color space
nullptr, // surface properties
nullptr, // release proc (texture is already ref counted in sk_cf_obj)
nullptr // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not create Skia surface from a Metal texture.";
return;
}
surface_ = std::move(surface);
context_ = std::move(skia_context);
is_valid_ = true;
}
// |TestMetalSurface|
TestMetalSurfaceImpl::~TestMetalSurfaceImpl() = default;
// |TestMetalSurface|
bool TestMetalSurfaceImpl::IsValid() const {
return is_valid_;
}
// |TestMetalSurface|
sk_sp<GrContext> TestMetalSurfaceImpl::GetGrContext() const {
return IsValid() ? context_ : nullptr;
}
// |TestMetalSurface|
sk_sp<SkSurface> TestMetalSurfaceImpl::GetSurface() const {
return IsValid() ? surface_ : nullptr;
}
} // namespace flutter
// 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/testing/test_metal_surface.h"
#include "flutter/testing/testing.h"
namespace flutter {
namespace testing {
TEST(TestMetalSurface, EmptySurfaceIsInvalid) {
if (!TestMetalSurface::PlatformSupportsMetal()) {
GTEST_SKIP();
}
auto surface = TestMetalSurface::Create();
ASSERT_NE(surface, nullptr);
ASSERT_FALSE(surface->IsValid());
}
TEST(TestMetalSurface, CanCreateValidTestMetalSurface) {
if (!TestMetalSurface::PlatformSupportsMetal()) {
GTEST_SKIP();
}
auto surface = TestMetalSurface::Create(SkISize::Make(100, 100));
ASSERT_NE(surface, nullptr);
ASSERT_TRUE(surface->IsValid());
ASSERT_NE(surface->GetSurface(), nullptr);
ASSERT_NE(surface->GetGrContext(), nullptr);
}
} // namespace testing
} // namespace flutter
......@@ -44,7 +44,7 @@ def get_out_dir(args):
if args.enable_vulkan:
target_dir.append('vulkan')
if args.enable_metal:
if args.enable_metal and args.target_os == 'ios':
target_dir.append('metal')
return os.path.join(args.out_dir, 'out', '_'.join(target_dir))
......@@ -75,9 +75,6 @@ def to_gn_args(args):
if args.target_os != 'android' and args.enable_vulkan:
raise Exception('--enable-vulkan is only supported on Android')
if args.target_os != 'ios' and args.enable_metal:
raise Exception('--enable-metal is only supported on iOS')
runtime_mode = args.runtime_mode
gn_args = {}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册