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:
Win32Timer()29 Win32Timer() {
30 LARGE_INTEGER freq;
31 QueryPerformanceFrequency(&freq);
32 freq_ = static_cast<double>(freq.QuadPart);
33
34 reset();
35 }
36
reset()37 void reset() { QueryPerformanceCounter(&start_); }
38
get() const39 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
ShellWin32(Game & game)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
~ShellWin32()59 ShellWin32::~ShellWin32() {
60 cleanup_vk();
61 FreeLibrary(hmodule_);
62 }
63
create_window()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
load_vk()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
can_present(VkPhysicalDevice phy,uint32_t queue_family)114 bool ShellWin32::can_present(VkPhysicalDevice phy, uint32_t queue_family) {
115 return vk::GetPhysicalDeviceWin32PresentationSupportKHR(phy, queue_family) == VK_TRUE;
116 }
117
create_surface(VkInstance instance)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
handle_message(UINT msg,WPARAM wparam,LPARAM lparam)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
quit()174 void ShellWin32::quit() { PostQuitMessage(0); }
175
run()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