Vulkan support in the Embedder API (flutter/engine#29391)
This commit is contained in:
@@ -1365,6 +1365,8 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_surface_metal.h
|
||||
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_metal.mm
|
||||
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.cc
|
||||
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.h
|
||||
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_vulkan.cc
|
||||
FILE: ../../../flutter/shell/platform/embedder/embedder_surface_vulkan.h
|
||||
FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.cc
|
||||
FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.h
|
||||
FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.cc
|
||||
@@ -1387,6 +1389,8 @@ FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_com
|
||||
FILE: ../../../flutter/shell/platform/embedder/fixtures/snapshot_large_scene.png
|
||||
FILE: ../../../flutter/shell/platform/embedder/fixtures/verifyb143464703.png
|
||||
FILE: ../../../flutter/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png
|
||||
FILE: ../../../flutter/shell/platform/embedder/fixtures/vk_dpr_noxform.png
|
||||
FILE: ../../../flutter/shell/platform/embedder/fixtures/vk_gradient.png
|
||||
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc
|
||||
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.h
|
||||
FILE: ../../../flutter/shell/platform/embedder/test_utils/key_codes.h
|
||||
|
||||
@@ -43,8 +43,9 @@ source_set("gpu_surface_vulkan") {
|
||||
"gpu_surface_vulkan_delegate.cc",
|
||||
"gpu_surface_vulkan_delegate.h",
|
||||
]
|
||||
|
||||
deps = gpu_common_deps + [ "//flutter/vulkan" ]
|
||||
deps = [ "//flutter/shell/platform/embedder:embedder_headers" ]
|
||||
deps += gpu_common_deps
|
||||
public_deps = [ "//flutter/vulkan" ]
|
||||
}
|
||||
|
||||
source_set("gpu_surface_metal") {
|
||||
|
||||
@@ -75,14 +75,14 @@ class GPUSurfaceMetalDelegate {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Returns the handle to the MTLTexture to render to. This is only
|
||||
/// called when the specefied render target type is `kMTLTexture`.
|
||||
/// called when the specified render target type is `kMTLTexture`.
|
||||
///
|
||||
virtual GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Presents the texture with `texture_id` to the "screen".
|
||||
/// `texture_id` corresponds to a texture that has been obtained by an earlier
|
||||
/// call to `GetMTLTexture`. This is only called when the specefied render
|
||||
/// call to `GetMTLTexture`. This is only called when the specified render
|
||||
/// target type is `kMTLTexture`.
|
||||
///
|
||||
/// @see |GPUSurfaceMetalDelegate::GetMTLTexture|
|
||||
|
||||
@@ -5,68 +5,77 @@
|
||||
#include "flutter/shell/gpu/gpu_surface_vulkan.h"
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "fml/trace_event.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "third_party/swiftshader/include/vulkan/vulkan_core.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
GPUSurfaceVulkan::GPUSurfaceVulkan(
|
||||
GPUSurfaceVulkanDelegate* delegate,
|
||||
std::unique_ptr<vulkan::VulkanNativeSurface> native_surface,
|
||||
bool render_to_surface)
|
||||
: GPUSurfaceVulkan(/*context=*/nullptr,
|
||||
delegate,
|
||||
std::move(native_surface),
|
||||
render_to_surface) {}
|
||||
|
||||
GPUSurfaceVulkan::GPUSurfaceVulkan(
|
||||
const sk_sp<GrDirectContext>& context,
|
||||
GPUSurfaceVulkanDelegate* delegate,
|
||||
std::unique_ptr<vulkan::VulkanNativeSurface> native_surface,
|
||||
bool render_to_surface)
|
||||
: window_(context,
|
||||
delegate->vk(),
|
||||
std::move(native_surface),
|
||||
render_to_surface),
|
||||
GPUSurfaceVulkan::GPUSurfaceVulkan(GPUSurfaceVulkanDelegate* delegate,
|
||||
const sk_sp<GrDirectContext>& skia_context,
|
||||
bool render_to_surface)
|
||||
: delegate_(delegate),
|
||||
skia_context_(skia_context),
|
||||
render_to_surface_(render_to_surface),
|
||||
weak_factory_(this) {}
|
||||
|
||||
GPUSurfaceVulkan::~GPUSurfaceVulkan() = default;
|
||||
|
||||
bool GPUSurfaceVulkan::IsValid() {
|
||||
return window_.IsValid();
|
||||
return skia_context_ != nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<SurfaceFrame> GPUSurfaceVulkan::AcquireFrame(
|
||||
const SkISize& size) {
|
||||
SurfaceFrame::FramebufferInfo framebuffer_info;
|
||||
framebuffer_info.supports_readback = true;
|
||||
const SkISize& frame_size) {
|
||||
if (!IsValid()) {
|
||||
FML_LOG(ERROR) << "Vulkan surface was invalid.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (frame_size.isEmpty()) {
|
||||
FML_LOG(ERROR) << "Vulkan surface was asked for an empty frame.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO(38466): Refactor GPU surface APIs take into account the fact that an
|
||||
// external view embedder may want to render to the root surface.
|
||||
if (!render_to_surface_) {
|
||||
return std::make_unique<SurfaceFrame>(
|
||||
nullptr, std::move(framebuffer_info),
|
||||
nullptr, SurfaceFrame::FramebufferInfo(),
|
||||
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
auto surface = window_.AcquireSurface();
|
||||
|
||||
if (surface == nullptr) {
|
||||
FlutterVulkanImage image = delegate_->AcquireImage(frame_size);
|
||||
if (!image.image) {
|
||||
FML_LOG(ERROR) << "Invalid VkImage given by the embedder.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SurfaceFrame::SubmitCallback callback =
|
||||
[weak_this = weak_factory_.GetWeakPtr()](const SurfaceFrame&,
|
||||
SkCanvas* canvas) -> bool {
|
||||
// Frames are only ever acquired on the raster thread. This is also the
|
||||
// thread on which the weak pointer factory is collected (as this instance
|
||||
// is owned by the rasterizer). So this use of weak pointers is safe.
|
||||
if (canvas == nullptr || !weak_this) {
|
||||
sk_sp<SkSurface> surface = CreateSurfaceFromVulkanImage(
|
||||
reinterpret_cast<VkImage>(image.image),
|
||||
static_cast<VkFormat>(image.format), frame_size);
|
||||
if (!surface) {
|
||||
FML_LOG(ERROR) << "Could not create the SkSurface from the Vulkan image.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SurfaceFrame::SubmitCallback callback = [image = image, delegate = delegate_](
|
||||
const SurfaceFrame&,
|
||||
SkCanvas* canvas) -> bool {
|
||||
TRACE_EVENT0("flutter", "GPUSurfaceVulkan::PresentImage");
|
||||
if (canvas == nullptr) {
|
||||
FML_DLOG(ERROR) << "Canvas not available.";
|
||||
return false;
|
||||
}
|
||||
return weak_this->window_.SwapBuffers();
|
||||
|
||||
canvas->flush();
|
||||
|
||||
return delegate->PresentImage(reinterpret_cast<VkImage>(image.image),
|
||||
static_cast<VkFormat>(image.format));
|
||||
};
|
||||
|
||||
SurfaceFrame::FramebufferInfo framebuffer_info{.supports_readback = true};
|
||||
|
||||
return std::make_unique<SurfaceFrame>(
|
||||
std::move(surface), std::move(framebuffer_info), std::move(callback));
|
||||
}
|
||||
@@ -80,7 +89,54 @@ SkMatrix GPUSurfaceVulkan::GetRootTransformation() const {
|
||||
}
|
||||
|
||||
GrDirectContext* GPUSurfaceVulkan::GetContext() {
|
||||
return window_.GetSkiaGrContext();
|
||||
return skia_context_.get();
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> GPUSurfaceVulkan::CreateSurfaceFromVulkanImage(
|
||||
const VkImage image,
|
||||
const VkFormat format,
|
||||
const SkISize& size) {
|
||||
GrVkImageInfo image_info = {
|
||||
.fImage = image,
|
||||
.fImageTiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.fFormat = format,
|
||||
.fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.fSampleCount = 1,
|
||||
.fLevelCount = 1,
|
||||
};
|
||||
GrBackendTexture backend_texture(size.width(), //
|
||||
size.height(), //
|
||||
image_info //
|
||||
);
|
||||
|
||||
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
|
||||
|
||||
return SkSurface::MakeFromBackendTexture(
|
||||
skia_context_.get(), // context
|
||||
backend_texture, // back-end texture
|
||||
kTopLeft_GrSurfaceOrigin, // surface origin
|
||||
1, // sample count
|
||||
ColorTypeFromFormat(format), // color type
|
||||
SkColorSpace::MakeSRGB(), // color space
|
||||
&surface_properties // surface properties
|
||||
);
|
||||
}
|
||||
|
||||
SkColorType GPUSurfaceVulkan::ColorTypeFromFormat(const VkFormat format) {
|
||||
switch (format) {
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
return SkColorType::kRGBA_8888_SkColorType;
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
return SkColorType::kBGRA_8888_SkColorType;
|
||||
default:
|
||||
return SkColorType::kUnknown_SkColorType;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@@ -11,29 +11,25 @@
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/fml/memory/weak_ptr.h"
|
||||
#include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h"
|
||||
#include "flutter/vulkan/vulkan_backbuffer.h"
|
||||
#include "flutter/vulkan/vulkan_native_surface.h"
|
||||
#include "flutter/vulkan/vulkan_window.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief A GPU surface backed by VkImages provided by a
|
||||
/// GPUSurfaceVulkanDelegate.
|
||||
///
|
||||
class GPUSurfaceVulkan : public Surface {
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Create a GPUSurfaceVulkan which implicitly creates its own
|
||||
/// GrDirectContext for Skia.
|
||||
///
|
||||
GPUSurfaceVulkan(GPUSurfaceVulkanDelegate* delegate,
|
||||
std::unique_ptr<vulkan::VulkanNativeSurface> native_surface,
|
||||
bool render_to_surface);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Create a GPUSurfaceVulkan while letting it reuse an existing
|
||||
/// GrDirectContext.
|
||||
///
|
||||
GPUSurfaceVulkan(const sk_sp<GrDirectContext>& context,
|
||||
GPUSurfaceVulkanDelegate* delegate,
|
||||
std::unique_ptr<vulkan::VulkanNativeSurface> native_surface,
|
||||
GPUSurfaceVulkan(GPUSurfaceVulkanDelegate* delegate,
|
||||
const sk_sp<GrDirectContext>& context,
|
||||
bool render_to_surface);
|
||||
|
||||
~GPUSurfaceVulkan() override;
|
||||
@@ -50,11 +46,19 @@ class GPUSurfaceVulkan : public Surface {
|
||||
// |Surface|
|
||||
GrDirectContext* GetContext() override;
|
||||
|
||||
static SkColorType ColorTypeFromFormat(const VkFormat format);
|
||||
|
||||
private:
|
||||
vulkan::VulkanWindow window_;
|
||||
const bool render_to_surface_;
|
||||
GPUSurfaceVulkanDelegate* delegate_;
|
||||
sk_sp<GrDirectContext> skia_context_;
|
||||
bool render_to_surface_;
|
||||
|
||||
fml::WeakPtrFactory<GPUSurfaceVulkan> weak_factory_;
|
||||
|
||||
sk_sp<SkSurface> CreateSurfaceFromVulkanImage(const VkImage image,
|
||||
const VkFormat format,
|
||||
const SkISize& size);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceVulkan);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,16 +6,43 @@
|
||||
#define FLUTTER_SHELL_GPU_GPU_SURFACE_VULKAN_DELEGATE_H_
|
||||
|
||||
#include "flutter/fml/memory/ref_ptr.h"
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
#include "flutter/vulkan/vulkan_device.h"
|
||||
#include "flutter/vulkan/vulkan_image.h"
|
||||
#include "flutter/vulkan/vulkan_proc_table.h"
|
||||
#include "third_party/skia/include/core/SkSize.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Interface implemented by all platform surfaces that can present
|
||||
/// a Vulkan backing store to the "screen". The GPU surface
|
||||
/// abstraction (which abstracts the client rendering API) uses this
|
||||
/// delegation pattern to tell the platform surface (which abstracts
|
||||
/// how backing stores fulfilled by the selected client rendering
|
||||
/// API end up on the "screen" on a particular platform) when the
|
||||
/// rasterizer needs to allocate and present the Vulkan backing
|
||||
/// store.
|
||||
///
|
||||
/// @see |EmbedderSurfaceVulkan|.
|
||||
///
|
||||
class GPUSurfaceVulkanDelegate {
|
||||
public:
|
||||
~GPUSurfaceVulkanDelegate();
|
||||
virtual ~GPUSurfaceVulkanDelegate();
|
||||
|
||||
// Obtain a reference to the Vulkan implementation's proc table.
|
||||
virtual fml::RefPtr<vulkan::VulkanProcTable> vk() = 0;
|
||||
/// @brief Obtain a reference to the Vulkan implementation's proc table.
|
||||
///
|
||||
virtual const vulkan::VulkanProcTable& vk() = 0;
|
||||
|
||||
/// @brief Called by the engine to fetch a VkImage for writing the next
|
||||
/// frame.
|
||||
///
|
||||
virtual FlutterVulkanImage AcquireImage(const SkISize& size) = 0;
|
||||
|
||||
/// @brief Called by the engine once a frame has been rendered to the image
|
||||
/// and it's ready to be bound for further reading/writing.
|
||||
///
|
||||
virtual bool PresentImage(VkImage image, VkFormat format) = 0;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@@ -109,6 +109,13 @@ template("embedder_source_set") {
|
||||
deps += [ "//flutter/shell/platform/darwin/graphics" ]
|
||||
}
|
||||
|
||||
if (embedder_enable_vulkan) {
|
||||
sources += [
|
||||
"embedder_surface_vulkan.cc",
|
||||
"embedder_surface_vulkan.h",
|
||||
]
|
||||
}
|
||||
|
||||
public_deps = [ ":embedder_headers" ]
|
||||
|
||||
public_configs += [
|
||||
@@ -168,6 +175,8 @@ test_fixtures("fixtures") {
|
||||
"fixtures/dpr_noxform.png",
|
||||
"fixtures/dpr_xform.png",
|
||||
"fixtures/gradient.png",
|
||||
"fixtures/vk_dpr_noxform.png",
|
||||
"fixtures/vk_gradient.png",
|
||||
"fixtures/gradient_metal.png",
|
||||
"fixtures/external_texture_metal.png",
|
||||
"fixtures/gradient_xform.png",
|
||||
@@ -250,6 +259,13 @@ if (enable_unittests) {
|
||||
}
|
||||
|
||||
if (test_enable_vulkan) {
|
||||
sources += [
|
||||
"tests/embedder_test_compositor_vulkan.cc",
|
||||
"tests/embedder_test_compositor_vulkan.h",
|
||||
"tests/embedder_test_context_vulkan.cc",
|
||||
"tests/embedder_test_context_vulkan.h",
|
||||
]
|
||||
|
||||
deps += [
|
||||
"//flutter/testing:vulkan",
|
||||
"//flutter/vulkan",
|
||||
|
||||
@@ -164,6 +164,26 @@ static bool IsMetalRendererConfigValid(const FlutterRendererConfig* config) {
|
||||
return device && command_queue && present && get_texture;
|
||||
}
|
||||
|
||||
static bool IsVulkanRendererConfigValid(const FlutterRendererConfig* config) {
|
||||
if (config->type != kVulkan) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const FlutterVulkanRendererConfig* vulkan_config = &config->vulkan;
|
||||
|
||||
if (!SAFE_EXISTS(vulkan_config, instance) ||
|
||||
!SAFE_EXISTS(vulkan_config, physical_device) ||
|
||||
!SAFE_EXISTS(vulkan_config, device) ||
|
||||
!SAFE_EXISTS(vulkan_config, queue) ||
|
||||
!SAFE_EXISTS(vulkan_config, get_instance_proc_address_callback) ||
|
||||
!SAFE_EXISTS(vulkan_config, get_next_image_callback) ||
|
||||
!SAFE_EXISTS(vulkan_config, present_image_callback)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsRendererValid(const FlutterRendererConfig* config) {
|
||||
if (config == nullptr) {
|
||||
return false;
|
||||
@@ -176,6 +196,8 @@ static bool IsRendererValid(const FlutterRendererConfig* config) {
|
||||
return IsSoftwareRendererConfigValid(config);
|
||||
case kMetal:
|
||||
return IsMetalRendererConfigValid(config);
|
||||
case kVulkan:
|
||||
return IsVulkanRendererConfigValid(config);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -388,6 +410,89 @@ InferMetalPlatformViewCreationCallback(
|
||||
#endif
|
||||
}
|
||||
|
||||
static flutter::Shell::CreateCallback<flutter::PlatformView>
|
||||
InferVulkanPlatformViewCreationCallback(
|
||||
const FlutterRendererConfig* config,
|
||||
void* user_data,
|
||||
flutter::PlatformViewEmbedder::PlatformDispatchTable
|
||||
platform_dispatch_table,
|
||||
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
|
||||
external_view_embedder) {
|
||||
if (config->type != kVulkan) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
std::function<void*(VkInstance, const char*)>
|
||||
vulkan_get_instance_proc_address =
|
||||
[ptr = config->vulkan.get_instance_proc_address_callback, user_data](
|
||||
VkInstance instance, const char* proc_name) -> void* {
|
||||
return ptr(user_data, instance, proc_name);
|
||||
};
|
||||
|
||||
auto vulkan_get_next_image =
|
||||
[ptr = config->vulkan.get_next_image_callback,
|
||||
user_data](const SkISize& frame_size) -> FlutterVulkanImage {
|
||||
FlutterFrameInfo frame_info = {
|
||||
.struct_size = sizeof(FlutterFrameInfo),
|
||||
.size = {static_cast<uint32_t>(frame_size.width()),
|
||||
static_cast<uint32_t>(frame_size.height())},
|
||||
};
|
||||
|
||||
return ptr(user_data, &frame_info);
|
||||
};
|
||||
|
||||
auto vulkan_present_image_callback =
|
||||
[ptr = config->vulkan.present_image_callback, user_data](
|
||||
VkImage image, VkFormat format) -> bool {
|
||||
FlutterVulkanImage image_desc = {
|
||||
.struct_size = sizeof(FlutterVulkanImage),
|
||||
.image = reinterpret_cast<uint64_t>(image),
|
||||
.format = static_cast<uint32_t>(format),
|
||||
};
|
||||
return ptr(user_data, &image_desc);
|
||||
};
|
||||
|
||||
flutter::EmbedderSurfaceVulkan::VulkanDispatchTable vulkan_dispatch_table = {
|
||||
.get_instance_proc_address = vulkan_get_instance_proc_address,
|
||||
.get_next_image = vulkan_get_next_image,
|
||||
.present_image = vulkan_present_image_callback,
|
||||
};
|
||||
|
||||
std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder =
|
||||
std::move(external_view_embedder);
|
||||
|
||||
std::unique_ptr<flutter::EmbedderSurfaceVulkan> embedder_surface =
|
||||
std::make_unique<flutter::EmbedderSurfaceVulkan>(
|
||||
config->vulkan.version,
|
||||
static_cast<VkInstance>(config->vulkan.instance),
|
||||
config->vulkan.enabled_instance_extension_count,
|
||||
config->vulkan.enabled_instance_extensions,
|
||||
config->vulkan.enabled_device_extension_count,
|
||||
config->vulkan.enabled_device_extensions,
|
||||
static_cast<VkPhysicalDevice>(config->vulkan.physical_device),
|
||||
static_cast<VkDevice>(config->vulkan.device),
|
||||
config->vulkan.queue_family_index,
|
||||
static_cast<VkQueue>(config->vulkan.queue), vulkan_dispatch_table,
|
||||
view_embedder);
|
||||
|
||||
return fml::MakeCopyable(
|
||||
[embedder_surface = std::move(embedder_surface), platform_dispatch_table,
|
||||
external_view_embedder =
|
||||
std::move(view_embedder)](flutter::Shell& shell) mutable {
|
||||
return std::make_unique<flutter::PlatformViewEmbedder>(
|
||||
shell, // delegate
|
||||
shell.GetTaskRunners(), // task runners
|
||||
std::move(embedder_surface), // embedder surface
|
||||
platform_dispatch_table, // platform dispatch table
|
||||
std::move(external_view_embedder) // external view embedder
|
||||
);
|
||||
});
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static flutter::Shell::CreateCallback<flutter::PlatformView>
|
||||
InferSoftwarePlatformViewCreationCallback(
|
||||
const FlutterRendererConfig* config,
|
||||
@@ -450,6 +555,10 @@ InferPlatformViewCreationCallback(
|
||||
return InferMetalPlatformViewCreationCallback(
|
||||
config, user_data, platform_dispatch_table,
|
||||
std::move(external_view_embedder));
|
||||
case kVulkan:
|
||||
return InferVulkanPlatformViewCreationCallback(
|
||||
config, user_data, platform_dispatch_table,
|
||||
std::move(external_view_embedder));
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
@@ -628,6 +737,59 @@ static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
|
||||
#endif
|
||||
}
|
||||
|
||||
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
|
||||
GrDirectContext* context,
|
||||
const FlutterBackingStoreConfig& config,
|
||||
const FlutterVulkanBackingStore* vulkan) {
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
if (!vulkan->image) {
|
||||
FML_LOG(ERROR) << "Embedder supplied null Vulkan image.";
|
||||
return nullptr;
|
||||
}
|
||||
GrVkImageInfo image_info = {
|
||||
.fImage = reinterpret_cast<VkImage>(vulkan->image->image),
|
||||
.fImageTiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.fFormat = static_cast<VkFormat>(vulkan->image->format),
|
||||
.fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.fSampleCount = 1,
|
||||
.fLevelCount = 1,
|
||||
};
|
||||
GrBackendTexture backend_texture(config.size.width, //
|
||||
config.size.height, //
|
||||
image_info //
|
||||
);
|
||||
|
||||
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
|
||||
|
||||
auto surface = SkSurface::MakeFromBackendTexture(
|
||||
context, // context
|
||||
backend_texture, // back-end texture
|
||||
kTopLeft_GrSurfaceOrigin, // surface origin
|
||||
1, // sample count
|
||||
flutter::GPUSurfaceVulkan::ColorTypeFromFormat(
|
||||
static_cast<VkFormat>(vulkan->image->format)), // color type
|
||||
SkColorSpace::MakeSRGB(), // color space
|
||||
&surface_properties, // surface properties
|
||||
static_cast<SkSurface::TextureReleaseProc>(
|
||||
vulkan->destruction_callback), // release proc
|
||||
vulkan->user_data // release context
|
||||
);
|
||||
|
||||
if (!surface) {
|
||||
FML_LOG(ERROR) << "Could not wrap embedder supplied Vulkan render texture.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return surface;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::unique_ptr<flutter::EmbedderRenderTarget>
|
||||
CreateEmbedderRenderTarget(const FlutterCompositor* compositor,
|
||||
const FlutterBackingStoreConfig& config,
|
||||
@@ -689,6 +851,11 @@ CreateEmbedderRenderTarget(const FlutterCompositor* compositor,
|
||||
render_surface =
|
||||
MakeSkSurfaceFromBackingStore(context, config, &backing_store.metal);
|
||||
break;
|
||||
|
||||
case kFlutterBackingStoreTypeVulkan:
|
||||
render_surface =
|
||||
MakeSkSurfaceFromBackingStore(context, config, &backing_store.vulkan);
|
||||
break;
|
||||
};
|
||||
|
||||
if (!render_surface) {
|
||||
|
||||
@@ -76,6 +76,7 @@ typedef enum {
|
||||
/// iOS version >= 10.0 (device), 13.0 (simulator)
|
||||
/// macOS version >= 10.14
|
||||
kMetal,
|
||||
kVulkan,
|
||||
} FlutterRendererType;
|
||||
|
||||
/// Additional accessibility features that may be enabled by the platform.
|
||||
@@ -494,7 +495,7 @@ typedef struct {
|
||||
size_t struct_size;
|
||||
/// Embedder provided unique identifier to the texture buffer. Given that the
|
||||
/// `texture` handle is passed to the engine to render to, the texture buffer
|
||||
/// is itseld owned by the embedder. This `texture_id` is then also given to
|
||||
/// is itself owned by the embedder. This `texture_id` is then also given to
|
||||
/// the embedder in the present callback.
|
||||
int64_t texture_id;
|
||||
/// Handle to the MTLTexture that is owned by the embedder. Engine will render
|
||||
@@ -541,6 +542,104 @@ typedef struct {
|
||||
FlutterMetalTextureFrameCallback external_texture_frame_callback;
|
||||
} FlutterMetalRendererConfig;
|
||||
|
||||
/// Alias for VkInstance.
|
||||
typedef void* FlutterVulkanInstanceHandle;
|
||||
|
||||
/// Alias for VkPhysicalDevice.
|
||||
typedef void* FlutterVulkanPhysicalDeviceHandle;
|
||||
|
||||
/// Alias for VkDevice.
|
||||
typedef void* FlutterVulkanDeviceHandle;
|
||||
|
||||
/// Alias for VkQueue.
|
||||
typedef void* FlutterVulkanQueueHandle;
|
||||
|
||||
/// Alias for VkImage.
|
||||
typedef uint64_t FlutterVulkanImageHandle;
|
||||
|
||||
typedef struct {
|
||||
/// The size of this struct. Must be sizeof(FlutterVulkanImage).
|
||||
size_t struct_size;
|
||||
/// Handle to the VkImage that is owned by the embedder. The engine will
|
||||
/// bind this image for writing the frame.
|
||||
FlutterVulkanImageHandle image;
|
||||
/// The VkFormat of the image (for example: VK_FORMAT_R8G8B8A8_UNORM).
|
||||
uint32_t format;
|
||||
} FlutterVulkanImage;
|
||||
|
||||
/// Callback to fetch a Vulkan function pointer for a given instance. Normally,
|
||||
/// this should return the results of vkGetInstanceProcAddr.
|
||||
typedef void* (*FlutterVulkanInstanceProcAddressCallback)(
|
||||
void* /* user data */,
|
||||
FlutterVulkanInstanceHandle /* instance */,
|
||||
const char* /* name */);
|
||||
|
||||
/// Callback for when a VkImage is requested.
|
||||
typedef FlutterVulkanImage (*FlutterVulkanImageCallback)(
|
||||
void* /* user data */,
|
||||
const FlutterFrameInfo* /* frame info */);
|
||||
|
||||
/// Callback for when a VkImage has been written to and is ready for use by the
|
||||
/// embedder.
|
||||
typedef bool (*FlutterVulkanPresentCallback)(
|
||||
void* /* user data */,
|
||||
const FlutterVulkanImage* /* image */);
|
||||
|
||||
typedef struct {
|
||||
/// The size of this struct. Must be sizeof(FlutterVulkanRendererConfig).
|
||||
size_t struct_size;
|
||||
|
||||
/// The Vulkan API version. This should match the value set in
|
||||
/// VkApplicationInfo::apiVersion when the VkInstance was created.
|
||||
uint32_t version;
|
||||
/// VkInstance handle. Must not be destroyed before `FlutterEngineShutdown` is
|
||||
/// called.
|
||||
FlutterVulkanInstanceHandle instance;
|
||||
/// VkPhysicalDevice handle.
|
||||
FlutterVulkanPhysicalDeviceHandle physical_device;
|
||||
/// VkDevice handle. Must not be destroyed before `FlutterEngineShutdown` is
|
||||
/// called.
|
||||
FlutterVulkanDeviceHandle device;
|
||||
/// The queue family index of the VkQueue supplied in the next field.
|
||||
uint32_t queue_family_index;
|
||||
/// VkQueue handle.
|
||||
FlutterVulkanQueueHandle queue;
|
||||
/// The number of instance extensions available for enumerating in the next
|
||||
/// field.
|
||||
size_t enabled_instance_extension_count;
|
||||
/// Array of enabled instance extension names. This should match the names
|
||||
/// passed to `VkInstanceCreateInfo.ppEnabledExtensionNames` when the instance
|
||||
/// was created, but any subset of enabled instance extensions may be
|
||||
/// specified.
|
||||
/// This field is optional; `nullptr` may be specified.
|
||||
/// This memory is only accessed during the call to FlutterEngineInitialize.
|
||||
const char** enabled_instance_extensions;
|
||||
/// The number of device extensions available for enumerating in the next
|
||||
/// field.
|
||||
size_t enabled_device_extension_count;
|
||||
/// Array of enabled logical device extension names. This should match the
|
||||
/// names passed to `VkDeviceCreateInfo.ppEnabledExtensionNames` when the
|
||||
/// logical device was created, but any subset of enabled logical device
|
||||
/// extensions may be specified.
|
||||
/// This field is optional; `nullptr` may be specified.
|
||||
/// This memory is only accessed during the call to FlutterEngineInitialize.
|
||||
/// For example: VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME
|
||||
const char** enabled_device_extensions;
|
||||
/// The callback invoked when resolving Vulkan function pointers.
|
||||
FlutterVulkanInstanceProcAddressCallback get_instance_proc_address_callback;
|
||||
/// The callback invoked when the engine requests a VkImage from the embedder
|
||||
/// for rendering the next frame.
|
||||
/// Not used if a FlutterCompositor is supplied in FlutterProjectArgs.
|
||||
FlutterVulkanImageCallback get_next_image_callback;
|
||||
/// The callback invoked when a VkImage has been written to and is ready for
|
||||
/// use by the embedder. Prior to calling this callback, the engine performs
|
||||
/// a host sync, and so the VkImage can be used in a pipeline by the embedder
|
||||
/// without any additional synchronization.
|
||||
/// Not used if a FlutterCompositor is supplied in FlutterProjectArgs.
|
||||
FlutterVulkanPresentCallback present_image_callback;
|
||||
|
||||
} FlutterVulkanRendererConfig;
|
||||
|
||||
typedef struct {
|
||||
/// The size of this struct. Must be sizeof(FlutterSoftwareRendererConfig).
|
||||
size_t struct_size;
|
||||
@@ -557,6 +656,7 @@ typedef struct {
|
||||
FlutterOpenGLRendererConfig open_gl;
|
||||
FlutterSoftwareRendererConfig software;
|
||||
FlutterMetalRendererConfig metal;
|
||||
FlutterVulkanRendererConfig vulkan;
|
||||
};
|
||||
} FlutterRendererConfig;
|
||||
|
||||
@@ -989,6 +1089,25 @@ typedef struct {
|
||||
};
|
||||
} FlutterMetalBackingStore;
|
||||
|
||||
typedef struct {
|
||||
/// The size of this struct. Must be sizeof(FlutterVulkanBackingStore).
|
||||
size_t struct_size;
|
||||
/// The image that the layer will be rendered to. This image must already be
|
||||
/// available for the engine to bind for writing when it's given to the engine
|
||||
/// via the backing store creation callback. The engine will perform a host
|
||||
/// sync for all layers prior to calling the compositor present callback, and
|
||||
/// so the written layer images can be freely bound by the embedder without
|
||||
/// any additional synchronization.
|
||||
const FlutterVulkanImage* image;
|
||||
/// A baton that is not interpreted by the engine in any way. It will be given
|
||||
/// back to the embedder in the destruction callback below. Embedder resources
|
||||
/// may be associated with this baton.
|
||||
void* user_data;
|
||||
/// The callback invoked by the engine when it no longer needs this backing
|
||||
/// store.
|
||||
VoidCallback destruction_callback;
|
||||
} FlutterVulkanBackingStore;
|
||||
|
||||
typedef enum {
|
||||
/// Indicates that the Flutter application requested that an opacity be
|
||||
/// applied to the platform view.
|
||||
@@ -1048,6 +1167,8 @@ typedef enum {
|
||||
kFlutterBackingStoreTypeSoftware,
|
||||
/// Specifies a Metal backing store. This is backed by a Metal texture.
|
||||
kFlutterBackingStoreTypeMetal,
|
||||
/// Specifies a Vulkan backing store. This is backed by a Vulkan VkImage.
|
||||
kFlutterBackingStoreTypeVulkan,
|
||||
} FlutterBackingStoreType;
|
||||
|
||||
typedef struct {
|
||||
@@ -1069,6 +1190,8 @@ typedef struct {
|
||||
FlutterSoftwareBackingStore software;
|
||||
// The description of the Metal backing store.
|
||||
FlutterMetalBackingStore metal;
|
||||
// The description of the Vulkan backing store.
|
||||
FlutterVulkanBackingStore vulkan;
|
||||
};
|
||||
} FlutterBackingStore;
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
// 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/platform/embedder/embedder_surface_vulkan.h"
|
||||
|
||||
#include "flutter/shell/common/shell_io_manager.h"
|
||||
#include "include/gpu/GrDirectContext.h"
|
||||
#include "include/gpu/vk/GrVkBackendContext.h"
|
||||
#include "include/gpu/vk/GrVkExtensions.h"
|
||||
#include "shell/gpu/gpu_surface_vulkan.h"
|
||||
#include "shell/gpu/gpu_surface_vulkan_delegate.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
EmbedderSurfaceVulkan::EmbedderSurfaceVulkan(
|
||||
uint32_t version,
|
||||
VkInstance instance,
|
||||
size_t instance_extension_count,
|
||||
const char** instance_extensions,
|
||||
size_t device_extension_count,
|
||||
const char** device_extensions,
|
||||
VkPhysicalDevice physical_device,
|
||||
VkDevice device,
|
||||
uint32_t queue_family_index,
|
||||
VkQueue queue,
|
||||
VulkanDispatchTable vulkan_dispatch_table,
|
||||
std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder)
|
||||
: vk_(fml::MakeRefCounted<vulkan::VulkanProcTable>(
|
||||
vulkan_dispatch_table.get_instance_proc_address)),
|
||||
device_(*vk_,
|
||||
vulkan::VulkanHandle<VkPhysicalDevice>{physical_device},
|
||||
vulkan::VulkanHandle<VkDevice>{device},
|
||||
queue_family_index,
|
||||
vulkan::VulkanHandle<VkQueue>{queue}),
|
||||
vulkan_dispatch_table_(vulkan_dispatch_table),
|
||||
external_view_embedder_(external_view_embedder) {
|
||||
// Make sure all required members of the dispatch table are checked.
|
||||
if (!vulkan_dispatch_table_.get_instance_proc_address ||
|
||||
!vulkan_dispatch_table_.get_next_image ||
|
||||
!vulkan_dispatch_table_.present_image) {
|
||||
return;
|
||||
}
|
||||
|
||||
vk_->SetupInstanceProcAddresses(vulkan::VulkanHandle<VkInstance>{instance});
|
||||
vk_->SetupDeviceProcAddresses(vulkan::VulkanHandle<VkDevice>{device});
|
||||
if (!vk_->IsValid()) {
|
||||
FML_LOG(ERROR) << "VulkanProcTable invalid.";
|
||||
return;
|
||||
}
|
||||
|
||||
main_context_ = CreateGrContext(instance, version, instance_extension_count,
|
||||
instance_extensions, device_extension_count,
|
||||
device_extensions, ContextType::kRender);
|
||||
// TODO(96954): Add a second (optional) queue+family index to the Embedder API
|
||||
// to allow embedders to specify a dedicated transfer queue for
|
||||
// use by the resource context. Queue families with graphics
|
||||
// capability can always be used for memory transferring, but it
|
||||
// would be advantageous to use a dedicated transter queue here.
|
||||
resource_context_ = CreateGrContext(
|
||||
instance, version, instance_extension_count, instance_extensions,
|
||||
device_extension_count, device_extensions, ContextType::kResource);
|
||||
|
||||
valid_ = main_context_ && resource_context_;
|
||||
}
|
||||
|
||||
EmbedderSurfaceVulkan::~EmbedderSurfaceVulkan() {
|
||||
if (main_context_) {
|
||||
main_context_->releaseResourcesAndAbandonContext();
|
||||
}
|
||||
if (resource_context_) {
|
||||
resource_context_->releaseResourcesAndAbandonContext();
|
||||
}
|
||||
}
|
||||
|
||||
// |GPUSurfaceVulkanDelegate|
|
||||
const vulkan::VulkanProcTable& EmbedderSurfaceVulkan::vk() {
|
||||
return *vk_;
|
||||
}
|
||||
|
||||
// |GPUSurfaceVulkanDelegate|
|
||||
FlutterVulkanImage EmbedderSurfaceVulkan::AcquireImage(const SkISize& size) {
|
||||
return vulkan_dispatch_table_.get_next_image(size);
|
||||
}
|
||||
|
||||
// |GPUSurfaceVulkanDelegate|
|
||||
bool EmbedderSurfaceVulkan::PresentImage(VkImage image, VkFormat format) {
|
||||
return vulkan_dispatch_table_.present_image(image, format);
|
||||
}
|
||||
|
||||
// |EmbedderSurface|
|
||||
bool EmbedderSurfaceVulkan::IsValid() const {
|
||||
return valid_;
|
||||
}
|
||||
|
||||
// |EmbedderSurface|
|
||||
std::unique_ptr<Surface> EmbedderSurfaceVulkan::CreateGPUSurface() {
|
||||
const bool render_to_surface = !external_view_embedder_;
|
||||
return std::make_unique<GPUSurfaceVulkan>(this, main_context_,
|
||||
render_to_surface);
|
||||
}
|
||||
|
||||
// |EmbedderSurface|
|
||||
sk_sp<GrDirectContext> EmbedderSurfaceVulkan::CreateResourceContext() const {
|
||||
return resource_context_;
|
||||
}
|
||||
|
||||
sk_sp<GrDirectContext> EmbedderSurfaceVulkan::CreateGrContext(
|
||||
VkInstance instance,
|
||||
uint32_t version,
|
||||
size_t instance_extension_count,
|
||||
const char** instance_extensions,
|
||||
size_t device_extension_count,
|
||||
const char** device_extensions,
|
||||
ContextType context_type) const {
|
||||
uint32_t skia_features = 0;
|
||||
if (!device_.GetPhysicalDeviceFeaturesSkia(&skia_features)) {
|
||||
FML_LOG(ERROR) << "Failed to get physical device features.";
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto get_proc = vk_->CreateSkiaGetProc();
|
||||
if (get_proc == nullptr) {
|
||||
FML_LOG(ERROR) << "Failed to create Vulkan getProc for Skia.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GrVkExtensions extensions;
|
||||
|
||||
GrVkBackendContext backend_context = {};
|
||||
backend_context.fInstance = instance;
|
||||
backend_context.fPhysicalDevice = device_.GetPhysicalDeviceHandle();
|
||||
backend_context.fDevice = device_.GetHandle();
|
||||
backend_context.fQueue = device_.GetQueueHandle();
|
||||
backend_context.fGraphicsQueueIndex = device_.GetGraphicsQueueIndex();
|
||||
backend_context.fMinAPIVersion = version;
|
||||
backend_context.fMaxAPIVersion = version;
|
||||
backend_context.fFeatures = skia_features;
|
||||
backend_context.fVkExtensions = &extensions;
|
||||
backend_context.fGetProc = get_proc;
|
||||
backend_context.fOwnsInstanceAndDevice = false;
|
||||
|
||||
extensions.init(backend_context.fGetProc, backend_context.fInstance,
|
||||
backend_context.fPhysicalDevice, instance_extension_count,
|
||||
instance_extensions, device_extension_count,
|
||||
device_extensions);
|
||||
|
||||
GrContextOptions options =
|
||||
MakeDefaultContextOptions(context_type, GrBackendApi::kVulkan);
|
||||
options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo;
|
||||
return GrDirectContext::MakeVulkan(backend_context, options);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SURFACE_VULKAN_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SURFACE_VULKAN_H_
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/shell/gpu/gpu_surface_vulkan.h"
|
||||
#include "flutter/shell/platform/embedder/embedder_external_view_embedder.h"
|
||||
#include "flutter/shell/platform/embedder/embedder_surface.h"
|
||||
#include "shell/common/context_options.h"
|
||||
#include "shell/gpu/gpu_surface_vulkan_delegate.h"
|
||||
#include "shell/platform/embedder/embedder.h"
|
||||
#include "vulkan/vulkan_proc_table.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
class EmbedderSurfaceVulkan final : public EmbedderSurface,
|
||||
public GPUSurfaceVulkanDelegate {
|
||||
public:
|
||||
struct VulkanDispatchTable {
|
||||
std::function<void*(VkInstance, const char*)>
|
||||
get_instance_proc_address; // required
|
||||
std::function<FlutterVulkanImage(const SkISize& frame_size)>
|
||||
get_next_image; // required
|
||||
std::function<bool(VkImage image, VkFormat format)>
|
||||
present_image; // required
|
||||
};
|
||||
|
||||
EmbedderSurfaceVulkan(
|
||||
uint32_t version,
|
||||
VkInstance instance,
|
||||
size_t instance_extension_count,
|
||||
const char** instance_extensions,
|
||||
size_t device_extension_count,
|
||||
const char** device_extensions,
|
||||
VkPhysicalDevice physical_device,
|
||||
VkDevice device,
|
||||
uint32_t queue_family_index,
|
||||
VkQueue queue,
|
||||
VulkanDispatchTable vulkan_dispatch_table,
|
||||
std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder);
|
||||
|
||||
~EmbedderSurfaceVulkan() override;
|
||||
|
||||
// |GPUSurfaceVulkanDelegate|
|
||||
const vulkan::VulkanProcTable& vk() override;
|
||||
|
||||
// |GPUSurfaceVulkanDelegate|
|
||||
FlutterVulkanImage AcquireImage(const SkISize& size) override;
|
||||
|
||||
// |GPUSurfaceVulkanDelegate|
|
||||
bool PresentImage(VkImage image, VkFormat format) override;
|
||||
|
||||
private:
|
||||
bool valid_ = false;
|
||||
fml::RefPtr<vulkan::VulkanProcTable> vk_;
|
||||
vulkan::VulkanDevice device_;
|
||||
VulkanDispatchTable vulkan_dispatch_table_;
|
||||
std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder_;
|
||||
sk_sp<GrDirectContext> main_context_;
|
||||
sk_sp<GrDirectContext> resource_context_;
|
||||
|
||||
// |EmbedderSurface|
|
||||
bool IsValid() const override;
|
||||
|
||||
// |EmbedderSurface|
|
||||
std::unique_ptr<Surface> CreateGPUSurface() override;
|
||||
|
||||
// |EmbedderSurface|
|
||||
sk_sp<GrDirectContext> CreateResourceContext() const override;
|
||||
|
||||
sk_sp<GrDirectContext> CreateGrContext(VkInstance instance,
|
||||
uint32_t version,
|
||||
size_t instance_extension_count,
|
||||
const char** instance_extensions,
|
||||
size_t device_extension_count,
|
||||
const char** device_extensions,
|
||||
ContextType context_type) const;
|
||||
|
||||
void* GetInstanceProcAddress(VkInstance instance, const char* proc_name);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceVulkan);
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SURFACE_VULKAN_H_
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -49,6 +49,19 @@ PlatformViewEmbedder::PlatformViewEmbedder(
|
||||
platform_dispatch_table_(platform_dispatch_table) {}
|
||||
#endif
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
PlatformViewEmbedder::PlatformViewEmbedder(
|
||||
PlatformView::Delegate& delegate,
|
||||
flutter::TaskRunners task_runners,
|
||||
std::unique_ptr<EmbedderSurfaceVulkan> embedder_surface,
|
||||
PlatformDispatchTable platform_dispatch_table,
|
||||
std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder)
|
||||
: PlatformView(delegate, std::move(task_runners)),
|
||||
external_view_embedder_(external_view_embedder),
|
||||
embedder_surface_(std::move(embedder_surface)),
|
||||
platform_dispatch_table_(platform_dispatch_table) {}
|
||||
#endif
|
||||
|
||||
PlatformViewEmbedder::~PlatformViewEmbedder() = default;
|
||||
|
||||
void PlatformViewEmbedder::UpdateSemantics(
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
#include "flutter/shell/platform/embedder/embedder_surface_metal.h"
|
||||
#endif
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
#include "flutter/shell/platform/embedder/embedder_surface_vulkan.h"
|
||||
#endif
|
||||
|
||||
namespace flutter {
|
||||
|
||||
class PlatformViewEmbedder final : public PlatformView {
|
||||
@@ -79,6 +83,16 @@ class PlatformViewEmbedder final : public PlatformView {
|
||||
std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder);
|
||||
#endif
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
// Creates a platform view that sets up an Vulkan rasterizer.
|
||||
PlatformViewEmbedder(
|
||||
PlatformView::Delegate& delegate,
|
||||
flutter::TaskRunners task_runners,
|
||||
std::unique_ptr<EmbedderSurfaceVulkan> embedder_surface,
|
||||
PlatformDispatchTable platform_dispatch_table,
|
||||
std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder);
|
||||
#endif
|
||||
|
||||
~PlatformViewEmbedder() override;
|
||||
|
||||
// |PlatformView|
|
||||
|
||||
@@ -70,6 +70,16 @@ inline bool operator==(const FlutterMetalTexture& a,
|
||||
return a.texture_id == b.texture_id && a.texture == b.texture;
|
||||
}
|
||||
|
||||
inline bool operator==(const FlutterVulkanImage& a,
|
||||
const FlutterVulkanImage& b) {
|
||||
return a.image == b.image && a.format == b.format;
|
||||
}
|
||||
|
||||
inline bool operator==(const FlutterVulkanBackingStore& a,
|
||||
const FlutterVulkanBackingStore& b) {
|
||||
return a.image == b.image;
|
||||
}
|
||||
|
||||
inline bool operator==(const FlutterMetalBackingStore& a,
|
||||
const FlutterMetalBackingStore& b) {
|
||||
return a.texture == b.texture;
|
||||
@@ -112,6 +122,8 @@ inline bool operator==(const FlutterBackingStore& a,
|
||||
return a.software == b.software;
|
||||
case kFlutterBackingStoreTypeMetal:
|
||||
return a.metal == b.metal;
|
||||
case kFlutterBackingStoreTypeVulkan:
|
||||
return a.vulkan == b.vulkan;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -230,6 +242,8 @@ inline std::string FlutterBackingStoreTypeToString(
|
||||
return "kFlutterBackingStoreTypeSoftware";
|
||||
case kFlutterBackingStoreTypeMetal:
|
||||
return "kFlutterBackingStoreTypeMetal";
|
||||
case kFlutterBackingStoreTypeVulkan:
|
||||
return "kFlutterBackingStoreTypeVulkan";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
@@ -256,6 +270,13 @@ inline std::ostream& operator<<(std::ostream& out,
|
||||
<< item.texture_id << std::dec << " Handle: 0x" << std::hex
|
||||
<< item.texture;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out,
|
||||
const FlutterVulkanImage& item) {
|
||||
return out << "(FlutterVulkanTexture) Image Handle: " << std::hex
|
||||
<< item.image << std::dec << " Format: " << item.format;
|
||||
}
|
||||
|
||||
inline std::string FlutterPlatformViewMutationTypeToString(
|
||||
FlutterPlatformViewMutationType type) {
|
||||
switch (type) {
|
||||
@@ -347,6 +368,11 @@ inline std::ostream& operator<<(std::ostream& out,
|
||||
return out << "(FlutterMetalBackingStore) Texture: " << item.texture;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out,
|
||||
const FlutterVulkanBackingStore& item) {
|
||||
return out << "(FlutterVulkanBackingStore) Image: " << item.image;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out,
|
||||
const FlutterBackingStore& backing_store) {
|
||||
out << "(FlutterBackingStore) Struct size: " << backing_store.struct_size
|
||||
@@ -366,6 +392,10 @@ inline std::ostream& operator<<(std::ostream& out,
|
||||
case kFlutterBackingStoreTypeMetal:
|
||||
out << backing_store.metal;
|
||||
break;
|
||||
|
||||
case kFlutterBackingStoreTypeVulkan:
|
||||
out << backing_store.vulkan;
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
@@ -6,13 +6,20 @@
|
||||
|
||||
#include "flutter/runtime/dart_vm.h"
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
#include "tests/embedder_test_context.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/swiftshader/include/vulkan/vulkan_core.h"
|
||||
|
||||
#ifdef SHELL_ENABLE_GL
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_compositor_gl.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_context_gl.h"
|
||||
#endif
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_context_vulkan.h"
|
||||
#include "flutter/vulkan/vulkan_device.h"
|
||||
#endif
|
||||
|
||||
#ifdef SHELL_ENABLE_METAL
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_context_metal.h"
|
||||
#endif
|
||||
@@ -73,6 +80,10 @@ EmbedderConfigBuilder::EmbedderConfigBuilder(
|
||||
InitializeMetalRendererConfig();
|
||||
#endif
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
InitializeVulkanRendererConfig();
|
||||
#endif
|
||||
|
||||
software_renderer_config_.struct_size = sizeof(FlutterSoftwareRendererConfig);
|
||||
software_renderer_config_.surface_present_callback =
|
||||
[](void* context, const void* allocation, size_t row_bytes,
|
||||
@@ -154,6 +165,24 @@ void EmbedderConfigBuilder::SetOpenGLPresentCallBack() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void EmbedderConfigBuilder::SetRendererConfig(EmbedderTestContextType type,
|
||||
SkISize surface_size) {
|
||||
switch (type) {
|
||||
case EmbedderTestContextType::kOpenGLContext:
|
||||
SetOpenGLRendererConfig(surface_size);
|
||||
break;
|
||||
case EmbedderTestContextType::kMetalContext:
|
||||
SetMetalRendererConfig(surface_size);
|
||||
break;
|
||||
case EmbedderTestContextType::kVulkanContext:
|
||||
SetVulkanRendererConfig(surface_size);
|
||||
break;
|
||||
case EmbedderTestContextType::kSoftwareContext:
|
||||
SetSoftwareRendererConfig(surface_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EmbedderConfigBuilder::SetOpenGLRendererConfig(SkISize surface_size) {
|
||||
#ifdef SHELL_ENABLE_GL
|
||||
renderer_config_.type = FlutterRendererType::kOpenGL;
|
||||
@@ -170,6 +199,14 @@ void EmbedderConfigBuilder::SetMetalRendererConfig(SkISize surface_size) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void EmbedderConfigBuilder::SetVulkanRendererConfig(SkISize surface_size) {
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
renderer_config_.type = FlutterRendererType::kVulkan;
|
||||
renderer_config_.vulkan = vulkan_renderer_config_;
|
||||
context_.SetupSurface(surface_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EmbedderConfigBuilder::SetAssetsPath() {
|
||||
project_args_.assets_path = context_.GetAssetsPath().c_str();
|
||||
}
|
||||
@@ -428,5 +465,60 @@ void EmbedderConfigBuilder::InitializeMetalRendererConfig() {
|
||||
|
||||
#endif // SHELL_ENABLE_METAL
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
|
||||
void EmbedderConfigBuilder::InitializeVulkanRendererConfig() {
|
||||
if (context_.GetContextType() != EmbedderTestContextType::kVulkanContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
vulkan_renderer_config_.struct_size = sizeof(FlutterVulkanRendererConfig);
|
||||
vulkan_renderer_config_.version =
|
||||
static_cast<EmbedderTestContextVulkan&>(context_)
|
||||
.vulkan_context_->application_->GetAPIVersion();
|
||||
vulkan_renderer_config_.instance =
|
||||
static_cast<EmbedderTestContextVulkan&>(context_)
|
||||
.vulkan_context_->application_->GetInstance();
|
||||
vulkan_renderer_config_.physical_device =
|
||||
static_cast<EmbedderTestContextVulkan&>(context_)
|
||||
.vulkan_context_->device_->GetPhysicalDeviceHandle();
|
||||
vulkan_renderer_config_.device =
|
||||
static_cast<EmbedderTestContextVulkan&>(context_)
|
||||
.vulkan_context_->device_->GetHandle();
|
||||
vulkan_renderer_config_.queue_family_index =
|
||||
static_cast<EmbedderTestContextVulkan&>(context_)
|
||||
.vulkan_context_->device_->GetGraphicsQueueIndex();
|
||||
vulkan_renderer_config_.queue =
|
||||
static_cast<EmbedderTestContextVulkan&>(context_)
|
||||
.vulkan_context_->device_->GetQueueHandle();
|
||||
vulkan_renderer_config_.get_instance_proc_address_callback =
|
||||
[](void* context, FlutterVulkanInstanceHandle instance,
|
||||
const char* name) -> void* {
|
||||
return reinterpret_cast<EmbedderTestContextVulkan*>(context)
|
||||
->vulkan_context_->vk_->GetInstanceProcAddr(
|
||||
reinterpret_cast<VkInstance>(instance), name);
|
||||
};
|
||||
vulkan_renderer_config_.get_next_image_callback =
|
||||
[](void* context,
|
||||
const FlutterFrameInfo* frame_info) -> FlutterVulkanImage {
|
||||
VkImage image =
|
||||
reinterpret_cast<EmbedderTestContextVulkan*>(context)->GetNextImage(
|
||||
{static_cast<int>(frame_info->size.width),
|
||||
static_cast<int>(frame_info->size.height)});
|
||||
return {
|
||||
.struct_size = sizeof(FlutterVulkanImage),
|
||||
.image = reinterpret_cast<uint64_t>(image),
|
||||
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
};
|
||||
};
|
||||
vulkan_renderer_config_.present_image_callback =
|
||||
[](void* context, const FlutterVulkanImage* image) -> bool {
|
||||
return reinterpret_cast<EmbedderTestContextVulkan*>(context)->PresentImage(
|
||||
reinterpret_cast<VkImage>(image->image));
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@@ -45,12 +45,16 @@ class EmbedderConfigBuilder {
|
||||
|
||||
FlutterProjectArgs& GetProjectArgs();
|
||||
|
||||
void SetRendererConfig(EmbedderTestContextType type, SkISize surface_size);
|
||||
|
||||
void SetSoftwareRendererConfig(SkISize surface_size = SkISize::Make(1, 1));
|
||||
|
||||
void SetOpenGLRendererConfig(SkISize surface_size);
|
||||
|
||||
void SetMetalRendererConfig(SkISize surface_size);
|
||||
|
||||
void SetVulkanRendererConfig(SkISize surface_size);
|
||||
|
||||
// Used to explicitly set an `open_gl.fbo_callback`. Using this method will
|
||||
// cause your test to fail since the ctor for this class sets
|
||||
// `open_gl.fbo_callback_with_frame_info`. This method exists as a utility to
|
||||
@@ -117,6 +121,10 @@ class EmbedderConfigBuilder {
|
||||
#ifdef SHELL_ENABLE_GL
|
||||
FlutterOpenGLRendererConfig opengl_renderer_config_ = {};
|
||||
#endif
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
void InitializeVulkanRendererConfig();
|
||||
FlutterVulkanRendererConfig vulkan_renderer_config_ = {};
|
||||
#endif
|
||||
#ifdef SHELL_ENABLE_METAL
|
||||
void InitializeMetalRendererConfig();
|
||||
FlutterMetalRendererConfig metal_renderer_config_ = {};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_context_software.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_context_vulkan.h"
|
||||
|
||||
#ifdef SHELL_ENABLE_GL
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_context_gl.h"
|
||||
@@ -33,6 +34,12 @@ EmbedderTestContext& EmbedderTest::GetEmbedderContext(
|
||||
std::make_unique<EmbedderTestContextSoftware>(
|
||||
GetFixturesDirectory());
|
||||
break;
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
case EmbedderTestContextType::kVulkanContext:
|
||||
embedder_contexts_[type] =
|
||||
std::make_unique<EmbedderTestContextVulkan>(GetFixturesDirectory());
|
||||
break;
|
||||
#endif
|
||||
#ifdef SHELL_ENABLE_GL
|
||||
case EmbedderTestContextType::kOpenGLContext:
|
||||
embedder_contexts_[type] =
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_context.h"
|
||||
#include "flutter/testing/testing.h"
|
||||
#include "flutter/testing/thread_test.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
@@ -31,6 +32,10 @@ class EmbedderTest : public ThreadTest {
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTest);
|
||||
};
|
||||
|
||||
class EmbedderTestMultiBackend
|
||||
: public EmbedderTest,
|
||||
public ::testing::WithParamInterface<EmbedderTestContextType> {};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_backingstore_producer.h"
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "third_party/skia/include/core/SkImageInfo.h"
|
||||
#include "third_party/skia/include/core/SkSize.h"
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -23,6 +23,10 @@ EmbedderTestBackingStoreProducer::EmbedderTestBackingStoreProducer(
|
||||
,
|
||||
test_metal_context_(std::make_unique<TestMetalContext>())
|
||||
#endif
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
,
|
||||
test_vulkan_context_(nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@@ -43,6 +47,10 @@ bool EmbedderTestBackingStoreProducer::Create(
|
||||
#ifdef SHELL_ENABLE_METAL
|
||||
case RenderTargetType::kMetalTexture:
|
||||
return CreateMTLTexture(config, renderer_out);
|
||||
#endif
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
case RenderTargetType::kVulkanImage:
|
||||
return CreateVulkanImage(config, renderer_out);
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
@@ -229,5 +237,86 @@ bool EmbedderTestBackingStoreProducer::CreateMTLTexture(
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EmbedderTestBackingStoreProducer::CreateVulkanImage(
|
||||
const FlutterBackingStoreConfig* config,
|
||||
FlutterBackingStore* backing_store_out) {
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
if (!test_vulkan_context_) {
|
||||
test_vulkan_context_ = fml::MakeRefCounted<TestVulkanContext>();
|
||||
}
|
||||
|
||||
auto surface_size = SkISize::Make(config->size.width, config->size.height);
|
||||
TestVulkanImage* test_image = new TestVulkanImage(
|
||||
std::move(test_vulkan_context_->CreateImage(surface_size).value()));
|
||||
|
||||
GrVkImageInfo image_info = {
|
||||
.fImage = test_image->GetImage(),
|
||||
.fImageTiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.fFormat = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
.fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.fSampleCount = 1,
|
||||
.fLevelCount = 1,
|
||||
};
|
||||
GrBackendTexture backend_texture(surface_size.width(), surface_size.height(),
|
||||
image_info);
|
||||
|
||||
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
|
||||
|
||||
SkSurface::TextureReleaseProc release_vktexture = [](void* user_data) {
|
||||
delete reinterpret_cast<TestVulkanImage*>(user_data);
|
||||
};
|
||||
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(
|
||||
context_.get(), // context
|
||||
backend_texture, // back-end texture
|
||||
kTopLeft_GrSurfaceOrigin, // surface origin
|
||||
1, // sample count
|
||||
kRGBA_8888_SkColorType, // color type
|
||||
SkColorSpace::MakeSRGB(), // color space
|
||||
&surface_properties, // surface properties
|
||||
release_vktexture, // texture release proc
|
||||
test_image // release context
|
||||
);
|
||||
|
||||
if (!surface) {
|
||||
FML_LOG(ERROR) << "Could not create Skia surface from Vulkan image.";
|
||||
return false;
|
||||
}
|
||||
backing_store_out->type = kFlutterBackingStoreTypeVulkan;
|
||||
|
||||
FlutterVulkanImage* image = new FlutterVulkanImage();
|
||||
image->image = reinterpret_cast<uint64_t>(image_info.fImage);
|
||||
image->format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
backing_store_out->vulkan.image = image;
|
||||
|
||||
// Collect all allocated resources in the destruction_callback.
|
||||
{
|
||||
UserData* user_data = new UserData();
|
||||
user_data->image = image;
|
||||
user_data->surface = surface.get();
|
||||
|
||||
backing_store_out->user_data = user_data;
|
||||
backing_store_out->vulkan.user_data = user_data;
|
||||
backing_store_out->vulkan.destruction_callback = [](void* user_data) {
|
||||
UserData* d = reinterpret_cast<UserData*>(user_data);
|
||||
d->surface->unref();
|
||||
delete d->image;
|
||||
delete d;
|
||||
};
|
||||
|
||||
// The balancing unref is in the destruction callback.
|
||||
surface->ref();
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/fml/memory/ref_ptr_internal.h"
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
||||
|
||||
@@ -14,16 +15,26 @@
|
||||
#include "flutter/testing/test_metal_context.h"
|
||||
#endif
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
#include "flutter/testing/test_vulkan_context.h"
|
||||
#endif
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
class EmbedderTestBackingStoreProducer {
|
||||
public:
|
||||
struct UserData {
|
||||
SkSurface* surface;
|
||||
FlutterVulkanImage* image;
|
||||
};
|
||||
|
||||
enum class RenderTargetType {
|
||||
kSoftwareBuffer,
|
||||
kOpenGLFramebuffer,
|
||||
kOpenGLTexture,
|
||||
kMetalTexture,
|
||||
kVulkanImage,
|
||||
};
|
||||
|
||||
EmbedderTestBackingStoreProducer(sk_sp<GrDirectContext> context,
|
||||
@@ -46,6 +57,9 @@ class EmbedderTestBackingStoreProducer {
|
||||
bool CreateMTLTexture(const FlutterBackingStoreConfig* config,
|
||||
FlutterBackingStore* renderer_out);
|
||||
|
||||
bool CreateVulkanImage(const FlutterBackingStoreConfig* config,
|
||||
FlutterBackingStore* renderer_out);
|
||||
|
||||
sk_sp<GrDirectContext> context_;
|
||||
RenderTargetType type_;
|
||||
|
||||
@@ -53,6 +67,10 @@ class EmbedderTestBackingStoreProducer {
|
||||
std::unique_ptr<TestMetalContext> test_metal_context_;
|
||||
#endif
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
fml::RefPtr<TestVulkanContext> test_vulkan_context_;
|
||||
#endif
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestBackingStoreProducer);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// 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/platform/embedder/tests/embedder_test_compositor_vulkan.h"
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_assertions.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_backingstore_producer.h"
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
EmbedderTestCompositorVulkan::EmbedderTestCompositorVulkan(
|
||||
SkISize surface_size,
|
||||
sk_sp<GrDirectContext> context)
|
||||
: EmbedderTestCompositor(surface_size, context) {}
|
||||
|
||||
EmbedderTestCompositorVulkan::~EmbedderTestCompositorVulkan() = default;
|
||||
|
||||
bool EmbedderTestCompositorVulkan::UpdateOffscrenComposition(
|
||||
const FlutterLayer** layers,
|
||||
size_t layers_count) {
|
||||
last_composition_ = nullptr;
|
||||
|
||||
const auto image_info = SkImageInfo::MakeN32Premul(surface_size_);
|
||||
|
||||
sk_sp<SkSurface> surface =
|
||||
SkSurface::MakeRenderTarget(context_.get(), // context
|
||||
SkBudgeted::kNo, // budgeted
|
||||
image_info, // image info
|
||||
1, // sample count
|
||||
kTopLeft_GrSurfaceOrigin, // surface origin
|
||||
nullptr, // surface properties
|
||||
false // create mipmaps
|
||||
);
|
||||
|
||||
if (!surface) {
|
||||
FML_LOG(ERROR) << "Could not update the off-screen composition.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto canvas = surface->getCanvas();
|
||||
|
||||
// This has to be transparent because we are going to be compositing this
|
||||
// sub-hierarchy onto the on-screen surface.
|
||||
canvas->clear(SK_ColorTRANSPARENT);
|
||||
|
||||
for (size_t i = 0; i < layers_count; ++i) {
|
||||
const auto* layer = layers[i];
|
||||
|
||||
sk_sp<SkImage> platform_rendered_contents;
|
||||
|
||||
sk_sp<SkImage> layer_image;
|
||||
SkIPoint canvas_offset = SkIPoint::Make(0, 0);
|
||||
|
||||
switch (layer->type) {
|
||||
case kFlutterLayerContentTypeBackingStore:
|
||||
layer_image =
|
||||
reinterpret_cast<EmbedderTestBackingStoreProducer::UserData*>(
|
||||
layer->backing_store->user_data)
|
||||
->surface->makeImageSnapshot();
|
||||
break;
|
||||
case kFlutterLayerContentTypePlatformView:
|
||||
layer_image =
|
||||
platform_view_renderer_callback_
|
||||
? platform_view_renderer_callback_(*layer, context_.get())
|
||||
: nullptr;
|
||||
canvas_offset = SkIPoint::Make(layer->offset.x, layer->offset.y);
|
||||
break;
|
||||
};
|
||||
|
||||
// If the layer is not a platform view but the engine did not specify an
|
||||
// image for the backing store, it is an error.
|
||||
if (!layer_image && layer->type != kFlutterLayerContentTypePlatformView) {
|
||||
FML_LOG(ERROR) << "Could not snapshot layer in test compositor: "
|
||||
<< *layer;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The test could have just specified no contents to be rendered in place of
|
||||
// a platform view. This is not an error.
|
||||
if (layer_image) {
|
||||
// The image rendered by Flutter already has the correct offset and
|
||||
// transformation applied. The layers offset is meant for the platform.
|
||||
canvas->drawImage(layer_image.get(), canvas_offset.x(),
|
||||
canvas_offset.y());
|
||||
}
|
||||
}
|
||||
|
||||
last_composition_ = surface->makeImageSnapshot();
|
||||
|
||||
if (!last_composition_) {
|
||||
FML_LOG(ERROR) << "Could not update the contents of the sub-composition.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (next_scene_callback_) {
|
||||
auto last_composition_snapshot = last_composition_->makeRasterImage();
|
||||
FML_CHECK(last_composition_snapshot);
|
||||
auto callback = next_scene_callback_;
|
||||
next_scene_callback_ = nullptr;
|
||||
callback(std::move(last_composition_snapshot));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_COMPOSITOR_VULKAN_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_COMPOSITOR_VULKAN_H_
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_compositor.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
class EmbedderTestCompositorVulkan : public EmbedderTestCompositor {
|
||||
public:
|
||||
EmbedderTestCompositorVulkan(SkISize surface_size,
|
||||
sk_sp<GrDirectContext> context);
|
||||
|
||||
~EmbedderTestCompositorVulkan() override;
|
||||
|
||||
private:
|
||||
bool UpdateOffscrenComposition(const FlutterLayer** layers,
|
||||
size_t layers_count) override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestCompositorVulkan);
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_COMPOSITOR_VULKAN_H_
|
||||
@@ -44,6 +44,7 @@ enum class EmbedderTestContextType {
|
||||
kSoftwareContext,
|
||||
kOpenGLContext,
|
||||
kMetalContext,
|
||||
kVulkanContext,
|
||||
};
|
||||
|
||||
class EmbedderTestContext {
|
||||
@@ -105,6 +106,12 @@ class EmbedderTestContext {
|
||||
|
||||
using NextSceneCallback = std::function<void(sk_sp<SkImage> image)>;
|
||||
|
||||
#ifdef SHELL_ENABLE_VULKAN
|
||||
// The TestVulkanContext destructor must be called _after_ the compositor is
|
||||
// freed.
|
||||
fml::RefPtr<TestVulkanContext> vulkan_context_ = nullptr;
|
||||
#endif
|
||||
|
||||
std::string assets_path_;
|
||||
ELFAOTSymbols aot_symbols_;
|
||||
std::unique_ptr<fml::Mapping> vm_snapshot_data_;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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/platform/embedder/tests/embedder_test_context_vulkan.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_compositor_vulkan.h"
|
||||
#include "flutter/testing/test_vulkan_context.h"
|
||||
#include "flutter/testing/test_vulkan_surface.h"
|
||||
#include "flutter/vulkan/vulkan_device.h"
|
||||
#include "flutter/vulkan/vulkan_proc_table.h"
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
EmbedderTestContextVulkan::EmbedderTestContextVulkan(std::string assets_path)
|
||||
: EmbedderTestContext(assets_path), surface_() {
|
||||
vulkan_context_ = fml::MakeRefCounted<TestVulkanContext>();
|
||||
}
|
||||
|
||||
EmbedderTestContextVulkan::~EmbedderTestContextVulkan() {}
|
||||
|
||||
void EmbedderTestContextVulkan::SetupSurface(SkISize surface_size) {
|
||||
FML_CHECK(surface_size_.isEmpty());
|
||||
surface_size_ = surface_size;
|
||||
surface_ = TestVulkanSurface::Create(*vulkan_context_, surface_size_);
|
||||
}
|
||||
|
||||
size_t EmbedderTestContextVulkan::GetSurfacePresentCount() const {
|
||||
return present_count_;
|
||||
}
|
||||
|
||||
VkImage EmbedderTestContextVulkan::GetNextImage(const SkISize& size) {
|
||||
return surface_->GetImage();
|
||||
}
|
||||
|
||||
bool EmbedderTestContextVulkan::PresentImage(VkImage image) {
|
||||
FireRootSurfacePresentCallbackIfPresent(
|
||||
[&]() { return surface_->GetSurfaceSnapshot(); });
|
||||
present_count_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
EmbedderTestContextType EmbedderTestContextVulkan::GetContextType() const {
|
||||
return EmbedderTestContextType::kVulkanContext;
|
||||
}
|
||||
|
||||
void EmbedderTestContextVulkan::SetupCompositor() {
|
||||
FML_CHECK(!compositor_) << "Already set up a compositor in this context.";
|
||||
FML_CHECK(surface_)
|
||||
<< "Set up the Vulkan surface before setting up a compositor.";
|
||||
compositor_ = std::make_unique<EmbedderTestCompositorVulkan>(
|
||||
surface_size_, vulkan_context_->GetGrDirectContext());
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_VULKAN_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_VULKAN_H_
|
||||
|
||||
#include <memory>
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_context.h"
|
||||
#include "flutter/testing/test_vulkan_context.h"
|
||||
#include "flutter/vulkan/vulkan_application.h"
|
||||
#include "testing/test_vulkan_surface.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
class EmbedderTestContextVulkan : public EmbedderTestContext {
|
||||
public:
|
||||
explicit EmbedderTestContextVulkan(std::string assets_path = "");
|
||||
|
||||
~EmbedderTestContextVulkan() override;
|
||||
|
||||
// |EmbedderTestContext|
|
||||
EmbedderTestContextType GetContextType() const override;
|
||||
|
||||
// |EmbedderTestContext|
|
||||
size_t GetSurfacePresentCount() const override;
|
||||
|
||||
// |EmbedderTestContext|
|
||||
void SetupCompositor() override;
|
||||
|
||||
VkImage GetNextImage(const SkISize& size);
|
||||
|
||||
bool PresentImage(VkImage image);
|
||||
|
||||
private:
|
||||
std::unique_ptr<TestVulkanSurface> surface_;
|
||||
|
||||
SkISize surface_size_ = SkISize::MakeEmpty();
|
||||
size_t present_count_ = 0;
|
||||
|
||||
void SetupSurface(SkISize surface_size) override;
|
||||
|
||||
friend class EmbedderConfigBuilder;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestContextVulkan);
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_VULKAN_H_
|
||||
@@ -2,19 +2,21 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "tests/embedder_test_context.h"
|
||||
#define FML_USED_ON_EMBEDDER
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "embedder.h"
|
||||
#include "embedder_engine.h"
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#include "flutter/flow/raster_cache.h"
|
||||
#include "flutter/fml/file.h"
|
||||
#include "flutter/fml/make_copyable.h"
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "flutter/fml/message_loop.h"
|
||||
#include "flutter/fml/message_loop_task_queues.h"
|
||||
#include "flutter/fml/native_library.h"
|
||||
#include "flutter/fml/paths.h"
|
||||
#include "flutter/fml/synchronization/count_down_latch.h"
|
||||
#include "flutter/fml/synchronization/waitable_event.h"
|
||||
@@ -29,7 +31,6 @@
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_unittests_util.h"
|
||||
#include "flutter/testing/assertions_skia.h"
|
||||
#include "flutter/testing/test_gl_surface.h"
|
||||
#include "flutter/testing/test_vulkan_context.h"
|
||||
#include "flutter/testing/testing.h"
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
#include "third_party/skia/src/gpu/gl/GrGLDefines.h"
|
||||
@@ -40,14 +41,9 @@ namespace testing {
|
||||
|
||||
using EmbedderTest = testing::EmbedderTest;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// This is a sanity check to ensure Swiftshader Vulkan is working. Once Vulkan
|
||||
/// support lands in the embedder API, it'll be tested via a new
|
||||
/// EmbedderTestContext type/config.
|
||||
///
|
||||
TEST_F(EmbedderTest, CanInitializeTestVulkanContext) {
|
||||
TestVulkanContext ctx;
|
||||
ASSERT_TRUE(ctx.IsValid());
|
||||
TEST_F(EmbedderTest, CanGetVulkanEmbedderContext) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kVulkanContext);
|
||||
EmbedderConfigBuilder builder(context);
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CanCreateOpenGLRenderingEngine) {
|
||||
@@ -1238,13 +1234,14 @@ TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositorWithTransformation) {
|
||||
"scene_without_custom_compositor_with_xform.png", rendered_scene));
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CanRenderGradientWithoutCompositor) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
TEST_P(EmbedderTestMultiBackend, CanRenderGradientWithoutCompositor) {
|
||||
EmbedderTestContextType backend = GetParam();
|
||||
auto& context = GetEmbedderContext(backend);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
|
||||
builder.SetDartEntrypoint("render_gradient");
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetRendererConfig(backend, SkISize::Make(800, 600));
|
||||
|
||||
auto rendered_scene = context.GetNextSceneImage();
|
||||
|
||||
@@ -1260,7 +1257,8 @@ TEST_F(EmbedderTest, CanRenderGradientWithoutCompositor) {
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
|
||||
ASSERT_TRUE(ImageMatchesFixture("gradient.png", rendered_scene));
|
||||
ASSERT_TRUE(ImageMatchesFixture(
|
||||
FixtureNameForBackend(backend, "gradient.png"), rendered_scene));
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CanRenderGradientWithoutCompositorWithXform) {
|
||||
@@ -1296,16 +1294,16 @@ TEST_F(EmbedderTest, CanRenderGradientWithoutCompositorWithXform) {
|
||||
ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", rendered_scene));
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CanRenderGradientWithCompositor) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
TEST_P(EmbedderTestMultiBackend, CanRenderGradientWithCompositor) {
|
||||
EmbedderTestContextType backend = GetParam();
|
||||
auto& context = GetEmbedderContext(backend);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
|
||||
builder.SetDartEntrypoint("render_gradient");
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetRendererConfig(backend, SkISize::Make(800, 600));
|
||||
builder.SetCompositor();
|
||||
builder.SetRenderTargetType(
|
||||
EmbedderTestBackingStoreProducer::RenderTargetType::kOpenGLFramebuffer);
|
||||
builder.SetRenderTargetType(GetRenderTargetFromBackend(backend, true));
|
||||
|
||||
auto rendered_scene = context.GetNextSceneImage();
|
||||
|
||||
@@ -1321,7 +1319,8 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositor) {
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
|
||||
ASSERT_TRUE(ImageMatchesFixture("gradient.png", rendered_scene));
|
||||
ASSERT_TRUE(ImageMatchesFixture(
|
||||
FixtureNameForBackend(backend, "gradient.png"), rendered_scene));
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CanRenderGradientWithCompositorWithXform) {
|
||||
@@ -1361,16 +1360,17 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorWithXform) {
|
||||
ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", rendered_scene));
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayer) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
TEST_P(EmbedderTestMultiBackend,
|
||||
CanRenderGradientWithCompositorOnNonRootLayer) {
|
||||
EmbedderTestContextType backend = GetParam();
|
||||
auto& context = GetEmbedderContext(backend);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
|
||||
builder.SetDartEntrypoint("render_gradient_on_non_root_backing_store");
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetRendererConfig(backend, SkISize::Make(800, 600));
|
||||
builder.SetCompositor();
|
||||
builder.SetRenderTargetType(
|
||||
EmbedderTestBackingStoreProducer::RenderTargetType::kOpenGLFramebuffer);
|
||||
builder.SetRenderTargetType(GetRenderTargetFromBackend(backend, true));
|
||||
|
||||
context.GetCompositor().SetNextPresentCallback(
|
||||
[&](const FlutterLayer** layers, size_t layers_count) {
|
||||
@@ -1379,9 +1379,8 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayer) {
|
||||
// Layer Root
|
||||
{
|
||||
FlutterBackingStore backing_store = *layers[0]->backing_store;
|
||||
backing_store.type = kFlutterBackingStoreTypeOpenGL;
|
||||
backing_store.did_update = true;
|
||||
backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
|
||||
ConfigureBackingStore(backing_store, backend, true);
|
||||
|
||||
FlutterLayer layer = {};
|
||||
layer.struct_size = sizeof(layer);
|
||||
@@ -1412,9 +1411,8 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayer) {
|
||||
// Layer 2
|
||||
{
|
||||
FlutterBackingStore backing_store = *layers[2]->backing_store;
|
||||
backing_store.type = kFlutterBackingStoreTypeOpenGL;
|
||||
backing_store.did_update = true;
|
||||
backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
|
||||
ConfigureBackingStore(backing_store, backend, true);
|
||||
|
||||
FlutterLayer layer = {};
|
||||
layer.struct_size = sizeof(layer);
|
||||
@@ -1463,7 +1461,8 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayer) {
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
|
||||
ASSERT_TRUE(ImageMatchesFixture("gradient.png", rendered_scene));
|
||||
ASSERT_TRUE(ImageMatchesFixture(
|
||||
FixtureNameForBackend(backend, "gradient.png"), rendered_scene));
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayerWithXform) {
|
||||
@@ -1738,7 +1737,7 @@ TEST_F(EmbedderTest, CanCreateEmbedderWithCustomRenderTaskRunner) {
|
||||
/// Asserts that the render task runner can be the same as the platform task
|
||||
/// runner.
|
||||
///
|
||||
TEST_F(EmbedderTest,
|
||||
TEST_P(EmbedderTestMultiBackend,
|
||||
CanCreateEmbedderWithCustomRenderTaskRunnerTheSameAsPlatformTaskRunner) {
|
||||
// A new thread needs to be created for the platform thread because the test
|
||||
// can't wait for assertions to be completed on the same thread that services
|
||||
@@ -1760,10 +1759,10 @@ TEST_F(EmbedderTest,
|
||||
});
|
||||
|
||||
platform_task_runner->PostTask([&]() {
|
||||
EmbedderConfigBuilder builder(
|
||||
GetEmbedderContext(EmbedderTestContextType::kOpenGLContext));
|
||||
EmbedderTestContextType backend = GetParam();
|
||||
EmbedderConfigBuilder builder(GetEmbedderContext(backend));
|
||||
builder.SetDartEntrypoint("can_render_scene_without_custom_compositor");
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetRendererConfig(backend, SkISize::Make(800, 600));
|
||||
builder.SetRenderTaskRunner(
|
||||
&common_task_runner.GetFlutterTaskRunnerDescription());
|
||||
builder.SetPlatformTaskRunner(
|
||||
@@ -1813,17 +1812,17 @@ TEST_F(EmbedderTest,
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest,
|
||||
TEST_P(EmbedderTestMultiBackend,
|
||||
CompositorMustBeAbleToRenderKnownScenePixelRatioOnSurface) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
EmbedderTestContextType backend = GetParam();
|
||||
auto& context = GetEmbedderContext(backend);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetRendererConfig(backend, SkISize::Make(800, 600));
|
||||
builder.SetCompositor();
|
||||
builder.SetDartEntrypoint("can_display_platform_view_with_pixel_ratio");
|
||||
|
||||
builder.SetRenderTargetType(
|
||||
EmbedderTestBackingStoreProducer::RenderTargetType::kOpenGLTexture);
|
||||
builder.SetRenderTargetType(GetRenderTargetFromBackend(backend, false));
|
||||
|
||||
fml::CountDownLatch latch(1);
|
||||
|
||||
@@ -1836,9 +1835,8 @@ TEST_F(EmbedderTest,
|
||||
// Layer 0 (Root)
|
||||
{
|
||||
FlutterBackingStore backing_store = *layers[0]->backing_store;
|
||||
backing_store.type = kFlutterBackingStoreTypeOpenGL;
|
||||
backing_store.did_update = true;
|
||||
backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture;
|
||||
ConfigureBackingStore(backing_store, backend, false);
|
||||
|
||||
FlutterLayer layer = {};
|
||||
layer.struct_size = sizeof(layer);
|
||||
@@ -1869,9 +1867,8 @@ TEST_F(EmbedderTest,
|
||||
// Layer 2
|
||||
{
|
||||
FlutterBackingStore backing_store = *layers[2]->backing_store;
|
||||
backing_store.type = kFlutterBackingStoreTypeOpenGL;
|
||||
backing_store.did_update = true;
|
||||
backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture;
|
||||
ConfigureBackingStore(backing_store, backend, false);
|
||||
|
||||
FlutterLayer layer = {};
|
||||
layer.struct_size = sizeof(layer);
|
||||
@@ -1900,7 +1897,8 @@ TEST_F(EmbedderTest,
|
||||
|
||||
latch.Wait();
|
||||
|
||||
ASSERT_TRUE(ImageMatchesFixture("dpr_noxform.png", rendered_scene));
|
||||
ASSERT_TRUE(ImageMatchesFixture(
|
||||
FixtureNameForBackend(backend, "dpr_noxform.png"), rendered_scene));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
@@ -2084,16 +2082,16 @@ TEST_F(EmbedderTest,
|
||||
FlutterEngineShutdown(engine.release());
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, PlatformViewMutatorsAreValid) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
TEST_P(EmbedderTestMultiBackend, PlatformViewMutatorsAreValid) {
|
||||
EmbedderTestContextType backend = GetParam();
|
||||
auto& context = GetEmbedderContext(backend);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetRendererConfig(backend, SkISize::Make(800, 600));
|
||||
builder.SetCompositor();
|
||||
builder.SetDartEntrypoint("platform_view_mutators");
|
||||
|
||||
builder.SetRenderTargetType(
|
||||
EmbedderTestBackingStoreProducer::RenderTargetType::kOpenGLTexture);
|
||||
builder.SetRenderTargetType(GetRenderTargetFromBackend(backend, false));
|
||||
|
||||
fml::CountDownLatch latch(1);
|
||||
context.GetCompositor().SetNextPresentCallback(
|
||||
@@ -2103,9 +2101,8 @@ TEST_F(EmbedderTest, PlatformViewMutatorsAreValid) {
|
||||
// Layer 0 (Root)
|
||||
{
|
||||
FlutterBackingStore backing_store = *layers[0]->backing_store;
|
||||
backing_store.type = kFlutterBackingStoreTypeOpenGL;
|
||||
backing_store.did_update = true;
|
||||
backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture;
|
||||
ConfigureBackingStore(backing_store, backend, false);
|
||||
|
||||
FlutterLayer layer = {};
|
||||
layer.struct_size = sizeof(layer);
|
||||
@@ -3691,5 +3688,11 @@ TEST_F(EmbedderTest, ExternalTextureGLRefreshedTooOften) {
|
||||
EXPECT_TRUE(resolve_called);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
EmbedderTestGlVk,
|
||||
EmbedderTestMultiBackend,
|
||||
::testing::Values(EmbedderTestContextType::kOpenGLContext,
|
||||
EmbedderTestContextType::kVulkanContext));
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_backingstore_producer.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_unittests_util.h"
|
||||
|
||||
namespace flutter {
|
||||
@@ -69,6 +70,61 @@ bool RasterImagesAreSame(sk_sp<SkImage> a, sk_sp<SkImage> b) {
|
||||
return normalized_a->equals(normalized_b.get());
|
||||
}
|
||||
|
||||
std::string FixtureNameForBackend(EmbedderTestContextType backend,
|
||||
const std::string& name) {
|
||||
switch (backend) {
|
||||
case EmbedderTestContextType::kVulkanContext:
|
||||
return "vk_" + name;
|
||||
default:
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
EmbedderTestBackingStoreProducer::RenderTargetType GetRenderTargetFromBackend(
|
||||
EmbedderTestContextType backend,
|
||||
bool opengl_framebuffer) {
|
||||
switch (backend) {
|
||||
case EmbedderTestContextType::kVulkanContext:
|
||||
return EmbedderTestBackingStoreProducer::RenderTargetType::kVulkanImage;
|
||||
case EmbedderTestContextType::kOpenGLContext:
|
||||
if (opengl_framebuffer) {
|
||||
return EmbedderTestBackingStoreProducer::RenderTargetType::
|
||||
kOpenGLFramebuffer;
|
||||
}
|
||||
return EmbedderTestBackingStoreProducer::RenderTargetType::kOpenGLTexture;
|
||||
case EmbedderTestContextType::kMetalContext:
|
||||
return EmbedderTestBackingStoreProducer::RenderTargetType::kMetalTexture;
|
||||
case EmbedderTestContextType::kSoftwareContext:
|
||||
return EmbedderTestBackingStoreProducer::RenderTargetType::
|
||||
kSoftwareBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureBackingStore(FlutterBackingStore& backing_store,
|
||||
EmbedderTestContextType backend,
|
||||
bool opengl_framebuffer) {
|
||||
switch (backend) {
|
||||
case EmbedderTestContextType::kVulkanContext:
|
||||
backing_store.type = kFlutterBackingStoreTypeVulkan;
|
||||
break;
|
||||
case EmbedderTestContextType::kOpenGLContext:
|
||||
if (opengl_framebuffer) {
|
||||
backing_store.type = kFlutterBackingStoreTypeOpenGL;
|
||||
backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
|
||||
} else {
|
||||
backing_store.type = kFlutterBackingStoreTypeOpenGL;
|
||||
backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture;
|
||||
}
|
||||
break;
|
||||
case EmbedderTestContextType::kMetalContext:
|
||||
backing_store.type = kFlutterBackingStoreTypeMetal;
|
||||
break;
|
||||
case EmbedderTestContextType::kSoftwareContext:
|
||||
backing_store.type = kFlutterBackingStoreTypeSoftware;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool WriteImageToDisk(const fml::UniqueFD& directory,
|
||||
const std::string& name,
|
||||
sk_sp<SkImage> image) {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <future>
|
||||
|
||||
#include "embedder.h"
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "flutter/fml/message_loop.h"
|
||||
#include "flutter/fml/paths.h"
|
||||
@@ -26,6 +25,40 @@ sk_sp<SkSurface> CreateRenderSurface(const FlutterLayer& layer,
|
||||
|
||||
bool RasterImagesAreSame(sk_sp<SkImage> a, sk_sp<SkImage> b);
|
||||
|
||||
/// @brief Prepends a prefix to the name which is unique to the test
|
||||
/// context type. This is useful for tests that use
|
||||
/// EmbedderTestMultiBackend and require different fixtures per
|
||||
/// backend. For OpenGL, the name remains unchanged.
|
||||
/// @param[in] backend The test context type used to determine the prepended
|
||||
/// prefix (e.g. `vk_[name]` for Vulkan).
|
||||
/// @param[in] name The name of the fixture without any special prefixes.
|
||||
std::string FixtureNameForBackend(EmbedderTestContextType backend,
|
||||
const std::string& name);
|
||||
|
||||
/// @brief Resolves a render target type for a given backend description.
|
||||
/// This is useful for tests that use EmbedderTestMultiBackend.
|
||||
/// @param[in] backend The test context type to resolve the render
|
||||
/// target for.
|
||||
/// @param[in] opengl_framebuffer Ignored for all non-OpenGL backends. Flutter
|
||||
/// supports rendering to both OpenGL textures
|
||||
/// and framebuffers. When false, the OpenGL
|
||||
/// texture render target type is returned.
|
||||
EmbedderTestBackingStoreProducer::RenderTargetType GetRenderTargetFromBackend(
|
||||
EmbedderTestContextType backend,
|
||||
bool opengl_framebuffer);
|
||||
|
||||
/// @brief Configures per-backend properties for a given backing store.
|
||||
/// @param[in] backing_store The backing store to configure.
|
||||
/// @param[in] backend The test context type used to decide which
|
||||
/// backend the backing store will be used with.
|
||||
/// @param[in] opengl_framebuffer Ignored for all non-OpenGL backends. Flutter
|
||||
/// supports rendering to both OpenGL textures
|
||||
/// and framebuffers. When false, the backing
|
||||
/// store is configured to be an OpenGL texture.
|
||||
void ConfigureBackingStore(FlutterBackingStore& backing_store,
|
||||
EmbedderTestContextType backend,
|
||||
bool opengl_framebuffer);
|
||||
|
||||
bool WriteImageToDisk(const fml::UniqueFD& directory,
|
||||
const std::string& name,
|
||||
sk_sp<SkImage> image);
|
||||
|
||||
@@ -128,6 +128,10 @@ if (enable_unittests) {
|
||||
sources = [
|
||||
"test_vulkan_context.cc",
|
||||
"test_vulkan_context.h",
|
||||
"test_vulkan_image.cc",
|
||||
"test_vulkan_image.h",
|
||||
"test_vulkan_surface.cc",
|
||||
"test_vulkan_surface.h",
|
||||
]
|
||||
|
||||
defines = [ "TEST_VULKAN_PROCS" ]
|
||||
@@ -135,6 +139,7 @@ if (enable_unittests) {
|
||||
deps = [
|
||||
":skia",
|
||||
"//flutter/fml",
|
||||
"//flutter/shell/common",
|
||||
"//flutter/vulkan",
|
||||
]
|
||||
|
||||
|
||||
@@ -2,9 +2,19 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "test_vulkan_context.h"
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/vulkan/vulkan_proc_table.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/shell/common/context_options.h"
|
||||
#include "flutter/testing/test_vulkan_context.h"
|
||||
|
||||
#include "flutter/fml/memory/ref_ptr.h"
|
||||
#include "flutter/fml/native_library.h"
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
||||
#include "third_party/skia/include/gpu/vk/GrVkExtensions.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#ifdef FML_OS_MACOSX
|
||||
#define VULKAN_SO_PATH "libvk_swiftshader.dylib"
|
||||
@@ -15,38 +25,164 @@
|
||||
#endif
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
TestVulkanContext::TestVulkanContext() : valid_(false) {
|
||||
vk_ = fml::MakeRefCounted<vulkan::VulkanProcTable>(VULKAN_SO_PATH);
|
||||
TestVulkanContext::TestVulkanContext() {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Initialize basic Vulkan state using the Swiftshader ICD.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const char* vulkan_icd = VULKAN_SO_PATH;
|
||||
|
||||
// TODO(96949): Clean this up and pass a native library directly to
|
||||
// VulkanProcTable.
|
||||
if (!fml::NativeLibrary::Create(VULKAN_SO_PATH)) {
|
||||
FML_LOG(ERROR) << "Couldn't find Vulkan ICD \"" << vulkan_icd
|
||||
<< "\", trying \"libvulkan.so\" instead.";
|
||||
vulkan_icd = "libvulkan.so";
|
||||
}
|
||||
|
||||
FML_LOG(INFO) << "Using Vulkan ICD: " << vulkan_icd;
|
||||
|
||||
vk_ = fml::MakeRefCounted<vulkan::VulkanProcTable>(vulkan_icd);
|
||||
if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) {
|
||||
FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses.";
|
||||
FML_LOG(ERROR) << "Proc table has not acquired mandatory proc addresses.";
|
||||
return;
|
||||
}
|
||||
|
||||
application_ = std::unique_ptr<vulkan::VulkanApplication>(
|
||||
new vulkan::VulkanApplication(*vk_, "Flutter Unittests", {}));
|
||||
application_ =
|
||||
std::unique_ptr<vulkan::VulkanApplication>(new vulkan::VulkanApplication(
|
||||
*vk_, "Flutter Unittests", {}, VK_MAKE_VERSION(1, 0, 0),
|
||||
VK_MAKE_VERSION(1, 0, 0), true));
|
||||
if (!application_->IsValid()) {
|
||||
FML_DLOG(ERROR) << "Failed to initialize basic Vulkan state.";
|
||||
FML_LOG(ERROR) << "Failed to initialize basic Vulkan state.";
|
||||
return;
|
||||
}
|
||||
if (!vk_->AreInstanceProcsSetup()) {
|
||||
FML_DLOG(ERROR) << "Failed to acquire full proc table.";
|
||||
FML_LOG(ERROR) << "Failed to acquire full proc table.";
|
||||
return;
|
||||
}
|
||||
|
||||
logical_device_ = application_->AcquireFirstCompatibleLogicalDevice();
|
||||
if (!logical_device_ || !logical_device_->IsValid()) {
|
||||
FML_DLOG(ERROR) << "Failed to create compatible logical device.";
|
||||
device_ = application_->AcquireFirstCompatibleLogicalDevice();
|
||||
if (!device_ || !device_->IsValid()) {
|
||||
FML_LOG(ERROR) << "Failed to create compatible logical device.";
|
||||
return;
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
// ---------------------------------------------------------------------------
|
||||
// Create a Skia context.
|
||||
// For creating SkSurfaces from VkImages and snapshotting them, etc.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
uint32_t skia_features = 0;
|
||||
if (!device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) {
|
||||
FML_LOG(ERROR) << "Failed to get physical device features.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto get_proc = vk_->CreateSkiaGetProc();
|
||||
if (get_proc == nullptr) {
|
||||
FML_LOG(ERROR) << "Failed to create Vulkan getProc for Skia.";
|
||||
return;
|
||||
}
|
||||
|
||||
GrVkExtensions extensions;
|
||||
|
||||
GrVkBackendContext backend_context = {};
|
||||
backend_context.fInstance = application_->GetInstance();
|
||||
backend_context.fPhysicalDevice = device_->GetPhysicalDeviceHandle();
|
||||
backend_context.fDevice = device_->GetHandle();
|
||||
backend_context.fQueue = device_->GetQueueHandle();
|
||||
backend_context.fGraphicsQueueIndex = device_->GetGraphicsQueueIndex();
|
||||
backend_context.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
backend_context.fMaxAPIVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
backend_context.fFeatures = skia_features;
|
||||
backend_context.fVkExtensions = &extensions;
|
||||
backend_context.fGetProc = get_proc;
|
||||
backend_context.fOwnsInstanceAndDevice = false;
|
||||
|
||||
GrContextOptions options =
|
||||
MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kVulkan);
|
||||
options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo;
|
||||
context_ = GrDirectContext::MakeVulkan(backend_context, options);
|
||||
}
|
||||
|
||||
TestVulkanContext::~TestVulkanContext() = default;
|
||||
|
||||
bool TestVulkanContext::IsValid() {
|
||||
return valid_;
|
||||
TestVulkanContext::~TestVulkanContext() {
|
||||
if (context_) {
|
||||
context_->releaseResourcesAndAbandonContext();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<TestVulkanImage> TestVulkanContext::CreateImage(
|
||||
const SkISize& size) const {
|
||||
TestVulkanImage result;
|
||||
|
||||
VkImageCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
.extent = VkExtent3D{static_cast<uint32_t>(size.width()),
|
||||
static_cast<uint32_t>(size.height()), 1},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
|
||||
VkImage image;
|
||||
if (VK_CALL_LOG_ERROR(VK_CALL_LOG_ERROR(
|
||||
vk_->CreateImage(device_->GetHandle(), &info, nullptr, &image)))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
result.image_ = vulkan::VulkanHandle<VkImage>(
|
||||
image, [&vk = vk_, &device = device_](VkImage image) {
|
||||
vk->DestroyImage(device->GetHandle(), image, nullptr);
|
||||
});
|
||||
|
||||
VkMemoryRequirements mem_req;
|
||||
vk_->GetImageMemoryRequirements(device_->GetHandle(), image, &mem_req);
|
||||
VkMemoryAllocateInfo alloc_info{};
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
alloc_info.allocationSize = mem_req.size;
|
||||
alloc_info.memoryTypeIndex = static_cast<uint32_t>(__builtin_ctz(
|
||||
mem_req.memoryTypeBits & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
|
||||
|
||||
VkDeviceMemory memory;
|
||||
if (VK_CALL_LOG_ERROR(vk_->AllocateMemory(device_->GetHandle(), &alloc_info,
|
||||
nullptr, &memory)) != VK_SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
result.memory_ = vulkan::VulkanHandle<VkDeviceMemory>{
|
||||
memory, [&vk = vk_, &device = device_](VkDeviceMemory memory) {
|
||||
vk->FreeMemory(device->GetHandle(), memory, nullptr);
|
||||
}};
|
||||
|
||||
if (VK_CALL_LOG_ERROR(VK_CALL_LOG_ERROR(vk_->BindImageMemory(
|
||||
device_->GetHandle(), result.image_, result.memory_, 0)))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
result.context_ =
|
||||
fml::RefPtr<TestVulkanContext>(const_cast<TestVulkanContext*>(this));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
sk_sp<GrDirectContext> TestVulkanContext::GetGrDirectContext() const {
|
||||
return context_;
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@@ -5,29 +5,44 @@
|
||||
#ifndef FLUTTER_TESTING_TEST_VULKAN_CONTEXT_H_
|
||||
#define FLUTTER_TESTING_TEST_VULKAN_CONTEXT_H_
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/fml/memory/ref_ptr.h"
|
||||
#include "flutter/testing/test_vulkan_image.h"
|
||||
#include "flutter/vulkan/vulkan_application.h"
|
||||
#include "flutter/vulkan/vulkan_device.h"
|
||||
#include "flutter/vulkan/vulkan_proc_table.h"
|
||||
|
||||
namespace flutter {
|
||||
#include "third_party/skia/include/core/SkSize.h"
|
||||
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
||||
|
||||
/// @brief Utility class to create a Vulkan device context, a corresponding
|
||||
/// Skia context, and device resources.
|
||||
class TestVulkanContext {
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
class TestVulkanContext : public fml::RefCountedThreadSafe<TestVulkanContext> {
|
||||
public:
|
||||
TestVulkanContext();
|
||||
~TestVulkanContext();
|
||||
bool IsValid();
|
||||
|
||||
std::optional<TestVulkanImage> CreateImage(const SkISize& size) const;
|
||||
|
||||
sk_sp<GrDirectContext> GetGrDirectContext() const;
|
||||
|
||||
private:
|
||||
bool valid_ = false;
|
||||
fml::RefPtr<vulkan::VulkanProcTable> vk_;
|
||||
std::unique_ptr<vulkan::VulkanApplication> application_;
|
||||
std::unique_ptr<vulkan::VulkanDevice> logical_device_;
|
||||
std::unique_ptr<vulkan::VulkanDevice> device_;
|
||||
|
||||
sk_sp<GrDirectContext> context_;
|
||||
|
||||
friend class EmbedderTestContextVulkan;
|
||||
friend class EmbedderConfigBuilder;
|
||||
|
||||
FML_FRIEND_MAKE_REF_COUNTED(TestVulkanContext);
|
||||
FML_FRIEND_REF_COUNTED_THREAD_SAFE(TestVulkanContext);
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(TestVulkanContext);
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_TESTING_TEST_VULKAN_CONTEXT_H_
|
||||
|
||||
24
engine/src/flutter/testing/test_vulkan_image.cc
Normal file
24
engine/src/flutter/testing/test_vulkan_image.cc
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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_vulkan_image.h"
|
||||
|
||||
#include "flutter/testing/test_vulkan_context.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
TestVulkanImage::TestVulkanImage() = default;
|
||||
|
||||
TestVulkanImage::TestVulkanImage(TestVulkanImage&& other) = default;
|
||||
TestVulkanImage& TestVulkanImage::operator=(TestVulkanImage&& other) = default;
|
||||
|
||||
TestVulkanImage::~TestVulkanImage() = default;
|
||||
|
||||
VkImage TestVulkanImage::GetImage() {
|
||||
return image_;
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
46
engine/src/flutter/testing/test_vulkan_image.h
Normal file
46
engine/src/flutter/testing/test_vulkan_image.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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_VULKAN_IMAGE_H_
|
||||
#define FLUTTER_TESTING_TEST_VULKAN_IMAGE_H_
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/vulkan/vulkan_handle.h"
|
||||
|
||||
#include "flutter/fml/memory/ref_ptr.h"
|
||||
#include "third_party/skia/include/core/SkSize.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
class TestVulkanContext;
|
||||
|
||||
/// Captures the lifetime of a test VkImage along with its bound memory.
|
||||
class TestVulkanImage {
|
||||
public:
|
||||
TestVulkanImage(TestVulkanImage&& other);
|
||||
TestVulkanImage& operator=(TestVulkanImage&& other);
|
||||
|
||||
~TestVulkanImage();
|
||||
|
||||
VkImage GetImage();
|
||||
|
||||
private:
|
||||
TestVulkanImage();
|
||||
|
||||
// The lifetime of the Vulkan state must exceed memory/image handles.
|
||||
fml::RefPtr<TestVulkanContext> context_;
|
||||
|
||||
vulkan::VulkanHandle<VkImage> image_;
|
||||
vulkan::VulkanHandle<VkDeviceMemory> memory_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(TestVulkanImage);
|
||||
|
||||
friend TestVulkanContext;
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_TESTING_TEST_VULKAN_IMAGE_H_
|
||||
110
engine/src/flutter/testing/test_vulkan_surface.cc
Normal file
110
engine/src/flutter/testing/test_vulkan_surface.cc
Normal file
@@ -0,0 +1,110 @@
|
||||
// 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_vulkan_surface.h"
|
||||
#include <memory>
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/testing/test_vulkan_context.h"
|
||||
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
#include "third_party/skia/include/core/SkSurfaceProps.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
TestVulkanSurface::TestVulkanSurface(TestVulkanImage&& image)
|
||||
: image_(std::move(image)){};
|
||||
|
||||
std::unique_ptr<TestVulkanSurface> TestVulkanSurface::Create(
|
||||
const TestVulkanContext& context,
|
||||
const SkISize& surface_size) {
|
||||
auto image_result = context.CreateImage(surface_size);
|
||||
|
||||
if (!image_result.has_value()) {
|
||||
FML_LOG(ERROR) << "Could not create VkImage.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GrVkImageInfo image_info = {
|
||||
.fImage = image_result.value().GetImage(),
|
||||
.fImageTiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.fFormat = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
.fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.fSampleCount = 1,
|
||||
.fLevelCount = 1,
|
||||
};
|
||||
GrBackendTexture backend_texture(surface_size.width(), //
|
||||
surface_size.height(), //
|
||||
image_info //
|
||||
);
|
||||
|
||||
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
|
||||
|
||||
auto result = std::unique_ptr<TestVulkanSurface>(
|
||||
new TestVulkanSurface(std::move(image_result.value())));
|
||||
result->surface_ = SkSurface::MakeFromBackendTexture(
|
||||
context.GetGrDirectContext().get(), // context
|
||||
backend_texture, // back-end texture
|
||||
kTopLeft_GrSurfaceOrigin, // surface origin
|
||||
1, // sample count
|
||||
kRGBA_8888_SkColorType, // color type
|
||||
SkColorSpace::MakeSRGB(), // color space
|
||||
&surface_properties, // surface properties
|
||||
nullptr, // release proc
|
||||
nullptr // release context
|
||||
);
|
||||
|
||||
if (!result->surface_) {
|
||||
FML_LOG(ERROR)
|
||||
<< "Could not wrap VkImage as an SkSurface Vulkan render texture.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TestVulkanSurface::IsValid() const {
|
||||
return surface_ != nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> TestVulkanSurface::GetSurfaceSnapshot() const {
|
||||
if (!IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!surface_) {
|
||||
FML_LOG(ERROR) << "Aborting snapshot because of on-screen surface "
|
||||
"acquisition failure.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto device_snapshot = surface_->makeImageSnapshot();
|
||||
|
||||
if (!device_snapshot) {
|
||||
FML_LOG(ERROR) << "Could not create the device snapshot while attempting "
|
||||
"to snapshot the Vulkan surface.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto host_snapshot = device_snapshot->makeRasterImage();
|
||||
|
||||
if (!host_snapshot) {
|
||||
FML_LOG(ERROR) << "Could not create the host snapshot while attempting to "
|
||||
"snapshot the Vulkan surface.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return host_snapshot;
|
||||
}
|
||||
|
||||
VkImage TestVulkanSurface::GetImage() {
|
||||
return image_.GetImage();
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
41
engine/src/flutter/testing/test_vulkan_surface.h
Normal file
41
engine/src/flutter/testing/test_vulkan_surface.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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_VULKAN_SURFACE_IMPL_H_
|
||||
#define FLUTTER_TESTING_TEST_VULKAN_SURFACE_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#include "flutter/testing/test_vulkan_context.h"
|
||||
|
||||
#include "third_party/skia/include/core/SkRefCnt.h"
|
||||
#include "third_party/skia/include/core/SkSize.h"
|
||||
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
namespace testing {
|
||||
|
||||
class TestVulkanSurface {
|
||||
public:
|
||||
static std::unique_ptr<TestVulkanSurface> Create(
|
||||
const TestVulkanContext& context,
|
||||
const SkISize& surface_size);
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
sk_sp<SkImage> GetSurfaceSnapshot() const;
|
||||
|
||||
VkImage GetImage();
|
||||
|
||||
private:
|
||||
explicit TestVulkanSurface(TestVulkanImage&& image);
|
||||
|
||||
TestVulkanImage image_;
|
||||
sk_sp<SkSurface> surface_;
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_TESTING_TEST_VULKAN_SURFACE_IMPL_H_
|
||||
@@ -35,8 +35,7 @@ VulkanDevice::VulkanDevice(VulkanProcTable& p_vk,
|
||||
: vk(p_vk),
|
||||
physical_device_(std::move(physical_device)),
|
||||
graphics_queue_index_(std::numeric_limits<uint32_t>::max()),
|
||||
valid_(false),
|
||||
enable_validation_layers_(enable_validation_layers) {
|
||||
valid_(false) {
|
||||
if (!physical_device_ || !vk.AreInstanceProcsSetup()) {
|
||||
return;
|
||||
}
|
||||
@@ -74,7 +73,7 @@ VulkanDevice::VulkanDevice(VulkanProcTable& p_vk,
|
||||
};
|
||||
|
||||
auto enabled_layers =
|
||||
DeviceLayersToEnable(vk, physical_device_, enable_validation_layers_);
|
||||
DeviceLayersToEnable(vk, physical_device_, enable_validation_layers);
|
||||
|
||||
const char* layers[enabled_layers.size()];
|
||||
|
||||
@@ -122,6 +121,36 @@ VulkanDevice::VulkanDevice(VulkanProcTable& p_vk,
|
||||
|
||||
queue_ = VulkanHandle<VkQueue>(queue);
|
||||
|
||||
if (!InitializeCommandPool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
VulkanDevice::VulkanDevice(VulkanProcTable& p_vk,
|
||||
VulkanHandle<VkPhysicalDevice> physical_device,
|
||||
VulkanHandle<VkDevice> device,
|
||||
uint32_t queue_family_index,
|
||||
VulkanHandle<VkQueue> queue)
|
||||
: vk(p_vk),
|
||||
physical_device_(std::move(physical_device)),
|
||||
device_(std::move(device)),
|
||||
queue_(std::move(queue)),
|
||||
graphics_queue_index_(queue_family_index),
|
||||
valid_(false) {
|
||||
if (!physical_device_ || !vk.AreInstanceProcsSetup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InitializeCommandPool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
bool VulkanDevice::InitializeCommandPool() {
|
||||
const VkCommandPoolCreateInfo command_pool_create_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -134,7 +163,7 @@ VulkanDevice::VulkanDevice(VulkanProcTable& p_vk,
|
||||
nullptr, &command_pool)) !=
|
||||
VK_SUCCESS) {
|
||||
FML_DLOG(INFO) << "Could not create the command pool.";
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
command_pool_ = VulkanHandle<VkCommandPool>{
|
||||
@@ -142,7 +171,7 @@ VulkanDevice::VulkanDevice(VulkanProcTable& p_vk,
|
||||
vk.DestroyCommandPool(device_, pool, nullptr);
|
||||
}};
|
||||
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
VulkanDevice::~VulkanDevice() {
|
||||
|
||||
@@ -18,10 +18,20 @@ class VulkanSurface;
|
||||
|
||||
class VulkanDevice {
|
||||
public:
|
||||
/// @brief Create a new VkDevice with a resolved VkQueue suitable for
|
||||
/// rendering with Skia.
|
||||
///
|
||||
VulkanDevice(VulkanProcTable& vk,
|
||||
VulkanHandle<VkPhysicalDevice> physical_device,
|
||||
bool enable_validation_layers);
|
||||
|
||||
/// @brief Wrap an existing VkDevice and VkQueue.
|
||||
///
|
||||
VulkanDevice(VulkanProcTable& vk,
|
||||
VulkanHandle<VkPhysicalDevice> physical_device,
|
||||
VulkanHandle<VkDevice> device,
|
||||
uint32_t queue_family_index,
|
||||
VulkanHandle<VkQueue> queue);
|
||||
~VulkanDevice();
|
||||
|
||||
bool IsValid() const;
|
||||
@@ -72,8 +82,8 @@ class VulkanDevice {
|
||||
VulkanHandle<VkCommandPool> command_pool_;
|
||||
uint32_t graphics_queue_index_;
|
||||
bool valid_;
|
||||
bool enable_validation_layers_;
|
||||
|
||||
bool InitializeCommandPool();
|
||||
std::vector<VkQueueFamilyProperties> GetQueueFamilyProperties() const;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(VulkanDevice);
|
||||
|
||||
@@ -16,10 +16,18 @@ namespace vulkan {
|
||||
|
||||
VulkanProcTable::VulkanProcTable() : VulkanProcTable("libvulkan.so"){};
|
||||
|
||||
VulkanProcTable::VulkanProcTable(const char* path)
|
||||
VulkanProcTable::VulkanProcTable(const char* so_path)
|
||||
: handle_(nullptr), acquired_mandatory_proc_addresses_(false) {
|
||||
acquired_mandatory_proc_addresses_ =
|
||||
OpenLibraryHandle(path) && SetupLoaderProcAddresses();
|
||||
acquired_mandatory_proc_addresses_ = OpenLibraryHandle(so_path) &&
|
||||
SetupGetInstanceProcAddress() &&
|
||||
SetupLoaderProcAddresses();
|
||||
}
|
||||
|
||||
VulkanProcTable::VulkanProcTable(
|
||||
std::function<void*(VkInstance, const char*)> get_instance_proc_addr)
|
||||
: handle_(nullptr), acquired_mandatory_proc_addresses_(false) {
|
||||
GetInstanceProcAddr = get_instance_proc_addr;
|
||||
acquired_mandatory_proc_addresses_ = SetupLoaderProcAddresses();
|
||||
}
|
||||
|
||||
VulkanProcTable::~VulkanProcTable() {
|
||||
@@ -42,24 +50,27 @@ bool VulkanProcTable::AreDeviceProcsSetup() const {
|
||||
return device_;
|
||||
}
|
||||
|
||||
bool VulkanProcTable::SetupLoaderProcAddresses() {
|
||||
bool VulkanProcTable::SetupGetInstanceProcAddress() {
|
||||
if (!handle_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
GetInstanceProcAddr =
|
||||
GetInstanceProcAddr = reinterpret_cast<void* (*)(VkInstance, const char*)>(
|
||||
#if VULKAN_LINK_STATICALLY
|
||||
GetInstanceProcAddr = &vkGetInstanceProcAddr;
|
||||
&vkGetInstanceProcAddr
|
||||
#else // VULKAN_LINK_STATICALLY
|
||||
reinterpret_cast<PFN_vkGetInstanceProcAddr>(const_cast<uint8_t*>(
|
||||
handle_->ResolveSymbol("vkGetInstanceProcAddr")));
|
||||
const_cast<uint8_t*>(handle_->ResolveSymbol("vkGetInstanceProcAddr"))
|
||||
#endif // VULKAN_LINK_STATICALLY
|
||||
|
||||
);
|
||||
if (!GetInstanceProcAddr) {
|
||||
FML_DLOG(WARNING) << "Could not acquire vkGetInstanceProcAddr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanProcTable::SetupLoaderProcAddresses() {
|
||||
VulkanHandle<VkInstance> null_instance(VK_NULL_HANDLE, nullptr);
|
||||
|
||||
ACQUIRE_PROC(CreateInstance, null_instance);
|
||||
@@ -157,7 +168,11 @@ bool VulkanProcTable::OpenLibraryHandle(const char* path) {
|
||||
#else // VULKAN_LINK_STATICALLY
|
||||
handle_ = fml::NativeLibrary::Create(path);
|
||||
#endif // VULKAN_LINK_STATICALLY
|
||||
return !!handle_;
|
||||
if (!handle_) {
|
||||
FML_DLOG(WARNING) << "Could not open Vulkan library handle: " << path;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanProcTable::CloseLibraryHandle() {
|
||||
@@ -173,7 +188,8 @@ PFN_vkVoidFunction VulkanProcTable::AcquireProc(
|
||||
}
|
||||
|
||||
// A VK_NULL_HANDLE as the instance is an acceptable parameter.
|
||||
return GetInstanceProcAddr(instance, proc_name);
|
||||
return reinterpret_cast<PFN_vkVoidFunction>(
|
||||
GetInstanceProcAddr(instance, proc_name));
|
||||
}
|
||||
|
||||
PFN_vkVoidFunction VulkanProcTable::AcquireProc(
|
||||
|
||||
@@ -48,6 +48,12 @@ class VulkanProcTable : public fml::RefCountedThreadSafe<VulkanProcTable> {
|
||||
T proc_;
|
||||
};
|
||||
|
||||
VulkanProcTable();
|
||||
explicit VulkanProcTable(const char* so_path);
|
||||
explicit VulkanProcTable(
|
||||
std::function<void*(VkInstance, const char*)> get_instance_proc_addr);
|
||||
~VulkanProcTable();
|
||||
|
||||
bool HasAcquiredMandatoryProcAddresses() const;
|
||||
|
||||
bool IsValid() const;
|
||||
@@ -62,6 +68,8 @@ class VulkanProcTable : public fml::RefCountedThreadSafe<VulkanProcTable> {
|
||||
|
||||
GrVkGetProc CreateSkiaGetProc() const;
|
||||
|
||||
std::function<void*(VkInstance, const char*)> GetInstanceProcAddr = nullptr;
|
||||
|
||||
#define DEFINE_PROC(name) Proc<PFN_vk##name> name;
|
||||
|
||||
DEFINE_PROC(AcquireNextImageKHR);
|
||||
@@ -98,7 +106,6 @@ class VulkanProcTable : public fml::RefCountedThreadSafe<VulkanProcTable> {
|
||||
DEFINE_PROC(GetDeviceProcAddr);
|
||||
DEFINE_PROC(GetDeviceQueue);
|
||||
DEFINE_PROC(GetImageMemoryRequirements);
|
||||
DEFINE_PROC(GetInstanceProcAddr);
|
||||
DEFINE_PROC(GetPhysicalDeviceFeatures);
|
||||
DEFINE_PROC(GetPhysicalDeviceQueueFamilyProperties);
|
||||
DEFINE_PROC(QueueSubmit);
|
||||
@@ -135,10 +142,8 @@ class VulkanProcTable : public fml::RefCountedThreadSafe<VulkanProcTable> {
|
||||
VulkanHandle<VkInstance> instance_;
|
||||
VulkanHandle<VkDevice> device_;
|
||||
|
||||
VulkanProcTable();
|
||||
explicit VulkanProcTable(const char* path);
|
||||
~VulkanProcTable();
|
||||
bool OpenLibraryHandle(const char* path);
|
||||
bool SetupGetInstanceProcAddress();
|
||||
bool SetupLoaderProcAddresses();
|
||||
bool CloseLibraryHandle();
|
||||
PFN_vkVoidFunction AcquireProc(
|
||||
|
||||
@@ -350,7 +350,7 @@ VulkanSwapchain::AcquireResult VulkanSwapchain::AcquireSurface() {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 2:
|
||||
// Put semaphores in unsignaled state.
|
||||
// Put fences in an unsignaled state.
|
||||
// ---------------------------------------------------------------------------
|
||||
if (!backbuffer->ResetFences()) {
|
||||
FML_DLOG(INFO) << "Could not reset fences.";
|
||||
|
||||
@@ -19,24 +19,21 @@
|
||||
namespace vulkan {
|
||||
|
||||
VulkanWindow::VulkanWindow(fml::RefPtr<VulkanProcTable> proc_table,
|
||||
std::unique_ptr<VulkanNativeSurface> native_surface,
|
||||
bool render_to_surface)
|
||||
std::unique_ptr<VulkanNativeSurface> native_surface)
|
||||
: VulkanWindow(/*context/*/ nullptr,
|
||||
proc_table,
|
||||
std::move(native_surface),
|
||||
render_to_surface) {}
|
||||
std::move(native_surface)) {}
|
||||
|
||||
VulkanWindow::VulkanWindow(const sk_sp<GrDirectContext>& context,
|
||||
fml::RefPtr<VulkanProcTable> proc_table,
|
||||
std::unique_ptr<VulkanNativeSurface> native_surface,
|
||||
bool render_to_surface)
|
||||
std::unique_ptr<VulkanNativeSurface> native_surface)
|
||||
: valid_(false), vk(std::move(proc_table)), skia_gr_context_(context) {
|
||||
if (!vk || !vk->HasAcquiredMandatoryProcAddresses()) {
|
||||
FML_DLOG(INFO) << "Proc table has not acquired mandatory proc addresses.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (native_surface == nullptr || !native_surface->IsValid()) {
|
||||
if (native_surface && !native_surface->IsValid()) {
|
||||
FML_DLOG(INFO) << "Native surface is invalid.";
|
||||
return;
|
||||
}
|
||||
@@ -70,9 +67,7 @@ VulkanWindow::VulkanWindow(const sk_sp<GrDirectContext>& context,
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(38466): Refactor GPU surface APIs take into account the fact that an
|
||||
// external view embedder may want to render to the root surface.
|
||||
if (!render_to_surface) {
|
||||
if (!native_surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@ class VulkanWindow {
|
||||
/// GrDirectContext.
|
||||
///
|
||||
VulkanWindow(fml::RefPtr<VulkanProcTable> proc_table,
|
||||
std::unique_ptr<VulkanNativeSurface> native_surface,
|
||||
bool render_to_surface);
|
||||
std::unique_ptr<VulkanNativeSurface> native_surface);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Construct a VulkanWindow. Let reuse an existing
|
||||
@@ -45,8 +44,7 @@ class VulkanWindow {
|
||||
///
|
||||
VulkanWindow(const sk_sp<GrDirectContext>& context,
|
||||
fml::RefPtr<VulkanProcTable> proc_table,
|
||||
std::unique_ptr<VulkanNativeSurface> native_surface,
|
||||
bool render_to_surface);
|
||||
std::unique_ptr<VulkanNativeSurface> native_surface);
|
||||
|
||||
~VulkanWindow();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user