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