Home | History | Annotate | Download | only in smoke
      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