Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision c458c5802f09e92a175cd4522469e66e5c2ce10f)
+++ vulkan-game.cpp	(revision e09ad383a5718745fae0cdaf71a7cf4aca1a9507)
@@ -18,4 +18,5 @@
 #include <optional>
 #include <algorithm>
+#include <fstream>
 
 #include "game-gui-sdl.hpp"
@@ -55,14 +56,4 @@
     vector<VkPresentModeKHR> presentModes;
 };
-
-static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
-      VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
-      VkDebugUtilsMessageTypeFlagsEXT messageType,
-      const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
-      void* pUserData) {
-   cerr << "validation layer: " << pCallbackData->pMessage << endl;
-
-   return VK_FALSE;
-}
 
 VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
@@ -568,4 +559,40 @@
 
       void createGraphicsPipeline() {
+         auto vertShaderCode = readFile("shaders/vert.spv");
+         auto fragShaderCode = readFile("shaders/frag.spv");
+
+         VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
+         VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
+
+         VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
+         vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+         vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
+         vertShaderStageInfo.module = vertShaderModule;
+         vertShaderStageInfo.pName = "main";
+
+         VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
+         fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+         fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+         fragShaderStageInfo.module = fragShaderModule;
+         fragShaderStageInfo.pName = "main";
+
+         VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
+
+         vkDestroyShaderModule(device, vertShaderModule, nullptr);
+         vkDestroyShaderModule(device, fragShaderModule, nullptr);
+      }
+
+      VkShaderModule createShaderModule(const vector<char>& code) {
+         VkShaderModuleCreateInfo createInfo = {};
+         createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+         createInfo.codeSize = code.size();
+         createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
+
+         VkShaderModule shaderModule;
+         if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
+            throw runtime_error("failed to create shader module!");
+         }
+
+         return shaderModule;
       }
 
@@ -587,9 +614,9 @@
                }
 
-               /*
+               /**/
                SDL_FillRect(sdlSurface, nullptr, SDL_MapRGB(sdlSurface->format, 0xFF, 0xFF, 0xFF));
 
                SDL_UpdateWindowSurface(window);
-               */
+               /**/
             }
          }
@@ -616,4 +643,32 @@
          gui->Shutdown();
          delete gui;
+      }
+
+      static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
+         VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+         VkDebugUtilsMessageTypeFlagsEXT messageType,
+         const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
+         void* pUserData) {
+         cerr << "validation layer: " << pCallbackData->pMessage << endl;
+
+         return VK_FALSE;
+}
+
+      static vector<char> readFile(const string& filename) {
+         ifstream file(filename, ios::ate | ios::binary);
+
+         if (!file.is_open()) {
+            throw runtime_error("failed to open file!");
+         }
+
+         size_t fileSize = (size_t)file.tellg();
+         vector<char> buffer(fileSize);
+
+         file.seekg(0);
+         file.read(buffer.data(), fileSize);
+
+         file.close();
+
+         return buffer;
       }
 };
