blob: 375bcb144562fc413485137457929c4756012e49 [file] [log] [blame]
Adam Sawickie6e498f2017-06-16 17:21:31 +02001//
2// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// 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 THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21//
22
23#define NOMINMAX
24#define WIN32_LEAN_AND_MEAN
25#include <Windows.h>
26
27#define VK_USE_PLATFORM_WIN32_KHR
28#include <vulkan/vulkan.h>
29
30#define VMA_IMPLEMENTATION
31#include "vk_mem_alloc.h"
32
33#define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT
34#include <mathfu/glsl_mappings.h>
35#include <mathfu/constants.h>
36
37#include <fstream>
38#include <vector>
39#include <string>
40#include <memory>
41#include <algorithm>
42#include <numeric>
43#include <array>
44#include <type_traits>
45#include <utility>
46
47#include <cmath>
48#include <cassert>
49#include <cstdlib>
50#include <cstdio>
51
52#define ERR_GUARD_VULKAN(Expr) do { VkResult res__ = (Expr); if (res__ < 0) assert(0); } while(0)
53
54static const char* const SHADER_PATH1 = "./";
55static const char* const SHADER_PATH2 = "../bin/";
56static const wchar_t* const WINDOW_CLASS_NAME = L"VULKAN_MEMORY_ALLOCATOR_SAMPLE";
57static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_LUNARG_standard_validation";
58static const char* const APP_TITLE_A = "Vulkan Memory Allocator Sample 1.0";
59static const wchar_t* const APP_TITLE_W = L"Vulkan Memory Allocator Sample 1.0";
60
61static const bool VSYNC = true;
62static const uint32_t COMMAND_BUFFER_COUNT = 2;
63
64static bool g_EnableValidationLayer = true;
65
66static HINSTANCE g_hAppInstance;
67static HWND g_hWnd;
68static LONG g_SizeX = 1280, g_SizeY = 720;
69static VkInstance g_hVulkanInstance;
70static VkSurfaceKHR g_hSurface;
71static VkPhysicalDevice g_hPhysicalDevice;
72static VkQueue g_hPresentQueue;
73static VkSurfaceFormatKHR g_SurfaceFormat;
74static VkExtent2D g_Extent;
75static VkSwapchainKHR g_hSwapchain;
76static std::vector<VkImage> g_SwapchainImages;
77static std::vector<VkImageView> g_SwapchainImageViews;
78static std::vector<VkFramebuffer> g_Framebuffers;
79static VkCommandPool g_hCommandPool;
80static VkCommandBuffer g_MainCommandBuffers[COMMAND_BUFFER_COUNT];
81static VkFence g_MainCommandBufferExecutedFances[COMMAND_BUFFER_COUNT];
82static uint32_t g_NextCommandBufferIndex;
83static VkSemaphore g_hImageAvailableSemaphore;
84static VkSemaphore g_hRenderFinishedSemaphore;
85static uint32_t g_GraphicsQueueFamilyIndex = UINT_MAX;
86static uint32_t g_PresentQueueFamilyIndex = UINT_MAX;
87static VkDescriptorSetLayout g_hDescriptorSetLayout;
88static VkDescriptorPool g_hDescriptorPool;
89static VkDescriptorSet g_hDescriptorSet; // Automatically destroyed with m_DescriptorPool.
90static VkSampler g_hSampler;
91static VkFormat g_DepthFormat;
92static VkImage g_hDepthImage;
93static VkImageView g_hDepthImageView;
94
95static VkSurfaceCapabilitiesKHR g_SurfaceCapabilities;
96static std::vector<VkSurfaceFormatKHR> g_SurfaceFormats;
97static std::vector<VkPresentModeKHR> g_PresentModes;
98
99static PFN_vkCreateDebugReportCallbackEXT g_pvkCreateDebugReportCallbackEXT;
100static PFN_vkDebugReportMessageEXT g_pvkDebugReportMessageEXT;
101static PFN_vkDestroyDebugReportCallbackEXT g_pvkDestroyDebugReportCallbackEXT;
102static VkDebugReportCallbackEXT g_hCallback;
103
104static VkDevice g_hDevice;
105static VmaAllocator g_hAllocator;
106static VkQueue g_hGraphicsQueue;
107static VkCommandBuffer g_hTemporaryCommandBuffer;
108
109static VkPipelineLayout g_hPipelineLayout;
110static VkRenderPass g_hRenderPass;
111static VkPipeline g_hPipeline;
112
113static VkBuffer g_hVertexBuffer;
114static VkBuffer g_hIndexBuffer;
115static uint32_t g_VertexCount;
116static uint32_t g_IndexCount;
117
118static VkImage g_hTextureImage;
119static VkImageView g_hTextureImageView;
120
121static void BeginSingleTimeCommands()
122{
123 VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
124 cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
125 ERR_GUARD_VULKAN( vkBeginCommandBuffer(g_hTemporaryCommandBuffer, &cmdBufBeginInfo) );
126}
127
128static void EndSingleTimeCommands()
129{
130 ERR_GUARD_VULKAN( vkEndCommandBuffer(g_hTemporaryCommandBuffer) );
131
132 VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
133 submitInfo.commandBufferCount = 1;
134 submitInfo.pCommandBuffers = &g_hTemporaryCommandBuffer;
135
136 ERR_GUARD_VULKAN( vkQueueSubmit(g_hGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) );
137 ERR_GUARD_VULKAN( vkQueueWaitIdle(g_hGraphicsQueue) );
138}
139
140static void LoadShader(std::vector<char>& out, const char* fileName)
141{
142 std::ifstream file(std::string(SHADER_PATH1) + fileName, std::ios::ate | std::ios::binary);
143 if(file.is_open() == false)
144 file.open(std::string(SHADER_PATH2) + fileName, std::ios::ate | std::ios::binary);
145 assert(file.is_open());
146 size_t fileSize = (size_t)file.tellg();
147 if(fileSize > 0)
148 {
149 out.resize(fileSize);
150 file.seekg(0);
151 file.read(out.data(), fileSize);
152 file.close();
153 }
154 else
155 out.clear();
156}
157
158VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback(
159 VkDebugReportFlagsEXT flags,
160 VkDebugReportObjectTypeEXT objectType,
161 uint64_t object,
162 size_t location,
163 int32_t messageCode,
164 const char* pLayerPrefix,
165 const char* pMessage,
166 void* pUserData)
167{
168 printf("%s \xBA %s\n", pLayerPrefix, pMessage);
169
170 if((flags == VK_DEBUG_REPORT_WARNING_BIT_EXT) ||
171 (flags == VK_DEBUG_REPORT_ERROR_BIT_EXT))
172 {
173 OutputDebugStringA(pMessage);
174 OutputDebugStringA("\n");
175 }
176
177 return VK_FALSE;
178}
179
180static VkSurfaceFormatKHR ChooseSurfaceFormat()
181{
182 assert(!g_SurfaceFormats.empty());
183
184 if((g_SurfaceFormats.size() == 1) && (g_SurfaceFormats[0].format == VK_FORMAT_UNDEFINED))
185 {
186 VkSurfaceFormatKHR result = { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
187 return result;
188 }
189
190 for(const auto& format : g_SurfaceFormats)
191 {
192 if((format.format == VK_FORMAT_B8G8R8A8_UNORM) &&
193 (format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR))
194 {
195 return format;
196 }
197 }
198
199 return g_SurfaceFormats[0];
200}
201
202VkPresentModeKHR ChooseSwapPresentMode()
203{
204 VkPresentModeKHR preferredMode = VSYNC ? VK_PRESENT_MODE_MAILBOX_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR;
205
206 if(std::find(g_PresentModes.begin(), g_PresentModes.end(), preferredMode) !=
207 g_PresentModes.end())
208 {
209 return preferredMode;
210 }
211
212 return VK_PRESENT_MODE_FIFO_KHR;
213}
214
215static VkExtent2D ChooseSwapExtent()
216{
217 if(g_SurfaceCapabilities.currentExtent.width != UINT_MAX)
218 return g_SurfaceCapabilities.currentExtent;
219
220 VkExtent2D result = {
221 std::max(g_SurfaceCapabilities.minImageExtent.width,
222 std::min(g_SurfaceCapabilities.maxImageExtent.width, (uint32_t)g_SizeX)),
223 std::max(g_SurfaceCapabilities.minImageExtent.height,
224 std::min(g_SurfaceCapabilities.maxImageExtent.height, (uint32_t)g_SizeY)) };
225 return result;
226}
227
228struct Vertex
229{
230 float pos[3];
231 float color[3];
232 float texCoord[2];
233};
234
235static void CreateMesh()
236{
237 assert(g_hAllocator);
238
239 static Vertex vertices[] = {
240 // -X
241 { { -1.f, -1.f, -1.f}, {1.0f, 0.0f, 0.0f}, {0.f, 0.f} },
242 { { -1.f, -1.f, 1.f}, {1.0f, 0.0f, 0.0f}, {1.f, 0.f} },
243 { { -1.f, 1.f, -1.f}, {1.0f, 0.0f, 0.0f}, {0.f, 1.f} },
244 { { -1.f, 1.f, 1.f}, {1.0f, 0.0f, 0.0f}, {1.f, 1.f} },
245 // +X
246 { { 1.f, -1.f, 1.f}, {0.0f, 1.0f, 0.0f}, {0.f, 0.f} },
247 { { 1.f, -1.f, -1.f}, {0.0f, 1.0f, 0.0f}, {1.f, 0.f} },
248 { { 1.f, 1.f, 1.f}, {0.0f, 1.0f, 0.0f}, {0.f, 1.f} },
249 { { 1.f, 1.f, -1.f}, {0.0f, 1.0f, 0.0f}, {1.f, 1.f} },
250 // -Z
251 { { 1.f, -1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {0.f, 0.f} },
252 { {-1.f, -1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {1.f, 0.f} },
253 { { 1.f, 1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {0.f, 1.f} },
254 { {-1.f, 1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {1.f, 1.f} },
255 // +Z
256 { {-1.f, -1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {0.f, 0.f} },
257 { { 1.f, -1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {1.f, 0.f} },
258 { {-1.f, 1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {0.f, 1.f} },
259 { { 1.f, 1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {1.f, 1.f} },
260 // -Y
261 { {-1.f, -1.f, -1.f}, {0.0f, 1.0f, 1.0f}, {0.f, 0.f} },
262 { { 1.f, -1.f, -1.f}, {0.0f, 1.0f, 1.0f}, {1.f, 0.f} },
263 { {-1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f}, {0.f, 1.f} },
264 { { 1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f}, {1.f, 1.f} },
265 // +Y
266 { { 1.f, 1.f, -1.f}, {1.0f, 0.0f, 1.0f}, {0.f, 0.f} },
267 { {-1.f, 1.f, -1.f}, {1.0f, 0.0f, 1.0f}, {1.f, 0.f} },
268 { { 1.f, 1.f, 1.f}, {1.0f, 0.0f, 1.0f}, {0.f, 1.f} },
269 { {-1.f, 1.f, 1.f}, {1.0f, 0.0f, 1.0f}, {1.f, 1.f} },
270 };
271 static uint16_t indices[] = {
272 0, 1, 2, 3, USHRT_MAX,
273 4, 5, 6, 7, USHRT_MAX,
274 8, 9, 10, 11, USHRT_MAX,
275 12, 13, 14, 15, USHRT_MAX,
276 16, 17, 18, 19, USHRT_MAX,
277 20, 21, 22, 23, USHRT_MAX,
278 };
279
280 size_t vertexBufferSize = sizeof(Vertex) * _countof(vertices);
281 size_t indexBufferSize = sizeof(uint16_t) * _countof(indices);
282 g_IndexCount = (uint32_t)_countof(indices);
283
284 // Create vertex buffer
285
286 VkBufferCreateInfo vbInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
287 vbInfo.size = vertexBufferSize;
288 vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
289 vbInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
290 VmaMemoryRequirements vbMemReq = {};
291 vbMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
292 VkMappedMemoryRange stagingVertexBufferMem;
293 VkBuffer stagingVertexBuffer = VK_NULL_HANDLE;
294 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbMemReq, &stagingVertexBuffer, &stagingVertexBufferMem, nullptr) );
295
296 void* pVbData = nullptr;
297 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingVertexBufferMem, &pVbData) );
298 memcpy(pVbData, vertices, vertexBufferSize);
299 vmaUnmapMemory(g_hAllocator, &stagingVertexBufferMem);
300
301 vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
302 vbMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
303 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbMemReq, &g_hVertexBuffer, nullptr, nullptr) );
304
305 // Create index buffer
306
307 VkBufferCreateInfo ibInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
308 ibInfo.size = indexBufferSize;
309 ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
310 ibInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
311 VmaMemoryRequirements ibMemReq = {};
312 ibMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
313 VkMappedMemoryRange stagingIndexBufferMem;
314 VkBuffer stagingIndexBuffer = VK_NULL_HANDLE;
315 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibMemReq, &stagingIndexBuffer, &stagingIndexBufferMem, nullptr) );
316
317 void* pIbData = nullptr;
318 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingIndexBufferMem, &pIbData) );
319 memcpy(pIbData, indices, indexBufferSize);
320 vmaUnmapMemory(g_hAllocator, &stagingIndexBufferMem);
321
322 ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
323 ibMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
324 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibMemReq, &g_hIndexBuffer, nullptr, nullptr) );
325
326 // Copy buffers
327
328 BeginSingleTimeCommands();
329
330 VkBufferCopy vbCopyRegion = {};
331 vbCopyRegion.srcOffset = 0;
332 vbCopyRegion.dstOffset = 0;
333 vbCopyRegion.size = vbInfo.size;
334 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingVertexBuffer, g_hVertexBuffer, 1, &vbCopyRegion);
335
336 VkBufferCopy ibCopyRegion = {};
337 ibCopyRegion.srcOffset = 0;
338 ibCopyRegion.dstOffset = 0;
339 ibCopyRegion.size = ibInfo.size;
340 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingIndexBuffer, g_hIndexBuffer, 1, &ibCopyRegion);
341
342 EndSingleTimeCommands();
343
344 vmaDestroyBuffer(g_hAllocator, stagingIndexBuffer);
345 vmaDestroyBuffer(g_hAllocator, stagingVertexBuffer);
346}
347
348static void CopyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height, uint32_t mipLevel)
349{
350 BeginSingleTimeCommands();
351
352 VkImageCopy imageCopy = {};
353 imageCopy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
354 imageCopy.srcSubresource.baseArrayLayer = 0;
355 imageCopy.srcSubresource.mipLevel = mipLevel;
356 imageCopy.srcSubresource.layerCount = 1;
357 imageCopy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
358 imageCopy.dstSubresource.baseArrayLayer = 0;
359 imageCopy.dstSubresource.mipLevel = mipLevel;
360 imageCopy.dstSubresource.layerCount = 1;
361 imageCopy.srcOffset.x = 0;
362 imageCopy.srcOffset.y = 0;
363 imageCopy.srcOffset.z = 0;
364 imageCopy.dstOffset.x = 0;
365 imageCopy.dstOffset.y = 0;
366 imageCopy.dstOffset.z = 0;
367 imageCopy.extent.width = width;
368 imageCopy.extent.height = height;
369 imageCopy.extent.depth = 1;
370 vkCmdCopyImage(
371 g_hTemporaryCommandBuffer,
372 srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
373 dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
374 1, &imageCopy);
375
376 EndSingleTimeCommands();
377}
378
379static void TransitionImageLayout(VkImage image, VkFormat format, uint32_t mipLevelCount, VkImageLayout oldLayout, VkImageLayout newLayout)
380{
381 BeginSingleTimeCommands();
382
383 VkImageMemoryBarrier imgMemBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
384 imgMemBarrier.oldLayout = oldLayout;
385 imgMemBarrier.newLayout = newLayout;
386 imgMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
387 imgMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
388 imgMemBarrier.image = image;
389 imgMemBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
390 imgMemBarrier.subresourceRange.baseMipLevel = 0;
391 imgMemBarrier.subresourceRange.levelCount = mipLevelCount;
392 imgMemBarrier.subresourceRange.baseArrayLayer = 0;
393 imgMemBarrier.subresourceRange.layerCount = 1;
394
395 if((oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) &&
396 (newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL))
397 {
398 imgMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
399 imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
400 }
401 else if((oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) &&
402 (newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL))
403 {
404 imgMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
405 imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
406 }
407 else if((oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) &&
408 (newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL))
409 {
410 imgMemBarrier.srcAccessMask = 0;
411 imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
412 }
413 else if((oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) &&
414 (newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL))
415 {
416 imgMemBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
417 imgMemBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
418 }
419 else if ((oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) &&
420 (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL))
421 {
422 imgMemBarrier.srcAccessMask = 0;
423 imgMemBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
424 }
425 else
426 assert(0);
427
428 if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
429 {
430 imgMemBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
431 if ((format == VK_FORMAT_D16_UNORM_S8_UINT) ||
432 (format == VK_FORMAT_D24_UNORM_S8_UINT) ||
433 (format == VK_FORMAT_D32_SFLOAT_S8_UINT))
434 {
435 imgMemBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
436 }
437 }
438
439 vkCmdPipelineBarrier(
440 g_hTemporaryCommandBuffer,
441 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
442 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
443 0,
444 0, nullptr,
445 0, nullptr,
446 1, &imgMemBarrier);
447
448 EndSingleTimeCommands();
449}
450
451static void CreateTexture(uint32_t sizeX, uint32_t sizeY)
452{
453 // Create Image
454
455 const VkDeviceSize imageSize = sizeX * sizeY * 4;
456
457 VkImageCreateInfo stagingImageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
458 stagingImageInfo.imageType = VK_IMAGE_TYPE_2D;
459 stagingImageInfo.extent.width = sizeX;
460 stagingImageInfo.extent.height = sizeY;
461 stagingImageInfo.extent.depth = 1;
462 stagingImageInfo.mipLevels = 1;
463 stagingImageInfo.arrayLayers = 1;
464 stagingImageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
465 stagingImageInfo.tiling = VK_IMAGE_TILING_LINEAR;
466 stagingImageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
467 stagingImageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
468 stagingImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
469 stagingImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
470 stagingImageInfo.flags = 0;
471 VmaMemoryRequirements stagingImageMemReq = {};
472 stagingImageMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
473 VkImage stagingImage = VK_NULL_HANDLE;
474 VkMappedMemoryRange stagingImageMem;
475 ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &stagingImageInfo, &stagingImageMemReq, &stagingImage, &stagingImageMem, nullptr) );
476
477 char* pImageData = nullptr;
478 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingImageMem, (void**)&pImageData) );
479
480 VkImageSubresource imageSubresource = {};
481 imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
482 imageSubresource.mipLevel = 0;
483 imageSubresource.arrayLayer = 0;
484
485 VkSubresourceLayout imageLayout = {};
486 vkGetImageSubresourceLayout(g_hDevice, stagingImage, &imageSubresource, &imageLayout);
487
488 char* const pMipLevelData = pImageData + imageLayout.offset;
489 uint8_t* pRowData = (uint8_t*)pMipLevelData;
490 for(uint32_t y = 0; y < sizeY; ++y)
491 {
492 uint32_t* pPixelData = (uint32_t*)pRowData;
493 for(uint32_t x = 0; x < sizeY; ++x)
494 {
495 *pPixelData =
496 ((x & 0x18) == 0x08 ? 0x000000FF : 0x00000000) |
497 ((x & 0x18) == 0x10 ? 0x0000FFFF : 0x00000000) |
498 ((y & 0x18) == 0x08 ? 0x0000FF00 : 0x00000000) |
499 ((y & 0x18) == 0x10 ? 0x00FF0000 : 0x00000000);
500 ++pPixelData;
501 }
502 pRowData += imageLayout.rowPitch;
503 }
504
505 vmaUnmapMemory(g_hAllocator, &stagingImageMem);
506
507 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
508 imageInfo.imageType = VK_IMAGE_TYPE_2D;
509 imageInfo.extent.width = sizeX;
510 imageInfo.extent.height = sizeY;
511 imageInfo.extent.depth = 1;
512 imageInfo.mipLevels = 1;
513 imageInfo.arrayLayers = 1;
514 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
515 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
516 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
517 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
518 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
519 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
520 imageInfo.flags = 0;
521 VmaMemoryRequirements imageMemReq = {};
522 imageMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
523 ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &imageInfo, &imageMemReq, &g_hTextureImage, nullptr, nullptr) );
524
525 TransitionImageLayout(
526 stagingImage,
527 VK_FORMAT_R8G8B8A8_UNORM,
528 1,
529 VK_IMAGE_LAYOUT_PREINITIALIZED,
530 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
531 TransitionImageLayout(
532 g_hTextureImage,
533 VK_FORMAT_R8G8B8A8_UNORM,
534 1,
535 VK_IMAGE_LAYOUT_UNDEFINED,
536 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
537 CopyImage(stagingImage, g_hTextureImage, sizeX, sizeY, 0);
538 TransitionImageLayout(
539 g_hTextureImage,
540 VK_FORMAT_R8G8B8A8_UNORM,
541 1,
542 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
543 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
544
545 vmaDestroyImage(g_hAllocator, stagingImage);
546
547 // Create ImageView
548
549 VkImageViewCreateInfo textureImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
550 textureImageViewInfo.image = g_hTextureImage;
551 textureImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
552 textureImageViewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
553 textureImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
554 textureImageViewInfo.subresourceRange.baseMipLevel = 0;
555 textureImageViewInfo.subresourceRange.levelCount = 1;
556 textureImageViewInfo.subresourceRange.baseArrayLayer = 0;
557 textureImageViewInfo.subresourceRange.layerCount = 1;
558 ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &textureImageViewInfo, nullptr, &g_hTextureImageView) );
559}
560
561struct UniformBufferObject
562{
563 mathfu::vec4_packed ModelViewProj[4];
564};
565
566static void RegisterDebugCallbacks()
567{
568 g_pvkCreateDebugReportCallbackEXT =
569 reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>
570 (vkGetInstanceProcAddr(g_hVulkanInstance, "vkCreateDebugReportCallbackEXT"));
571 g_pvkDebugReportMessageEXT =
572 reinterpret_cast<PFN_vkDebugReportMessageEXT>
573 (vkGetInstanceProcAddr(g_hVulkanInstance, "vkDebugReportMessageEXT"));
574 g_pvkDestroyDebugReportCallbackEXT =
575 reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>
576 (vkGetInstanceProcAddr(g_hVulkanInstance, "vkDestroyDebugReportCallbackEXT"));
577 assert(g_pvkCreateDebugReportCallbackEXT);
578 assert(g_pvkDebugReportMessageEXT);
579 assert(g_pvkDestroyDebugReportCallbackEXT);
580
581 VkDebugReportCallbackCreateInfoEXT callbackCreateInfo;
582 callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
583 callbackCreateInfo.pNext = nullptr;
584 callbackCreateInfo.flags = //VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
585 VK_DEBUG_REPORT_ERROR_BIT_EXT |
586 VK_DEBUG_REPORT_WARNING_BIT_EXT |
587 VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT /*|
588 VK_DEBUG_REPORT_DEBUG_BIT_EXT*/;
589 callbackCreateInfo.pfnCallback = &MyDebugReportCallback;
590 callbackCreateInfo.pUserData = nullptr;
591
592 ERR_GUARD_VULKAN( g_pvkCreateDebugReportCallbackEXT(g_hVulkanInstance, &callbackCreateInfo, nullptr, &g_hCallback) );
593}
594
595static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, const char* pLayerName)
596{
597 const VkLayerProperties* propsEnd = pProps + propCount;
598 return std::find_if(
599 pProps,
600 propsEnd,
601 [pLayerName](const VkLayerProperties& prop) -> bool {
602 return strcmp(pLayerName, prop.layerName) == 0;
603 }) != propsEnd;
604}
605
606static VkFormat FindSupportedFormat(
607 const std::vector<VkFormat>& candidates,
608 VkImageTiling tiling,
609 VkFormatFeatureFlags features)
610{
611 for (VkFormat format : candidates)
612 {
613 VkFormatProperties props;
614 vkGetPhysicalDeviceFormatProperties(g_hPhysicalDevice, format, &props);
615
616 if ((tiling == VK_IMAGE_TILING_LINEAR) &&
617 ((props.linearTilingFeatures & features) == features))
618 {
619 return format;
620 }
621 else if ((tiling == VK_IMAGE_TILING_OPTIMAL) &&
622 ((props.optimalTilingFeatures & features) == features))
623 {
624 return format;
625 }
626 }
627 return VK_FORMAT_UNDEFINED;
628}
629
630static VkFormat FindDepthFormat()
631{
632 std::vector<VkFormat> formats;
633 formats.push_back(VK_FORMAT_D32_SFLOAT);
634 formats.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT);
635 formats.push_back(VK_FORMAT_D24_UNORM_S8_UINT);
636
637 return FindSupportedFormat(
638 formats,
639 VK_IMAGE_TILING_OPTIMAL,
640 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
641}
642
643static void CreateSwapchain()
644{
645 // Query surface formats.
646
647 ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_hPhysicalDevice, g_hSurface, &g_SurfaceCapabilities) );
648
649 uint32_t formatCount = 0;
650 ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceFormatsKHR(g_hPhysicalDevice, g_hSurface, &formatCount, nullptr) );
651 g_SurfaceFormats.resize(formatCount);
652 ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceFormatsKHR(g_hPhysicalDevice, g_hSurface, &formatCount, g_SurfaceFormats.data()) );
653
654 uint32_t presentModeCount = 0;
655 ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfacePresentModesKHR(g_hPhysicalDevice, g_hSurface, &presentModeCount, nullptr) );
656 g_PresentModes.resize(presentModeCount);
657 ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfacePresentModesKHR(g_hPhysicalDevice, g_hSurface, &presentModeCount, g_PresentModes.data()) );
658
659 // Create swap chain
660
661 g_SurfaceFormat = ChooseSurfaceFormat();
662 VkPresentModeKHR presentMode = ChooseSwapPresentMode();
663 g_Extent = ChooseSwapExtent();
664
665 uint32_t imageCount = g_SurfaceCapabilities.minImageCount + 1;
666 if((g_SurfaceCapabilities.maxImageCount > 0) &&
667 (imageCount > g_SurfaceCapabilities.maxImageCount))
668 {
669 imageCount = g_SurfaceCapabilities.maxImageCount;
670 }
671
672 VkSwapchainCreateInfoKHR swapChainInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
673 swapChainInfo.surface = g_hSurface;
674 swapChainInfo.minImageCount = imageCount;
675 swapChainInfo.imageFormat = g_SurfaceFormat.format;
676 swapChainInfo.imageColorSpace = g_SurfaceFormat.colorSpace;
677 swapChainInfo.imageExtent = g_Extent;
678 swapChainInfo.imageArrayLayers = 1;
679 swapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
680 swapChainInfo.preTransform = g_SurfaceCapabilities.currentTransform;
681 swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
682 swapChainInfo.presentMode = presentMode;
683 swapChainInfo.clipped = VK_TRUE;
684 swapChainInfo.oldSwapchain = g_hSwapchain;
685
686 uint32_t queueFamilyIndices[] = { g_GraphicsQueueFamilyIndex, g_PresentQueueFamilyIndex };
687 if(g_PresentQueueFamilyIndex != g_GraphicsQueueFamilyIndex)
688 {
689 swapChainInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
690 swapChainInfo.queueFamilyIndexCount = 2;
691 swapChainInfo.pQueueFamilyIndices = queueFamilyIndices;
692 }
693 else
694 {
695 swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
696 }
697
698 VkSwapchainKHR hNewSwapchain = VK_NULL_HANDLE;
699 ERR_GUARD_VULKAN( vkCreateSwapchainKHR(g_hDevice, &swapChainInfo, nullptr, &hNewSwapchain) );
700 if(g_hSwapchain != VK_NULL_HANDLE)
701 vkDestroySwapchainKHR(g_hDevice, g_hSwapchain, nullptr);
702 g_hSwapchain = hNewSwapchain;
703
704 // Retrieve swapchain images.
705
706 uint32_t swapchainImageCount = 0;
707 ERR_GUARD_VULKAN( vkGetSwapchainImagesKHR(g_hDevice, g_hSwapchain, &swapchainImageCount, nullptr) );
708 g_SwapchainImages.resize(swapchainImageCount);
709 ERR_GUARD_VULKAN( vkGetSwapchainImagesKHR(g_hDevice, g_hSwapchain, &swapchainImageCount, g_SwapchainImages.data()) );
710
711 // Create swapchain image views.
712
713 for(size_t i = g_SwapchainImageViews.size(); i--; )
714 vkDestroyImageView(g_hDevice, g_SwapchainImageViews[i], nullptr);
715 g_SwapchainImageViews.clear();
716
717 VkImageViewCreateInfo swapchainImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
718 g_SwapchainImageViews.resize(swapchainImageCount);
719 for(uint32_t i = 0; i < swapchainImageCount; ++i)
720 {
721 swapchainImageViewInfo.image = g_SwapchainImages[i];
722 swapchainImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
723 swapchainImageViewInfo.format = g_SurfaceFormat.format;
724 swapchainImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
725 swapchainImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
726 swapchainImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
727 swapchainImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
728 swapchainImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
729 swapchainImageViewInfo.subresourceRange.baseMipLevel = 0;
730 swapchainImageViewInfo.subresourceRange.levelCount = 1;
731 swapchainImageViewInfo.subresourceRange.baseArrayLayer = 0;
732 swapchainImageViewInfo.subresourceRange.layerCount = 1;
733 ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &swapchainImageViewInfo, nullptr, &g_SwapchainImageViews[i]) );
734 }
735
736 // Create depth buffer
737
738 g_DepthFormat = FindDepthFormat();
739 assert(g_DepthFormat != VK_FORMAT_UNDEFINED);
740
741 VkImageCreateInfo depthImageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
742 depthImageInfo.imageType = VK_IMAGE_TYPE_2D;
743 depthImageInfo.extent.width = g_Extent.width;
744 depthImageInfo.extent.height = g_Extent.height;
745 depthImageInfo.extent.depth = 1;
746 depthImageInfo.mipLevels = 1;
747 depthImageInfo.arrayLayers = 1;
748 depthImageInfo.format = g_DepthFormat;
749 depthImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
750 depthImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
751 depthImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
752 depthImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
753 depthImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
754 depthImageInfo.flags = 0;
755
756 VmaMemoryRequirements depthImageMemReq = {};
757 depthImageMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
758
759 ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &depthImageInfo, &depthImageMemReq, &g_hDepthImage, nullptr, nullptr) );
760
761 VkImageViewCreateInfo depthImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
762 depthImageViewInfo.image = g_hDepthImage;
763 depthImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
764 depthImageViewInfo.format = g_DepthFormat;
765 depthImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
766 depthImageViewInfo.subresourceRange.baseMipLevel = 0;
767 depthImageViewInfo.subresourceRange.levelCount = 1;
768 depthImageViewInfo.subresourceRange.baseArrayLayer = 0;
769 depthImageViewInfo.subresourceRange.layerCount = 1;
770
771 ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &depthImageViewInfo, nullptr, &g_hDepthImageView) );
772
773 TransitionImageLayout(
774 g_hDepthImage,
775 g_DepthFormat,
776 1,
777 VK_IMAGE_LAYOUT_UNDEFINED,
778 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
779
780 // Create pipeline layout
781 {
782 if(g_hPipelineLayout != VK_NULL_HANDLE)
783 {
784 vkDestroyPipelineLayout(g_hDevice, g_hPipelineLayout, nullptr);
785 g_hPipelineLayout = VK_NULL_HANDLE;
786 }
787
788 VkPushConstantRange pushConstantRanges[1];
789 ZeroMemory(&pushConstantRanges, sizeof pushConstantRanges);
790 pushConstantRanges[0].offset = 0;
791 pushConstantRanges[0].size = sizeof(UniformBufferObject);
792 pushConstantRanges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
793
794 VkDescriptorSetLayout descriptorSetLayouts[] = { g_hDescriptorSetLayout };
795 VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
796 pipelineLayoutInfo.setLayoutCount = 1;
797 pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts;
798 pipelineLayoutInfo.pushConstantRangeCount = 1;
799 pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges;
800 ERR_GUARD_VULKAN( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutInfo, nullptr, &g_hPipelineLayout) );
801 }
802
803 // Create render pass
804 {
805 if(g_hRenderPass != VK_NULL_HANDLE)
806 {
807 vkDestroyRenderPass(g_hDevice, g_hRenderPass, nullptr);
808 g_hRenderPass = VK_NULL_HANDLE;
809 }
810
811 VkAttachmentDescription attachments[2];
812 ZeroMemory(attachments, sizeof(attachments));
813
814 attachments[0].format = g_SurfaceFormat.format;
815 attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
816 attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
817 attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
818 attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
819 attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
820 attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
821 attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
822
823 attachments[1].format = g_DepthFormat;
824 attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
825 attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
826 attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
827 attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
828 attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
829 attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
830 attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
831
832 VkAttachmentReference colorAttachmentRef = {};
833 colorAttachmentRef.attachment = 0;
834 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
835
836 VkAttachmentReference depthStencilAttachmentRef = {};
837 depthStencilAttachmentRef.attachment = 1;
838 depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
839
840 VkSubpassDescription subpassDesc = {};
841 subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
842 subpassDesc.colorAttachmentCount = 1;
843 subpassDesc.pColorAttachments = &colorAttachmentRef;
844 subpassDesc.pDepthStencilAttachment = &depthStencilAttachmentRef;
845
846 VkSubpassDependency subpassDependency = {};
847 subpassDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
848 subpassDependency.dstSubpass = 0;
849 subpassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
850 subpassDependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
851 subpassDependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
852 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
853 subpassDependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
854 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
855
856 VkRenderPassCreateInfo renderPassInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
857 renderPassInfo.attachmentCount = (uint32_t)_countof(attachments);
858 renderPassInfo.pAttachments = attachments;
859 renderPassInfo.subpassCount = 1;
860 renderPassInfo.pSubpasses = &subpassDesc;
861 renderPassInfo.dependencyCount = 1;
862 renderPassInfo.pDependencies = &subpassDependency;
863 ERR_GUARD_VULKAN( vkCreateRenderPass(g_hDevice, &renderPassInfo, nullptr, &g_hRenderPass) );
864 }
865
866 // Create pipeline
867 {
868 std::vector<char> vertShaderCode;
869 LoadShader(vertShaderCode, "Shader.vert.spv");
870 VkShaderModuleCreateInfo shaderModuleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
871 shaderModuleInfo.codeSize = vertShaderCode.size();
872 shaderModuleInfo.pCode = (const uint32_t*)vertShaderCode.data();
873 VkShaderModule hVertShaderModule = VK_NULL_HANDLE;
874 ERR_GUARD_VULKAN( vkCreateShaderModule(g_hDevice, &shaderModuleInfo, nullptr, &hVertShaderModule) );
875
876 std::vector<char> hFragShaderCode;
877 LoadShader(hFragShaderCode, "Shader.frag.spv");
878 shaderModuleInfo.codeSize = hFragShaderCode.size();
879 shaderModuleInfo.pCode = (const uint32_t*)hFragShaderCode.data();
880 VkShaderModule fragShaderModule = VK_NULL_HANDLE;
881 ERR_GUARD_VULKAN( vkCreateShaderModule(g_hDevice, &shaderModuleInfo, nullptr, &fragShaderModule) );
882
883 VkPipelineShaderStageCreateInfo vertPipelineShaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
884 vertPipelineShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
885 vertPipelineShaderStageInfo.module = hVertShaderModule;
886 vertPipelineShaderStageInfo.pName = "main";
887
888 VkPipelineShaderStageCreateInfo fragPipelineShaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
889 fragPipelineShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
890 fragPipelineShaderStageInfo.module = fragShaderModule;
891 fragPipelineShaderStageInfo.pName = "main";
892
893 VkPipelineShaderStageCreateInfo pipelineShaderStageInfos[] = {
894 vertPipelineShaderStageInfo,
895 fragPipelineShaderStageInfo
896 };
897
898 VkVertexInputBindingDescription bindingDescription = {};
899 bindingDescription.binding = 0;
900 bindingDescription.stride = sizeof(Vertex);
901 bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
902
903 VkVertexInputAttributeDescription attributeDescriptions[3];
904 ZeroMemory(attributeDescriptions, sizeof(attributeDescriptions));
905
906 attributeDescriptions[0].binding = 0;
907 attributeDescriptions[0].location = 0;
908 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
909 attributeDescriptions[0].offset = offsetof(Vertex, pos);
910
911 attributeDescriptions[1].binding = 0;
912 attributeDescriptions[1].location = 1;
913 attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
914 attributeDescriptions[1].offset = offsetof(Vertex, color);
915
916 attributeDescriptions[2].binding = 0;
917 attributeDescriptions[2].location = 2;
918 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
919 attributeDescriptions[2].offset = offsetof(Vertex, texCoord);
920
921 VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
922 pipelineVertexInputStateInfo.vertexBindingDescriptionCount = 1;
923 pipelineVertexInputStateInfo.pVertexBindingDescriptions = &bindingDescription;
924 pipelineVertexInputStateInfo.vertexAttributeDescriptionCount = _countof(attributeDescriptions);
925 pipelineVertexInputStateInfo.pVertexAttributeDescriptions = attributeDescriptions;
926
927 VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
928 pipelineInputAssemblyStateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
929 pipelineInputAssemblyStateInfo.primitiveRestartEnable = VK_TRUE;
930
931 VkViewport viewport = {};
932 viewport.x = 0.f;
933 viewport.y = 0.f;
934 viewport.width = (float)g_Extent.width;
935 viewport.height = (float)g_Extent.height;
936 viewport.minDepth = 0.f;
937 viewport.maxDepth = 1.f;
938
939 VkRect2D scissor = {};
940 scissor.offset.x = 0;
941 scissor.offset.y = 0;
942 scissor.extent = g_Extent;
943
944 VkPipelineViewportStateCreateInfo pipelineViewportStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
945 pipelineViewportStateInfo.viewportCount = 1;
946 pipelineViewportStateInfo.pViewports = &viewport;
947 pipelineViewportStateInfo.scissorCount = 1;
948 pipelineViewportStateInfo.pScissors = &scissor;
949
950 VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
951 pipelineRasterizationStateInfo.depthClampEnable = VK_FALSE;
952 pipelineRasterizationStateInfo.rasterizerDiscardEnable = VK_FALSE;
953 pipelineRasterizationStateInfo.polygonMode = VK_POLYGON_MODE_FILL;
954 pipelineRasterizationStateInfo.lineWidth = 1.f;
955 pipelineRasterizationStateInfo.cullMode = VK_CULL_MODE_BACK_BIT;
956 pipelineRasterizationStateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
957 pipelineRasterizationStateInfo.depthBiasEnable = VK_FALSE;
958 pipelineRasterizationStateInfo.depthBiasConstantFactor = 0.f;
959 pipelineRasterizationStateInfo.depthBiasClamp = 0.f;
960 pipelineRasterizationStateInfo.depthBiasSlopeFactor = 0.f;
961
962 VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
963 pipelineMultisampleStateInfo.sampleShadingEnable = VK_FALSE;
964 pipelineMultisampleStateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
965 pipelineMultisampleStateInfo.minSampleShading = 1.f;
966 pipelineMultisampleStateInfo.pSampleMask = nullptr;
967 pipelineMultisampleStateInfo.alphaToCoverageEnable = VK_FALSE;
968 pipelineMultisampleStateInfo.alphaToOneEnable = VK_FALSE;
969
970 VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = {};
971 pipelineColorBlendAttachmentState.colorWriteMask =
972 VK_COLOR_COMPONENT_R_BIT |
973 VK_COLOR_COMPONENT_G_BIT |
974 VK_COLOR_COMPONENT_B_BIT |
975 VK_COLOR_COMPONENT_A_BIT;
976 pipelineColorBlendAttachmentState.blendEnable = VK_FALSE;
977 pipelineColorBlendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
978 pipelineColorBlendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
979 pipelineColorBlendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; // Optional
980 pipelineColorBlendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
981 pipelineColorBlendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
982 pipelineColorBlendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; // Optional
983
984 VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
985 pipelineColorBlendStateInfo.logicOpEnable = VK_FALSE;
986 pipelineColorBlendStateInfo.logicOp = VK_LOGIC_OP_COPY;
987 pipelineColorBlendStateInfo.attachmentCount = 1;
988 pipelineColorBlendStateInfo.pAttachments = &pipelineColorBlendAttachmentState;
989
990 VkPipelineDepthStencilStateCreateInfo depthStencilStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
991 depthStencilStateInfo.depthTestEnable = VK_TRUE;
992 depthStencilStateInfo.depthWriteEnable = VK_TRUE;
993 depthStencilStateInfo.depthCompareOp = VK_COMPARE_OP_LESS;
994 depthStencilStateInfo.depthBoundsTestEnable = VK_FALSE;
995 depthStencilStateInfo.stencilTestEnable = VK_FALSE;
996
997 VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
998 pipelineInfo.stageCount = 2;
999 pipelineInfo.pStages = pipelineShaderStageInfos;
1000 pipelineInfo.pVertexInputState = &pipelineVertexInputStateInfo;
1001 pipelineInfo.pInputAssemblyState = &pipelineInputAssemblyStateInfo;
1002 pipelineInfo.pViewportState = &pipelineViewportStateInfo;
1003 pipelineInfo.pRasterizationState = &pipelineRasterizationStateInfo;
1004 pipelineInfo.pMultisampleState = &pipelineMultisampleStateInfo;
1005 pipelineInfo.pDepthStencilState = &depthStencilStateInfo;
1006 pipelineInfo.pColorBlendState = &pipelineColorBlendStateInfo;
1007 pipelineInfo.pDynamicState = nullptr;
1008 pipelineInfo.layout = g_hPipelineLayout;
1009 pipelineInfo.renderPass = g_hRenderPass;
1010 pipelineInfo.subpass = 0;
1011 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
1012 pipelineInfo.basePipelineIndex = -1;
1013 ERR_GUARD_VULKAN( vkCreateGraphicsPipelines(
1014 g_hDevice,
1015 VK_NULL_HANDLE,
1016 1,
1017 &pipelineInfo, nullptr,
1018 &g_hPipeline) );
1019
1020 vkDestroyShaderModule(g_hDevice, fragShaderModule, nullptr);
1021 vkDestroyShaderModule(g_hDevice, hVertShaderModule, nullptr);
1022 }
1023
1024 // Create frambuffers
1025
1026 for(size_t i = g_Framebuffers.size(); i--; )
1027 vkDestroyFramebuffer(g_hDevice, g_Framebuffers[i], nullptr);
1028 g_Framebuffers.clear();
1029
1030 g_Framebuffers.resize(g_SwapchainImageViews.size());
1031 for(size_t i = 0; i < g_SwapchainImages.size(); ++i)
1032 {
1033 VkImageView attachments[] = { g_SwapchainImageViews[i], g_hDepthImageView };
1034
1035 VkFramebufferCreateInfo framebufferInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
1036 framebufferInfo.renderPass = g_hRenderPass;
1037 framebufferInfo.attachmentCount = (uint32_t)_countof(attachments);
1038 framebufferInfo.pAttachments = attachments;
1039 framebufferInfo.width = g_Extent.width;
1040 framebufferInfo.height = g_Extent.height;
1041 framebufferInfo.layers = 1;
1042 ERR_GUARD_VULKAN( vkCreateFramebuffer(g_hDevice, &framebufferInfo, nullptr, &g_Framebuffers[i]) );
1043 }
1044
1045 // Create semaphores
1046
1047 if(g_hImageAvailableSemaphore != VK_NULL_HANDLE)
1048 {
1049 vkDestroySemaphore(g_hDevice, g_hImageAvailableSemaphore, nullptr);
1050 g_hImageAvailableSemaphore = VK_NULL_HANDLE;
1051 }
1052 if(g_hRenderFinishedSemaphore != VK_NULL_HANDLE)
1053 {
1054 vkDestroySemaphore(g_hDevice, g_hRenderFinishedSemaphore, nullptr);
1055 g_hRenderFinishedSemaphore = VK_NULL_HANDLE;
1056 }
1057
1058 VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
1059 ERR_GUARD_VULKAN( vkCreateSemaphore(g_hDevice, &semaphoreInfo, nullptr, &g_hImageAvailableSemaphore) );
1060 ERR_GUARD_VULKAN( vkCreateSemaphore(g_hDevice, &semaphoreInfo, nullptr, &g_hRenderFinishedSemaphore) );
1061}
1062
1063static void DestroySwapchain(bool destroyActualSwapchain)
1064{
1065 if(g_hImageAvailableSemaphore != VK_NULL_HANDLE)
1066 {
1067 vkDestroySemaphore(g_hDevice, g_hImageAvailableSemaphore, nullptr);
1068 g_hImageAvailableSemaphore = VK_NULL_HANDLE;
1069 }
1070 if(g_hRenderFinishedSemaphore != VK_NULL_HANDLE)
1071 {
1072 vkDestroySemaphore(g_hDevice, g_hRenderFinishedSemaphore, nullptr);
1073 g_hRenderFinishedSemaphore = VK_NULL_HANDLE;
1074 }
1075
1076 for(size_t i = g_Framebuffers.size(); i--; )
1077 vkDestroyFramebuffer(g_hDevice, g_Framebuffers[i], nullptr);
1078 g_Framebuffers.clear();
1079
1080 if(g_hDepthImageView != VK_NULL_HANDLE)
1081 {
1082 vkDestroyImageView(g_hDevice, g_hDepthImageView, nullptr);
1083 g_hDepthImageView = VK_NULL_HANDLE;
1084 }
1085 if(g_hDepthImage != VK_NULL_HANDLE)
1086 {
1087 vmaDestroyImage(g_hAllocator, g_hDepthImage);
1088 g_hDepthImage = VK_NULL_HANDLE;
1089 }
1090
1091 if(g_hPipeline != VK_NULL_HANDLE)
1092 {
1093 vkDestroyPipeline(g_hDevice, g_hPipeline, nullptr);
1094 g_hPipeline = VK_NULL_HANDLE;
1095 }
1096
1097 if(g_hRenderPass != VK_NULL_HANDLE)
1098 {
1099 vkDestroyRenderPass(g_hDevice, g_hRenderPass, nullptr);
1100 g_hRenderPass = VK_NULL_HANDLE;
1101 }
1102
1103 if(g_hPipelineLayout != VK_NULL_HANDLE)
1104 {
1105 vkDestroyPipelineLayout(g_hDevice, g_hPipelineLayout, nullptr);
1106 g_hPipelineLayout = VK_NULL_HANDLE;
1107 }
1108
1109 for(size_t i = g_SwapchainImageViews.size(); i--; )
1110 vkDestroyImageView(g_hDevice, g_SwapchainImageViews[i], nullptr);
1111 g_SwapchainImageViews.clear();
1112
1113 if(destroyActualSwapchain && (g_hSwapchain != VK_NULL_HANDLE))
1114 {
1115 vkDestroySwapchainKHR(g_hDevice, g_hSwapchain, nullptr);
1116 g_hSwapchain = VK_NULL_HANDLE;
1117 }
1118}
1119
1120static void InitializeApplication()
1121{
1122 uint32_t instanceLayerPropCount = 0;
1123 ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) );
1124 std::vector<VkLayerProperties> instanceLayerProps(instanceLayerPropCount);
1125 if(instanceLayerPropCount > 0)
1126 {
1127 ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()) );
1128 }
1129
1130 if(g_EnableValidationLayer == true)
1131 {
1132 if(IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME) == false)
1133 {
1134 printf("Layer \"%s\" not supported.", VALIDATION_LAYER_NAME);
1135 g_EnableValidationLayer = false;
1136 }
1137 }
1138
1139 std::vector<const char*> instanceExtensions;
1140 instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
1141 instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
1142
1143 std::vector<const char*> instanceLayers;
1144 if(g_EnableValidationLayer == true)
1145 {
1146 instanceLayers.push_back(VALIDATION_LAYER_NAME);
1147 instanceExtensions.push_back("VK_EXT_debug_report");
1148 }
1149
1150 VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
1151 appInfo.pApplicationName = APP_TITLE_A;
1152 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
1153 appInfo.pEngineName = "Adam Sawicki Engine";
1154 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
1155 appInfo.apiVersion = VK_API_VERSION_1_0;
1156
1157 VkInstanceCreateInfo instInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
1158 instInfo.pApplicationInfo = &appInfo;
1159 instInfo.enabledExtensionCount = static_cast<uint32_t>(instanceExtensions.size());
1160 instInfo.ppEnabledExtensionNames = instanceExtensions.data();
1161 instInfo.enabledLayerCount = static_cast<uint32_t>(instanceLayers.size());
1162 instInfo.ppEnabledLayerNames = instanceLayers.data();
1163
1164 ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, NULL, &g_hVulkanInstance) );
1165
1166 // Create VkSurfaceKHR.
1167 VkWin32SurfaceCreateInfoKHR surfaceInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
1168 surfaceInfo.hinstance = g_hAppInstance;
1169 surfaceInfo.hwnd = g_hWnd;
1170 VkResult result = vkCreateWin32SurfaceKHR(g_hVulkanInstance, &surfaceInfo, NULL, &g_hSurface);
1171 assert(result == VK_SUCCESS);
1172
1173 if(g_EnableValidationLayer == true)
1174 RegisterDebugCallbacks();
1175
1176 // Find physical device
1177
1178 uint32_t deviceCount = 0;
1179 ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, nullptr) );
1180 assert(deviceCount > 0);
1181
1182 std::vector<VkPhysicalDevice> physicalDevices(deviceCount);
1183 ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, physicalDevices.data()) );
1184
1185 g_hPhysicalDevice = physicalDevices[0];
1186
1187 // Query for features
1188
1189 VkPhysicalDeviceProperties physicalDeviceProperties = {};
1190 vkGetPhysicalDeviceProperties(g_hPhysicalDevice, &physicalDeviceProperties);
1191
1192 //VkPhysicalDeviceFeatures physicalDeviceFreatures = {};
1193 //vkGetPhysicalDeviceFeatures(g_PhysicalDevice, &physicalDeviceFreatures);
1194
1195 // Find queue family index
1196
1197 uint32_t queueFamilyCount = 0;
1198 vkGetPhysicalDeviceQueueFamilyProperties(g_hPhysicalDevice, &queueFamilyCount, nullptr);
1199 assert(queueFamilyCount > 0);
1200 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
1201 vkGetPhysicalDeviceQueueFamilyProperties(g_hPhysicalDevice, &queueFamilyCount, queueFamilies.data());
1202 for(uint32_t i = 0;
1203 (i < queueFamilyCount) &&
1204 (g_GraphicsQueueFamilyIndex == UINT_MAX || g_PresentQueueFamilyIndex == UINT_MAX);
1205 ++i)
1206 {
1207 if(queueFamilies[i].queueCount > 0)
1208 {
1209 if((g_GraphicsQueueFamilyIndex != 0) &&
1210 ((queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0))
1211 {
1212 g_GraphicsQueueFamilyIndex = i;
1213 }
1214
1215 VkBool32 surfaceSupported = 0;
1216 VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(g_hPhysicalDevice, i, g_hSurface, &surfaceSupported);
1217 if((res >= 0) && (surfaceSupported == VK_TRUE))
1218 {
1219 g_PresentQueueFamilyIndex = i;
1220 }
1221 }
1222 }
1223 assert(g_GraphicsQueueFamilyIndex != UINT_MAX);
1224
1225 // Create logical device
1226
1227 const float queuePriority = 1.f;
1228
1229 VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
1230 deviceQueueCreateInfo[0].queueFamilyIndex = g_GraphicsQueueFamilyIndex;
1231 deviceQueueCreateInfo[0].queueCount = 1;
1232 deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority;
1233 deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1234 deviceQueueCreateInfo[1].queueFamilyIndex = g_PresentQueueFamilyIndex;
1235 deviceQueueCreateInfo[1].queueCount = 1;
1236 deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority;
1237
1238 VkPhysicalDeviceFeatures deviceFeatures = {};
1239 deviceFeatures.fillModeNonSolid = VK_TRUE;
1240 deviceFeatures.samplerAnisotropy = VK_TRUE;
1241
1242 std::vector<const char*> enabledDeviceExtensions;
1243 enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
1244
1245 VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
1246 deviceCreateInfo.enabledLayerCount = 0;
1247 deviceCreateInfo.ppEnabledLayerNames = nullptr;
1248 deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size();
1249 deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data();
1250 deviceCreateInfo.queueCreateInfoCount = g_PresentQueueFamilyIndex != g_GraphicsQueueFamilyIndex ? 2 : 1;
1251 deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
1252 deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
1253
1254 ERR_GUARD_VULKAN( vkCreateDevice(g_hPhysicalDevice, &deviceCreateInfo, nullptr, &g_hDevice) );
1255
1256 // Create memory allocator
1257
1258 VmaAllocatorCreateInfo allocatorInfo = {};
1259 allocatorInfo.physicalDevice = g_hPhysicalDevice;
1260 allocatorInfo.device = g_hDevice;
1261 ERR_GUARD_VULKAN( vmaCreateAllocator(&allocatorInfo, &g_hAllocator) );
1262
1263 // Retrieve queue (doesn't need to be destroyed)
1264
1265 vkGetDeviceQueue(g_hDevice, g_GraphicsQueueFamilyIndex, 0, &g_hGraphicsQueue);
1266 vkGetDeviceQueue(g_hDevice, g_PresentQueueFamilyIndex, 0, &g_hPresentQueue);
1267 assert(g_hGraphicsQueue);
1268 assert(g_hPresentQueue);
1269
1270 // Create command pool
1271
1272 VkCommandPoolCreateInfo commandPoolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
1273 commandPoolInfo.queueFamilyIndex = g_GraphicsQueueFamilyIndex;
1274 commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1275 ERR_GUARD_VULKAN( vkCreateCommandPool(g_hDevice, &commandPoolInfo, nullptr, &g_hCommandPool) );
1276
1277 VkCommandBufferAllocateInfo commandBufferInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
1278 commandBufferInfo.commandPool = g_hCommandPool;
1279 commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1280 commandBufferInfo.commandBufferCount = COMMAND_BUFFER_COUNT;
1281 ERR_GUARD_VULKAN( vkAllocateCommandBuffers(g_hDevice, &commandBufferInfo, g_MainCommandBuffers) );
1282
1283 VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
1284 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1285 for(size_t i = 0; i < COMMAND_BUFFER_COUNT; ++i)
1286 {
1287 ERR_GUARD_VULKAN( vkCreateFence(g_hDevice, &fenceInfo, nullptr, &g_MainCommandBufferExecutedFances[i]) );
1288 }
1289
1290 commandBufferInfo.commandBufferCount = 1;
1291 ERR_GUARD_VULKAN( vkAllocateCommandBuffers(g_hDevice, &commandBufferInfo, &g_hTemporaryCommandBuffer) );
1292
1293 // Create texture sampler
1294
1295 VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
1296 samplerInfo.magFilter = VK_FILTER_LINEAR;
1297 samplerInfo.minFilter = VK_FILTER_LINEAR;
1298 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1299 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1300 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1301 samplerInfo.anisotropyEnable = VK_TRUE;
1302 samplerInfo.maxAnisotropy = 16;
1303 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1304 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1305 samplerInfo.compareEnable = VK_FALSE;
1306 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1307 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1308 samplerInfo.mipLodBias = 0.f;
1309 samplerInfo.minLod = 0.f;
1310 samplerInfo.maxLod = FLT_MAX;
1311 ERR_GUARD_VULKAN( vkCreateSampler(g_hDevice, &samplerInfo, nullptr, &g_hSampler) );
1312
1313 CreateTexture(128, 128);
1314 CreateMesh();
1315
1316 VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
1317 samplerLayoutBinding.binding = 1;
1318 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1319 samplerLayoutBinding.descriptorCount = 1;
1320 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1321
1322 VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
1323 descriptorSetLayoutInfo.bindingCount = 1;
1324 descriptorSetLayoutInfo.pBindings = &samplerLayoutBinding;
1325 ERR_GUARD_VULKAN( vkCreateDescriptorSetLayout(g_hDevice, &descriptorSetLayoutInfo, nullptr, &g_hDescriptorSetLayout) );
1326
1327 // Create descriptor pool
1328
1329 VkDescriptorPoolSize descriptorPoolSizes[2];
1330 ZeroMemory(descriptorPoolSizes, sizeof(descriptorPoolSizes));
1331 descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1332 descriptorPoolSizes[0].descriptorCount = 1;
1333 descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1334 descriptorPoolSizes[1].descriptorCount = 1;
1335
1336 VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
1337 descriptorPoolInfo.poolSizeCount = (uint32_t)_countof(descriptorPoolSizes);
1338 descriptorPoolInfo.pPoolSizes = descriptorPoolSizes;
1339 descriptorPoolInfo.maxSets = 1;
1340 ERR_GUARD_VULKAN( vkCreateDescriptorPool(g_hDevice, &descriptorPoolInfo, nullptr, &g_hDescriptorPool) );
1341
1342 // Create descriptor set layout
1343
1344 VkDescriptorSetLayout descriptorSetLayouts[] = { g_hDescriptorSetLayout };
1345 VkDescriptorSetAllocateInfo descriptorSetInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
1346 descriptorSetInfo.descriptorPool = g_hDescriptorPool;
1347 descriptorSetInfo.descriptorSetCount = 1;
1348 descriptorSetInfo.pSetLayouts = descriptorSetLayouts;
1349 ERR_GUARD_VULKAN( vkAllocateDescriptorSets(g_hDevice, &descriptorSetInfo, &g_hDescriptorSet) );
1350
1351 VkDescriptorImageInfo descriptorImageInfo = {};
1352 descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1353 descriptorImageInfo.imageView = g_hTextureImageView;
1354 descriptorImageInfo.sampler = g_hSampler;
1355
1356 VkWriteDescriptorSet writeDescriptorSet = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
1357 writeDescriptorSet.dstSet = g_hDescriptorSet;
1358 writeDescriptorSet.dstBinding = 1;
1359 writeDescriptorSet.dstArrayElement = 0;
1360 writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1361 writeDescriptorSet.descriptorCount = 1;
1362 writeDescriptorSet.pImageInfo = &descriptorImageInfo;
1363
1364 vkUpdateDescriptorSets(g_hDevice, 1, &writeDescriptorSet, 0, nullptr);
1365
1366 CreateSwapchain();
1367}
1368
1369static void FinalizeApplication()
1370{
1371 vkDeviceWaitIdle(g_hDevice);
1372
1373 DestroySwapchain(true);
1374
1375 if(g_hDescriptorPool != VK_NULL_HANDLE)
1376 {
1377 vkDestroyDescriptorPool(g_hDevice, g_hDescriptorPool, nullptr);
1378 g_hDescriptorPool = VK_NULL_HANDLE;
1379 }
1380
1381 if(g_hDescriptorSetLayout != VK_NULL_HANDLE)
1382 {
1383 vkDestroyDescriptorSetLayout(g_hDevice, g_hDescriptorSetLayout, nullptr);
1384 g_hDescriptorSetLayout = VK_NULL_HANDLE;
1385 }
1386
1387 if(g_hTextureImageView != VK_NULL_HANDLE)
1388 {
1389 vkDestroyImageView(g_hDevice, g_hTextureImageView, nullptr);
1390 g_hTextureImageView = VK_NULL_HANDLE;
1391 }
1392 if(g_hTextureImage != VK_NULL_HANDLE)
1393 {
1394 vmaDestroyImage(g_hAllocator, g_hTextureImage);
1395 g_hTextureImage = VK_NULL_HANDLE;
1396 }
1397
1398 if(g_hIndexBuffer != VK_NULL_HANDLE)
1399 {
1400 vmaDestroyBuffer(g_hAllocator, g_hIndexBuffer);
1401 g_hIndexBuffer = VK_NULL_HANDLE;
1402 }
1403 if(g_hVertexBuffer != VK_NULL_HANDLE)
1404 {
1405 vmaDestroyBuffer(g_hAllocator, g_hVertexBuffer);
1406 g_hVertexBuffer = VK_NULL_HANDLE;
1407 }
1408
1409 if(g_hSampler != VK_NULL_HANDLE)
1410 {
1411 vkDestroySampler(g_hDevice, g_hSampler, nullptr);
1412 g_hSampler = VK_NULL_HANDLE;
1413 }
1414
1415 for(size_t i = COMMAND_BUFFER_COUNT; i--; )
1416 {
1417 if(g_MainCommandBufferExecutedFances[i] != VK_NULL_HANDLE)
1418 {
1419 vkDestroyFence(g_hDevice, g_MainCommandBufferExecutedFances[i], nullptr);
1420 g_MainCommandBufferExecutedFances[i] = VK_NULL_HANDLE;
1421 }
1422 }
1423 if(g_MainCommandBuffers[0] != VK_NULL_HANDLE)
1424 {
1425 vkFreeCommandBuffers(g_hDevice, g_hCommandPool, COMMAND_BUFFER_COUNT, g_MainCommandBuffers);
1426 ZeroMemory(g_MainCommandBuffers, sizeof(g_MainCommandBuffers));
1427 }
1428 if(g_hTemporaryCommandBuffer != VK_NULL_HANDLE)
1429 {
1430 vkFreeCommandBuffers(g_hDevice, g_hCommandPool, 1, &g_hTemporaryCommandBuffer);
1431 g_hTemporaryCommandBuffer = VK_NULL_HANDLE;
1432 }
1433
1434 if(g_hCommandPool != VK_NULL_HANDLE)
1435 {
1436 vkDestroyCommandPool(g_hDevice, g_hCommandPool, nullptr);
1437 g_hCommandPool = VK_NULL_HANDLE;
1438 }
1439
1440 if(g_hAllocator != VK_NULL_HANDLE)
1441 {
1442 vmaDestroyAllocator(g_hAllocator);
1443 g_hAllocator = nullptr;
1444 }
1445
1446 if(g_hDevice != VK_NULL_HANDLE)
1447 {
1448 vkDestroyDevice(g_hDevice, nullptr);
1449 g_hDevice = nullptr;
1450 }
1451
1452 if(g_pvkDestroyDebugReportCallbackEXT && g_hCallback != VK_NULL_HANDLE)
1453 {
1454 g_pvkDestroyDebugReportCallbackEXT(g_hVulkanInstance, g_hCallback, nullptr);
1455 g_hCallback = VK_NULL_HANDLE;
1456 }
1457
1458 if(g_hSurface != VK_NULL_HANDLE)
1459 {
1460 vkDestroySurfaceKHR(g_hVulkanInstance, g_hSurface, NULL);
1461 g_hSurface = VK_NULL_HANDLE;
1462 }
1463
1464 if(g_hVulkanInstance != VK_NULL_HANDLE)
1465 {
1466 vkDestroyInstance(g_hVulkanInstance, NULL);
1467 g_hVulkanInstance = VK_NULL_HANDLE;
1468 }
1469}
1470
1471static void PrintAllocatorStats()
1472{
1473#if VMA_STATS_STRING_ENABLED
1474 char* statsString = nullptr;
1475 vmaBuildStatsString(g_hAllocator, &statsString, true);
1476 printf("%s\n", statsString);
1477 vmaFreeStatsString(g_hAllocator, statsString);
1478#endif
1479}
1480
1481static void RecreateSwapChain()
1482{
1483 vkDeviceWaitIdle(g_hDevice);
1484 DestroySwapchain(false);
1485 CreateSwapchain();
1486}
1487
1488static void DrawFrame()
1489{
1490 // Begin main command buffer
1491 size_t cmdBufIndex = (g_NextCommandBufferIndex++) % COMMAND_BUFFER_COUNT;
1492 VkCommandBuffer hCommandBuffer = g_MainCommandBuffers[cmdBufIndex];
1493 VkFence hCommandBufferExecutedFence = g_MainCommandBufferExecutedFances[cmdBufIndex];
1494
1495 ERR_GUARD_VULKAN( vkWaitForFences(g_hDevice, 1, &hCommandBufferExecutedFence, VK_TRUE, UINT64_MAX) );
1496 ERR_GUARD_VULKAN( vkResetFences(g_hDevice, 1, &hCommandBufferExecutedFence) );
1497
1498 VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
1499 commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1500 ERR_GUARD_VULKAN( vkBeginCommandBuffer(hCommandBuffer, &commandBufferBeginInfo) );
1501
1502 // Acquire swapchain image
1503 uint32_t imageIndex = 0;
1504 VkResult res = vkAcquireNextImageKHR(g_hDevice, g_hSwapchain, UINT64_MAX, g_hImageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
1505 if(res == VK_ERROR_OUT_OF_DATE_KHR)
1506 {
1507 RecreateSwapChain();
1508 return;
1509 }
1510 else if(res < 0)
1511 {
1512 ERR_GUARD_VULKAN(res);
1513 }
1514
1515 // Record geometry pass
1516
1517 VkClearValue clearValues[2];
1518 ZeroMemory(clearValues, sizeof(clearValues));
1519 clearValues[0].color.float32[0] = 0.25f;
1520 clearValues[0].color.float32[1] = 0.25f;
1521 clearValues[0].color.float32[2] = 0.5f;
1522 clearValues[0].color.float32[3] = 1.0f;
1523 clearValues[1].depthStencil.depth = 1.0f;
1524
1525 VkRenderPassBeginInfo renderPassBeginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
1526 renderPassBeginInfo.renderPass = g_hRenderPass;
1527 renderPassBeginInfo.framebuffer = g_Framebuffers[imageIndex];
1528 renderPassBeginInfo.renderArea.offset.x = 0;
1529 renderPassBeginInfo.renderArea.offset.y = 0;
1530 renderPassBeginInfo.renderArea.extent = g_Extent;
1531 renderPassBeginInfo.clearValueCount = (uint32_t)_countof(clearValues);
1532 renderPassBeginInfo.pClearValues = clearValues;
1533 vkCmdBeginRenderPass(hCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
1534
1535 vkCmdBindPipeline(
1536 hCommandBuffer,
1537 VK_PIPELINE_BIND_POINT_GRAPHICS,
1538 g_hPipeline);
1539
1540 mathfu::mat4 view = mathfu::mat4::LookAt(
1541 mathfu::kZeros3f,
1542 mathfu::vec3(0.f, -2.f, 4.f),
1543 mathfu::kAxisY3f);
1544 mathfu::mat4 proj = mathfu::mat4::Perspective(
1545 1.0471975511966f, // 60 degrees
1546 (float)g_Extent.width / (float)g_Extent.height,
1547 0.1f,
1548 1000.f,
1549 -1.f);
1550 //proj[1][1] *= -1.f;
1551 mathfu::mat4 viewProj = proj * view;
1552
1553 vkCmdBindDescriptorSets(
1554 hCommandBuffer,
1555 VK_PIPELINE_BIND_POINT_GRAPHICS,
1556 g_hPipelineLayout,
1557 0,
1558 1,
1559 &g_hDescriptorSet,
1560 0,
1561 nullptr);
1562
1563 float rotationAngle = (float)GetTickCount() * 0.001f * (float)M_PI * 0.2f;
1564 mathfu::mat3 model_3 = mathfu::mat3::RotationY(rotationAngle);
1565 mathfu::mat4 model_4 = mathfu::mat4(
1566 model_3(0, 0), model_3(0, 1), model_3(0, 2), 0.f,
1567 model_3(1, 0), model_3(1, 1), model_3(1, 2), 0.f,
1568 model_3(2, 0), model_3(2, 1), model_3(2, 2), 0.f,
1569 0.f, 0.f, 0.f, 1.f);
1570 mathfu::mat4 modelViewProj = viewProj * model_4;
1571
1572 UniformBufferObject ubo = {};
1573 modelViewProj.Pack(ubo.ModelViewProj);
1574 vkCmdPushConstants(hCommandBuffer, g_hPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(UniformBufferObject), &ubo);
1575
1576 VkBuffer vertexBuffers[] = { g_hVertexBuffer };
1577 VkDeviceSize offsets[] = { 0 };
1578 vkCmdBindVertexBuffers(hCommandBuffer, 0, 1, vertexBuffers, offsets);
1579
1580 vkCmdBindIndexBuffer(hCommandBuffer, g_hIndexBuffer, 0, VK_INDEX_TYPE_UINT16);
1581
1582 vkCmdDrawIndexed(hCommandBuffer, g_IndexCount, 1, 0, 0, 0);
1583
1584 vkCmdEndRenderPass(hCommandBuffer);
1585
1586 vkEndCommandBuffer(hCommandBuffer);
1587
1588 // Submit command buffer
1589
1590 VkSemaphore submitWaitSemaphores[] = { g_hImageAvailableSemaphore };
1591 VkPipelineStageFlags submitWaitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
1592 VkSemaphore submitSignalSemaphores[] = { g_hRenderFinishedSemaphore };
1593 VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
1594 submitInfo.waitSemaphoreCount = 1;
1595 submitInfo.pWaitSemaphores = submitWaitSemaphores;
1596 submitInfo.pWaitDstStageMask = submitWaitStages;
1597 submitInfo.commandBufferCount = 1;
1598 submitInfo.pCommandBuffers = &hCommandBuffer;
1599 submitInfo.signalSemaphoreCount = _countof(submitSignalSemaphores);
1600 submitInfo.pSignalSemaphores = submitSignalSemaphores;
1601 ERR_GUARD_VULKAN( vkQueueSubmit(g_hGraphicsQueue, 1, &submitInfo, hCommandBufferExecutedFence) );
1602
1603 VkSemaphore presentWaitSemaphores[] = { g_hRenderFinishedSemaphore };
1604
1605 VkSwapchainKHR swapchains[] = { g_hSwapchain };
1606 VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
1607 presentInfo.waitSemaphoreCount = _countof(presentWaitSemaphores);
1608 presentInfo.pWaitSemaphores = presentWaitSemaphores;
1609 presentInfo.swapchainCount = 1;
1610 presentInfo.pSwapchains = swapchains;
1611 presentInfo.pImageIndices = &imageIndex;
1612 presentInfo.pResults = nullptr;
1613 res = vkQueuePresentKHR(g_hPresentQueue, &presentInfo);
1614 if(res == VK_ERROR_OUT_OF_DATE_KHR)
1615 {
1616 RecreateSwapChain();
1617 }
1618 else
1619 ERR_GUARD_VULKAN(res);
1620}
1621
1622static void HandlePossibleSizeChange()
1623{
1624 RECT clientRect;
1625 GetClientRect(g_hWnd, &clientRect);
1626 LONG newSizeX = clientRect.right - clientRect.left;
1627 LONG newSizeY = clientRect.bottom - clientRect.top;
1628 if((newSizeX > 0) &&
1629 (newSizeY > 0) &&
1630 ((newSizeX != g_SizeX) || (newSizeY != g_SizeY)))
1631 {
1632 g_SizeX = newSizeX;
1633 g_SizeY = newSizeY;
1634
1635 RecreateSwapChain();
1636 }
1637}
1638
1639static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1640{
1641 switch(msg)
1642 {
1643 case WM_CREATE:
1644 // This is intentionally assigned here because we are now inside CreateWindow, before it returns.
1645 g_hWnd = hWnd;
1646 InitializeApplication();
1647 PrintAllocatorStats();
1648 return 0;
1649
1650 case WM_DESTROY:
1651 FinalizeApplication();
1652 PostQuitMessage(0);
1653 return 0;
1654
1655 // This prevents app from freezing when left Alt is pressed
1656 // (which normally enters modal menu loop).
1657 case WM_SYSKEYDOWN:
1658 case WM_SYSKEYUP:
1659 return 0;
1660
1661 case WM_SIZE:
1662 if((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))
1663 HandlePossibleSizeChange();
1664 return 0;
1665
1666 case WM_EXITSIZEMOVE:
1667 HandlePossibleSizeChange();
1668 return 0;
1669
1670 case WM_KEYDOWN:
1671 if(wParam == VK_ESCAPE)
1672 PostMessage(hWnd, WM_CLOSE, 0, 0);
1673 return 0;
1674
1675 default:
1676 break;
1677 }
1678
1679 return DefWindowProc(hWnd, msg, wParam, lParam);
1680}
1681
1682int main()
1683{
1684 g_hAppInstance = (HINSTANCE)GetModuleHandle(NULL);
1685
1686 WNDCLASSEX wndClassDesc = { sizeof(WNDCLASSEX) };
1687 wndClassDesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
1688 wndClassDesc.hbrBackground = NULL;
1689 wndClassDesc.hCursor = LoadCursor(NULL, IDC_CROSS);
1690 wndClassDesc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
1691 wndClassDesc.hInstance = g_hAppInstance;
1692 wndClassDesc.lpfnWndProc = WndProc;
1693 wndClassDesc.lpszClassName = WINDOW_CLASS_NAME;
1694
1695 const ATOM hWndClass = RegisterClassEx(&wndClassDesc);
1696 assert(hWndClass);
1697
1698 const DWORD style = WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
1699 const DWORD exStyle = 0;
1700
1701 RECT rect = { 0, 0, g_SizeX, g_SizeY };
1702 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
1703
1704 HWND hWnd = CreateWindowEx(
1705 exStyle, WINDOW_CLASS_NAME, APP_TITLE_W, style,
1706 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
1707 NULL, NULL, g_hAppInstance, NULL);
1708
1709 MSG msg;
1710 for(;;)
1711 {
1712 if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
1713 {
1714 if(msg.message == WM_QUIT)
1715 break;
1716 TranslateMessage(&msg);
1717 DispatchMessage(&msg);
1718 }
1719 if(g_hDevice != VK_NULL_HANDLE)
1720 DrawFrame();
1721 }
1722
1723 return 0;
1724}