1 /* 2 * Copyright (C) 2016 Google, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <cassert> 18 #include <iostream> 19 #include <sstream> 20 21 #include "Helpers.h" 22 #include "Game.h" 23 #include "ShellWin32.h" 24 25 namespace { 26 27 class Win32Timer { 28 public: 29 Win32Timer() 30 { 31 LARGE_INTEGER freq; 32 QueryPerformanceFrequency(&freq); 33 freq_ = static_cast<double>(freq.QuadPart); 34 35 reset(); 36 } 37 38 void reset() 39 { 40 QueryPerformanceCounter(&start_); 41 } 42 43 double get() const 44 { 45 LARGE_INTEGER now; 46 QueryPerformanceCounter(&now); 47 48 return static_cast<double>(now.QuadPart - start_.QuadPart) / freq_; 49 } 50 51 private: 52 double freq_; 53 LARGE_INTEGER start_; 54 }; 55 56 } // namespace 57 58 ShellWin32::ShellWin32(Game &game) : Shell(game), hwnd_(nullptr) 59 { 60 instance_extensions_.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); 61 init_vk(); 62 } 63 64 ShellWin32::~ShellWin32() 65 { 66 cleanup_vk(); 67 FreeLibrary(hmodule_); 68 } 69 70 void ShellWin32::create_window() 71 { 72 const std::string class_name(settings_.name + "WindowClass"); 73 74 hinstance_ = GetModuleHandle(nullptr); 75 76 WNDCLASSEX win_class = {}; 77 win_class.cbSize = sizeof(WNDCLASSEX); 78 win_class.style = CS_HREDRAW | CS_VREDRAW; 79 win_class.lpfnWndProc = window_proc; 80 win_class.hInstance = hinstance_; 81 win_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 82 win_class.lpszClassName = class_name.c_str(); 83 RegisterClassEx(&win_class); 84 85 const DWORD win_style = 86 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_OVERLAPPEDWINDOW; 87 88 RECT win_rect = { 0, 0, settings_.initial_width, settings_.initial_height }; 89 AdjustWindowRect(&win_rect, win_style, false); 90 91 hwnd_ = CreateWindowEx(WS_EX_APPWINDOW, 92 class_name.c_str(), 93 settings_.name.c_str(), 94 win_style, 95 0, 96 0, 97 win_rect.right - win_rect.left, 98 win_rect.bottom - win_rect.top, 99 nullptr, 100 nullptr, 101 hinstance_, 102 nullptr); 103 104 SetForegroundWindow(hwnd_); 105 SetWindowLongPtr(hwnd_, GWLP_USERDATA, (LONG_PTR) this); 106 } 107 108 PFN_vkGetInstanceProcAddr ShellWin32::load_vk() 109 { 110 const char filename[] = "vulkan-1.dll"; 111 HMODULE mod; 112 PFN_vkGetInstanceProcAddr get_proc; 113 114 mod = LoadLibrary(filename); 115 if (mod) { 116 get_proc = reinterpret_cast<PFN_vkGetInstanceProcAddr>(GetProcAddress( 117 mod, "vkGetInstanceProcAddr")); 118 } 119 120 if (!mod || !get_proc) { 121 std::stringstream ss; 122 ss << "failed to load " << filename; 123 124 if (mod) 125 FreeLibrary(mod); 126 127 throw std::runtime_error(ss.str()); 128 } 129 130 hmodule_ = mod; 131 132 return get_proc; 133 } 134 135 bool ShellWin32::can_present(VkPhysicalDevice phy, uint32_t queue_family) 136 { 137 return vk::GetPhysicalDeviceWin32PresentationSupportKHR( 138 phy, queue_family) == VK_TRUE; 139 } 140 141 VkSurfaceKHR ShellWin32::create_surface(VkInstance instance) 142 { 143 VkWin32SurfaceCreateInfoKHR surface_info = {}; 144 surface_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; 145 surface_info.hinstance = hinstance_; 146 surface_info.hwnd = hwnd_; 147 148 VkSurfaceKHR surface; 149 vk::assert_success(vk::CreateWin32SurfaceKHR(instance, &surface_info, nullptr, &surface)); 150 151 return surface; 152 } 153 154 LRESULT ShellWin32::handle_message(UINT msg, WPARAM wparam, LPARAM lparam) 155 { 156 switch (msg) { 157 case WM_SIZE: 158 { 159 UINT w = LOWORD(lparam); 160 UINT h = HIWORD(lparam); 161 resize_swapchain(w, h); 162 } 163 break; 164 case WM_KEYDOWN: 165 { 166 Game::Key key; 167 168 switch (wparam) { 169 case VK_ESCAPE: 170 key = Game::KEY_ESC; 171 break; 172 case VK_UP: 173 key = Game::KEY_UP; 174 break; 175 case VK_DOWN: 176 key = Game::KEY_DOWN; 177 break; 178 case VK_SPACE: 179 key = Game::KEY_SPACE; 180 break; 181 default: 182 key = Game::KEY_UNKNOWN; 183 break; 184 } 185 186 game_.on_key(key); 187 } 188 break; 189 case WM_CLOSE: 190 game_.on_key(Game::KEY_SHUTDOWN); 191 break; 192 case WM_DESTROY: 193 quit(); 194 break; 195 default: 196 return DefWindowProc(hwnd_, msg, wparam, lparam); 197 break; 198 } 199 200 return 0; 201 } 202 203 void ShellWin32::quit() 204 { 205 PostQuitMessage(0); 206 } 207 208 void ShellWin32::run() 209 { 210 create_window(); 211 212 create_context(); 213 resize_swapchain(settings_.initial_width, settings_.initial_height); 214 215 Win32Timer timer; 216 double current_time = timer.get(); 217 218 while (true) { 219 bool quit = false; 220 221 assert(settings_.animate); 222 223 // process all messages 224 MSG msg; 225 while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { 226 if (msg.message == WM_QUIT) { 227 quit = true; 228 break; 229 } 230 231 TranslateMessage(&msg); 232 DispatchMessage(&msg); 233 } 234 235 if (quit) 236 break; 237 238 acquire_back_buffer(); 239 240 double t = timer.get(); 241 add_game_time(static_cast<float>(t - current_time)); 242 243 present_back_buffer(); 244 245 current_time = t; 246 } 247 248 destroy_context(); 249 250 DestroyWindow(hwnd_); 251 } 252