Commit a2b8508c authored by Peng Huang's avatar Peng Huang Committed by Commit Bot

Fix vkCreateSwapchainKHR issue with NV GPU and sandbox on Windows

There is an issue in NVIDIA driver, vkCreateSwapChianKHR() will fail
with sandbox on Windows, if the gpu::SurfaceHandle(HWND) is created by
browser process. Workaround the issue, by creating a child HWND use to
create VulkanSurface.

Bug: 962633,1068742
Change-Id: I471db2e1db673fece37a5746b5e42a8cf9d12965
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2152707
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarSergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarVasiliy Telezhnikov <vasilyt@chromium.org>
Auto-Submit: Peng Huang <penghuang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#760518}
parent f94d132e
......@@ -60,6 +60,14 @@ SkiaOutputDeviceVulkan::~SkiaOutputDeviceVulkan() {
vulkan_surface_->Destroy();
}
#if defined(OS_WIN)
gpu::SurfaceHandle SkiaOutputDeviceVulkan::GetChildSurfaceHandle() {
if (vulkan_surface_->accelerated_widget() != surface_handle_)
return vulkan_surface_->accelerated_widget();
return gpu::kNullSurfaceHandle;
}
#endif
bool SkiaOutputDeviceVulkan::Reshape(const gfx::Size& size,
float device_scale_factor,
const gfx::ColorSpace& color_space,
......
......@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/optional.h"
#include "base/util/type_safety/pass_key.h"
#include "build/build_config.h"
#include "components/viz/service/display_embedder/skia_output_device.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/vulkan/vulkan_swap_chain.h"
......@@ -39,6 +40,9 @@ class SkiaOutputDeviceVulkan final : public SkiaOutputDevice {
gpu::MemoryTracker* memory_tracker,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback);
#if defined(OS_WIN)
gpu::SurfaceHandle GetChildSurfaceHandle();
#endif
// SkiaOutputDevice implementation:
bool Reshape(const gfx::Size& size,
float device_scale_factor,
......
......@@ -1580,9 +1580,19 @@ bool SkiaOutputSurfaceImplOnGpu::InitializeForVulkan() {
gl_surface_ = output_device->gl_surface();
output_device_ = std::move(output_device);
} else {
output_device_ = SkiaOutputDeviceVulkan::Create(
auto output_device = SkiaOutputDeviceVulkan::Create(
vulkan_context_provider_, dependency_->GetSurfaceHandle(),
memory_tracker_.get(), did_swap_buffer_complete_callback_);
#if defined(OS_WIN)
gpu::SurfaceHandle child_surface =
output_device ? output_device->GetChildSurfaceHandle()
: gpu::kNullSurfaceHandle;
if (child_surface != gpu::kNullSurfaceHandle) {
DidCreateAcceleratedSurfaceChildWindow(dependency_->GetSurfaceHandle(),
child_surface);
}
#endif
output_device_ = std::move(output_device);
}
#endif
}
......
......@@ -60,7 +60,7 @@ std::unique_ptr<VulkanSurface> VulkanImplementationAndroid::CreateViewSurface(
return nullptr;
}
return std::make_unique<VulkanSurface>(vulkan_instance_.vk_instance(),
return std::make_unique<VulkanSurface>(vulkan_instance_.vk_instance(), window,
surface,
false /* use_protected_memory */);
}
......
......@@ -75,9 +75,11 @@ VulkanSurface::~VulkanSurface() {
}
VulkanSurface::VulkanSurface(VkInstance vk_instance,
gfx::AcceleratedWidget accelerated_widget,
VkSurfaceKHR surface,
bool enforce_protected_memory)
: vk_instance_(vk_instance),
accelerated_widget_(accelerated_widget),
surface_(surface),
enforce_protected_memory_(enforce_protected_memory) {
DCHECK_NE(static_cast<VkSurfaceKHR>(VK_NULL_HANDLE), surface_);
......
......@@ -12,6 +12,7 @@
#include "gpu/vulkan/vulkan_export.h"
#include "gpu/vulkan/vulkan_swap_chain.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/overlay_transform.h"
#include "ui/gfx/swap_result.h"
......@@ -32,6 +33,7 @@ class VULKAN_EXPORT VulkanSurface {
};
VulkanSurface(VkInstance vk_instance,
gfx::AcceleratedWidget accelerated_widget,
VkSurfaceKHR surface,
bool enforce_protected_memory);
......@@ -53,6 +55,9 @@ class VULKAN_EXPORT VulkanSurface {
// See VkSwapchainCreateInfoKHR::preTransform for detail.
virtual bool Reshape(const gfx::Size& size, gfx::OverlayTransform transform);
gfx::AcceleratedWidget accelerated_widget() const {
return accelerated_widget_;
}
VulkanSwapChain* swap_chain() const { return swap_chain_.get(); }
uint32_t swap_chain_generation() const { return swap_chain_generation_; }
const gfx::Size& image_size() const { return image_size_; }
......@@ -65,6 +70,7 @@ class VULKAN_EXPORT VulkanSurface {
const VkInstance vk_instance_;
const gfx::AcceleratedWidget accelerated_widget_;
VkSurfaceKHR surface_ = VK_NULL_HANDLE;
VkSurfaceFormatKHR surface_format_ = {};
VulkanDeviceQueue* device_queue_ = nullptr;
......
......@@ -18,13 +18,18 @@ component("win32") {
sources = [
"vulkan_implementation_win32.cc",
"vulkan_implementation_win32.h",
"vulkan_surface_win32.cc",
"vulkan_surface_win32.h",
]
defines = [ "IS_VULKAN_WIN32_IMPL" ]
public_configs = [ ":vulkan_win32" ]
deps = [ "//ui/gfx" ]
deps = [
"//base/util/type_safety",
"//ui/gfx",
]
public_deps = [
"//base",
......
......@@ -11,8 +11,8 @@
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_image.h"
#include "gpu/vulkan/vulkan_instance.h"
#include "gpu/vulkan/vulkan_surface.h"
#include "gpu/vulkan/vulkan_util.h"
#include "gpu/vulkan/win32/vulkan_surface_win32.h"
#include "ui/gfx/gpu_fence.h"
#include "ui/gfx/gpu_memory_buffer.h"
......@@ -55,22 +55,7 @@ VulkanInstance* VulkanImplementationWin32::GetVulkanInstance() {
std::unique_ptr<VulkanSurface> VulkanImplementationWin32::CreateViewSurface(
gfx::AcceleratedWidget window) {
VkSurfaceKHR surface;
VkWin32SurfaceCreateInfoKHR surface_create_info = {};
surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
surface_create_info.hinstance =
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(window, GWLP_HINSTANCE));
surface_create_info.hwnd = window;
VkResult result = vkCreateWin32SurfaceKHR(
vulkan_instance_.vk_instance(), &surface_create_info, nullptr, &surface);
if (VK_SUCCESS != result) {
DLOG(ERROR) << "vkCreatWin32SurfaceKHR() failed: " << result;
return nullptr;
}
return std::make_unique<VulkanSurface>(vulkan_instance_.vk_instance(),
surface,
/* use_protected_memory */ false);
return VulkanSurfaceWin32::Create(vulkan_instance_.vk_instance(), window);
}
bool VulkanImplementationWin32::GetPhysicalDevicePresentationSupport(
......
// Copyright 2020 The Chromium 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 "gpu/vulkan/win32/vulkan_surface_win32.h"
#include <windows.h>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "ui/gfx/win/window_impl.h"
namespace gpu {
namespace {
// It is set by WindowThread ctor, and cleared by WindowThread dtor.
VulkanSurfaceWin32::WindowThread* g_thread = nullptr;
// It is set by HiddenToplevelWindow ctor, and cleared by HiddenToplevelWindow
// dtor.
class HiddenToplevelWindow* g_initial_parent_window = nullptr;
class HiddenToplevelWindow : public gfx::WindowImpl,
public base::RefCounted<HiddenToplevelWindow> {
public:
HiddenToplevelWindow() : gfx::WindowImpl("VulkanHiddenToplevelWindow") {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!g_initial_parent_window);
set_initial_class_style(CS_OWNDC);
set_window_style(WS_POPUP | WS_DISABLED);
Init(GetDesktopWindow(), gfx::Rect());
g_initial_parent_window = this;
}
HiddenToplevelWindow(const HiddenToplevelWindow&) = delete;
HiddenToplevelWindow& operator=(const HiddenToplevelWindow&) = delete;
private:
friend class base::RefCounted<HiddenToplevelWindow>;
// gfx::WindowImpl:
BOOL ProcessWindowMessage(HWND window,
UINT message,
WPARAM w_param,
LPARAM l_param,
LRESULT& result,
DWORD msg_map_id) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (message == WM_CLOSE) {
// Prevent closing the window, since external apps may get a handle to
// this window and attempt to close it.
result = 0;
return true;
}
return false;
}
~HiddenToplevelWindow() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(g_initial_parent_window, this);
g_initial_parent_window = nullptr;
}
THREAD_CHECKER(thread_checker_);
};
class ChildWindow : public gfx::WindowImpl {
public:
explicit ChildWindow(HWND parent_window)
: gfx::WindowImpl("VulkanHiddenToplevelWindow") {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// If there is no other ChildWindow instance, |g_initial_parent_window| will
// be nullptr, and then we need to create one. If |g_initial_parent_window|
// is not nullptr, so |g_initial_parent_window| will not be destroyed until
// this ChildWindow is destroyed.
initial_parent_window_ = g_initial_parent_window;
if (!initial_parent_window_)
initial_parent_window_ = base::MakeRefCounted<HiddenToplevelWindow>();
set_initial_class_style(CS_OWNDC);
set_window_style(WS_VISIBLE | WS_CHILD | WS_DISABLED);
RECT window_rect;
if (!GetClientRect(parent_window, &window_rect))
PLOG(DFATAL) << "GetClientRect() failed.";
gfx::Rect bounds(window_rect);
bounds.set_origin(gfx::Point(0, 0));
Init(initial_parent_window_->hwnd(), bounds);
}
~ChildWindow() override { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
ChildWindow(const ChildWindow&) = delete;
ChildWindow& operator=(const ChildWindow&) = delete;
private:
// gfx::WindowImpl:
BOOL ProcessWindowMessage(HWND window,
UINT message,
WPARAM w_param,
LPARAM l_param,
LRESULT& result,
DWORD msg_map_id) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
switch (message) {
case WM_ERASEBKGND:
// Prevent windows from erasing the background.
return true;
case WM_PAINT:
// Do not paint anything.
PAINTSTRUCT paint;
if (BeginPaint(window, &paint))
EndPaint(window, &paint);
return false;
default:
return false;
}
}
// A temporary parent window for this child window, it will be reparented
// by browser soon. All child windows share one initial parent window.
// The initial parent window will be destroyed with the last child window.
scoped_refptr<HiddenToplevelWindow> initial_parent_window_;
THREAD_CHECKER(thread_checker_);
};
void CreateChildWindow(HWND parent_window,
std::unique_ptr<ChildWindow>* window,
base::WaitableEvent* event) {
*window = std::make_unique<ChildWindow>(parent_window);
event->Signal();
}
} // namespace
class VulkanSurfaceWin32::WindowThread : public base::Thread,
public base::RefCounted<WindowThread> {
public:
WindowThread() : base::Thread("VulkanWindowThread") {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!g_thread);
g_thread = this;
base::Thread::Options options(base::MessagePumpType::UI, 0);
StartWithOptions(options);
}
WindowThread(const WindowThread&) = delete;
WindowThread& operator=(const WindowThread&) = delete;
private:
friend class base::RefCounted<WindowThread>;
~WindowThread() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(g_thread, this);
Stop();
g_thread = nullptr;
}
THREAD_CHECKER(thread_checker_);
};
// static
std::unique_ptr<VulkanSurfaceWin32> VulkanSurfaceWin32::Create(
VkInstance vk_instance,
HWND parent_window) {
scoped_refptr<WindowThread> thread = g_thread;
if (!thread) {
// If there is no other VulkanSurfaceWin32, g_thread will be nullptr, and
// we need to create a thread for running child window message loop.
// Otherwise keep a ref of g_thread, so it will not be destroyed until the
// this VulkanSurfaceWin32 is destroyed.
thread = base::MakeRefCounted<WindowThread>();
g_thread = thread.get();
}
// vkCreateSwapChainKHR() fails in sandbox with a window which is created by
// other process with NVIDIA driver. Workaround the problem by creating a
// child window and use it to create vulkan surface.
// TODO(penghuang): Only apply this workaround with NVIDIA GPU?
// https://crbug.com/1068742
std::unique_ptr<ChildWindow> window;
base::WaitableEvent event;
thread->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&CreateChildWindow, parent_window, &window, &event));
event.Wait();
VkSurfaceKHR surface;
VkWin32SurfaceCreateInfoKHR surface_create_info = {
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
.hinstance = reinterpret_cast<HINSTANCE>(
GetWindowLongPtr(window->hwnd(), GWLP_HINSTANCE)),
.hwnd = window->hwnd(),
};
VkResult result = vkCreateWin32SurfaceKHR(vk_instance, &surface_create_info,
nullptr, &surface);
if (VK_SUCCESS != result) {
LOG(DFATAL) << "vkCreatWin32SurfaceKHR() failed: " << result;
return nullptr;
}
return std::make_unique<VulkanSurfaceWin32>(
util::PassKey<VulkanSurfaceWin32>(), vk_instance, surface,
std::move(thread), std::move(window));
}
VulkanSurfaceWin32::VulkanSurfaceWin32(
util::PassKey<VulkanSurfaceWin32> pass_key,
VkInstance vk_instance,
VkSurfaceKHR vk_surface,
scoped_refptr<WindowThread> thread,
std::unique_ptr<gfx::WindowImpl> window)
: VulkanSurface(vk_instance,
window->hwnd(),
vk_surface,
false /* use_protected_memory */),
thread_(std::move(thread)),
window_(std::move(window)) {}
VulkanSurfaceWin32::~VulkanSurfaceWin32() {
thread_->task_runner()->DeleteSoon(FROM_HERE, std::move(window_));
}
bool VulkanSurfaceWin32::Reshape(const gfx::Size& size,
gfx::OverlayTransform pre_transform) {
DCHECK_EQ(pre_transform, gfx::OVERLAY_TRANSFORM_NONE);
constexpr auto kFlags = SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS |
SWP_NOOWNERZORDER | SWP_NOZORDER;
if (!SetWindowPos(window_->hwnd(), nullptr, 0, 0, size.width(), size.height(),
kFlags)) {
PLOG(DFATAL) << "SetWindowPos() failed";
return false;
}
return VulkanSurface::Reshape(size, pre_transform);
}
} // namespace gpu
\ No newline at end of file
// Copyright 2020 The Chromium 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 GPU_VULKAN_WIN32_VULKAN_SURFACE_WIN32_H_
#define GPU_VULKAN_WIN32_VULKAN_SURFACE_WIN32_H_
#include <vulkan/vulkan.h>
#include "base/component_export.h"
#include "base/util/type_safety/pass_key.h"
#include "gpu/vulkan/vulkan_surface.h"
namespace gfx {
class WindowImpl;
}
namespace gpu {
class COMPONENT_EXPORT(VULKAN_WIN32) VulkanSurfaceWin32 : public VulkanSurface {
public:
static std::unique_ptr<VulkanSurfaceWin32> Create(VkInstance vk_instance,
HWND parent_window);
class WindowThread;
VulkanSurfaceWin32(util::PassKey<VulkanSurfaceWin32> pass_key,
VkInstance vk_instance,
VkSurfaceKHR vk_surface,
scoped_refptr<WindowThread> thread,
std::unique_ptr<gfx::WindowImpl> window);
~VulkanSurfaceWin32() override;
VulkanSurfaceWin32(const VulkanSurfaceWin32&) = delete;
VulkanSurfaceWin32& operator=(const VulkanSurfaceWin32&) = delete;
private:
// VulkanSurface:
bool Reshape(const gfx::Size& size,
gfx::OverlayTransform pre_transform) override;
// The thread for running message loop of child |window_|.
// All VulkanSurfaceWin32 share one thread. The thread will be destroyed with
// the last VulkanSurfaceWin32.
scoped_refptr<WindowThread> thread_;
std::unique_ptr<gfx::WindowImpl> window_;
};
} // namespace gpu
#endif // GPU_VULKAN_WIN32_VULKAN_SURFACE_WIN32_H_
\ No newline at end of file
......@@ -76,7 +76,10 @@ VulkanSurfaceX11::VulkanSurfaceX11(VkInstance vk_instance,
VkSurfaceKHR vk_surface,
Window parent_window,
Window window)
: VulkanSurface(vk_instance, vk_surface, false /* use_protected_memory */),
: VulkanSurface(vk_instance,
window,
vk_surface,
false /* use_protected_memory */),
parent_window_(parent_window),
window_(window),
expose_event_forwarder_(new ExposeEventForwarder(this)) {}
......
......@@ -111,7 +111,7 @@ VulkanImplementationScenic::CreateViewSurface(gfx::AcceleratedWidget window) {
}
return std::make_unique<gpu::VulkanSurface>(
vulkan_instance_.vk_instance(), surface,
vulkan_instance_.vk_instance(), window, surface,
enforce_protected_memory() /* use_protected_memory */);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment