/* * Copyright (C) 2016 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "Helpers.h" #include "Game.h" #include "ShellWin32.h" namespace { class Win32Timer { public: Win32Timer() { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); freq_ = static_cast(freq.QuadPart); reset(); } void reset() { QueryPerformanceCounter(&start_); } double get() const { LARGE_INTEGER now; QueryPerformanceCounter(&now); return static_cast(now.QuadPart - start_.QuadPart) / freq_; } private: double freq_; LARGE_INTEGER start_; }; } // namespace ShellWin32::ShellWin32(Game &game) : Shell(game), hwnd_(nullptr) { instance_extensions_.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); init_vk(); } ShellWin32::~ShellWin32() { cleanup_vk(); FreeLibrary(hmodule_); } void ShellWin32::create_window() { const std::string class_name(settings_.name + "WindowClass"); hinstance_ = GetModuleHandle(nullptr); WNDCLASSEX win_class = {}; win_class.cbSize = sizeof(WNDCLASSEX); win_class.style = CS_HREDRAW | CS_VREDRAW; win_class.lpfnWndProc = window_proc; win_class.hInstance = hinstance_; win_class.hCursor = LoadCursor(nullptr, IDC_ARROW); win_class.lpszClassName = class_name.c_str(); RegisterClassEx(&win_class); const DWORD win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_OVERLAPPEDWINDOW; RECT win_rect = { 0, 0, settings_.initial_width, settings_.initial_height }; AdjustWindowRect(&win_rect, win_style, false); hwnd_ = CreateWindowEx(WS_EX_APPWINDOW, class_name.c_str(), settings_.name.c_str(), win_style, 0, 0, win_rect.right - win_rect.left, win_rect.bottom - win_rect.top, nullptr, nullptr, hinstance_, nullptr); SetForegroundWindow(hwnd_); SetWindowLongPtr(hwnd_, GWLP_USERDATA, (LONG_PTR) this); } PFN_vkGetInstanceProcAddr ShellWin32::load_vk() { const char filename[] = "vulkan-1.dll"; HMODULE mod; PFN_vkGetInstanceProcAddr get_proc; mod = LoadLibrary(filename); if (mod) { get_proc = reinterpret_cast(GetProcAddress( mod, "vkGetInstanceProcAddr")); } if (!mod || !get_proc) { std::stringstream ss; ss << "failed to load " << filename; if (mod) FreeLibrary(mod); throw std::runtime_error(ss.str()); } hmodule_ = mod; return get_proc; } bool ShellWin32::can_present(VkPhysicalDevice phy, uint32_t queue_family) { return vk::GetPhysicalDeviceWin32PresentationSupportKHR( phy, queue_family) == VK_TRUE; } VkSurfaceKHR ShellWin32::create_surface(VkInstance instance) { VkWin32SurfaceCreateInfoKHR surface_info = {}; surface_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; surface_info.hinstance = hinstance_; surface_info.hwnd = hwnd_; VkSurfaceKHR surface; vk::assert_success(vk::CreateWin32SurfaceKHR(instance, &surface_info, nullptr, &surface)); return surface; } LRESULT ShellWin32::handle_message(UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_SIZE: { UINT w = LOWORD(lparam); UINT h = HIWORD(lparam); resize_swapchain(w, h); } break; case WM_KEYDOWN: { Game::Key key; switch (wparam) { case VK_ESCAPE: key = Game::KEY_ESC; break; case VK_UP: key = Game::KEY_UP; break; case VK_DOWN: key = Game::KEY_DOWN; break; case VK_SPACE: key = Game::KEY_SPACE; break; default: key = Game::KEY_UNKNOWN; break; } game_.on_key(key); } break; case WM_CLOSE: game_.on_key(Game::KEY_SHUTDOWN); break; case WM_DESTROY: quit(); break; default: return DefWindowProc(hwnd_, msg, wparam, lparam); break; } return 0; } void ShellWin32::quit() { PostQuitMessage(0); } void ShellWin32::run() { create_window(); create_context(); resize_swapchain(settings_.initial_width, settings_.initial_height); Win32Timer timer; double current_time = timer.get(); while (true) { bool quit = false; assert(settings_.animate); // process all messages MSG msg; while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { quit = true; break; } TranslateMessage(&msg); DispatchMessage(&msg); } if (quit) break; acquire_back_buffer(); double t = timer.get(); add_game_time(static_cast(t - current_time)); present_back_buffer(); current_time = t; } destroy_context(); DestroyWindow(hwnd_); }