blob: 4f205a9247880572946a91da6a9af5d6039354cb [file] [log] [blame]
Tony Barbour2f18b292016-02-25 15:44:10 -07001/*
2 * Copyright (C) 2016 Google, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23#include <cassert>
24#include <iostream>
25#include <sstream>
26
27#include "Helpers.h"
28#include "Game.h"
29#include "ShellWin32.h"
30
31namespace {
32
33class Win32Timer {
34public:
35 Win32Timer()
36 {
37 LARGE_INTEGER freq;
38 QueryPerformanceFrequency(&freq);
39 freq_ = static_cast<double>(freq.QuadPart);
40
41 reset();
42 }
43
44 void reset()
45 {
46 QueryPerformanceCounter(&start_);
47 }
48
49 double get() const
50 {
51 LARGE_INTEGER now;
52 QueryPerformanceCounter(&now);
53
54 return static_cast<double>(now.QuadPart - start_.QuadPart) / freq_;
55 }
56
57private:
58 double freq_;
59 LARGE_INTEGER start_;
60};
61
62} // namespace
63
64ShellWin32::ShellWin32(Game &game) : Shell(game), hwnd_(nullptr)
65{
66 instance_extensions_.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
67 init_vk();
68}
69
70ShellWin32::~ShellWin32()
71{
72 cleanup_vk();
73 FreeLibrary(hmodule_);
74}
75
76void ShellWin32::create_window()
77{
78 const std::string class_name(settings_.name + "WindowClass");
79
80 hinstance_ = GetModuleHandle(nullptr);
81
82 WNDCLASSEX win_class = {};
83 win_class.cbSize = sizeof(WNDCLASSEX);
84 win_class.style = CS_HREDRAW | CS_VREDRAW;
85 win_class.lpfnWndProc = window_proc;
86 win_class.hInstance = hinstance_;
87 win_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
88 win_class.lpszClassName = class_name.c_str();
89 RegisterClassEx(&win_class);
90
91 const DWORD win_style =
92 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_OVERLAPPEDWINDOW;
93
94 RECT win_rect = { 0, 0, settings_.initial_width, settings_.initial_height };
95 AdjustWindowRect(&win_rect, win_style, false);
96
97 hwnd_ = CreateWindowEx(WS_EX_APPWINDOW,
98 class_name.c_str(),
99 settings_.name.c_str(),
100 win_style,
101 0,
102 0,
103 win_rect.right - win_rect.left,
104 win_rect.bottom - win_rect.top,
105 nullptr,
106 nullptr,
107 hinstance_,
108 nullptr);
109
110 SetForegroundWindow(hwnd_);
111 SetWindowLongPtr(hwnd_, GWLP_USERDATA, (LONG_PTR) this);
112}
113
114PFN_vkGetInstanceProcAddr ShellWin32::load_vk()
115{
116 const char filename[] = "vulkan-1.dll";
117 HMODULE mod;
118 PFN_vkGetInstanceProcAddr get_proc;
119
120 mod = LoadLibrary(filename);
121 if (mod) {
122 get_proc = reinterpret_cast<PFN_vkGetInstanceProcAddr>(GetProcAddress(
123 mod, "vkGetInstanceProcAddr"));
124 }
125
126 if (!mod || !get_proc) {
127 std::stringstream ss;
128 ss << "failed to load " << filename;
129
130 if (mod)
131 FreeLibrary(mod);
132
133 throw std::runtime_error(ss.str());
134 }
135
136 hmodule_ = mod;
137
138 return get_proc;
139}
140
141bool ShellWin32::can_present(VkPhysicalDevice phy, uint32_t queue_family)
142{
Dustin Graves20f5fc02016-04-06 10:16:05 -0600143 return vk::GetPhysicalDeviceWin32PresentationSupportKHR(
144 phy, queue_family) == VK_TRUE;
Tony Barbour2f18b292016-02-25 15:44:10 -0700145}
146
147VkSurfaceKHR ShellWin32::create_surface(VkInstance instance)
148{
149 VkWin32SurfaceCreateInfoKHR surface_info = {};
150 surface_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
151 surface_info.hinstance = hinstance_;
152 surface_info.hwnd = hwnd_;
153
154 VkSurfaceKHR surface;
155 vk::assert_success(vk::CreateWin32SurfaceKHR(instance, &surface_info, nullptr, &surface));
156
157 return surface;
158}
159
160LRESULT ShellWin32::handle_message(UINT msg, WPARAM wparam, LPARAM lparam)
161{
162 switch (msg) {
163 case WM_SIZE:
164 {
165 UINT w = LOWORD(lparam);
166 UINT h = HIWORD(lparam);
167 resize_swapchain(w, h);
168 }
169 break;
170 case WM_KEYDOWN:
171 {
172 Game::Key key;
173
174 switch (wparam) {
175 case VK_ESCAPE:
176 key = Game::KEY_ESC;
177 break;
178 case VK_UP:
179 key = Game::KEY_UP;
180 break;
181 case VK_DOWN:
182 key = Game::KEY_DOWN;
183 break;
184 case VK_SPACE:
185 key = Game::KEY_SPACE;
186 break;
187 default:
188 key = Game::KEY_UNKNOWN;
189 break;
190 }
191
192 game_.on_key(key);
193 }
194 break;
195 case WM_CLOSE:
196 game_.on_key(Game::KEY_SHUTDOWN);
197 break;
198 case WM_DESTROY:
199 quit();
200 break;
201 default:
202 return DefWindowProc(hwnd_, msg, wparam, lparam);
203 break;
204 }
205
206 return 0;
207}
208
209void ShellWin32::quit()
210{
211 PostQuitMessage(0);
212}
213
214void ShellWin32::run()
215{
216 create_window();
217
218 create_context();
219 resize_swapchain(settings_.initial_width, settings_.initial_height);
220
221 Win32Timer timer;
222 double current_time = timer.get();
223
224 while (true) {
225 bool quit = false;
226
227 assert(settings_.animate);
228
229 // process all messages
230 MSG msg;
231 while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
232 if (msg.message == WM_QUIT) {
233 quit = true;
234 break;
235 }
236
237 TranslateMessage(&msg);
238 DispatchMessage(&msg);
239 }
240
241 if (quit)
242 break;
243
244 acquire_back_buffer();
245
246 double t = timer.get();
247 add_game_time(static_cast<float>(t - current_time));
248
249 present_back_buffer();
250
251 current_time = t;
252 }
253
254 destroy_context();
255
256 DestroyWindow(hwnd_);
257}