vulkan_surface_win32.cc 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "gpu/vulkan/win32/vulkan_surface_win32.h"
  5. #include <windows.h>
  6. #include "base/bind.h"
  7. #include "base/logging.h"
  8. #include "base/memory/ref_counted.h"
  9. #include "base/synchronization/waitable_event.h"
  10. #include "base/threading/thread.h"
  11. #include "base/threading/thread_checker.h"
  12. #include "gpu/vulkan/vulkan_function_pointers.h"
  13. #include "ui/gfx/win/window_impl.h"
  14. namespace gpu {
  15. namespace {
  16. // It is set by WindowThread ctor, and cleared by WindowThread dtor.
  17. VulkanSurfaceWin32::WindowThread* g_thread = nullptr;
  18. // It is set by HiddenToplevelWindow ctor, and cleared by HiddenToplevelWindow
  19. // dtor.
  20. class HiddenToplevelWindow* g_initial_parent_window = nullptr;
  21. class HiddenToplevelWindow : public gfx::WindowImpl,
  22. public base::RefCounted<HiddenToplevelWindow> {
  23. public:
  24. HiddenToplevelWindow() : gfx::WindowImpl("VulkanHiddenToplevelWindow") {
  25. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  26. DCHECK(!g_initial_parent_window);
  27. set_initial_class_style(CS_OWNDC);
  28. set_window_style(WS_POPUP | WS_DISABLED);
  29. Init(GetDesktopWindow(), gfx::Rect());
  30. g_initial_parent_window = this;
  31. }
  32. HiddenToplevelWindow(const HiddenToplevelWindow&) = delete;
  33. HiddenToplevelWindow& operator=(const HiddenToplevelWindow&) = delete;
  34. private:
  35. friend class base::RefCounted<HiddenToplevelWindow>;
  36. // gfx::WindowImpl:
  37. BOOL ProcessWindowMessage(HWND window,
  38. UINT message,
  39. WPARAM w_param,
  40. LPARAM l_param,
  41. LRESULT& result,
  42. DWORD msg_map_id) override {
  43. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  44. if (message == WM_CLOSE) {
  45. // Prevent closing the window, since external apps may get a handle to
  46. // this window and attempt to close it.
  47. result = 0;
  48. return true;
  49. }
  50. return false;
  51. }
  52. ~HiddenToplevelWindow() override {
  53. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  54. DCHECK_EQ(g_initial_parent_window, this);
  55. g_initial_parent_window = nullptr;
  56. }
  57. THREAD_CHECKER(thread_checker_);
  58. };
  59. class ChildWindow : public gfx::WindowImpl {
  60. public:
  61. explicit ChildWindow(HWND parent_window)
  62. : gfx::WindowImpl("VulkanHiddenToplevelWindow") {
  63. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  64. // If there is no other ChildWindow instance, |g_initial_parent_window| will
  65. // be nullptr, and then we need to create one. If |g_initial_parent_window|
  66. // is not nullptr, so |g_initial_parent_window| will not be destroyed until
  67. // this ChildWindow is destroyed.
  68. initial_parent_window_ = g_initial_parent_window;
  69. if (!initial_parent_window_)
  70. initial_parent_window_ = base::MakeRefCounted<HiddenToplevelWindow>();
  71. set_initial_class_style(CS_OWNDC);
  72. set_window_style(WS_VISIBLE | WS_CHILD | WS_DISABLED);
  73. RECT window_rect;
  74. if (!GetClientRect(parent_window, &window_rect))
  75. PLOG(DFATAL) << "GetClientRect() failed.";
  76. gfx::Rect bounds(window_rect);
  77. bounds.set_origin(gfx::Point(0, 0));
  78. Init(initial_parent_window_->hwnd(), bounds);
  79. }
  80. ~ChildWindow() override { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
  81. ChildWindow(const ChildWindow&) = delete;
  82. ChildWindow& operator=(const ChildWindow&) = delete;
  83. private:
  84. // gfx::WindowImpl:
  85. BOOL ProcessWindowMessage(HWND window,
  86. UINT message,
  87. WPARAM w_param,
  88. LPARAM l_param,
  89. LRESULT& result,
  90. DWORD msg_map_id) override {
  91. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  92. switch (message) {
  93. case WM_ERASEBKGND:
  94. // Prevent windows from erasing the background.
  95. return true;
  96. case WM_PAINT:
  97. // Do not paint anything.
  98. PAINTSTRUCT paint;
  99. if (BeginPaint(window, &paint))
  100. EndPaint(window, &paint);
  101. return false;
  102. default:
  103. return false;
  104. }
  105. }
  106. // A temporary parent window for this child window, it will be reparented
  107. // by browser soon. All child windows share one initial parent window.
  108. // The initial parent window will be destroyed with the last child window.
  109. scoped_refptr<HiddenToplevelWindow> initial_parent_window_;
  110. THREAD_CHECKER(thread_checker_);
  111. };
  112. void CreateChildWindow(HWND parent_window,
  113. std::unique_ptr<ChildWindow>* window,
  114. base::WaitableEvent* event) {
  115. *window = std::make_unique<ChildWindow>(parent_window);
  116. event->Signal();
  117. }
  118. } // namespace
  119. class VulkanSurfaceWin32::WindowThread : public base::Thread,
  120. public base::RefCounted<WindowThread> {
  121. public:
  122. WindowThread() : base::Thread("VulkanWindowThread") {
  123. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  124. DCHECK(!g_thread);
  125. g_thread = this;
  126. base::Thread::Options options(base::MessagePumpType::UI, 0);
  127. StartWithOptions(std::move(options));
  128. }
  129. WindowThread(const WindowThread&) = delete;
  130. WindowThread& operator=(const WindowThread&) = delete;
  131. private:
  132. friend class base::RefCounted<WindowThread>;
  133. ~WindowThread() override {
  134. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  135. DCHECK_EQ(g_thread, this);
  136. Stop();
  137. g_thread = nullptr;
  138. }
  139. THREAD_CHECKER(thread_checker_);
  140. };
  141. // static
  142. std::unique_ptr<VulkanSurfaceWin32> VulkanSurfaceWin32::Create(
  143. VkInstance vk_instance,
  144. HWND parent_window) {
  145. scoped_refptr<WindowThread> thread = g_thread;
  146. if (!thread) {
  147. // If there is no other VulkanSurfaceWin32, g_thread will be nullptr, and
  148. // we need to create a thread for running child window message loop.
  149. // Otherwise keep a ref of g_thread, so it will not be destroyed until the
  150. // this VulkanSurfaceWin32 is destroyed.
  151. thread = base::MakeRefCounted<WindowThread>();
  152. g_thread = thread.get();
  153. }
  154. // vkCreateSwapChainKHR() fails in sandbox with a window which is created by
  155. // other process with NVIDIA driver. Workaround the problem by creating a
  156. // child window and use it to create vulkan surface.
  157. // TODO(penghuang): Only apply this workaround with NVIDIA GPU?
  158. // https://crbug.com/1068742
  159. std::unique_ptr<ChildWindow> window;
  160. base::WaitableEvent event;
  161. thread->task_runner()->PostTask(
  162. FROM_HERE,
  163. base::BindOnce(&CreateChildWindow, parent_window, &window, &event));
  164. event.Wait();
  165. VkSurfaceKHR surface;
  166. VkWin32SurfaceCreateInfoKHR surface_create_info = {
  167. .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
  168. .hinstance = reinterpret_cast<HINSTANCE>(
  169. GetWindowLongPtr(window->hwnd(), GWLP_HINSTANCE)),
  170. .hwnd = window->hwnd(),
  171. };
  172. VkResult result = vkCreateWin32SurfaceKHR(vk_instance, &surface_create_info,
  173. nullptr, &surface);
  174. if (VK_SUCCESS != result) {
  175. LOG(DFATAL) << "vkCreatWin32SurfaceKHR() failed: " << result;
  176. return nullptr;
  177. }
  178. return std::make_unique<VulkanSurfaceWin32>(
  179. base::PassKey<VulkanSurfaceWin32>(), vk_instance, surface,
  180. std::move(thread), std::move(window));
  181. }
  182. VulkanSurfaceWin32::VulkanSurfaceWin32(
  183. base::PassKey<VulkanSurfaceWin32> pass_key,
  184. VkInstance vk_instance,
  185. VkSurfaceKHR vk_surface,
  186. scoped_refptr<WindowThread> thread,
  187. std::unique_ptr<gfx::WindowImpl> window)
  188. : VulkanSurface(vk_instance, window->hwnd(), vk_surface),
  189. thread_(std::move(thread)),
  190. window_(std::move(window)) {}
  191. VulkanSurfaceWin32::~VulkanSurfaceWin32() {
  192. thread_->task_runner()->DeleteSoon(FROM_HERE, std::move(window_));
  193. }
  194. bool VulkanSurfaceWin32::Reshape(const gfx::Size& size,
  195. gfx::OverlayTransform pre_transform) {
  196. DCHECK_EQ(pre_transform, gfx::OVERLAY_TRANSFORM_NONE);
  197. constexpr auto kFlags = SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS |
  198. SWP_NOOWNERZORDER | SWP_NOZORDER;
  199. if (!SetWindowPos(window_->hwnd(), nullptr, 0, 0, size.width(), size.height(),
  200. kFlags)) {
  201. PLOG(DFATAL) << "SetWindowPos() failed";
  202. return false;
  203. }
  204. return VulkanSurface::Reshape(size, pre_transform);
  205. }
  206. } // namespace gpu