aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry Song <henry.song@samsung.com>2015-05-13 14:53:31 -0700
committerHenry Song <henry.song@samsung.com>2015-05-13 14:53:31 -0700
commit5a1a158dc7207eab9c8bde56f4603e3543c0dde4 (patch)
tree7d40663b12cdaa3f7355c9d71758ef1a6f7f7f8d
parentb4d9a6c80f62f768d17e3173147c5de11efb5726 (diff)
downloadusermoji-5a1a158dc7207eab9c8bde56f4603e3543c0dde4.tar.xz
demos: Add msaa-tri.c for msaa rendering example modified from tri.c
-rw-r--r--demos/msaa-tri.c1948
1 files changed, 1948 insertions, 0 deletions
diff --git a/demos/msaa-tri.c b/demos/msaa-tri.c
new file mode 100644
index 00000000..a8e10c9b
--- /dev/null
+++ b/demos/msaa-tri.c
@@ -0,0 +1,1948 @@
+/*
+ * Vulkan
+ *
+ * Copyright (C) 2014-2015 LunarG, Inc.
+ * Copyright (c) 2015 Samsung Electronics, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Draw a textured triangle with depth testing. This is written against Intel
+ * ICD. It does not do state transition nor object memory binding like it
+ * should. It also does no error checking.
+ */
+/* Modified to use MSAA
+ *
+ * Changes to tri.c
+ * (1) No color attachment views created for swap chain images
+ * (2) create an offscreen MSAA (samples = 4) image as color attachment
+ * (3) create msaa depth/stencil image
+ * (4) during draw time, attach msaa image and msaa depth/stencil buffer
+ * as color and depth/stencil attachment
+ * (5) once drawn to msaa color image, resolve it to swap chain image
+ */
+/* Contributed by Henry Song (henry.song@samsung.com) */
+
+#ifndef VK_PROTOTYPES
+#define VK_PROTOTYPES
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#ifdef _WIN32
+#pragma comment(linker, "/subsystem:windows")
+#include <windows.h>
+#include <fcntl.h>
+#include <io.h>
+#define APP_NAME_STR_LEN 80
+#else // _WIN32
+#include <xcb/xcb.h>
+#endif // _WIN32
+
+#include <Vk/vulkan.h>
+#include <Vk/vkDbg.h>
+#include <Vk/vk_wsi_lunarg.h>
+
+#include "icd-spv.h"
+
+#define DEMO_BUFFER_COUNT 2
+#define DEMO_TEXTURE_COUNT 1
+#define VERTEX_BUFFER_BIND_ID 0
+#define APP_SHORT_NAME "tri"
+#define APP_LONG_NAME "The Vulkan Triangle Demo Program"
+
+#ifdef _WIN32
+bool consoleCreated = false;
+
+#define WAIT_FOR_CONSOLE_DESTROY \
+ do { \
+ if (consoleCreated) \
+ Sleep(INFINITE); \
+ } while (0)
+
+#define ERR_EXIT(err_msg, err_class) \
+ do { \
+ MessageBox(NULL, err_msg, err_class, MB_OK); \
+ if (consoleCreated) { \
+ printf("\nPlease close this window when you are finished " \
+ "looking at the console output.\n"); \
+ fflush(stdout); \
+ } \
+ WAIT_FOR_CONSOLE_DESTROY; \
+ exit(1); \
+ } while (0)
+
+// NOTE: If the following values (copied from "loader_platform.h") change, they
+// need to change here as well:
+#define LAYER_NAMES_ENV "VK_LAYER_NAMES"
+#define LAYER_NAMES_REGISTRY_VALUE "VK_LAYER_NAMES"
+#else // _WIN32
+ #define WAIT_FOR_CONSOLE_DESTROY
+
+#define ERR_EXIT(err_msg, err_class) \
+ do { \
+ printf(err_msg); \
+ fflush(stdout); \
+ exit(1); \
+ } while (0)
+#endif // _WIN32
+
+struct texture_object {
+ VkSampler sampler;
+
+ VkImage image;
+ VkImageLayout imageLayout;
+
+ uint32_t num_mem;
+ VkDeviceMemory *mem;
+ VkImageView view;
+ int32_t tex_width, tex_height;
+};
+
+struct demo {
+#ifdef _WIN32
+#define APP_NAME_STR_LEN 80
+ HINSTANCE connection; // hInstance - Windows Instance
+ char name[APP_NAME_STR_LEN]; // Name to put on the window/icon
+ HWND window; // hWnd - window handle
+#else // _WIN32
+ xcb_connection_t *connection;
+ xcb_screen_t *screen;
+ xcb_window_t window;
+ xcb_intern_atom_reply_t *atom_wm_delete_window;
+#endif // _WIN32
+ bool prepared;
+ bool use_staging_buffer;
+
+ VkInstance inst;
+ VkPhysicalDevice gpu;
+ VkDevice device;
+ VkQueue queue;
+ VkPhysicalDeviceProperties *gpu_props;
+ VkPhysicalDeviceQueueProperties *queue_props;
+ uint32_t graphics_queue_node_index;
+
+ int width, height;
+ VkFormat format;
+
+ VkSwapChainWSI swap_chain;
+ struct {
+ VkImage image;
+ VkDeviceMemory mem;
+ } buffers[DEMO_BUFFER_COUNT];
+
+ struct {
+ VkFormat format;
+
+ VkImage image;
+ uint32_t num_mem;
+ VkDeviceMemory *mem;
+ VkColorAttachmentView view;
+ } msaa_color;
+
+ struct {
+ VkFormat format;
+
+ VkImage image;
+ uint32_t num_mem;
+ VkDeviceMemory *mem;
+ VkDepthStencilView view;
+ } msaa_depth;
+
+ struct texture_object textures[DEMO_TEXTURE_COUNT];
+
+ struct {
+ VkBuffer buf;
+ uint32_t num_mem;
+ VkDeviceMemory *mem;
+
+ VkPipelineVertexInputCreateInfo vi;
+ VkVertexInputBindingDescription vi_bindings[1];
+ VkVertexInputAttributeDescription vi_attrs[2];
+ } vertices;
+
+ VkCmdBuffer cmd; // Buffer for initialization commands
+ VkPipelineLayout pipeline_layout;
+ VkDescriptorSetLayout desc_layout;
+ VkPipeline pipeline;
+
+ VkDynamicVpState viewport;
+ VkDynamicRsState raster;
+ VkDynamicCbState color_blend;
+ VkDynamicDsState depth_stencil;
+
+ VkDescriptorPool desc_pool;
+ VkDescriptorSet desc_set;
+
+ bool quit;
+ uint32_t current_buffer;
+};
+
+static void demo_flush_init_cmd(struct demo *demo)
+{
+ VkResult err;
+
+ if (demo->cmd == VK_NULL_HANDLE)
+ return;
+
+ err = vkEndCommandBuffer(demo->cmd);
+ assert(!err);
+
+ const VkCmdBuffer cmd_bufs[] = { demo->cmd };
+
+ err = vkQueueSubmit(demo->queue, 1, cmd_bufs, VK_NULL_HANDLE);
+ assert(!err);
+
+ err = vkQueueWaitIdle(demo->queue);
+ assert(!err);
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_COMMAND_BUFFER, demo->cmd);
+ demo->cmd = VK_NULL_HANDLE;
+}
+
+static void demo_add_mem_refs(
+ struct demo *demo,
+ int num_refs, VkDeviceMemory *mem)
+{
+ vkQueueAddMemReferences(demo->queue, num_refs, mem);
+}
+
+static void demo_remove_mem_refs(
+ struct demo *demo,
+ int num_refs, VkDeviceMemory *mem)
+{
+ vkQueueRemoveMemReferences(demo->queue, num_refs, mem);
+}
+
+static void demo_set_image_layout(
+ struct demo *demo,
+ VkImage image,
+ VkImageLayout old_image_layout,
+ VkImageLayout new_image_layout)
+{
+ VkResult err;
+
+ if (demo->cmd == VK_NULL_HANDLE) {
+ const VkCmdBufferCreateInfo cmd = {
+ .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+ .pNext = NULL,
+ .queueNodeIndex = demo->graphics_queue_node_index,
+ .flags = 0,
+ };
+
+ err = vkCreateCommandBuffer(demo->device, &cmd, &demo->cmd);
+ assert(!err);
+
+ VkCmdBufferBeginInfo cmd_buf_info = {
+ .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+ .pNext = NULL,
+ .flags = VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT |
+ VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,
+ };
+ err = vkBeginCommandBuffer(demo->cmd, &cmd_buf_info);
+ }
+
+ VkImageMemoryBarrier image_memory_barrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = NULL,
+ .outputMask = 0,
+ .inputMask = 0,
+ .oldLayout = old_image_layout,
+ .newLayout = new_image_layout,
+ .image = image,
+ .subresourceRange = { VK_IMAGE_ASPECT_COLOR, 0, 1, 0, 0 }
+ };
+
+ if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL) {
+ /* Make sure anything that was copying from this image has completed */
+ image_memory_barrier.inputMask = VK_MEMORY_INPUT_TRANSFER_BIT;
+ }
+
+ if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ /* Make sure any Copy or CPU writes to image are flushed */
+ image_memory_barrier.outputMask = VK_MEMORY_OUTPUT_TRANSFER_BIT | VK_MEMORY_OUTPUT_CPU_WRITE_BIT;
+ }
+
+ VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
+
+ VkPipeEvent set_events[] = { VK_PIPE_EVENT_TOP_OF_PIPE };
+
+ vkCmdPipelineBarrier(demo->cmd, VK_WAIT_EVENT_TOP_OF_PIPE, 1, set_events, 1, (const void **)&pmemory_barrier);
+}
+
+static void demo_draw_build_cmd(struct demo *demo)
+{
+ const VkColorAttachmentBindInfo color_attachment = {
+ .view = demo->msaa_color.view,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ };
+ const VkDepthStencilBindInfo depth_stencil = {
+ .view = demo->msaa_depth.view,
+ .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ };
+ const VkClearColor clear_color = {
+ .color.floatColor = { 0.2f, 0.2f, 0.2f, 1.0f },
+ .useRawValue = false,
+ };
+ const float clear_depth = 0.0f;
+ VkImageSubresourceRange clear_range;
+ VkCmdBufferBeginInfo cmd_buf_info = {
+ .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+ .pNext = NULL,
+ .flags = VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT |
+ VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,
+ };
+ VkResult err;
+ VkAttachmentLoadOp load_op = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ VkAttachmentStoreOp store_op = VK_ATTACHMENT_STORE_OP_RESOLVE_MSAA;
+ const VkFramebufferCreateInfo fb_info = {
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .pNext = NULL,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = (VkColorAttachmentBindInfo*) &color_attachment,
+ .pDepthStencilAttachment = (VkDepthStencilBindInfo*) &depth_stencil,
+ .sampleCount = 4,
+ .width = demo->width,
+ .height = demo->height,
+ .layers = 1,
+ };
+ VkRenderPassCreateInfo rp_info;
+ VkRenderPassBegin rp_begin;
+
+ memset(&rp_info, 0 , sizeof(rp_info));
+ err = vkCreateFramebuffer(demo->device, &fb_info, &rp_begin.framebuffer);
+ assert(!err);
+ rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ rp_info.renderArea.extent.width = demo->width;
+ rp_info.renderArea.extent.height = demo->height;
+ rp_info.colorAttachmentCount = fb_info.colorAttachmentCount;
+ rp_info.pColorFormats = &demo->format;
+ rp_info.pColorLayouts = &color_attachment.layout;
+ rp_info.pColorLoadOps = &load_op;
+ rp_info.pColorStoreOps = &store_op;
+ rp_info.pColorLoadClearValues = &clear_color;
+ rp_info.depthStencilFormat = VK_FORMAT_D16_UNORM;
+ rp_info.depthStencilLayout = depth_stencil.layout;
+ rp_info.depthLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ rp_info.depthLoadClearValue = clear_depth;
+ rp_info.depthStoreOp = VK_ATTACHMENT_STORE_OP_RESOLVE_MSAA;
+ rp_info.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ rp_info.stencilLoadClearValue = 0;
+ rp_info.stencilStoreOp = VK_ATTACHMENT_STORE_OP_RESOLVE_MSAA;
+ err = vkCreateRenderPass(demo->device, &rp_info, &(rp_begin.renderPass));
+ assert(!err);
+
+ err = vkBeginCommandBuffer(demo->cmd, &cmd_buf_info);
+ assert(!err);
+
+
+ vkCmdBindPipeline(demo->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ demo->pipeline);
+ vkCmdBindDescriptorSets(demo->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ 0, 1, & demo->desc_set, 0, NULL);
+
+ vkCmdBindDynamicStateObject(demo->cmd, VK_STATE_BIND_POINT_VIEWPORT, demo->viewport);
+ vkCmdBindDynamicStateObject(demo->cmd, VK_STATE_BIND_POINT_RASTER, demo->raster);
+ vkCmdBindDynamicStateObject(demo->cmd, VK_STATE_BIND_POINT_COLOR_BLEND,
+ demo->color_blend);
+ vkCmdBindDynamicStateObject(demo->cmd, VK_STATE_BIND_POINT_DEPTH_STENCIL,
+ demo->depth_stencil);
+
+ VkDeviceSize offsets[1] = {0};
+ vkCmdBindVertexBuffers(demo->cmd, VERTEX_BUFFER_BIND_ID, 1, &demo->vertices.buf, offsets);
+
+ vkCmdBeginRenderPass(demo->cmd, &rp_begin);
+ clear_range.aspect = VK_IMAGE_ASPECT_COLOR;
+ clear_range.baseMipLevel = 0;
+ clear_range.mipLevels = 1;
+ clear_range.baseArraySlice = 0;
+ clear_range.arraySize = 1;
+
+ vkCmdClearColorImage(demo->cmd,
+ demo->msaa_color.image,
+ VK_IMAGE_LAYOUT_CLEAR_OPTIMAL,
+ clear_color, 1, &clear_range);
+
+ clear_range.aspect = VK_IMAGE_ASPECT_DEPTH;
+ vkCmdClearDepthStencil(demo->cmd,
+ demo->msaa_depth.image, VK_IMAGE_LAYOUT_CLEAR_OPTIMAL,
+ clear_depth, 0, 1, &clear_range);
+
+ vkCmdDraw(demo->cmd, 0, 3, 0, 1);
+ vkCmdEndRenderPass(demo->cmd, rp_begin.renderPass);
+
+ VkImageResolve resolve = {
+ .srcSubresource = { VK_IMAGE_ASPECT_COLOR, 0, 0 },
+ .srcOffset = { 0, 0, 0 },
+ .destSubresource = { VK_IMAGE_ASPECT_COLOR, 0, 0 },
+ .destOffset = { 0, 0, 0 },
+ .extent = { demo->width, demo->height, 1 }
+ };
+
+ demo_set_image_layout(demo, demo->msaa_color.image,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL);
+
+ vkCmdResolveImage(demo->cmd, demo->msaa_color.image,
+ VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL,
+ demo->buffers[demo->current_buffer].image,
+ VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL, 1, &resolve);
+
+ err = vkEndCommandBuffer(demo->cmd);
+ assert(!err);
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_RENDER_PASS, rp_begin.renderPass);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_FRAMEBUFFER, rp_begin.framebuffer);
+}
+
+static void demo_draw(struct demo *demo)
+{
+ const VkPresentInfoWSI present = {
+ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_WSI,
+ .pNext = NULL,
+ .image = demo->buffers[demo->current_buffer].image,
+ .flipInterval = 0,
+ };
+ VkResult err;
+
+ demo_draw_build_cmd(demo);
+
+ err = vkQueueSubmit(demo->queue, 1, &demo->cmd, VK_NULL_HANDLE);
+ assert(!err);
+
+ err = vkQueuePresentWSI(demo->queue, &present);
+ assert(!err);
+
+ demo->current_buffer = (demo->current_buffer + 1) % DEMO_BUFFER_COUNT;
+
+ err = vkQueueWaitIdle(demo->queue);
+ assert(err == VK_SUCCESS);
+}
+
+static void demo_prepare_buffers(struct demo *demo)
+{
+ const VkSwapChainCreateInfoWSI swap_chain = {
+ .sType = VK_STRUCTURE_TYPE_SWAP_CHAIN_CREATE_INFO_WSI,
+ .pNext = NULL,
+ .pNativeWindowSystemHandle = demo->connection,
+ .pNativeWindowHandle = (void *) (intptr_t) demo->window,
+ .imageCount = DEMO_BUFFER_COUNT,
+ .imageFormat = demo->format,
+ .imageExtent = {
+ .width = demo->width,
+ .height = demo->height,
+ },
+ .imageArraySize = 1,
+ .imageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT,
+ };
+ VkSwapChainImageInfoWSI images[DEMO_BUFFER_COUNT];
+ size_t images_size = sizeof(images);
+ VkResult err;
+ uint32_t i;
+
+ err = vkCreateSwapChainWSI(demo->device, &swap_chain, &demo->swap_chain);
+ assert(!err);
+
+ err = vkGetSwapChainInfoWSI(demo->swap_chain,
+ VK_SWAP_CHAIN_INFO_TYPE_PERSISTENT_IMAGES_WSI,
+ &images_size, images);
+ assert(!err && images_size == sizeof(images));
+
+ for (i = 0; i < DEMO_BUFFER_COUNT; i++) {
+ demo->buffers[i].image = images[i].image;
+ demo->buffers[i].mem = images[i].memory;
+
+ demo_add_mem_refs(demo, 1, &demo->buffers[i].mem);
+ demo_set_image_layout(demo, demo->buffers[i].image,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL);
+ }
+
+ demo->current_buffer = 0;
+
+ /* create MSAA image */
+ const VkFormat msaa_format = VK_FORMAT_B8G8R8A8_UNORM;
+
+ const VkImageCreateInfo msaa_image_create_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = NULL,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = msaa_format,
+ .extent = { demo->width, demo->height, 1 },
+ .mipLevels = 1,
+ .arraySize = 1,
+ .samples = 4,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ .flags = 0,
+ };
+ VkMemoryAllocInfo msaa_mem_alloc = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
+ .pNext = NULL,
+ .allocationSize = 0,
+ .memProps = 0,
+ .memPriority = VK_MEMORY_PRIORITY_NORMAL,
+ };
+
+ VkMemoryRequirements *msaa_mem_reqs;
+ size_t msaa_mem_reqs_size = sizeof(VkMemoryRequirements);
+ uint32_t msaa_num_allocations = 0;
+ size_t msaa_num_alloc_size = sizeof(msaa_num_allocations);
+
+ err = vkCreateImage(demo->device, &msaa_image_create_info,
+ &demo->msaa_color.image);
+ assert(!err);
+
+ err = vkGetObjectInfo(demo->device,
+ VK_OBJECT_TYPE_IMAGE, demo->msaa_color.image,
+ VK_OBJECT_INFO_TYPE_MEMORY_ALLOCATION_COUNT,
+ &msaa_num_alloc_size, &msaa_num_allocations);
+ assert(!err && msaa_num_alloc_size == sizeof(msaa_num_allocations));
+ msaa_mem_reqs = malloc(msaa_num_allocations * sizeof(VkMemoryRequirements));
+ demo->msaa_color.mem = malloc(msaa_num_allocations * sizeof(VkDeviceMemory));
+ err = vkGetObjectInfo(demo->device,
+ VK_OBJECT_TYPE_IMAGE, demo->msaa_color.image,
+ VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
+ &msaa_mem_reqs_size, msaa_mem_reqs);
+ assert(!err && msaa_mem_reqs_size == msaa_num_allocations * sizeof(VkMemoryRequirements));
+ for (uint32_t j = 0; j < msaa_num_allocations; j ++) {
+ msaa_mem_alloc.allocationSize = msaa_mem_reqs[j].size;
+
+ /* allocate memory */
+ err = vkAllocMemory(demo->device, &msaa_mem_alloc,
+ &(demo->msaa_color.mem[j]));
+ assert(!err);
+
+ /* bind memory */
+ err = vkQueueBindObjectMemory(demo->queue,
+ VK_OBJECT_TYPE_IMAGE, demo->msaa_color.image,
+ j, demo->msaa_color.mem[j], 0);
+ assert(!err);
+ }
+ free(msaa_mem_reqs);
+ msaa_mem_reqs = NULL;
+
+ demo->msaa_color.num_mem = msaa_num_allocations;
+
+ demo_add_mem_refs(demo, 1, demo->msaa_color.mem);
+ demo_set_image_layout(demo, demo->msaa_color.image,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+
+ VkColorAttachmentViewCreateInfo msaa_color_attachment_view = {
+ .sType = VK_STRUCTURE_TYPE_COLOR_ATTACHMENT_VIEW_CREATE_INFO,
+ .pNext = NULL,
+ .format = demo->format,
+ .mipLevel = 0,
+ .baseArraySlice = 0,
+ .arraySize = 1,
+ };
+
+ msaa_color_attachment_view.image = demo->msaa_color.image;
+
+ err = vkCreateColorAttachmentView(demo->device,
+ &msaa_color_attachment_view, &demo->msaa_color.view);
+ assert(!err);
+}
+
+static void demo_prepare_depth(struct demo *demo)
+{
+ const VkFormat depth_format = VK_FORMAT_D16_UNORM;
+ const VkImageCreateInfo image = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = NULL,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = depth_format,
+ .extent = { demo->width, demo->height, 1 },
+ .mipLevels = 1,
+ .arraySize = 1,
+ .samples = 4,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_BIT,
+ .flags = 0,
+ };
+ VkMemoryAllocInfo mem_alloc = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
+ .pNext = NULL,
+ .allocationSize = 0,
+ .memProps = VK_MEMORY_PROPERTY_DEVICE_ONLY,
+ .memPriority = VK_MEMORY_PRIORITY_NORMAL,
+ };
+ VkDepthStencilViewCreateInfo view = {
+ .sType = VK_STRUCTURE_TYPE_DEPTH_STENCIL_VIEW_CREATE_INFO,
+ .pNext = NULL,
+ .image = VK_NULL_HANDLE,
+ .mipLevel = 0,
+ .baseArraySlice = 0,
+ .arraySize = 1,
+ .flags = 0,
+ };
+
+ VkMemoryRequirements *mem_reqs;
+ size_t mem_reqs_size = sizeof(VkMemoryRequirements);
+ VkResult err;
+ uint32_t num_allocations = 0;
+ size_t num_alloc_size = sizeof(num_allocations);
+
+ demo->msaa_depth.format = depth_format;
+
+ /* create image */
+ err = vkCreateImage(demo->device, &image,
+ &demo->msaa_depth.image);
+ assert(!err);
+
+ err = vkGetObjectInfo(demo->device,
+ VK_OBJECT_TYPE_IMAGE, demo->msaa_depth.image,
+ VK_OBJECT_INFO_TYPE_MEMORY_ALLOCATION_COUNT,
+ &num_alloc_size, &num_allocations);
+ assert(!err && num_alloc_size == sizeof(num_allocations));
+ mem_reqs = malloc(num_allocations * sizeof(VkMemoryRequirements));
+ demo->msaa_depth.mem = malloc(num_allocations * sizeof(VkDeviceMemory));
+ demo->msaa_depth.num_mem = num_allocations;
+ err = vkGetObjectInfo(demo->device,
+ VK_OBJECT_TYPE_IMAGE, demo->msaa_depth.image,
+ VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
+ &mem_reqs_size, mem_reqs);
+ assert(!err && mem_reqs_size == num_allocations * sizeof(VkMemoryRequirements));
+ for (uint32_t i = 0; i < num_allocations; i ++) {
+ mem_alloc.allocationSize = mem_reqs[i].size;
+
+ /* allocate memory */
+ err = vkAllocMemory(demo->device, &mem_alloc,
+ &(demo->msaa_depth.mem[i]));
+ assert(!err);
+
+ /* bind memory */
+ err = vkQueueBindObjectMemory(demo->queue,
+ VK_OBJECT_TYPE_IMAGE, demo->msaa_depth.image,
+ i, demo->msaa_depth.mem[i], 0);
+ assert(!err);
+ }
+
+ demo_set_image_layout(demo, demo->msaa_depth.image,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+
+ demo_add_mem_refs(demo, demo->msaa_depth.num_mem, demo->msaa_depth.mem);
+
+ /* create image view */
+ view.image = demo->msaa_depth.image;
+ err = vkCreateDepthStencilView(demo->device, &view,
+ &demo->msaa_depth.view);
+ assert(!err);
+}
+
+static void demo_prepare_texture_image(struct demo *demo,
+ const uint32_t *tex_colors,
+ struct texture_object *tex_obj,
+ VkImageTiling tiling,
+ VkImageUsageFlags usage,
+ VkFlags mem_props)
+{
+ const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
+ const int32_t tex_width = 2;
+ const int32_t tex_height = 2;
+ VkResult err;
+
+ tex_obj->tex_width = tex_width;
+ tex_obj->tex_height = tex_height;
+
+ const VkImageCreateInfo image_create_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = NULL,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = tex_format,
+ .extent = { tex_width, tex_height, 1 },
+ .mipLevels = 1,
+ .arraySize = 1,
+ .samples = 1,
+ .tiling = tiling,
+ .usage = usage,
+ .flags = 0,
+ };
+ VkMemoryAllocInfo mem_alloc = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
+ .pNext = NULL,
+ .allocationSize = 0,
+ .memProps = mem_props,
+ .memPriority = VK_MEMORY_PRIORITY_NORMAL,
+ };
+
+ VkMemoryRequirements *mem_reqs;
+ size_t mem_reqs_size = sizeof(VkMemoryRequirements);
+ uint32_t num_allocations = 0;
+ size_t num_alloc_size = sizeof(num_allocations);
+
+ err = vkCreateImage(demo->device, &image_create_info,
+ &tex_obj->image);
+ assert(!err);
+
+ err = vkGetObjectInfo(demo->device,
+ VK_OBJECT_TYPE_IMAGE, tex_obj->image,
+ VK_OBJECT_INFO_TYPE_MEMORY_ALLOCATION_COUNT,
+ &num_alloc_size, &num_allocations);
+ assert(!err && num_alloc_size == sizeof(num_allocations));
+ mem_reqs = malloc(num_allocations * sizeof(VkMemoryRequirements));
+ tex_obj->mem = malloc(num_allocations * sizeof(VkDeviceMemory));
+ err = vkGetObjectInfo(demo->device,
+ VK_OBJECT_TYPE_IMAGE, tex_obj->image,
+ VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
+ &mem_reqs_size, mem_reqs);
+ assert(!err && mem_reqs_size == num_allocations * sizeof(VkMemoryRequirements));
+ for (uint32_t j = 0; j < num_allocations; j ++) {
+ mem_alloc.allocationSize = mem_reqs[j].size;
+
+ /* allocate memory */
+ err = vkAllocMemory(demo->device, &mem_alloc,
+ &(tex_obj->mem[j]));
+ assert(!err);
+
+ /* bind memory */
+ err = vkQueueBindObjectMemory(demo->queue,
+ VK_OBJECT_TYPE_IMAGE, tex_obj->image,
+ j, tex_obj->mem[j], 0);
+ assert(!err);
+ }
+ free(mem_reqs);
+ mem_reqs = NULL;
+
+ tex_obj->num_mem = num_allocations;
+
+ if (mem_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+ const VkImageSubresource subres = {
+ .aspect = VK_IMAGE_ASPECT_COLOR,
+ .mipLevel = 0,
+ .arraySlice = 0,
+ };
+ VkSubresourceLayout layout;
+ size_t layout_size = sizeof(VkSubresourceLayout);
+ void *data;
+ int32_t x, y;
+
+ err = vkGetImageSubresourceInfo(demo->device, tex_obj->image, &subres,
+ VK_SUBRESOURCE_INFO_TYPE_LAYOUT,
+ &layout_size, &layout);
+ assert(!err && layout_size == sizeof(layout));
+ /* Linear texture must be within a single memory object */
+ assert(num_allocations == 1);
+
+ err = vkMapMemory(demo->device, tex_obj->mem[0], 0, 0, 0, &data);
+ assert(!err);
+
+ for (y = 0; y < tex_height; y++) {
+ uint32_t *row = (uint32_t *) ((char *) data + layout.rowPitch * y);
+ for (x = 0; x < tex_width; x++)
+ row[x] = tex_colors[(x & 1) ^ (y & 1)];
+ }
+
+ err = vkUnmapMemory(demo->device, tex_obj->mem[0]);
+ assert(!err);
+ }
+
+ tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ demo_set_image_layout(demo, tex_obj->image,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ tex_obj->imageLayout);
+ /* setting the image layout does not reference the actual memory so no need to add a mem ref */
+}
+
+static void demo_destroy_texture_image(struct demo *demo, struct texture_object *tex_obj)
+{
+ /* clean up staging resources */
+ for (uint32_t j = 0; j < tex_obj->num_mem; j ++) {
+ vkQueueBindObjectMemory(demo->queue,
+ VK_OBJECT_TYPE_IMAGE, tex_obj->image, j, VK_NULL_HANDLE, 0);
+ vkFreeMemory(demo->device, tex_obj->mem[j]);
+ }
+
+ free(tex_obj->mem);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_IMAGE, tex_obj->image);
+}
+
+static void demo_prepare_textures(struct demo *demo)
+{
+ const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
+ VkFormatProperties props;
+ size_t size = sizeof(props);
+ const uint32_t tex_colors[DEMO_TEXTURE_COUNT][2] = {
+ { 0xffff0000, 0xff00ff00 },
+ };
+ VkResult err;
+ uint32_t i;
+
+ err = vkGetFormatInfo(demo->device, tex_format,
+ VK_FORMAT_INFO_TYPE_PROPERTIES,
+ &size, &props);
+ assert(!err);
+
+ for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
+ if ((props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !demo->use_staging_buffer) {
+ /* Device can texture using linear textures */
+ demo_prepare_texture_image(demo, tex_colors[i], &demo->textures[i],
+ VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+ } else if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT){
+ /* Must use staging buffer to copy linear texture to optimized */
+ struct texture_object staging_texture;
+
+ memset(&staging_texture, 0, sizeof(staging_texture));
+ demo_prepare_texture_image(demo, tex_colors[i], &staging_texture,
+ VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SOURCE_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+
+ demo_prepare_texture_image(demo, tex_colors[i], &demo->textures[i],
+ VK_IMAGE_TILING_OPTIMAL,
+ (VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT | VK_IMAGE_USAGE_SAMPLED_BIT),
+ VK_MEMORY_PROPERTY_DEVICE_ONLY);
+
+ demo_set_image_layout(demo, staging_texture.image,
+ staging_texture.imageLayout,
+ VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL);
+
+ demo_set_image_layout(demo, demo->textures[i].image,
+ demo->textures[i].imageLayout,
+ VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL);
+
+ VkImageCopy copy_region = {
+ .srcSubresource = { VK_IMAGE_ASPECT_COLOR, 0, 0 },
+ .srcOffset = { 0, 0, 0 },
+ .destSubresource = { VK_IMAGE_ASPECT_COLOR, 0, 0 },
+ .destOffset = { 0, 0, 0 },
+ .extent = { staging_texture.tex_width, staging_texture.tex_height, 1 },
+ };
+ vkCmdCopyImage(demo->cmd,
+ staging_texture.image, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL,
+ demo->textures[i].image, VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL,
+ 1, &copy_region);
+
+ demo_add_mem_refs(demo, staging_texture.num_mem, staging_texture.mem);
+ demo_add_mem_refs(demo, demo->textures[i].num_mem, demo->textures[i].mem);
+
+ demo_set_image_layout(demo, demo->textures[i].image,
+ VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL,
+ demo->textures[i].imageLayout);
+
+ demo_flush_init_cmd(demo);
+
+ demo_remove_mem_refs(demo, staging_texture.num_mem, staging_texture.mem);
+ demo_destroy_texture_image(demo, &staging_texture);
+ } else {
+ /* Can't support VK_FORMAT_B8G8R8A8_UNORM !? */
+ assert(!"No support for B8G8R8A8_UNORM as texture image format");
+ }
+
+ const VkSamplerCreateInfo sampler = {
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ .pNext = NULL,
+ .magFilter = VK_TEX_FILTER_NEAREST,
+ .minFilter = VK_TEX_FILTER_NEAREST,
+ .mipMode = VK_TEX_MIPMAP_MODE_BASE,
+ .addressU = VK_TEX_ADDRESS_WRAP,
+ .addressV = VK_TEX_ADDRESS_WRAP,
+ .addressW = VK_TEX_ADDRESS_WRAP,
+ .mipLodBias = 0.0f,
+ .maxAnisotropy = 1,
+ .compareOp = VK_COMPARE_OP_NEVER,
+ .minLod = 0.0f,
+ .maxLod = 0.0f,
+ .borderColor = VK_BORDER_COLOR_OPAQUE_WHITE,
+ };
+ VkImageViewCreateInfo view = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = NULL,
+ .image = VK_NULL_HANDLE,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = tex_format,
+ .channels = { VK_CHANNEL_SWIZZLE_R,
+ VK_CHANNEL_SWIZZLE_G,
+ VK_CHANNEL_SWIZZLE_B,
+ VK_CHANNEL_SWIZZLE_A, },
+ .subresourceRange = { VK_IMAGE_ASPECT_COLOR, 0, 1, 0, 1 },
+ .minLod = 0.0f,
+ };
+
+ /* create sampler */
+ err = vkCreateSampler(demo->device, &sampler,
+ &demo->textures[i].sampler);
+ assert(!err);
+
+ /* create image view */
+ view.image = demo->textures[i].image;
+ err = vkCreateImageView(demo->device, &view,
+ &demo->textures[i].view);
+ assert(!err);
+ }
+}
+
+static void demo_prepare_vertices(struct demo *demo)
+{
+ const float vb[3][5] = {
+ /* position texcoord */
+ { -1.0f, -1.0f, -0.6f, 0.0f, 0.0f },
+ { 1.0f, -1.0f, -0.5f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f, 0.5f, 1.0f },
+ };
+ const VkBufferCreateInfo buf_info = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .pNext = NULL,
+ .size = sizeof(vb),
+ .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ .flags = 0,
+ };
+ VkMemoryAllocInfo mem_alloc = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
+ .pNext = NULL,
+ .allocationSize = 0,
+ .memProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
+ .memPriority = VK_MEMORY_PRIORITY_NORMAL,
+ };
+ VkMemoryRequirements *mem_reqs;
+ size_t mem_reqs_size = sizeof(VkMemoryRequirements);
+ uint32_t num_allocations = 0;
+ size_t num_alloc_size = sizeof(num_allocations);
+ VkResult err;
+ void *data;
+
+ memset(&demo->vertices, 0, sizeof(demo->vertices));
+
+ err = vkCreateBuffer(demo->device, &buf_info, &demo->vertices.buf);
+ assert(!err);
+
+ err = vkGetObjectInfo(demo->device,
+ VK_OBJECT_TYPE_BUFFER, demo->vertices.buf,
+ VK_OBJECT_INFO_TYPE_MEMORY_ALLOCATION_COUNT,
+ &num_alloc_size, &num_allocations);
+ assert(!err && num_alloc_size == sizeof(num_allocations));
+ mem_reqs = malloc(num_allocations * sizeof(VkMemoryRequirements));
+ demo->vertices.mem = malloc(num_allocations * sizeof(VkDeviceMemory));
+ demo->vertices.num_mem = num_allocations;
+ err = vkGetObjectInfo(demo->device,
+ VK_OBJECT_TYPE_BUFFER, demo->vertices.buf,
+ VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
+ &mem_reqs_size, mem_reqs);
+ assert(!err && mem_reqs_size == sizeof(*mem_reqs));
+ for (uint32_t i = 0; i < num_allocations; i ++) {
+ mem_alloc.allocationSize = mem_reqs[i].size;
+
+ err = vkAllocMemory(demo->device, &mem_alloc, &demo->vertices.mem[i]);
+ assert(!err);
+
+ err = vkMapMemory(demo->device, demo->vertices.mem[i], 0, 0, 0, &data);
+ assert(!err);
+
+ memcpy(data, vb, sizeof(vb));
+
+ err = vkUnmapMemory(demo->device, demo->vertices.mem[i]);
+ assert(!err);
+
+ err = vkQueueBindObjectMemory(demo->queue,
+ VK_OBJECT_TYPE_BUFFER, demo->vertices.buf,
+ i, demo->vertices.mem[i], 0);
+ assert(!err);
+ }
+
+ demo_add_mem_refs(demo, demo->vertices.num_mem, demo->vertices.mem);
+
+ demo->vertices.vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_CREATE_INFO;
+ demo->vertices.vi.pNext = NULL;
+ demo->vertices.vi.bindingCount = 1;
+ demo->vertices.vi.pVertexBindingDescriptions = demo->vertices.vi_bindings;
+ demo->vertices.vi.attributeCount = 2;
+ demo->vertices.vi.pVertexAttributeDescriptions = demo->vertices.vi_attrs;
+
+ demo->vertices.vi_bindings[0].binding = VERTEX_BUFFER_BIND_ID;
+ demo->vertices.vi_bindings[0].strideInBytes = sizeof(vb[0]);
+ demo->vertices.vi_bindings[0].stepRate = VK_VERTEX_INPUT_STEP_RATE_VERTEX;
+
+ demo->vertices.vi_attrs[0].binding = VERTEX_BUFFER_BIND_ID;
+ demo->vertices.vi_attrs[0].location = 0;
+ demo->vertices.vi_attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
+ demo->vertices.vi_attrs[0].offsetInBytes = 0;
+
+ demo->vertices.vi_attrs[1].binding = VERTEX_BUFFER_BIND_ID;
+ demo->vertices.vi_attrs[1].location = 1;
+ demo->vertices.vi_attrs[1].format = VK_FORMAT_R32G32_SFLOAT;
+ demo->vertices.vi_attrs[1].offsetInBytes = sizeof(float) * 3;
+}
+
+static void demo_prepare_descriptor_layout(struct demo *demo)
+{
+ const VkDescriptorSetLayoutBinding layout_binding = {
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .count = DEMO_TEXTURE_COUNT,
+ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
+ .pImmutableSamplers = NULL,
+ };
+ const VkDescriptorSetLayoutCreateInfo descriptor_layout = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ .pNext = NULL,
+ .count = 1,
+ .pBinding = &layout_binding,
+ };
+ VkResult err;
+
+ err = vkCreateDescriptorSetLayout(demo->device,
+ &descriptor_layout, &demo->desc_layout);
+ assert(!err);
+
+ const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .pNext = NULL,
+ .descriptorSetCount = 1,
+ .pSetLayouts = &demo->desc_layout,
+ };
+
+ err = vkCreatePipelineLayout(demo->device,
+ &pPipelineLayoutCreateInfo,
+ &demo->pipeline_layout);
+ assert(!err);
+}
+
+static VkShader demo_prepare_shader(struct demo *demo,
+ VkShaderStage stage,
+ const void *code,
+ size_t size)
+{
+ VkShaderCreateInfo createInfo;
+ VkShader shader;
+ VkResult err;
+
+ createInfo.sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO;
+ createInfo.pNext = NULL;
+
+ // Create fake SPV structure to feed GLSL
+ // to the driver "under the covers"
+ createInfo.codeSize = 3 * sizeof(uint32_t) + size + 1;
+ createInfo.pCode = malloc(createInfo.codeSize);
+ createInfo.flags = 0;
+
+ /* try version 0 first: VkShaderStage followed by GLSL */
+ ((uint32_t *) createInfo.pCode)[0] = ICD_SPV_MAGIC;
+ ((uint32_t *) createInfo.pCode)[1] = 0;
+ ((uint32_t *) createInfo.pCode)[2] = stage;
+ memcpy(((uint32_t *) createInfo.pCode + 3), code, size + 1);
+
+ err = vkCreateShader(demo->device, &createInfo, &shader);
+ if (err) {
+ free((void *) createInfo.pCode);
+ return VK_NULL_HANDLE;
+ }
+
+ return shader;
+}
+
+static VkShader demo_prepare_vs(struct demo *demo)
+{
+ static const char *vertShaderText =
+ "#version 140\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n"
+ "layout (location = 0) in vec4 pos;\n"
+ "layout (location = 1) in vec2 attr;\n"
+ "out vec2 texcoord;\n"
+ "void main() {\n"
+ " texcoord = attr;\n"
+ " gl_Position = pos;\n"
+ "}\n";
+
+ return demo_prepare_shader(demo, VK_SHADER_STAGE_VERTEX,
+ (const void *) vertShaderText,
+ strlen(vertShaderText));
+}
+
+static VkShader demo_prepare_fs(struct demo *demo)
+{
+ static const char *fragShaderText =
+ "#version 140\n"
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n"
+ "layout (binding = 0) uniform sampler2D tex;\n"
+ "layout (location = 0) in vec2 texcoord;\n"
+ "void main() {\n"
+ " gl_FragColor = texture(tex, texcoord);\n"
+ "}\n";
+
+ return demo_prepare_shader(demo, VK_SHADER_STAGE_FRAGMENT,
+ (const void *) fragShaderText,
+ strlen(fragShaderText));
+}
+
+static void demo_prepare_pipeline(struct demo *demo)
+{
+ VkGraphicsPipelineCreateInfo pipeline;
+ VkPipelineVertexInputCreateInfo vi;
+ VkPipelineIaStateCreateInfo ia;
+ VkPipelineRsStateCreateInfo rs;
+ VkPipelineCbStateCreateInfo cb;
+ VkPipelineDsStateCreateInfo ds;
+ VkPipelineShaderStageCreateInfo vs;
+ VkPipelineShaderStageCreateInfo fs;
+ VkPipelineVpStateCreateInfo vp;
+ VkPipelineMsStateCreateInfo ms;
+ VkResult err;
+
+ memset(&pipeline, 0, sizeof(pipeline));
+ pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipeline.layout = demo->pipeline_layout;
+
+ vi = demo->vertices.vi;
+
+ memset(&ia, 0, sizeof(ia));
+ ia.sType = VK_STRUCTURE_TYPE_PIPELINE_IA_STATE_CREATE_INFO;
+ ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ memset(&rs, 0, sizeof(rs));
+ rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RS_STATE_CREATE_INFO;
+ rs.fillMode = VK_FILL_MODE_SOLID;
+ rs.cullMode = VK_CULL_MODE_NONE;
+ rs.frontFace = VK_FRONT_FACE_CCW;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.sType = VK_STRUCTURE_TYPE_PIPELINE_CB_STATE_CREATE_INFO;
+ VkPipelineCbAttachmentState att_state[1];
+ memset(att_state, 0, sizeof(att_state));
+ att_state[0].format = demo->format;
+ att_state[0].channelWriteMask = 0xf;
+ att_state[0].blendEnable = VK_FALSE;
+ cb.attachmentCount = 1;
+ cb.pAttachments = att_state;
+
+
+ memset(&vp, 0, sizeof(vp));
+ vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VP_STATE_CREATE_INFO;
+ vp.viewportCount = 1;
+ vp.clipOrigin = VK_COORDINATE_ORIGIN_UPPER_LEFT;
+
+ memset(&ds, 0, sizeof(ds));
+ ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DS_STATE_CREATE_INFO;
+ ds.format = demo->msaa_depth.format;
+ ds.depthTestEnable = VK_TRUE;
+ ds.depthWriteEnable = VK_TRUE;
+ ds.depthCompareOp = VK_COMPARE_OP_LESS_EQUAL;
+ ds.depthBoundsEnable = VK_FALSE;
+ ds.back.stencilFailOp = VK_STENCIL_OP_KEEP;
+ ds.back.stencilPassOp = VK_STENCIL_OP_KEEP;
+ ds.back.stencilCompareOp = VK_COMPARE_OP_ALWAYS;
+ ds.stencilTestEnable = VK_FALSE;
+ ds.front = ds.back;
+
+ memset(&vs, 0, sizeof(vs));
+ vs.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ vs.shader.stage = VK_SHADER_STAGE_VERTEX;
+ vs.shader.shader = demo_prepare_vs(demo);
+ vs.shader.linkConstBufferCount = 0;
+
+ memset(&fs, 0, sizeof(fs));
+ fs.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ fs.shader.stage = VK_SHADER_STAGE_FRAGMENT;
+ fs.shader.shader = demo_prepare_fs(demo);
+
+ memset(&ms, 0, sizeof(ms));
+ ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MS_STATE_CREATE_INFO;
+ ms.sampleMask = 0xffff;
+ ms.multisampleEnable = VK_TRUE;
+ ms.samples = 4;
+
+ pipeline.pNext = (const void *) &vi;
+ vi.pNext = (void *) &ia;
+ ia.pNext = (const void *) &rs;
+ rs.pNext = (const void *) &cb;
+ cb.pNext = (const void *) &ms;
+ ms.pNext = (const void *) &vp;
+ vp.pNext = (const void *) &ds;
+ ds.pNext = (const void *) &vs;
+ vs.pNext = (const void *) &fs;
+
+ err = vkCreateGraphicsPipeline(demo->device, &pipeline, &demo->pipeline);
+ assert(!err);
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_SHADER, vs.shader.shader);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_SHADER, fs.shader.shader);
+}
+
+static void demo_prepare_dynamic_states(struct demo *demo)
+{
+ VkDynamicVpStateCreateInfo viewport_create;
+ VkDynamicRsStateCreateInfo raster;
+ VkDynamicCbStateCreateInfo color_blend;
+ VkDynamicDsStateCreateInfo depth_stencil;
+ VkResult err;
+
+ memset(&viewport_create, 0, sizeof(viewport_create));
+ viewport_create.sType = VK_STRUCTURE_TYPE_DYNAMIC_VP_STATE_CREATE_INFO;
+ viewport_create.viewportAndScissorCount = 1;
+ VkViewport viewport;
+ memset(&viewport, 0, sizeof(viewport));
+ viewport.height = (float) demo->height;
+ viewport.width = (float) demo->width;
+ viewport.minDepth = (float) 0.0f;
+ viewport.maxDepth = (float) 1.0f;
+ viewport_create.pViewports = &viewport;
+ VkRect scissor;
+ memset(&scissor, 0, sizeof(scissor));
+ scissor.extent.width = demo->width;
+ scissor.extent.height = demo->height;
+ scissor.offset.x = 0;
+ scissor.offset.y = 0;
+ viewport_create.pScissors = &scissor;
+
+ memset(&raster, 0, sizeof(raster));
+ raster.sType = VK_STRUCTURE_TYPE_DYNAMIC_RS_STATE_CREATE_INFO;
+ raster.pointSize = 1.0;
+ raster.lineWidth = 1.0;
+
+ memset(&color_blend, 0, sizeof(color_blend));
+ color_blend.sType = VK_STRUCTURE_TYPE_DYNAMIC_CB_STATE_CREATE_INFO;
+ color_blend.blendConst[0] = 1.0f;
+ color_blend.blendConst[1] = 1.0f;
+ color_blend.blendConst[2] = 1.0f;
+ color_blend.blendConst[3] = 1.0f;
+
+ memset(&depth_stencil, 0, sizeof(depth_stencil));
+ depth_stencil.sType = VK_STRUCTURE_TYPE_DYNAMIC_DS_STATE_CREATE_INFO;
+ depth_stencil.minDepth = 0.0f;
+ depth_stencil.maxDepth = 1.0f;
+ depth_stencil.stencilBackRef = 0;
+ depth_stencil.stencilFrontRef = 0;
+ depth_stencil.stencilReadMask = 0xff;
+ depth_stencil.stencilWriteMask = 0xff;
+
+ err = vkCreateDynamicViewportState(demo->device, &viewport_create, &demo->viewport);
+ assert(!err);
+
+ err = vkCreateDynamicRasterState(demo->device, &raster, &demo->raster);
+ assert(!err);
+
+ err = vkCreateDynamicColorBlendState(demo->device,
+ &color_blend, &demo->color_blend);
+ assert(!err);
+
+ err = vkCreateDynamicDepthStencilState(demo->device,
+ &depth_stencil, &demo->depth_stencil);
+ assert(!err);
+}
+
+static void demo_prepare_descriptor_pool(struct demo *demo)
+{
+ const VkDescriptorTypeCount type_count = {
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .count = DEMO_TEXTURE_COUNT,
+ };
+ const VkDescriptorPoolCreateInfo descriptor_pool = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .pNext = NULL,
+ .count = 1,
+ .pTypeCount = &type_count,
+ };
+ VkResult err;
+
+ err = vkCreateDescriptorPool(demo->device,
+ VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1,
+ &descriptor_pool, &demo->desc_pool);
+ assert(!err);
+}
+
+static void demo_prepare_descriptor_set(struct demo *demo)
+{
+ VkImageViewAttachInfo view_info[DEMO_TEXTURE_COUNT];
+ VkSamplerImageViewInfo combined_info[DEMO_TEXTURE_COUNT];
+ VkUpdateSamplerTextures update;
+ const void *update_array[1] = { &update };
+ VkResult err;
+ uint32_t count;
+ uint32_t i;
+
+ for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
+ view_info[i].sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_ATTACH_INFO;
+ view_info[i].pNext = NULL;
+ view_info[i].view = demo->textures[i].view,
+ view_info[i].layout = VK_IMAGE_LAYOUT_GENERAL;
+
+ combined_info[i].sampler = demo->textures[i].sampler;
+ combined_info[i].pImageView = &view_info[i];
+ }
+
+ memset(&update, 0, sizeof(update));
+ update.sType = VK_STRUCTURE_TYPE_UPDATE_SAMPLER_TEXTURES;
+ update.count = DEMO_TEXTURE_COUNT;
+ update.pSamplerImageViews = combined_info;
+
+ err = vkAllocDescriptorSets(demo->device, demo->desc_pool,
+ VK_DESCRIPTOR_SET_USAGE_STATIC,
+ 1, &demo->desc_layout,
+ &demo->desc_set, &count);
+ assert(!err && count == 1);
+
+ vkBeginDescriptorPoolUpdate(demo->device,
+ VK_DESCRIPTOR_UPDATE_MODE_FASTEST);
+
+ vkClearDescriptorSets(demo->device, demo->desc_pool, 1, &demo->desc_set);
+ vkUpdateDescriptors(demo->device, demo->desc_set, 1, update_array);
+
+ vkEndDescriptorPoolUpdate(demo->device, demo->cmd);
+}
+
+static void demo_prepare(struct demo *demo)
+{
+ const VkCmdBufferCreateInfo cmd = {
+ .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+ .pNext = NULL,
+ .queueNodeIndex = demo->graphics_queue_node_index,
+ .flags = 0,
+ };
+ VkResult err;
+
+ demo_prepare_buffers(demo);
+ demo_prepare_depth(demo);
+ demo_prepare_textures(demo);
+ demo_prepare_vertices(demo);
+ demo_prepare_descriptor_layout(demo);
+ demo_prepare_pipeline(demo);
+ demo_prepare_dynamic_states(demo);
+
+ err = vkCreateCommandBuffer(demo->device, &cmd, &demo->cmd);
+ assert(!err);
+
+ demo_prepare_descriptor_pool(demo);
+ demo_prepare_descriptor_set(demo);
+ demo->prepared = true;
+}
+
+#ifdef _WIN32
+static void demo_run(struct demo *demo)
+{
+ if (!demo->prepared)
+ return;
+ demo_draw(demo);
+}
+
+// On MS-Windows, make this a global, so it's available to WndProc()
+struct demo demo;
+
+// MS-Windows event handling function:
+LRESULT CALLBACK WndProc(HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ char tmp_str[] = APP_LONG_NAME;
+
+ switch(uMsg)
+ {
+ case WM_CREATE:
+ return 0;
+ case WM_CLOSE:
+ demo.prepared = false;
+ DestroyWindow(hWnd);
+ PostQuitMessage(0);
+ return 0;
+ case WM_PAINT:
+ demo_run(&demo);
+ return 0;
+ default:
+ break;
+ }
+ return (DefWindowProc(hWnd, uMsg, wParam, lParam));
+}
+
+static void demo_create_window(struct demo *demo)
+{
+ WNDCLASSEX win_class;
+
+ // Initialize the window class structure:
+ win_class.cbSize = sizeof(WNDCLASSEX);
+ win_class.style = CS_HREDRAW | CS_VREDRAW;
+ win_class.lpfnWndProc = WndProc;
+ win_class.cbClsExtra = 0;
+ win_class.cbWndExtra = 0;
+ win_class.hInstance = demo->connection; // hInstance
+ win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+ win_class.lpszMenuName = NULL;
+ win_class.lpszClassName = demo->name;
+ win_class.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
+ // Register window class:
+ if (!RegisterClassEx(&win_class)) {
+ // It didn't work, so try to give a useful error:
+ printf("Unexpected error trying to start the application!\n");
+ fflush(stdout);
+ WAIT_FOR_CONSOLE_DESTROY;
+ exit(1);
+ }
+ // Create window with the registered class:
+ demo->window = CreateWindowEx(0,
+ demo->name, // class name
+ demo->name, // app name
+ WS_OVERLAPPEDWINDOW | // window style
+ WS_VISIBLE |
+ WS_SYSMENU,
+ 100,100, // x/y coords
+ demo->width, // width
+ demo->height, // height
+ NULL, // handle to parent
+ NULL, // handle to menu
+ demo->connection, // hInstance
+ NULL); // no extra parameters
+ if (!demo->window) {
+ // It didn't work, so try to give a useful error:
+ printf("Cannot create a window in which to draw!\n");
+ fflush(stdout);
+ WAIT_FOR_CONSOLE_DESTROY;
+ exit(1);
+ }
+}
+#else // _WIN32
+
+static void demo_handle_event(struct demo *demo,
+ const xcb_generic_event_t *event)
+{
+ switch (event->response_type & 0x7f) {
+ case XCB_EXPOSE:
+ demo_draw(demo);
+ break;
+ case XCB_CLIENT_MESSAGE:
+ if((*(xcb_client_message_event_t*)event).data.data32[0] ==
+ (*demo->atom_wm_delete_window).atom) {
+ demo->quit = true;
+ }
+ break;
+ case XCB_KEY_RELEASE:
+ {
+ const xcb_key_release_event_t *key =
+ (const xcb_key_release_event_t *) event;
+
+ if (key->detail == 0x9)
+ demo->quit = true;
+ }
+ break;
+ case XCB_DESTROY_NOTIFY:
+ demo->quit = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void demo_run(struct demo *demo)
+{
+ xcb_flush(demo->connection);
+
+ while (!demo->quit) {
+ xcb_generic_event_t *event;
+
+ event = xcb_wait_for_event(demo->connection);
+ if (event) {
+ demo_handle_event(demo, event);
+ free(event);
+ }
+ }
+}
+
+static void demo_create_window(struct demo *demo)
+{
+ uint32_t value_mask, value_list[32];
+
+ demo->window = xcb_generate_id(demo->connection);
+
+ value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+ value_list[0] = demo->screen->black_pixel;
+ value_list[1] = XCB_EVENT_MASK_KEY_RELEASE |
+ XCB_EVENT_MASK_EXPOSURE |
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY;
+
+ xcb_create_window(demo->connection,
+ XCB_COPY_FROM_PARENT,
+ demo->window, demo->screen->root,
+ 0, 0, demo->width, demo->height, 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ demo->screen->root_visual,
+ value_mask, value_list);
+
+ /* Magic code that will send notification when window is destroyed */
+ xcb_intern_atom_cookie_t cookie = xcb_intern_atom(demo->connection, 1, 12,
+ "WM_PROTOCOLS");
+ xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(demo->connection, cookie, 0);
+
+ xcb_intern_atom_cookie_t cookie2 = xcb_intern_atom(demo->connection, 0, 16, "WM_DELETE_WINDOW");
+ demo->atom_wm_delete_window = xcb_intern_atom_reply(demo->connection, cookie2, 0);
+
+ xcb_change_property(demo->connection, XCB_PROP_MODE_REPLACE,
+ demo->window, (*reply).atom, 4, 32, 1,
+ &(*demo->atom_wm_delete_window).atom);
+ free(reply);
+
+ xcb_map_window(demo->connection, demo->window);
+}
+#endif // _WIN32
+
+static void demo_init_vk(struct demo *demo)
+{
+ VkResult err;
+ // Extensions to enable
+ const char *ext_names[] = {
+ "VK_WSI_LunarG",
+ };
+ size_t extSize = sizeof(uint32_t);
+ uint32_t extCount = 0;
+ err = vkGetGlobalExtensionInfo(VK_EXTENSION_INFO_TYPE_COUNT, 0, &extSize, &extCount);
+ assert(!err);
+
+ VkExtensionProperties extProp;
+ extSize = sizeof(VkExtensionProperties);
+ bool32_t extFound = 0;
+ for (uint32_t i = 0; i < extCount; i++) {
+ err = vkGetGlobalExtensionInfo(VK_EXTENSION_INFO_TYPE_PROPERTIES, i, &extSize, &extProp);
+ if (!strcmp(ext_names[0], extProp.extName))
+ extFound = 1;
+ }
+ if (!extFound) {
+ ERR_EXIT("vkGetGlobalExtensionInfo failed to find the "
+ "\"VK_WSI_LunarG\" extension.\n\nDo you have a compatible "
+ "Vulkan installable client driver (ICD) installed?\nPlease "
+ "look at the Getting Started guide for additional "
+ "information.\n",
+ "vkCreateInstance Failure");
+ }
+ const VkApplicationInfo app = {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pNext = NULL,
+ .pAppName = APP_SHORT_NAME,
+ .appVersion = 0,
+ .pEngineName = APP_SHORT_NAME,
+ .engineVersion = 0,
+ .apiVersion = VK_API_VERSION,
+ };
+ const VkInstanceCreateInfo inst_info = {
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pNext = NULL,
+ .pAppInfo = &app,
+ .pAllocCb = NULL,
+ .extensionCount = 1,
+ .ppEnabledExtensionNames = ext_names,
+ };
+ const VkDeviceQueueCreateInfo queue = {
+ .queueNodeIndex = 0,
+ .queueCount = 1,
+ };
+ const VkDeviceCreateInfo device = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ .pNext = NULL,
+ .queueRecordCount = 1,
+ .pRequestedQueues = &queue,
+ .extensionCount = 1,
+ .ppEnabledExtensionNames = ext_names,
+ .flags = VK_DEVICE_CREATE_VALIDATION_BIT,
+ };
+ uint32_t gpu_count;
+ uint32_t i;
+ size_t data_size;
+ uint32_t queue_count;
+
+ err = vkCreateInstance(&inst_info, &demo->inst);
+ if (err == VK_ERROR_INCOMPATIBLE_DRIVER) {
+ ERR_EXIT("Cannot find a compatible Vulkan installable client driver "
+ "(ICD).\n\nPlease look at the Getting Started guide for "
+ "additional information.\n",
+ "vkCreateInstance Failure");
+ } else if (err) {
+ ERR_EXIT("vkCreateInstance failed.\n\nDo you have a compatible Vulkan "
+ "installable client driver (ICD) installed?\nPlease look at "
+ "the Getting Started guide for additional information.\n",
+ "vkCreateInstance Failure");
+ }
+
+ gpu_count = 1;
+ err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, &demo->gpu);
+ assert(!err && gpu_count == 1);
+
+ err = vkCreateDevice(demo->gpu, &device, &demo->device);
+ assert(!err);
+
+ err = vkGetPhysicalDeviceInfo(demo->gpu, VK_PHYSICAL_DEVICE_INFO_TYPE_PROPERTIES,
+ &data_size, NULL);
+ assert(!err);
+
+ demo->gpu_props = (VkPhysicalDeviceProperties *) malloc(data_size);
+ err = vkGetPhysicalDeviceInfo(demo->gpu, VK_PHYSICAL_DEVICE_INFO_TYPE_PROPERTIES,
+ &data_size, demo->gpu_props);
+ assert(!err);
+
+ err = vkGetPhysicalDeviceInfo(demo->gpu, VK_PHYSICAL_DEVICE_INFO_TYPE_QUEUE_PROPERTIES,
+ &data_size, NULL);
+ assert(!err);
+
+ demo->queue_props = (VkPhysicalDeviceQueueProperties *) malloc(data_size);
+ err = vkGetPhysicalDeviceInfo(demo->gpu, VK_PHYSICAL_DEVICE_INFO_TYPE_QUEUE_PROPERTIES,
+ &data_size, demo->queue_props);
+ assert(!err);
+ queue_count = (uint32_t) (data_size / sizeof(VkPhysicalDeviceQueueProperties));
+ assert(queue_count >= 1);
+
+ for (i = 0; i < queue_count; i++) {
+ if (demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
+ break;
+ if (demo->queue_props[i].queueFlags & VK_QUEUE_MEMMGR_BIT)
+ break;
+ }
+ assert(i < queue_count);
+ demo->graphics_queue_node_index = i;
+
+ err = vkGetDeviceQueue(demo->device, demo->graphics_queue_node_index,
+ 0, &demo->queue);
+ assert(!err);
+}
+
+static void demo_init_connection(struct demo *demo)
+{
+#ifndef _WIN32
+ const xcb_setup_t *setup;
+ xcb_screen_iterator_t iter;
+ int scr;
+
+ demo->connection = xcb_connect(NULL, &scr);
+ if (demo->connection == NULL) {
+ printf("Cannot find a compatible Vulkan installable client driver "
+ "(ICD).\nExiting ...\n");
+ fflush(stdout);
+ exit(1);
+ }
+
+ setup = xcb_get_setup(demo->connection);
+ iter = xcb_setup_roots_iterator(setup);
+ while (scr-- > 0)
+ xcb_screen_next(&iter);
+
+ demo->screen = iter.data;
+#endif // _WIN32
+}
+
+#ifdef _WIN32
+static void demo_init(struct demo *demo, HINSTANCE hInstance, LPSTR pCmdLine)
+#else // _WIN32
+static void demo_init(struct demo *demo, const int argc, const char *argv[])
+#endif // _WIN32
+{
+ bool argv_error = false;
+
+ memset(demo, 0, sizeof(*demo));
+
+#ifdef _WIN32
+ demo->connection = hInstance;
+ strncpy(demo->name, APP_SHORT_NAME, APP_NAME_STR_LEN);
+
+ if (strncmp(pCmdLine, "--use_staging", strlen("--use_staging")) == 0)
+ demo->use_staging_buffer = true;
+ else if (strlen(pCmdLine) != 0) {
+ fprintf(stderr, "Do not recognize argument \"%s\".\n", pCmdLine);
+ argv_error = true;
+ }
+#else // _WIN32
+ for (int i = 0; i < argc; i++) {
+ if (strncmp(argv[i], "--use_staging", strlen("--use_staging")) == 0)
+ demo->use_staging_buffer = true;
+ }
+#endif // _WIN32
+ if (argv_error) {
+ fprintf(stderr, "Usage:\n %s [--use_staging]\n", APP_SHORT_NAME);
+ fflush(stderr);
+ WAIT_FOR_CONSOLE_DESTROY;
+ exit(1);
+ }
+
+ demo_init_connection(demo);
+ demo_init_vk(demo);
+
+ demo->width = 300;
+ demo->height = 300;
+ demo->format = VK_FORMAT_B8G8R8A8_UNORM;
+}
+
+static void demo_cleanup(struct demo *demo)
+{
+ uint32_t i, j;
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_DESCRIPTOR_SET, demo->desc_set);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_DESCRIPTOR_POOL, demo->desc_pool);
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_COMMAND_BUFFER, demo->cmd);
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_DYNAMIC_VP_STATE, demo->viewport);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_DYNAMIC_RS_STATE, demo->raster);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_DYNAMIC_CB_STATE, demo->color_blend);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_DYNAMIC_DS_STATE, demo->depth_stencil);
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_PIPELINE, demo->pipeline);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_PIPELINE_LAYOUT, demo->pipeline_layout);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, demo->desc_layout);
+
+ vkQueueBindObjectMemory(demo->queue, VK_OBJECT_TYPE_BUFFER, demo->vertices.buf, 0, VK_NULL_HANDLE, 0);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_BUFFER, demo->vertices.buf);
+ demo_remove_mem_refs(demo, demo->vertices.num_mem, demo->vertices.mem);
+ for (j = 0; j < demo->vertices.num_mem; j++)
+ vkFreeMemory(demo->device, demo->vertices.mem[j]);
+
+ for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_IMAGE_VIEW, demo->textures[i].view);
+ vkQueueBindObjectMemory(demo->queue, VK_OBJECT_TYPE_IMAGE, demo->textures[i].image, 0, VK_NULL_HANDLE, 0);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_IMAGE, demo->textures[i].image);
+ demo_remove_mem_refs(demo, demo->textures[i].num_mem, demo->textures[i].mem);
+ for (j = 0; j < demo->textures[i].num_mem; j++)
+ vkFreeMemory(demo->device, demo->textures[i].mem[j]);
+ free(demo->textures[i].mem);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_SAMPLER, demo->textures[i].sampler);
+ }
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_COLOR_ATTACHMENT_VIEW, demo->msaa_color.view);
+ vkQueueBindObjectMemory(demo->queue, VK_OBJECT_TYPE_IMAGE, demo->msaa_color.image, 0, VK_NULL_HANDLE, 0);
+ demo_remove_mem_refs(demo, demo->msaa_color.num_mem, demo->msaa_color.mem);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_IMAGE, demo->msaa_color.image);
+ for (j = 0; j < demo->msaa_color.num_mem; j++) {
+ vkFreeMemory(demo->device, demo->msaa_color.mem[j]);
+ }
+
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_DEPTH_STENCIL_VIEW, demo->msaa_depth.view);
+ vkQueueBindObjectMemory(demo->queue, VK_OBJECT_TYPE_IMAGE, demo->msaa_depth.image, 0, VK_NULL_HANDLE, 0);
+ demo_remove_mem_refs(demo, demo->msaa_depth.num_mem, demo->msaa_depth.mem);
+ vkDestroyObject(demo->device, VK_OBJECT_TYPE_IMAGE, demo->msaa_depth.image);
+ for (j = 0; j < demo->msaa_depth.num_mem; j++) {
+ vkFreeMemory(demo->device, demo->msaa_depth.mem[j]);
+ }
+
+ for (i = 0; i < DEMO_BUFFER_COUNT; i++) {
+ demo_remove_mem_refs(demo, 1, &demo->buffers[i].mem);
+ }
+ vkDestroySwapChainWSI(demo->swap_chain);
+
+ vkDestroyDevice(demo->device);
+ vkDestroyInstance(demo->inst);
+
+#ifndef _WIN32
+ xcb_destroy_window(demo->connection, demo->window);
+ xcb_disconnect(demo->connection);
+#endif // _WIN32
+}
+
+#ifdef _WIN32
+// The following function was copied from the Vulkan loader:
+char *get_registry_string(const HKEY hive,
+ const LPCTSTR sub_key,
+ const char *value)
+{
+ DWORD access_flags = KEY_QUERY_VALUE;
+ DWORD value_type;
+ HKEY key;
+ VkResult rtn_value;
+ char *rtn_str = NULL;
+ size_t rtn_len = 0;
+ size_t allocated_len = 0;
+
+ rtn_value = RegOpenKeyEx(hive, sub_key, 0, access_flags, &key);
+ if (rtn_value != ERROR_SUCCESS) {
+ // We didn't find the key. Try the 32-bit hive (where we've seen the
+ // key end up on some people's systems):
+ access_flags |= KEY_WOW64_32KEY;
+ rtn_value = RegOpenKeyEx(hive, sub_key, 0, access_flags, &key);
+ if (rtn_value != ERROR_SUCCESS) {
+ // We still couldn't find the key, so give up:
+ return NULL;
+ }
+ }
+
+ rtn_value = RegQueryValueEx(key, value, NULL, &value_type,
+ (PVOID) rtn_str, (LPDWORD) &rtn_len);
+ if (rtn_value == ERROR_SUCCESS) {
+ // If we get to here, we found the key, and need to allocate memory
+ // large enough for rtn_str, and query again:
+ allocated_len = rtn_len + 4;
+ rtn_str = malloc(allocated_len);
+ rtn_value = RegQueryValueEx(key, value, NULL, &value_type,
+ (PVOID) rtn_str, (LPDWORD) &rtn_len);
+ if (rtn_value == ERROR_SUCCESS) {
+ // We added 4 extra bytes to rtn_str, so that we can ensure that
+ // the string is NULL-terminated (albeit, in a brute-force manner):
+ rtn_str[allocated_len-1] = '\0';
+ } else {
+ // This should never occur, but in case it does, clean up:
+ free(rtn_str);
+ rtn_str = NULL;
+ }
+ } // else - shouldn't happen, but if it does, return rtn_str, which is NULL
+
+ // Close the registry key that was opened:
+ RegCloseKey(key);
+
+ return rtn_str;
+}
+
+
+// The following function was copied from the Vulkan loader:
+static char *get_registry_and_env(const char *env_var,
+ const char *registry_value)
+{
+ char *env_str = getenv(env_var);
+ size_t env_len = (env_str == NULL) ? 0 : strlen(env_str);
+ char *registry_str = NULL;
+ DWORD registry_len = 0;
+ char *rtn_str = NULL;
+ size_t rtn_len;
+
+ registry_str = get_registry_string(HKEY_LOCAL_MACHINE,
+ "Software\\Vulkan",
+ registry_value);
+ registry_len = (registry_str) ? (DWORD) strlen(registry_str) : 0;
+
+ rtn_len = env_len + registry_len + 1;
+ if (rtn_len <= 2) {
+ // We found neither the desired registry value, nor the environment
+ // variable; return NULL:
+ return NULL;
+ } else {
+ // We found something, and so we need to allocate memory for the string
+ // to return:
+ rtn_str = malloc(rtn_len);
+ }
+
+ if (registry_len == 0) {
+ // We didn't find the desired registry value, and so we must have found
+ // only the environment variable:
+ _snprintf(rtn_str, rtn_len, "%s", env_str);
+ } else if (env_str != NULL) {
+ // We found both the desired registry value and the environment
+ // variable, so concatenate them both:
+ _snprintf(rtn_str, rtn_len, "%s;%s", registry_str, env_str);
+ } else {
+ // We must have only found the desired registry value:
+ _snprintf(rtn_str, rtn_len, "%s", registry_str);
+ }
+
+ if (registry_str) {
+ free(registry_str);
+ }
+
+ return(rtn_str);
+}
+
+// Create a console window with a large scrollback size to which to send stdout.
+// Returns true if console window was successfully created, false otherwise.
+bool SetStdOutToNewConsole()
+{
+ // don't do anything if we already have a console
+ if (GetStdHandle(STD_OUTPUT_HANDLE))
+ return false;
+
+ // allocate a console for this app
+ AllocConsole();
+
+ // redirect unbuffered STDOUT to the console
+ HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+ int fileDescriptor = _open_osfhandle((intptr_t)consoleHandle, _O_TEXT);
+ FILE *fp = _fdopen( fileDescriptor, "w" );
+ *stdout = *fp;
+ setvbuf( stdout, NULL, _IONBF, 0 );
+
+ // make the console window bigger
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ SMALL_RECT r;
+ COORD bufferSize;
+ if (!GetConsoleScreenBufferInfo(consoleHandle, &csbi))
+ return false;
+ bufferSize.X = csbi.dwSize.X;
+ bufferSize.Y = 1000;
+ if (!SetConsoleScreenBufferSize(consoleHandle, bufferSize))
+ return false;
+ r.Left = r.Top = 0;
+ r.Right = csbi.dwSize.X-1;
+ r.Bottom = 60;
+ if (!SetConsoleWindowInfo(consoleHandle, true, &r))
+ return false;
+
+ // change the console window title
+ if (!SetConsoleTitle(TEXT(APP_SHORT_NAME)))
+ return false;
+
+ return true;
+}
+
+int APIENTRY WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR pCmdLine,
+ int nCmdShow)
+{
+ MSG msg; // message
+ bool done; // flag saying when app is complete
+ char *layers_enabled = get_registry_and_env(LAYER_NAMES_ENV,
+ LAYER_NAMES_REGISTRY_VALUE);
+
+ if (layers_enabled != NULL) {
+ consoleCreated = SetStdOutToNewConsole();
+ free(layers_enabled);
+ }
+
+ demo_init(&demo, hInstance, pCmdLine);
+ demo_create_window(&demo);
+
+ demo_prepare(&demo);
+
+ done = false; //initialize loop condition variable
+ /* main message loop*/
+ while(!done)
+ {
+ PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+ if (msg.message == WM_QUIT) //check for a quit message
+ {
+ done = true; //if found, quit app
+ }
+ else
+ {
+ /* Translate and dispatch to event queue*/
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ demo_cleanup(&demo);
+
+ if (consoleCreated) {
+ printf("\nPlease close this window when you are finished looking at "
+ "the console output.\n");
+ fflush(stdout);
+ WAIT_FOR_CONSOLE_DESTROY;
+ }
+
+ return (int) msg.wParam;
+}
+#else // _WIN32
+int main(const int argc, const char *argv[])
+{
+ struct demo demo;
+
+ demo_init(&demo, argc, argv);
+ demo_create_window(&demo);
+
+ demo_prepare(&demo);
+ demo_run(&demo);
+
+ demo_cleanup(&demo);
+
+ return 0;
+}
+#endif // _WIN32