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 LARGE_INTEGER freq; 31 QueryPerformanceFrequency(&freq); 32 freq_ = static_cast<double>(freq.QuadPart); 33 34 reset(); 35 } 36 37 void reset() { QueryPerformanceCounter(&start_); } 38 39 double get() const { 40 LARGE_INTEGER now; 41 QueryPerformanceCounter(&now); 42 43 return static_cast<double>(now.QuadPart - start_.QuadPart) / freq_; 44 } 45 46 private: 47 double freq_; 48 LARGE_INTEGER start_; 49 }; 50 51 } // namespace 52 53 ShellWin32::ShellWin32(Game &game) : Shell(game), hwnd_(nullptr) { 54 if (game.settings().validate) instance_layers_.push_back("VK_LAYER_LUNARG_standard_validation"); 55 instance_extensions_.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); 56 init_vk(); 57 } 58 59 ShellWin32::~ShellWin32() { 60 cleanup_vk(); 61 FreeLibrary(hmodule_); 62 } 63 64 void ShellWin32::create_window() { 65 const std::string class_name(settings_.name + "WindowClass"); 66 67 hinstance_ = GetModuleHandle(nullptr); 68 69 WNDCLASSEX win_class = {}; 70 win_class.cbSize = sizeof(WNDCLASSEX); 71 win_class.style = CS_HREDRAW | CS_VREDRAW; 72 win_class.lpfnWndProc = window_proc; 73 win_class.hInstance = hinstance_; 74 win_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 75 win_class.lpszClassName = class_name.c_str(); 76 RegisterClassEx(&win_class); 77 78 const DWORD win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_OVERLAPPEDWINDOW; 79 80 RECT win_rect = {0, 0, settings_.initial_width, settings_.initial_height}; 81 AdjustWindowRect(&win_rect, win_style, false); 82 83 hwnd_ = CreateWindowEx(WS_EX_APPWINDOW, class_name.c_str(), settings_.name.c_str(), win_style, 0, 0, 84 win_rect.right - win_rect.left, win_rect.bottom - win_rect.top, nullptr, nullptr, hinstance_, nullptr); 85 86 SetForegroundWindow(hwnd_); 87 SetWindowLongPtr(hwnd_, GWLP_USERDATA, (LONG_PTR) this); 88 } 89 90 PFN_vkGetInstanceProcAddr ShellWin32::load_vk() { 91 const char filename[] = "vulkan-1.dll"; 92 HMODULE mod; 93 PFN_vkGetInstanceProcAddr get_proc = NULL; 94 95 mod = LoadLibrary(filename); 96 if (mod) { 97 get_proc = reinterpret_cast<PFN_vkGetInstanceProcAddr>(GetProcAddress(mod, "vkGetInstanceProcAddr")); 98 } 99 100 if (!mod || !get_proc) { 101 std::stringstream ss; 102 ss << "failed to load " << filename; 103 104 if (mod) FreeLibrary(mod); 105 106 throw std::runtime_error(ss.str()); 107 } 108 109 hmodule_ = mod; 110 111 return get_proc; 112 } 113 114 bool ShellWin32::can_present(VkPhysicalDevice phy, uint32_t queue_family) { 115 return vk::GetPhysicalDeviceWin32PresentationSupportKHR(phy, queue_family) == VK_TRUE; 116 } 117 118 VkSurfaceKHR ShellWin32::create_surface(VkInstance instance) { 119 VkWin32SurfaceCreateInfoKHR surface_info = {}; 120 surface_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; 121 surface_info.hinstance = hinstance_; 122 surface_info.hwnd = hwnd_; 123 124 VkSurfaceKHR surface; 125 vk::assert_success(vk::CreateWin32SurfaceKHR(instance, &surface_info, nullptr, &surface)); 126 127 return surface; 128 } 129 130 LRESULT ShellWin32::handle_message(UINT msg, WPARAM wparam, LPARAM lparam) { 131 switch (msg) { 132 case WM_SIZE: { 133 UINT w = LOWORD(lparam); 134 UINT h = HIWORD(lparam); 135 resize_swapchain(w, h); 136 } break; 137 case WM_KEYDOWN: { 138 Game::Key key; 139 140 switch (wparam) { 141 case VK_ESCAPE: 142 key = Game::KEY_ESC; 143 break; 144 case VK_UP: 145 key = Game::KEY_UP; 146 break; 147 case VK_DOWN: 148 key = Game::KEY_DOWN; 149 break; 150 case VK_SPACE: 151 key = Game::KEY_SPACE; 152 break; 153 default: 154 key = Game::KEY_UNKNOWN; 155 break; 156 } 157 158 game_.on_key(key); 159 } break; 160 case WM_CLOSE: 161 game_.on_key(Game::KEY_SHUTDOWN); 162 break; 163 case WM_DESTROY: 164 quit(); 165 break; 166 default: 167 return DefWindowProc(hwnd_, msg, wparam, lparam); 168 break; 169 } 170 171 return 0; 172 } 173 174 void ShellWin32::quit() { PostQuitMessage(0); } 175 176 void ShellWin32::run() { 177 create_window(); 178 179 create_context(); 180 resize_swapchain(settings_.initial_width, settings_.initial_height); 181 182 Win32Timer timer; 183 double current_time = timer.get(); 184 185 while (true) { 186 bool quit = false; 187 188 assert(settings_.animate); 189 190 // process all messages 191 MSG msg; 192 while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { 193 if (msg.message == WM_QUIT) { 194 quit = true; 195 break; 196 } 197 198 TranslateMessage(&msg); 199 DispatchMessage(&msg); 200 } 201 202 if (quit) break; 203 204 acquire_back_buffer(); 205 206 double t = timer.get(); 207 add_game_time(static_cast<float>(t - current_time)); 208 209 present_back_buffer(); 210 211 current_time = t; 212 } 213 214 destroy_context(); 215 216 DestroyWindow(hwnd_); 217 } 218