Last active
April 17, 2016 17:18
-
-
Save bwrsandman/663c50508fc9ddafb5269f3d44bcddbc to your computer and use it in GitHub Desktop.
LunarG Vulkan Triangle Demo ported to SDL2 (Linux-tested only)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2015-2016 The Khronos Group Inc. | |
* Copyright (c) 2015-2016 Valve Corporation | |
* Copyright (c) 2015-2016 LunarG, Inc. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and/or associated documentation files (the "Materials"), to | |
* deal in the Materials without restriction, including without limitation the | |
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Materials, and to permit persons to whom the Materials are | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice(s) and this permission notice shall be included in | |
* all copies or substantial portions of the Materials. | |
* | |
* THE MATERIALS ARE 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 MATERIALS OR THE | |
* USE OR OTHER DEALINGS IN THE MATERIALS. | |
*/ | |
/* | |
* Fragment shader for tri demo | |
*/ | |
#version 400 | |
#extension GL_ARB_separate_shader_objects : enable | |
#extension GL_ARB_shading_language_420pack : enable | |
layout (binding = 0) uniform sampler2D tex; | |
layout (location = 0) in vec2 texcoord; | |
layout (location = 0) out vec4 uFragColor; | |
void main() { | |
uFragColor = texture(tex, texcoord); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2015-2016 The Khronos Group Inc. | |
* Copyright (c) 2015-2016 Valve Corporation | |
* Copyright (c) 2015-2016 LunarG, Inc. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and/or associated documentation files (the "Materials"), to | |
* deal in the Materials without restriction, including without limitation the | |
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Materials, and to permit persons to whom the Materials are | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice(s) and this permission notice shall be included in | |
* all copies or substantial portions of the Materials. | |
* | |
* THE MATERIALS ARE 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 MATERIALS OR THE | |
* USE OR OTHER DEALINGS IN THE MATERIALS. | |
*/ | |
/* | |
* Vertex shader used by tri demo. | |
*/ | |
#version 400 | |
#extension GL_ARB_separate_shader_objects : enable | |
#extension GL_ARB_shading_language_420pack : enable | |
layout (location = 0) in vec4 pos; | |
layout (location = 1) in vec2 attr; | |
layout (location = 0) out vec2 texcoord; | |
void main() { | |
texcoord = attr; | |
gl_Position = pos; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2015-2016 The Khronos Group Inc. | |
* Copyright (c) 2015-2016 Valve Corporation | |
* Copyright (c) 2015-2016 LunarG, Inc. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and/or associated documentation files (the "Materials"), to | |
* deal in the Materials without restriction, including without limitation the | |
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Materials, and to permit persons to whom the Materials are | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice(s) and this permission notice shall be included in | |
* all copies or substantial portions of the Materials. | |
* | |
* THE MATERIALS ARE 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 MATERIALS OR THE | |
* USE OR OTHER DEALINGS IN THE MATERIALS. | |
* | |
* Author: Chia-I Wu <[email protected]> | |
* Author: Cody Northrop <[email protected]> | |
* Author: Courtney Goeltzenleuchter <[email protected]> | |
* Author: Ian Elliott <[email protected]> | |
* Author: Jon Ashburn <[email protected]> | |
* Author: Piers Daniell <[email protected]> | |
* Author (SDL2): Ethan Lee <[email protected]> | |
*/ | |
/* | |
* 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. | |
*/ | |
#ifndef _MSC_VER | |
#define _ISOC11_SOURCE /* for aligned_alloc() */ | |
#endif | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdbool.h> | |
#include <assert.h> | |
#include <signal.h> | |
#include <SDL2/SDL.h> | |
#include <SDL2/SDL_syswm.h> | |
#ifdef _WIN32 | |
#pragma comment(linker, "/subsystem:windows") | |
#define APP_NAME_STR_LEN 80 | |
#define VK_USE_PLATFORM_WIN32_KHR | |
#define PLATFORM_SURFACE_EXTENSION_NAME VK_KHR_WIN32_SURFACE_EXTENSION_NAME | |
#define PlatformSurfaceCreateInfo VkWin32SurfaceCreateInfoKHR | |
#define PLATFORM_SURFACE_CREATE_INFO VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; | |
#define PLATFORM_ASSIGN_SURFACEINFO \ | |
createInfo.hinstance = GetModuleHandle(NULL); \ | |
createInfo.hwnd = info.info.win.window | |
#define PlatformCreateSurface vkCreateWin32SurfaceKHR | |
#else /* _WIN32 */ | |
#define VK_USE_PLATFORM_XLIB_KHR | |
#define PLATFORM_SURFACE_EXTENSION_NAME VK_KHR_XLIB_SURFACE_EXTENSION_NAME | |
#define PlatformSurfaceCreateInfo VkXlibSurfaceCreateInfoKHR | |
#define PLATFORM_SURFACE_CREATE_INFO VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; | |
#define PLATFORM_ASSIGN_SURFACEINFO \ | |
createInfo.dpy = info.info.x11.display; \ | |
createInfo.window = info.info.x11.window | |
#define PlatformCreateSurface vkCreateXlibSurfaceKHR | |
#endif /* _WIN32 */ | |
#include <vulkan/vulkan.h> | |
#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" | |
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | |
#if defined(NDEBUG) && defined(__GNUC__) | |
#define U_ASSERT_ONLY __attribute__((unused)) | |
#else | |
#define U_ASSERT_ONLY | |
#endif | |
#define ERR_EXIT(err_msg, err_class) \ | |
do { \ | |
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, APP_LONG_NAME, err_msg, NULL); \ | |
exit(1); \ | |
} while (0) | |
#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ | |
{ \ | |
demo->fp##entrypoint = \ | |
(PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \ | |
if (demo->fp##entrypoint == NULL) { \ | |
ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, \ | |
"vkGetInstanceProcAddr Failure"); \ | |
} \ | |
} | |
#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ | |
{ \ | |
demo->fp##entrypoint = \ | |
(PFN_vk##entrypoint)vkGetDeviceProcAddr(dev, "vk" #entrypoint); \ | |
if (demo->fp##entrypoint == NULL) { \ | |
ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, \ | |
"vkGetDeviceProcAddr Failure"); \ | |
} \ | |
} | |
struct texture_object { | |
VkSampler sampler; | |
VkImage image; | |
VkImageLayout imageLayout; | |
VkDeviceMemory mem; | |
VkImageView view; | |
int32_t tex_width, tex_height; | |
}; | |
static int validation_error = 0; | |
VKAPI_ATTR VkBool32 VKAPI_CALL | |
dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, | |
uint64_t srcObject, size_t location, int32_t msgCode, | |
const char *pLayerPrefix, const char *pMsg, void *pUserData) { | |
char *message = (char *)malloc(strlen(pMsg) + 100); | |
assert(message); | |
validation_error = 1; | |
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { | |
sprintf(message, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, | |
pMsg); | |
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { | |
sprintf(message, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode, | |
pMsg); | |
} else { | |
return false; | |
} | |
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, APP_LONG_NAME, message, NULL); | |
free(message); | |
/* | |
* false indicates that layer should not bail-out of an | |
* API call that had validation failures. This may mean that the | |
* app dies inside the driver due to invalid parameter(s). | |
* That's what would happen without validation layers, so we'll | |
* keep that behavior here. | |
*/ | |
return false; | |
} | |
VKAPI_ATTR VkBool32 VKAPI_CALL | |
BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, | |
uint64_t srcObject, size_t location, int32_t msgCode, | |
const char *pLayerPrefix, const char *pMsg, | |
void *pUserData) { | |
raise(SIGTRAP); | |
return false; | |
} | |
typedef struct _SwapchainBuffers { | |
VkImage image; | |
VkCommandBuffer cmd; | |
VkImageView view; | |
} SwapchainBuffers; | |
struct demo { | |
SDL_Window *window; | |
VkSurfaceKHR surface; | |
bool prepared; | |
bool use_staging_buffer; | |
VkInstance inst; | |
VkPhysicalDevice gpu; | |
VkDevice device; | |
VkQueue queue; | |
VkPhysicalDeviceProperties gpu_props; | |
VkQueueFamilyProperties *queue_props; | |
uint32_t graphics_queue_node_index; | |
uint32_t enabled_extension_count; | |
char *extension_names[64]; | |
int width, height; | |
VkFormat format; | |
VkColorSpaceKHR color_space; | |
uint32_t swapchainImageCount; | |
VkSwapchainKHR swapchain; | |
SwapchainBuffers *buffers; | |
VkCommandPool cmd_pool; | |
struct { | |
VkFormat format; | |
VkImage image; | |
VkDeviceMemory mem; | |
VkImageView view; | |
} depth; | |
struct texture_object textures[DEMO_TEXTURE_COUNT]; | |
struct { | |
VkBuffer buf; | |
VkDeviceMemory mem; | |
VkPipelineVertexInputStateCreateInfo vi; | |
VkVertexInputBindingDescription vi_bindings[1]; | |
VkVertexInputAttributeDescription vi_attrs[2]; | |
} vertices; | |
VkCommandBuffer setup_cmd; // Command Buffer for initialization commands | |
VkCommandBuffer draw_cmd; // Command Buffer for drawing commands | |
VkPipelineLayout pipeline_layout; | |
VkDescriptorSetLayout desc_layout; | |
VkPipelineCache pipelineCache; | |
VkRenderPass render_pass; | |
VkPipeline pipeline; | |
VkShaderModule vert_shader_module; | |
VkShaderModule frag_shader_module; | |
VkDescriptorPool desc_pool; | |
VkDescriptorSet desc_set; | |
VkFramebuffer *framebuffers; | |
VkPhysicalDeviceMemoryProperties memory_properties; | |
int32_t curFrame; | |
int32_t frameCount; | |
bool use_break; | |
float depthStencil; | |
float depthIncrement; | |
bool quit; | |
uint32_t current_buffer; | |
uint32_t queue_count; | |
}; | |
// Forward declaration: | |
static void demo_resize(struct demo *demo); | |
static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits, | |
VkFlags requirements_mask, | |
uint32_t *typeIndex) { | |
// Search memtypes to find first index with those properties | |
for (uint32_t i = 0; i < 32; i++) { | |
if ((typeBits & 1) == 1) { | |
// Type is available, does it match user properties? | |
if ((demo->memory_properties.memoryTypes[i].propertyFlags & | |
requirements_mask) == requirements_mask) { | |
*typeIndex = i; | |
return true; | |
} | |
} | |
typeBits >>= 1; | |
} | |
// No memory types matched, return failure | |
return false; | |
} | |
static void demo_flush_init_cmd(struct demo *demo) { | |
VkResult U_ASSERT_ONLY err; | |
if (demo->setup_cmd == VK_NULL_HANDLE) | |
return; | |
err = vkEndCommandBuffer(demo->setup_cmd); | |
assert(!err); | |
const VkCommandBuffer cmd_bufs[] = {demo->setup_cmd}; | |
VkFence nullFence = {VK_NULL_HANDLE}; | |
VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
.pNext = NULL, | |
.waitSemaphoreCount = 0, | |
.pWaitSemaphores = NULL, | |
.pWaitDstStageMask = NULL, | |
.commandBufferCount = 1, | |
.pCommandBuffers = cmd_bufs, | |
.signalSemaphoreCount = 0, | |
.pSignalSemaphores = NULL}; | |
err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence); | |
assert(!err); | |
err = vkQueueWaitIdle(demo->queue); | |
assert(!err); | |
vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, cmd_bufs); | |
demo->setup_cmd = VK_NULL_HANDLE; | |
} | |
static void demo_set_image_layout(struct demo *demo, VkImage image, | |
VkImageAspectFlags aspectMask, | |
VkImageLayout old_image_layout, | |
VkImageLayout new_image_layout, | |
VkAccessFlagBits srcAccessMask) { | |
VkResult U_ASSERT_ONLY err; | |
if (demo->setup_cmd == VK_NULL_HANDLE) { | |
const VkCommandBufferAllocateInfo cmd = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
.pNext = NULL, | |
.commandPool = demo->cmd_pool, | |
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
.commandBufferCount = 1, | |
}; | |
err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->setup_cmd); | |
assert(!err); | |
VkCommandBufferInheritanceInfo cmd_buf_hinfo = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, | |
.pNext = NULL, | |
.renderPass = VK_NULL_HANDLE, | |
.subpass = 0, | |
.framebuffer = VK_NULL_HANDLE, | |
.occlusionQueryEnable = VK_FALSE, | |
.queryFlags = 0, | |
.pipelineStatistics = 0, | |
}; | |
VkCommandBufferBeginInfo cmd_buf_info = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
.pNext = NULL, | |
.flags = 0, | |
.pInheritanceInfo = &cmd_buf_hinfo, | |
}; | |
err = vkBeginCommandBuffer(demo->setup_cmd, &cmd_buf_info); | |
assert(!err); | |
} | |
VkImageMemoryBarrier image_memory_barrier = { | |
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | |
.pNext = NULL, | |
.srcAccessMask = srcAccessMask, | |
.dstAccessMask = 0, | |
.oldLayout = old_image_layout, | |
.newLayout = new_image_layout, | |
.image = image, | |
.subresourceRange = {aspectMask, 0, 1, 0, 1}}; | |
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { | |
/* Make sure anything that was copying from this image has completed */ | |
image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | |
} | |
if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { | |
image_memory_barrier.dstAccessMask = | |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
} | |
if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { | |
image_memory_barrier.dstAccessMask = | |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_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.dstAccessMask = | |
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; | |
} | |
VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier; | |
VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | |
VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | |
vkCmdPipelineBarrier(demo->setup_cmd, src_stages, dest_stages, 0, 0, NULL, | |
0, NULL, 1, pmemory_barrier); | |
} | |
static void demo_draw_build_cmd(struct demo *demo) { | |
const VkCommandBufferInheritanceInfo cmd_buf_hinfo = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, | |
.pNext = NULL, | |
.renderPass = VK_NULL_HANDLE, | |
.subpass = 0, | |
.framebuffer = VK_NULL_HANDLE, | |
.occlusionQueryEnable = VK_FALSE, | |
.queryFlags = 0, | |
.pipelineStatistics = 0, | |
}; | |
const VkCommandBufferBeginInfo cmd_buf_info = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
.pNext = NULL, | |
.flags = 0, | |
.pInheritanceInfo = &cmd_buf_hinfo, | |
}; | |
const VkClearValue clear_values[2] = { | |
[0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}}, | |
[1] = {.depthStencil = {demo->depthStencil, 0}}, | |
}; | |
const VkRenderPassBeginInfo rp_begin = { | |
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | |
.pNext = NULL, | |
.renderPass = demo->render_pass, | |
.framebuffer = demo->framebuffers[demo->current_buffer], | |
.renderArea.offset.x = 0, | |
.renderArea.offset.y = 0, | |
.renderArea.extent.width = demo->width, | |
.renderArea.extent.height = demo->height, | |
.clearValueCount = 2, | |
.pClearValues = clear_values, | |
}; | |
VkResult U_ASSERT_ONLY err; | |
err = vkBeginCommandBuffer(demo->draw_cmd, &cmd_buf_info); | |
assert(!err); | |
vkCmdBeginRenderPass(demo->draw_cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); | |
vkCmdBindPipeline(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, | |
demo->pipeline); | |
vkCmdBindDescriptorSets(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, | |
demo->pipeline_layout, 0, 1, &demo->desc_set, 0, | |
NULL); | |
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; | |
vkCmdSetViewport(demo->draw_cmd, 0, 1, &viewport); | |
VkRect2D scissor; | |
memset(&scissor, 0, sizeof(scissor)); | |
scissor.extent.width = demo->width; | |
scissor.extent.height = demo->height; | |
scissor.offset.x = 0; | |
scissor.offset.y = 0; | |
vkCmdSetScissor(demo->draw_cmd, 0, 1, &scissor); | |
VkDeviceSize offsets[1] = {0}; | |
vkCmdBindVertexBuffers(demo->draw_cmd, VERTEX_BUFFER_BIND_ID, 1, | |
&demo->vertices.buf, offsets); | |
vkCmdDraw(demo->draw_cmd, 3, 1, 0, 0); | |
vkCmdEndRenderPass(demo->draw_cmd); | |
VkImageMemoryBarrier prePresentBarrier = { | |
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | |
.pNext = NULL, | |
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, | |
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, | |
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | |
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; | |
prePresentBarrier.image = demo->buffers[demo->current_buffer].image; | |
VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier; | |
vkCmdPipelineBarrier(demo->draw_cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | |
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, | |
NULL, 1, pmemory_barrier); | |
err = vkEndCommandBuffer(demo->draw_cmd); | |
assert(!err); | |
} | |
static void demo_draw(struct demo *demo) { | |
VkResult U_ASSERT_ONLY err; | |
VkSemaphore presentCompleteSemaphore; | |
VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = { | |
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, | |
.pNext = NULL, | |
.flags = 0, | |
}; | |
err = vkCreateSemaphore(demo->device, &presentCompleteSemaphoreCreateInfo, | |
NULL, &presentCompleteSemaphore); | |
assert(!err); | |
// Get the index of the next available swapchain image: | |
err = vkAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX, | |
presentCompleteSemaphore, | |
(VkFence)0, // TODO: Show use of fence | |
&demo->current_buffer); | |
if (err == VK_ERROR_OUT_OF_DATE_KHR) { | |
// demo->swapchain is out of date (e.g. the window was resized) and | |
// must be recreated: | |
demo_resize(demo); | |
demo_draw(demo); | |
vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); | |
return; | |
} else if (err == VK_SUBOPTIMAL_KHR) { | |
// demo->swapchain is not as optimal as it could be, but the platform's | |
// presentation engine will still present the image correctly. | |
} else { | |
assert(!err); | |
} | |
// Assume the command buffer has been run on current_buffer before so | |
// we need to set the image layout back to COLOR_ATTACHMENT_OPTIMAL | |
demo_set_image_layout(demo, demo->buffers[demo->current_buffer].image, | |
VK_IMAGE_ASPECT_COLOR_BIT, | |
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | |
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
0); | |
demo_flush_init_cmd(demo); | |
// Wait for the present complete semaphore to be signaled to ensure | |
// that the image won't be rendered to until the presentation | |
// engine has fully released ownership to the application, and it is | |
// okay to render to the image. | |
// FIXME/TODO: DEAL WITH VK_IMAGE_LAYOUT_PRESENT_SRC_KHR | |
demo_draw_build_cmd(demo); | |
VkFence nullFence = VK_NULL_HANDLE; | |
VkPipelineStageFlags pipe_stage_flags = | |
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; | |
VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
.pNext = NULL, | |
.waitSemaphoreCount = 1, | |
.pWaitSemaphores = &presentCompleteSemaphore, | |
.pWaitDstStageMask = &pipe_stage_flags, | |
.commandBufferCount = 1, | |
.pCommandBuffers = &demo->draw_cmd, | |
.signalSemaphoreCount = 0, | |
.pSignalSemaphores = NULL}; | |
err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence); | |
assert(!err); | |
VkPresentInfoKHR present = { | |
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, | |
.pNext = NULL, | |
.swapchainCount = 1, | |
.pSwapchains = &demo->swapchain, | |
.pImageIndices = &demo->current_buffer, | |
}; | |
// TBD/TODO: SHOULD THE "present" PARAMETER BE "const" IN THE HEADER? | |
//err = demo->fpQueuePresentKHR(demo->queue, &present); | |
err = vkQueuePresentKHR(demo->queue, &present); | |
if (err == VK_ERROR_OUT_OF_DATE_KHR) { | |
// demo->swapchain is out of date (e.g. the window was resized) and | |
// must be recreated: | |
demo_resize(demo); | |
} else if (err == VK_SUBOPTIMAL_KHR) { | |
// demo->swapchain is not as optimal as it could be, but the platform's | |
// presentation engine will still present the image correctly. | |
} else { | |
assert(!err); | |
} | |
err = vkQueueWaitIdle(demo->queue); | |
assert(err == VK_SUCCESS); | |
vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); | |
} | |
static void demo_prepare_buffers(struct demo *demo) { | |
VkResult U_ASSERT_ONLY err; | |
VkSwapchainKHR oldSwapchain = demo->swapchain; | |
// Check the surface capabilities and formats | |
VkSurfaceCapabilitiesKHR surfCapabilities; | |
err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( | |
demo->gpu, demo->surface, &surfCapabilities); | |
assert(!err); | |
uint32_t presentModeCount; | |
err = vkGetPhysicalDeviceSurfacePresentModesKHR( | |
demo->gpu, demo->surface, &presentModeCount, NULL); | |
assert(!err); | |
VkPresentModeKHR *presentModes = | |
(VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); | |
assert(presentModes); | |
err = vkGetPhysicalDeviceSurfacePresentModesKHR( | |
demo->gpu, demo->surface, &presentModeCount, presentModes); | |
assert(!err); | |
VkExtent2D swapchainExtent; | |
// width and height are either both -1, or both not -1. | |
if (surfCapabilities.currentExtent.width == (uint32_t)-1) { | |
// If the surface size is undefined, the size is set to | |
// the size of the images requested. | |
swapchainExtent.width = demo->width; | |
swapchainExtent.height = demo->height; | |
} else { | |
// If the surface size is defined, the swap chain size must match | |
swapchainExtent = surfCapabilities.currentExtent; | |
demo->width = surfCapabilities.currentExtent.width; | |
demo->height = surfCapabilities.currentExtent.height; | |
} | |
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; | |
// Determine the number of VkImage's to use in the swap chain (we desire to | |
// own only 1 image at a time, besides the images being displayed and | |
// queued for display): | |
uint32_t desiredNumberOfSwapchainImages = | |
surfCapabilities.minImageCount + 1; | |
if ((surfCapabilities.maxImageCount > 0) && | |
(desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) { | |
// Application must settle for fewer images than desired: | |
desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount; | |
} | |
VkSurfaceTransformFlagsKHR preTransform; | |
if (surfCapabilities.supportedTransforms & | |
VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { | |
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; | |
} else { | |
preTransform = surfCapabilities.currentTransform; | |
} | |
const VkSwapchainCreateInfoKHR swapchain = { | |
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, | |
.pNext = NULL, | |
.surface = demo->surface, | |
.minImageCount = desiredNumberOfSwapchainImages, | |
.imageFormat = demo->format, | |
.imageColorSpace = demo->color_space, | |
.imageExtent = | |
{ | |
.width = swapchainExtent.width, .height = swapchainExtent.height, | |
}, | |
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, | |
.preTransform = preTransform, | |
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, | |
.imageArrayLayers = 1, | |
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, | |
.queueFamilyIndexCount = 0, | |
.pQueueFamilyIndices = NULL, | |
.presentMode = swapchainPresentMode, | |
.oldSwapchain = oldSwapchain, | |
.clipped = true, | |
}; | |
uint32_t i; | |
err = vkCreateSwapchainKHR(demo->device, &swapchain, NULL, | |
&demo->swapchain); | |
assert(!err); | |
// If we just re-created an existing swapchain, we should destroy the old | |
// swapchain at this point. | |
// Note: destroying the swapchain also cleans up all its associated | |
// presentable images once the platform is done with them. | |
if (oldSwapchain != VK_NULL_HANDLE) { | |
vkDestroySwapchainKHR(demo->device, oldSwapchain, NULL); | |
} | |
err = vkGetSwapchainImagesKHR(demo->device, demo->swapchain, | |
&demo->swapchainImageCount, NULL); | |
assert(!err); | |
VkImage *swapchainImages = | |
(VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage)); | |
assert(swapchainImages); | |
err = vkGetSwapchainImagesKHR(demo->device, demo->swapchain, | |
&demo->swapchainImageCount, | |
swapchainImages); | |
assert(!err); | |
demo->buffers = (SwapchainBuffers *)malloc(sizeof(SwapchainBuffers) * | |
demo->swapchainImageCount); | |
assert(demo->buffers); | |
for (i = 0; i < demo->swapchainImageCount; i++) { | |
VkImageViewCreateInfo color_attachment_view = { | |
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
.pNext = NULL, | |
.format = demo->format, | |
.components = | |
{ | |
.r = VK_COMPONENT_SWIZZLE_R, | |
.g = VK_COMPONENT_SWIZZLE_G, | |
.b = VK_COMPONENT_SWIZZLE_B, | |
.a = VK_COMPONENT_SWIZZLE_A, | |
}, | |
.subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | |
.baseMipLevel = 0, | |
.levelCount = 1, | |
.baseArrayLayer = 0, | |
.layerCount = 1}, | |
.viewType = VK_IMAGE_VIEW_TYPE_2D, | |
.flags = 0, | |
}; | |
demo->buffers[i].image = swapchainImages[i]; | |
// Render loop will expect image to have been used before and in | |
// VK_IMAGE_LAYOUT_PRESENT_SRC_KHR | |
// layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image | |
// to that state | |
demo_set_image_layout( | |
demo, demo->buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, | |
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | |
0); | |
color_attachment_view.image = demo->buffers[i].image; | |
err = vkCreateImageView(demo->device, &color_attachment_view, NULL, | |
&demo->buffers[i].view); | |
assert(!err); | |
} | |
demo->current_buffer = 0; | |
if (NULL != presentModes) { | |
free(presentModes); | |
} | |
} | |
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, | |
.arrayLayers = 1, | |
.samples = VK_SAMPLE_COUNT_1_BIT, | |
.tiling = VK_IMAGE_TILING_OPTIMAL, | |
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, | |
.flags = 0, | |
}; | |
VkMemoryAllocateInfo mem_alloc = { | |
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
.pNext = NULL, | |
.allocationSize = 0, | |
.memoryTypeIndex = 0, | |
}; | |
VkImageViewCreateInfo view = { | |
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
.pNext = NULL, | |
.image = VK_NULL_HANDLE, | |
.format = depth_format, | |
.subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, | |
.baseMipLevel = 0, | |
.levelCount = 1, | |
.baseArrayLayer = 0, | |
.layerCount = 1}, | |
.flags = 0, | |
.viewType = VK_IMAGE_VIEW_TYPE_2D, | |
}; | |
VkMemoryRequirements mem_reqs; | |
VkResult U_ASSERT_ONLY err; | |
bool U_ASSERT_ONLY pass; | |
demo->depth.format = depth_format; | |
/* create image */ | |
err = vkCreateImage(demo->device, &image, NULL, &demo->depth.image); | |
assert(!err); | |
/* get memory requirements for this object */ | |
vkGetImageMemoryRequirements(demo->device, demo->depth.image, &mem_reqs); | |
/* select memory size and type */ | |
mem_alloc.allocationSize = mem_reqs.size; | |
pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, | |
0, /* No requirements */ | |
&mem_alloc.memoryTypeIndex); | |
assert(pass); | |
/* allocate memory */ | |
err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->depth.mem); | |
assert(!err); | |
/* bind memory */ | |
err = | |
vkBindImageMemory(demo->device, demo->depth.image, demo->depth.mem, 0); | |
assert(!err); | |
demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, | |
VK_IMAGE_LAYOUT_UNDEFINED, | |
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, | |
0); | |
/* create image view */ | |
view.image = demo->depth.image; | |
err = vkCreateImageView(demo->device, &view, NULL, &demo->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 required_props) { | |
const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM; | |
const int32_t tex_width = 2; | |
const int32_t tex_height = 2; | |
VkResult U_ASSERT_ONLY err; | |
bool U_ASSERT_ONLY pass; | |
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, | |
.arrayLayers = 1, | |
.samples = VK_SAMPLE_COUNT_1_BIT, | |
.tiling = tiling, | |
.usage = usage, | |
.flags = 0, | |
.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED | |
}; | |
VkMemoryAllocateInfo mem_alloc = { | |
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
.pNext = NULL, | |
.allocationSize = 0, | |
.memoryTypeIndex = 0, | |
}; | |
VkMemoryRequirements mem_reqs; | |
err = | |
vkCreateImage(demo->device, &image_create_info, NULL, &tex_obj->image); | |
assert(!err); | |
vkGetImageMemoryRequirements(demo->device, tex_obj->image, &mem_reqs); | |
mem_alloc.allocationSize = mem_reqs.size; | |
pass = | |
memory_type_from_properties(demo, mem_reqs.memoryTypeBits, | |
required_props, &mem_alloc.memoryTypeIndex); | |
assert(pass); | |
/* allocate memory */ | |
err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &tex_obj->mem); | |
assert(!err); | |
/* bind memory */ | |
err = vkBindImageMemory(demo->device, tex_obj->image, tex_obj->mem, 0); | |
assert(!err); | |
if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { | |
const VkImageSubresource subres = { | |
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | |
.mipLevel = 0, | |
.arrayLayer = 0, | |
}; | |
VkSubresourceLayout layout; | |
void *data; | |
int32_t x, y; | |
vkGetImageSubresourceLayout(demo->device, tex_obj->image, &subres, | |
&layout); | |
err = vkMapMemory(demo->device, tex_obj->mem, 0, | |
mem_alloc.allocationSize, 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)]; | |
} | |
vkUnmapMemory(demo->device, tex_obj->mem); | |
} | |
tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; | |
demo_set_image_layout(demo, tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT, | |
VK_IMAGE_LAYOUT_PREINITIALIZED, tex_obj->imageLayout, | |
VK_ACCESS_HOST_WRITE_BIT); | |
/* 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 */ | |
vkDestroyImage(demo->device, tex_obj->image, NULL); | |
vkFreeMemory(demo->device, tex_obj->mem, NULL); | |
} | |
static void demo_prepare_textures(struct demo *demo) { | |
const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM; | |
VkFormatProperties props; | |
const uint32_t tex_colors[DEMO_TEXTURE_COUNT][2] = { | |
{0xffff0000, 0xff00ff00}, | |
}; | |
uint32_t i; | |
VkResult U_ASSERT_ONLY err; | |
vkGetPhysicalDeviceFormatProperties(demo->gpu, tex_format, &props); | |
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_SRC_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_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), | |
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); | |
demo_set_image_layout(demo, staging_texture.image, | |
VK_IMAGE_ASPECT_COLOR_BIT, | |
staging_texture.imageLayout, | |
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | |
0); | |
demo_set_image_layout(demo, demo->textures[i].image, | |
VK_IMAGE_ASPECT_COLOR_BIT, | |
demo->textures[i].imageLayout, | |
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | |
0); | |
VkImageCopy copy_region = { | |
.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, | |
.srcOffset = {0, 0, 0}, | |
.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, | |
.dstOffset = {0, 0, 0}, | |
.extent = {staging_texture.tex_width, | |
staging_texture.tex_height, 1}, | |
}; | |
vkCmdCopyImage( | |
demo->setup_cmd, staging_texture.image, | |
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, demo->textures[i].image, | |
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); | |
demo_set_image_layout(demo, demo->textures[i].image, | |
VK_IMAGE_ASPECT_COLOR_BIT, | |
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | |
demo->textures[i].imageLayout, | |
0); | |
demo_flush_init_cmd(demo); | |
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_FILTER_NEAREST, | |
.minFilter = VK_FILTER_NEAREST, | |
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, | |
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, | |
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, | |
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, | |
.mipLodBias = 0.0f, | |
.anisotropyEnable = VK_FALSE, | |
.maxAnisotropy = 1, | |
.compareOp = VK_COMPARE_OP_NEVER, | |
.minLod = 0.0f, | |
.maxLod = 0.0f, | |
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, | |
.unnormalizedCoordinates = VK_FALSE, | |
}; | |
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, | |
.components = | |
{ | |
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, | |
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A, | |
}, | |
.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, | |
.flags = 0, | |
}; | |
/* create sampler */ | |
err = vkCreateSampler(demo->device, &sampler, NULL, | |
&demo->textures[i].sampler); | |
assert(!err); | |
/* create image view */ | |
view.image = demo->textures[i].image; | |
err = vkCreateImageView(demo->device, &view, NULL, | |
&demo->textures[i].view); | |
assert(!err); | |
} | |
} | |
static void demo_prepare_vertices(struct demo *demo) { | |
// clang-format off | |
const float vb[3][5] = { | |
/* position texcoord */ | |
{ -1.0f, -1.0f, 0.25f, 0.0f, 0.0f }, | |
{ 1.0f, -1.0f, 0.25f, 1.0f, 0.0f }, | |
{ 0.0f, 1.0f, 1.0f, 0.5f, 1.0f }, | |
}; | |
// clang-format on | |
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, | |
}; | |
VkMemoryAllocateInfo mem_alloc = { | |
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
.pNext = NULL, | |
.allocationSize = 0, | |
.memoryTypeIndex = 0, | |
}; | |
VkMemoryRequirements mem_reqs; | |
VkResult U_ASSERT_ONLY err; | |
bool U_ASSERT_ONLY pass; | |
void *data; | |
memset(&demo->vertices, 0, sizeof(demo->vertices)); | |
err = vkCreateBuffer(demo->device, &buf_info, NULL, &demo->vertices.buf); | |
assert(!err); | |
vkGetBufferMemoryRequirements(demo->device, demo->vertices.buf, &mem_reqs); | |
assert(!err); | |
mem_alloc.allocationSize = mem_reqs.size; | |
pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, | |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, | |
&mem_alloc.memoryTypeIndex); | |
assert(pass); | |
err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->vertices.mem); | |
assert(!err); | |
err = vkMapMemory(demo->device, demo->vertices.mem, 0, | |
mem_alloc.allocationSize, 0, &data); | |
assert(!err); | |
memcpy(data, vb, sizeof(vb)); | |
vkUnmapMemory(demo->device, demo->vertices.mem); | |
err = vkBindBufferMemory(demo->device, demo->vertices.buf, | |
demo->vertices.mem, 0); | |
assert(!err); | |
demo->vertices.vi.sType = | |
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | |
demo->vertices.vi.pNext = NULL; | |
demo->vertices.vi.vertexBindingDescriptionCount = 1; | |
demo->vertices.vi.pVertexBindingDescriptions = demo->vertices.vi_bindings; | |
demo->vertices.vi.vertexAttributeDescriptionCount = 2; | |
demo->vertices.vi.pVertexAttributeDescriptions = demo->vertices.vi_attrs; | |
demo->vertices.vi_bindings[0].binding = VERTEX_BUFFER_BIND_ID; | |
demo->vertices.vi_bindings[0].stride = sizeof(vb[0]); | |
demo->vertices.vi_bindings[0].inputRate = VK_VERTEX_INPUT_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].offset = 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].offset = sizeof(float) * 3; | |
} | |
static void demo_prepare_descriptor_layout(struct demo *demo) { | |
const VkDescriptorSetLayoutBinding layout_binding = { | |
.binding = 0, | |
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | |
.descriptorCount = 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, | |
.bindingCount = 1, | |
.pBindings = &layout_binding, | |
}; | |
VkResult U_ASSERT_ONLY err; | |
err = vkCreateDescriptorSetLayout(demo->device, &descriptor_layout, NULL, | |
&demo->desc_layout); | |
assert(!err); | |
const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = { | |
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
.pNext = NULL, | |
.setLayoutCount = 1, | |
.pSetLayouts = &demo->desc_layout, | |
}; | |
err = vkCreatePipelineLayout(demo->device, &pPipelineLayoutCreateInfo, NULL, | |
&demo->pipeline_layout); | |
assert(!err); | |
} | |
static void demo_prepare_render_pass(struct demo *demo) { | |
const VkAttachmentDescription attachments[2] = { | |
[0] = | |
{ | |
.format = demo->format, | |
.samples = VK_SAMPLE_COUNT_1_BIT, | |
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | |
.storeOp = VK_ATTACHMENT_STORE_OP_STORE, | |
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
}, | |
[1] = | |
{ | |
.format = demo->depth.format, | |
.samples = VK_SAMPLE_COUNT_1_BIT, | |
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | |
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
.initialLayout = | |
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, | |
.finalLayout = | |
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, | |
}, | |
}; | |
const VkAttachmentReference color_reference = { | |
.attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
}; | |
const VkAttachmentReference depth_reference = { | |
.attachment = 1, | |
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, | |
}; | |
const VkSubpassDescription subpass = { | |
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, | |
.flags = 0, | |
.inputAttachmentCount = 0, | |
.pInputAttachments = NULL, | |
.colorAttachmentCount = 1, | |
.pColorAttachments = &color_reference, | |
.pResolveAttachments = NULL, | |
.pDepthStencilAttachment = &depth_reference, | |
.preserveAttachmentCount = 0, | |
.pPreserveAttachments = NULL, | |
}; | |
const VkRenderPassCreateInfo rp_info = { | |
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | |
.pNext = NULL, | |
.attachmentCount = 2, | |
.pAttachments = attachments, | |
.subpassCount = 1, | |
.pSubpasses = &subpass, | |
.dependencyCount = 0, | |
.pDependencies = NULL, | |
}; | |
VkResult U_ASSERT_ONLY err; | |
err = vkCreateRenderPass(demo->device, &rp_info, NULL, &demo->render_pass); | |
assert(!err); | |
} | |
static VkShaderModule | |
demo_prepare_shader_module(struct demo *demo, const void *code, size_t size) { | |
VkShaderModuleCreateInfo moduleCreateInfo; | |
VkShaderModule module; | |
VkResult U_ASSERT_ONLY err; | |
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; | |
moduleCreateInfo.pNext = NULL; | |
moduleCreateInfo.codeSize = size; | |
moduleCreateInfo.pCode = code; | |
moduleCreateInfo.flags = 0; | |
err = vkCreateShaderModule(demo->device, &moduleCreateInfo, NULL, &module); | |
assert(!err); | |
return module; | |
} | |
char *demo_read_spv(const char *filename, size_t *psize) { | |
long int size; | |
void *shader_code; | |
size_t retVal; | |
FILE *fp = fopen(filename, "rb"); | |
if (!fp) | |
{ | |
char str[128]; | |
sprintf(str, "Could not find spv file: %s\n", filename); | |
ERR_EXIT(str, ""); | |
} | |
fseek(fp, 0L, SEEK_END); | |
size = ftell(fp); | |
fseek(fp, 0L, SEEK_SET); | |
shader_code = malloc(size); | |
retVal = fread(shader_code, size, 1, fp); | |
if (!retVal) | |
return NULL; | |
*psize = size; | |
fclose(fp); | |
return shader_code; | |
} | |
static VkShaderModule demo_prepare_vs(struct demo *demo) { | |
void *vertShaderCode; | |
size_t size; | |
vertShaderCode = demo_read_spv("tri-vert.spv", &size); | |
demo->vert_shader_module = | |
demo_prepare_shader_module(demo, vertShaderCode, size); | |
free(vertShaderCode); | |
return demo->vert_shader_module; | |
} | |
static VkShaderModule demo_prepare_fs(struct demo *demo) { | |
void *fragShaderCode; | |
size_t size; | |
fragShaderCode = demo_read_spv("tri-frag.spv", &size); | |
demo->frag_shader_module = | |
demo_prepare_shader_module(demo, fragShaderCode, size); | |
free(fragShaderCode); | |
return demo->frag_shader_module; | |
} | |
static void demo_prepare_pipeline(struct demo *demo) { | |
VkGraphicsPipelineCreateInfo pipeline; | |
VkPipelineCacheCreateInfo pipelineCache; | |
VkPipelineVertexInputStateCreateInfo vi; | |
VkPipelineInputAssemblyStateCreateInfo ia; | |
VkPipelineRasterizationStateCreateInfo rs; | |
VkPipelineColorBlendStateCreateInfo cb; | |
VkPipelineDepthStencilStateCreateInfo ds; | |
VkPipelineViewportStateCreateInfo vp; | |
VkPipelineMultisampleStateCreateInfo ms; | |
VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE]; | |
VkPipelineDynamicStateCreateInfo dynamicState; | |
VkResult U_ASSERT_ONLY err; | |
memset(dynamicStateEnables, 0, sizeof dynamicStateEnables); | |
memset(&dynamicState, 0, sizeof dynamicState); | |
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; | |
dynamicState.pDynamicStates = dynamicStateEnables; | |
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_INPUT_ASSEMBLY_STATE_CREATE_INFO; | |
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; | |
memset(&rs, 0, sizeof(rs)); | |
rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | |
rs.polygonMode = VK_POLYGON_MODE_FILL; | |
rs.cullMode = VK_CULL_MODE_BACK_BIT; | |
rs.frontFace = VK_FRONT_FACE_CLOCKWISE; | |
rs.depthClampEnable = VK_FALSE; | |
rs.rasterizerDiscardEnable = VK_FALSE; | |
rs.depthBiasEnable = VK_FALSE; | |
memset(&cb, 0, sizeof(cb)); | |
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; | |
VkPipelineColorBlendAttachmentState att_state[1]; | |
memset(att_state, 0, sizeof(att_state)); | |
att_state[0].colorWriteMask = 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_VIEWPORT_STATE_CREATE_INFO; | |
vp.viewportCount = 1; | |
dynamicStateEnables[dynamicState.dynamicStateCount++] = | |
VK_DYNAMIC_STATE_VIEWPORT; | |
vp.scissorCount = 1; | |
dynamicStateEnables[dynamicState.dynamicStateCount++] = | |
VK_DYNAMIC_STATE_SCISSOR; | |
memset(&ds, 0, sizeof(ds)); | |
ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; | |
ds.depthTestEnable = VK_TRUE; | |
ds.depthWriteEnable = VK_TRUE; | |
ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; | |
ds.depthBoundsTestEnable = VK_FALSE; | |
ds.back.failOp = VK_STENCIL_OP_KEEP; | |
ds.back.passOp = VK_STENCIL_OP_KEEP; | |
ds.back.compareOp = VK_COMPARE_OP_ALWAYS; | |
ds.stencilTestEnable = VK_FALSE; | |
ds.front = ds.back; | |
memset(&ms, 0, sizeof(ms)); | |
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; | |
ms.pSampleMask = NULL; | |
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; | |
// Two stages: vs and fs | |
pipeline.stageCount = 2; | |
VkPipelineShaderStageCreateInfo shaderStages[2]; | |
memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo)); | |
shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; | |
shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; | |
shaderStages[0].module = demo_prepare_vs(demo); | |
shaderStages[0].pName = "main"; | |
shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; | |
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; | |
shaderStages[1].module = demo_prepare_fs(demo); | |
shaderStages[1].pName = "main"; | |
pipeline.pVertexInputState = &vi; | |
pipeline.pInputAssemblyState = &ia; | |
pipeline.pRasterizationState = &rs; | |
pipeline.pColorBlendState = &cb; | |
pipeline.pMultisampleState = &ms; | |
pipeline.pViewportState = &vp; | |
pipeline.pDepthStencilState = &ds; | |
pipeline.pStages = shaderStages; | |
pipeline.renderPass = demo->render_pass; | |
pipeline.pDynamicState = &dynamicState; | |
memset(&pipelineCache, 0, sizeof(pipelineCache)); | |
pipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; | |
err = vkCreatePipelineCache(demo->device, &pipelineCache, NULL, | |
&demo->pipelineCache); | |
assert(!err); | |
err = vkCreateGraphicsPipelines(demo->device, demo->pipelineCache, 1, | |
&pipeline, NULL, &demo->pipeline); | |
assert(!err); | |
vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL); | |
vkDestroyShaderModule(demo->device, demo->frag_shader_module, NULL); | |
vkDestroyShaderModule(demo->device, demo->vert_shader_module, NULL); | |
} | |
static void demo_prepare_descriptor_pool(struct demo *demo) { | |
const VkDescriptorPoolSize type_count = { | |
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | |
.descriptorCount = DEMO_TEXTURE_COUNT, | |
}; | |
const VkDescriptorPoolCreateInfo descriptor_pool = { | |
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | |
.pNext = NULL, | |
.maxSets = 1, | |
.poolSizeCount = 1, | |
.pPoolSizes = &type_count, | |
}; | |
VkResult U_ASSERT_ONLY err; | |
err = vkCreateDescriptorPool(demo->device, &descriptor_pool, NULL, | |
&demo->desc_pool); | |
assert(!err); | |
} | |
static void demo_prepare_descriptor_set(struct demo *demo) { | |
VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT]; | |
VkWriteDescriptorSet write; | |
VkResult U_ASSERT_ONLY err; | |
uint32_t i; | |
VkDescriptorSetAllocateInfo alloc_info = { | |
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, | |
.pNext = NULL, | |
.descriptorPool = demo->desc_pool, | |
.descriptorSetCount = 1, | |
.pSetLayouts = &demo->desc_layout}; | |
err = vkAllocateDescriptorSets(demo->device, &alloc_info, &demo->desc_set); | |
assert(!err); | |
memset(&tex_descs, 0, sizeof(tex_descs)); | |
for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { | |
tex_descs[i].sampler = demo->textures[i].sampler; | |
tex_descs[i].imageView = demo->textures[i].view; | |
tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; | |
} | |
memset(&write, 0, sizeof(write)); | |
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; | |
write.dstSet = demo->desc_set; | |
write.descriptorCount = DEMO_TEXTURE_COUNT; | |
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; | |
write.pImageInfo = tex_descs; | |
vkUpdateDescriptorSets(demo->device, 1, &write, 0, NULL); | |
} | |
static void demo_prepare_framebuffers(struct demo *demo) { | |
VkImageView attachments[2]; | |
attachments[1] = demo->depth.view; | |
const VkFramebufferCreateInfo fb_info = { | |
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | |
.pNext = NULL, | |
.renderPass = demo->render_pass, | |
.attachmentCount = 2, | |
.pAttachments = attachments, | |
.width = demo->width, | |
.height = demo->height, | |
.layers = 1, | |
}; | |
VkResult U_ASSERT_ONLY err; | |
uint32_t i; | |
demo->framebuffers = (VkFramebuffer *)malloc(demo->swapchainImageCount * | |
sizeof(VkFramebuffer)); | |
assert(demo->framebuffers); | |
for (i = 0; i < demo->swapchainImageCount; i++) { | |
attachments[0] = demo->buffers[i].view; | |
err = vkCreateFramebuffer(demo->device, &fb_info, NULL, | |
&demo->framebuffers[i]); | |
assert(!err); | |
} | |
} | |
static void demo_prepare(struct demo *demo) { | |
VkResult U_ASSERT_ONLY err; | |
const VkCommandPoolCreateInfo cmd_pool_info = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
.pNext = NULL, | |
.queueFamilyIndex = demo->graphics_queue_node_index, | |
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, | |
}; | |
err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL, | |
&demo->cmd_pool); | |
assert(!err); | |
const VkCommandBufferAllocateInfo cmd = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
.pNext = NULL, | |
.commandPool = demo->cmd_pool, | |
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
.commandBufferCount = 1, | |
}; | |
err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->draw_cmd); | |
assert(!err); | |
demo_prepare_buffers(demo); | |
demo_prepare_depth(demo); | |
demo_prepare_textures(demo); | |
demo_prepare_vertices(demo); | |
demo_prepare_descriptor_layout(demo); | |
demo_prepare_render_pass(demo); | |
demo_prepare_pipeline(demo); | |
demo_prepare_descriptor_pool(demo); | |
demo_prepare_descriptor_set(demo); | |
demo_prepare_framebuffers(demo); | |
demo->prepared = true; | |
} | |
/* | |
* Return 1 (true) if all layer names specified in check_names | |
* can be found in given layer properties. | |
*/ | |
static VkBool32 demo_check_layers(uint32_t check_count, char **check_names, | |
uint32_t layer_count, | |
VkLayerProperties *layers) { | |
for (uint32_t i = 0; i < check_count; i++) { | |
VkBool32 found = 0; | |
for (uint32_t j = 0; j < layer_count; j++) { | |
if (!strcmp(check_names[i], layers[j].layerName)) { | |
found = 1; | |
break; | |
} | |
} | |
if (!found) { | |
fprintf(stderr, "Cannot find layer: %s\n", check_names[i]); | |
return 0; | |
} | |
} | |
return 1; | |
} | |
static void demo_init_vk(struct demo *demo) { | |
VkResult err; | |
uint32_t instance_extension_count = 0; | |
char **instance_validation_layers = NULL; | |
demo->enabled_extension_count = 0; | |
/* Look for instance extensions */ | |
VkBool32 surfaceExtFound = 0; | |
VkBool32 platformSurfaceExtFound = 0; | |
memset(demo->extension_names, 0, sizeof(demo->extension_names)); | |
err = vkEnumerateInstanceExtensionProperties( | |
NULL, &instance_extension_count, NULL); | |
assert(!err); | |
if (instance_extension_count > 0) { | |
VkExtensionProperties *instance_extensions = | |
malloc(sizeof(VkExtensionProperties) * instance_extension_count); | |
err = vkEnumerateInstanceExtensionProperties( | |
NULL, &instance_extension_count, instance_extensions); | |
assert(!err); | |
for (uint32_t i = 0; i < instance_extension_count; i++) { | |
if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME, | |
instance_extensions[i].extensionName)) { | |
surfaceExtFound = 1; | |
demo->extension_names[demo->enabled_extension_count++] = | |
VK_KHR_SURFACE_EXTENSION_NAME; | |
} | |
if (!strcmp(PLATFORM_SURFACE_EXTENSION_NAME, | |
instance_extensions[i].extensionName)) { | |
platformSurfaceExtFound = 1; | |
demo->extension_names[demo->enabled_extension_count++] = | |
PLATFORM_SURFACE_EXTENSION_NAME; | |
} | |
assert(demo->enabled_extension_count < 64); | |
} | |
free(instance_extensions); | |
} | |
if (!surfaceExtFound) { | |
ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " | |
"the " VK_KHR_SURFACE_EXTENSION_NAME | |
" 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"); | |
} | |
if (!platformSurfaceExtFound) { | |
ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " | |
"the " PLATFORM_SURFACE_EXTENSION_NAME | |
" 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, | |
.pApplicationName = APP_SHORT_NAME, | |
.applicationVersion = 0, | |
.pEngineName = APP_SHORT_NAME, | |
.engineVersion = 0, | |
.apiVersion = VK_API_VERSION_1_0, | |
}; | |
VkInstanceCreateInfo inst_info = { | |
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
.pNext = NULL, | |
.pApplicationInfo = &app, | |
.enabledLayerCount = 0, // demo->enabled_layer_count, | |
.ppEnabledLayerNames = NULL, // (const char *const *)instance_validation_layers, | |
.enabledExtensionCount = demo->enabled_extension_count, | |
.ppEnabledExtensionNames = (const char *const *)demo->extension_names, | |
}; | |
uint32_t gpu_count; | |
err = vkCreateInstance(&inst_info, NULL, &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 == VK_ERROR_EXTENSION_NOT_PRESENT) { | |
ERR_EXIT("Cannot find a specified extension library" | |
".\nMake sure your layers path is set appropriately\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"); | |
} | |
/* Make initial call to query gpu_count, then second call for gpu info*/ | |
err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL); | |
assert(!err && gpu_count > 0); | |
if (gpu_count > 0) { | |
VkPhysicalDevice *physical_devices = | |
malloc(sizeof(VkPhysicalDevice) * gpu_count); | |
err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, | |
physical_devices); | |
assert(!err); | |
/* For tri demo we just grab the first physical device */ | |
demo->gpu = physical_devices[0]; | |
free(physical_devices); | |
} else { | |
ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices." | |
"\n\nDo you have a compatible Vulkan installable client" | |
" driver (ICD) installed?\nPlease look at the Getting Started" | |
" guide for additional information.\n", | |
"vkEnumeratePhysicalDevices Failure"); | |
} | |
/* Look for device extensions */ | |
uint32_t device_extension_count = 0; | |
VkBool32 swapchainExtFound = 0; | |
demo->enabled_extension_count = 0; | |
memset(demo->extension_names, 0, sizeof(demo->extension_names)); | |
err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL, | |
&device_extension_count, NULL); | |
assert(!err); | |
if (device_extension_count > 0) { | |
VkExtensionProperties *device_extensions = | |
malloc(sizeof(VkExtensionProperties) * device_extension_count); | |
err = vkEnumerateDeviceExtensionProperties( | |
demo->gpu, NULL, &device_extension_count, device_extensions); | |
assert(!err); | |
for (uint32_t i = 0; i < device_extension_count; i++) { | |
if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, | |
device_extensions[i].extensionName)) { | |
swapchainExtFound = 1; | |
demo->extension_names[demo->enabled_extension_count++] = | |
VK_KHR_SWAPCHAIN_EXTENSION_NAME; | |
} | |
assert(demo->enabled_extension_count < 64); | |
} | |
free(device_extensions); | |
} | |
if (!swapchainExtFound) { | |
ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find " | |
"the " VK_KHR_SWAPCHAIN_EXTENSION_NAME | |
" 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"); | |
} | |
vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props); | |
// Query with NULL data to get count | |
vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count, | |
NULL); | |
demo->queue_props = (VkQueueFamilyProperties *)malloc( | |
demo->queue_count * sizeof(VkQueueFamilyProperties)); | |
vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count, | |
demo->queue_props); | |
assert(demo->queue_count >= 1); | |
VkPhysicalDeviceFeatures features; | |
vkGetPhysicalDeviceFeatures(demo->gpu, &features); | |
if (!features.shaderClipDistance) { | |
ERR_EXIT("Required device feature `shaderClipDistance` not supported\n", | |
"GetPhysicalDeviceFeatures failure"); | |
} | |
// Graphics queue and MemMgr queue can be separate. | |
// TODO: Add support for separate queues, including synchronization, | |
// and appropriate tracking for QueueSubmit | |
} | |
static void demo_init_device(struct demo *demo) { | |
VkResult U_ASSERT_ONLY err; | |
float queue_priorities[1] = {0.0}; | |
const VkDeviceQueueCreateInfo queue = { | |
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, | |
.pNext = NULL, | |
.queueFamilyIndex = demo->graphics_queue_node_index, | |
.queueCount = 1, | |
.pQueuePriorities = queue_priorities}; | |
VkPhysicalDeviceFeatures features = { | |
.shaderClipDistance = VK_TRUE, | |
}; | |
VkDeviceCreateInfo device = { | |
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | |
.pNext = NULL, | |
.queueCreateInfoCount = 1, | |
.pQueueCreateInfos = &queue, | |
.enabledLayerCount = 0, | |
.ppEnabledLayerNames = NULL, | |
.enabledExtensionCount = demo->enabled_extension_count, | |
.ppEnabledExtensionNames = (const char *const *)demo->extension_names, | |
.pEnabledFeatures = &features, | |
}; | |
err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device); | |
assert(!err); | |
} | |
static void demo_init_vk_swapchain(struct demo *demo) { | |
VkResult U_ASSERT_ONLY err; | |
uint32_t i; | |
// Create a WSI surface for the window: | |
PlatformSurfaceCreateInfo createInfo; | |
createInfo.sType = PLATFORM_SURFACE_CREATE_INFO; | |
createInfo.pNext = NULL; | |
createInfo.flags = 0; | |
SDL_SysWMinfo info; | |
SDL_VERSION(&info.version); | |
SDL_GetWindowWMInfo(demo->window, &info); | |
PLATFORM_ASSIGN_SURFACEINFO; | |
err = PlatformCreateSurface(demo->inst, &createInfo, NULL, &demo->surface); | |
// Iterate over each queue to learn whether it supports presenting: | |
VkBool32 *supportsPresent = | |
(VkBool32 *)malloc(demo->queue_count * sizeof(VkBool32)); | |
for (i = 0; i < demo->queue_count; i++) { | |
vkGetPhysicalDeviceSurfaceSupportKHR(demo->gpu, i, demo->surface, | |
&supportsPresent[i]); | |
} | |
// Search for a graphics and a present queue in the array of queue | |
// families, try to find one that supports both | |
uint32_t graphicsQueueNodeIndex = UINT32_MAX; | |
uint32_t presentQueueNodeIndex = UINT32_MAX; | |
for (i = 0; i < demo->queue_count; i++) { | |
if ((demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { | |
if (graphicsQueueNodeIndex == UINT32_MAX) { | |
graphicsQueueNodeIndex = i; | |
} | |
if (supportsPresent[i] == VK_TRUE) { | |
graphicsQueueNodeIndex = i; | |
presentQueueNodeIndex = i; | |
break; | |
} | |
} | |
} | |
if (presentQueueNodeIndex == UINT32_MAX) { | |
// If didn't find a queue that supports both graphics and present, then | |
// find a separate present queue. | |
for (uint32_t i = 0; i < demo->queue_count; ++i) { | |
if (supportsPresent[i] == VK_TRUE) { | |
presentQueueNodeIndex = i; | |
break; | |
} | |
} | |
} | |
free(supportsPresent); | |
// Generate error if could not find both a graphics and a present queue | |
if (graphicsQueueNodeIndex == UINT32_MAX || | |
presentQueueNodeIndex == UINT32_MAX) { | |
ERR_EXIT("Could not find a graphics and a present queue\n", | |
"Swapchain Initialization Failure"); | |
} | |
// TODO: Add support for separate queues, including presentation, | |
// synchronization, and appropriate tracking for QueueSubmit. | |
// NOTE: While it is possible for an application to use a separate graphics | |
// and a present queues, this demo program assumes it is only using | |
// one: | |
if (graphicsQueueNodeIndex != presentQueueNodeIndex) { | |
ERR_EXIT("Could not find a common graphics and a present queue\n", | |
"Swapchain Initialization Failure"); | |
} | |
demo->graphics_queue_node_index = graphicsQueueNodeIndex; | |
demo_init_device(demo); | |
vkGetDeviceQueue(demo->device, demo->graphics_queue_node_index, 0, | |
&demo->queue); | |
// Get the list of VkFormat's that are supported: | |
uint32_t formatCount; | |
err = vkGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, | |
&formatCount, NULL); | |
assert(!err); | |
VkSurfaceFormatKHR *surfFormats = | |
(VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); | |
err = vkGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, | |
&formatCount, surfFormats); | |
assert(!err); | |
// If the format list includes just one entry of VK_FORMAT_UNDEFINED, | |
// the surface has no preferred format. Otherwise, at least one | |
// supported format will be returned. | |
if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { | |
demo->format = VK_FORMAT_B8G8R8A8_UNORM; | |
} else { | |
assert(formatCount >= 1); | |
demo->format = surfFormats[0].format; | |
} | |
demo->color_space = surfFormats[0].colorSpace; | |
demo->quit = false; | |
demo->curFrame = 0; | |
// Get Memory information and properties | |
vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties); | |
} | |
static void demo_init(struct demo *demo, const int argc, const char *argv[]) | |
{ | |
memset(demo, 0, sizeof(*demo)); | |
demo->frameCount = INT32_MAX; | |
for (int i = 1; i < argc; i++) { | |
if (strcmp(argv[i], "--use_staging") == 0) { | |
demo->use_staging_buffer = true; | |
continue; | |
} | |
if (strcmp(argv[i], "--break") == 0) { | |
demo->use_break = true; | |
continue; | |
} | |
if (strcmp(argv[i], "--c") == 0 && demo->frameCount == INT32_MAX && | |
i < argc - 1 && sscanf(argv[i + 1], "%d", &demo->frameCount) == 1 && | |
demo->frameCount >= 0) { | |
i++; | |
continue; | |
} | |
fprintf(stderr, "Usage:\n %s [--use_staging] [--break] " | |
"[--c <framecount>]\n", | |
APP_SHORT_NAME); | |
fflush(stderr); | |
exit(1); | |
} | |
demo->width = 300; | |
demo->height = 300; | |
demo->depthStencil = 1.0; | |
demo->depthIncrement = -0.01f; | |
} | |
static void demo_cleanup(struct demo *demo) { | |
uint32_t i; | |
demo->prepared = false; | |
for (i = 0; i < demo->swapchainImageCount; i++) { | |
vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); | |
} | |
free(demo->framebuffers); | |
vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL); | |
if (demo->setup_cmd) { | |
vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd); | |
} | |
vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd); | |
vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL); | |
vkDestroyPipeline(demo->device, demo->pipeline, NULL); | |
vkDestroyRenderPass(demo->device, demo->render_pass, NULL); | |
vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL); | |
vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL); | |
vkDestroyBuffer(demo->device, demo->vertices.buf, NULL); | |
vkFreeMemory(demo->device, demo->vertices.mem, NULL); | |
for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { | |
vkDestroyImageView(demo->device, demo->textures[i].view, NULL); | |
vkDestroyImage(demo->device, demo->textures[i].image, NULL); | |
vkFreeMemory(demo->device, demo->textures[i].mem, NULL); | |
vkDestroySampler(demo->device, demo->textures[i].sampler, NULL); | |
} | |
for (i = 0; i < demo->swapchainImageCount; i++) { | |
vkDestroyImageView(demo->device, demo->buffers[i].view, NULL); | |
} | |
vkDestroyImageView(demo->device, demo->depth.view, NULL); | |
vkDestroyImage(demo->device, demo->depth.image, NULL); | |
vkFreeMemory(demo->device, demo->depth.mem, NULL); | |
vkDestroySwapchainKHR(demo->device, demo->swapchain, NULL); | |
free(demo->buffers); | |
vkDestroyDevice(demo->device, NULL); | |
vkDestroySurfaceKHR(demo->inst, demo->surface, NULL); | |
vkDestroyInstance(demo->inst, NULL); | |
free(demo->queue_props); | |
} | |
static void demo_resize(struct demo *demo) { | |
uint32_t i; | |
// Don't react to resize until after first initialization. | |
if (!demo->prepared) { | |
return; | |
} | |
// In order to properly resize the window, we must re-create the swapchain | |
// AND redo the command buffers, etc. | |
// | |
// First, perform part of the demo_cleanup() function: | |
demo->prepared = false; | |
for (i = 0; i < demo->swapchainImageCount; i++) { | |
vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); | |
} | |
free(demo->framebuffers); | |
vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL); | |
if (demo->setup_cmd) { | |
vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd); | |
} | |
vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd); | |
vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL); | |
vkDestroyPipeline(demo->device, demo->pipeline, NULL); | |
vkDestroyRenderPass(demo->device, demo->render_pass, NULL); | |
vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL); | |
vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL); | |
vkDestroyBuffer(demo->device, demo->vertices.buf, NULL); | |
vkFreeMemory(demo->device, demo->vertices.mem, NULL); | |
for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { | |
vkDestroyImageView(demo->device, demo->textures[i].view, NULL); | |
vkDestroyImage(demo->device, demo->textures[i].image, NULL); | |
vkFreeMemory(demo->device, demo->textures[i].mem, NULL); | |
vkDestroySampler(demo->device, demo->textures[i].sampler, NULL); | |
} | |
for (i = 0; i < demo->swapchainImageCount; i++) { | |
vkDestroyImageView(demo->device, demo->buffers[i].view, NULL); | |
} | |
vkDestroyImageView(demo->device, demo->depth.view, NULL); | |
vkDestroyImage(demo->device, demo->depth.image, NULL); | |
vkFreeMemory(demo->device, demo->depth.mem, NULL); | |
free(demo->buffers); | |
// Second, re-perform the demo_prepare() function, which will re-create the | |
// swapchain: | |
demo_prepare(demo); | |
} | |
int main(const int argc, const char *argv[]) { | |
struct demo demo; | |
SDL_Event evt; | |
demo_init(&demo, argc, argv); | |
demo.window = SDL_CreateWindow( | |
APP_LONG_NAME, | |
SDL_WINDOWPOS_CENTERED, | |
SDL_WINDOWPOS_CENTERED, | |
demo.width, | |
demo.height, | |
SDL_WINDOW_RESIZABLE | |
); | |
demo_init_vk(&demo); | |
demo_init_vk_swapchain(&demo); | |
demo_prepare(&demo); | |
while (!demo.quit) { | |
while (SDL_PollEvent(&evt) == 1) { | |
if (evt.type == SDL_QUIT) | |
demo.quit = true; | |
if (evt.type == SDL_WINDOWEVENT | |
&& evt.window.event == SDL_WINDOWEVENT_RESIZED) { | |
if ((demo.width != evt.window.data1) | |
|| (demo.height != evt.window.data2)) { | |
demo.width = evt.window.data1; | |
demo.height = evt.window.data2; | |
demo_resize(&demo); | |
} | |
} | |
} | |
demo_draw(&demo); | |
if (demo.depthStencil > 0.99f) | |
demo.depthIncrement = -0.001f; | |
if (demo.depthStencil < 0.8f) | |
demo.depthIncrement = 0.001f; | |
demo.depthStencil += demo.depthIncrement; | |
// Wait for work to finish before updating MVP. | |
vkDeviceWaitIdle(demo.device); | |
demo.curFrame++; | |
if (demo.frameCount != INT32_MAX && demo.curFrame == demo.frameCount) | |
demo.quit = true; | |
} | |
demo_cleanup(&demo); | |
SDL_DestroyWindow(demo.window); | |
SDL_Quit(); | |
return validation_error; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30a31 | |
> * Author (SDL2): Ethan Lee <[email protected]> | |
48a50,52 | |
> #include <SDL2/SDL.h> | |
> #include <SDL2/SDL_syswm.h> | |
> | |
52,53c56,73 | |
< #endif // _WIN32 | |
< | |
--- | |
> #define VK_USE_PLATFORM_WIN32_KHR | |
> #define PLATFORM_SURFACE_EXTENSION_NAME VK_KHR_WIN32_SURFACE_EXTENSION_NAME | |
> #define PlatformSurfaceCreateInfo VkWin32SurfaceCreateInfoKHR | |
> #define PLATFORM_SURFACE_CREATE_INFO VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; | |
> #define PLATFORM_ASSIGN_SURFACEINFO \ | |
> createInfo.hinstance = GetModuleHandle(NULL); \ | |
> createInfo.hwnd = info.info.win.window | |
> #define PlatformCreateSurface vkCreateWin32SurfaceKHR | |
> #else /* _WIN32 */ | |
> #define VK_USE_PLATFORM_XLIB_KHR | |
> #define PLATFORM_SURFACE_EXTENSION_NAME VK_KHR_XLIB_SURFACE_EXTENSION_NAME | |
> #define PlatformSurfaceCreateInfo VkXlibSurfaceCreateInfoKHR | |
> #define PLATFORM_SURFACE_CREATE_INFO VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; | |
> #define PLATFORM_ASSIGN_SURFACEINFO \ | |
> createInfo.dpy = info.info.x11.display; \ | |
> createInfo.window = info.info.x11.window | |
> #define PlatformCreateSurface vkCreateXlibSurfaceKHR | |
> #endif /* _WIN32 */ | |
69,76d88 | |
< #ifdef _WIN32 | |
< #define ERR_EXIT(err_msg, err_class) \ | |
< do { \ | |
< MessageBox(NULL, err_msg, err_class, MB_OK); \ | |
< exit(1); \ | |
< } while (0) | |
< #else // _WIN32 | |
< | |
79,80c91 | |
< printf(err_msg); \ | |
< fflush(stdout); \ | |
--- | |
> SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, APP_LONG_NAME, err_msg, NULL); \ | |
83d93 | |
< #endif // _WIN32 | |
138,143c148,149 | |
< #ifdef _WIN32 | |
< MessageBox(NULL, message, "Alert", MB_OK); | |
< #else | |
< printf("%s\n", message); | |
< fflush(stdout); | |
< #endif | |
--- | |
> SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, APP_LONG_NAME, message, NULL); | |
> | |
161d166 | |
< #ifndef WIN32 | |
163,165d167 | |
< #else | |
< DebugBreak(); | |
< #endif | |
177,187c179 | |
< #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 | |
--- | |
> SDL_Window *window; | |
1523,1722d1514 | |
< #ifdef _WIN32 | |
< static void demo_run(struct demo *demo) { | |
< if (!demo->prepared) | |
< return; | |
< demo_draw(demo); | |
< | |
< if (demo->depthStencil > 0.99f) | |
< demo->depthIncrement = -0.001f; | |
< if (demo->depthStencil < 0.8f) | |
< demo->depthIncrement = 0.001f; | |
< | |
< demo->depthStencil += demo->depthIncrement; | |
< | |
< demo->curFrame++; | |
< if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) { | |
< PostQuitMessage(validation_error); | |
< } | |
< } | |
< | |
< // 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: | |
< PostQuitMessage(validation_error); | |
< return 0; | |
< case WM_PAINT: | |
< if (demo.prepared) { | |
< demo_run(&demo); | |
< break; | |
< } | |
< case WM_SIZE: | |
< // Resize the application to the new window size, except when | |
< // it was minimized. Vulkan doesn't support images or swapchains | |
< // with width=0 and height=0. | |
< if (wParam != SIZE_MINIMIZED) { | |
< demo.width = lParam & 0xffff; | |
< demo.height = lParam & 0xffff0000 >> 16; | |
< demo_resize(&demo); | |
< } | |
< break; | |
< 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); | |
< exit(1); | |
< } | |
< // Create window with the registered class: | |
< RECT wr = {0, 0, demo->width, demo->height}; | |
< AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); | |
< 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 | |
< wr.right - wr.left, // width | |
< wr.bottom - wr.top, // 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); | |
< 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; | |
< case XCB_CONFIGURE_NOTIFY: { | |
< const xcb_configure_notify_event_t *cfg = | |
< (const xcb_configure_notify_event_t *)event; | |
< if ((demo->width != cfg->width) || (demo->height != cfg->height)) { | |
< demo->width = cfg->width; | |
< demo->height = cfg->height; | |
< demo_resize(demo); | |
< } | |
< } break; | |
< default: | |
< break; | |
< } | |
< } | |
< | |
< static void demo_run(struct demo *demo) { | |
< xcb_flush(demo->connection); | |
< | |
< while (!demo->quit) { | |
< xcb_generic_event_t *event; | |
< | |
< event = xcb_poll_for_event(demo->connection); | |
< if (event) { | |
< demo_handle_event(demo, event); | |
< free(event); | |
< } | |
< | |
< demo_draw(demo); | |
< | |
< if (demo->depthStencil > 0.99f) | |
< demo->depthIncrement = -0.001f; | |
< if (demo->depthStencil < 0.8f) | |
< demo->depthIncrement = 0.001f; | |
< | |
< demo->depthStencil += demo->depthIncrement; | |
< | |
< // Wait for work to finish before updating MVP. | |
< vkDeviceWaitIdle(demo->device); | |
< demo->curFrame++; | |
< if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) | |
< demo->quit = true; | |
< } | |
< } | |
< | |
< 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 | |
< | |
1839,1840c1631 | |
< #ifdef _WIN32 | |
< if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, | |
--- | |
> if (!strcmp(PLATFORM_SURFACE_EXTENSION_NAME, | |
1844c1635 | |
< VK_KHR_WIN32_SURFACE_EXTENSION_NAME; | |
--- | |
> PLATFORM_SURFACE_EXTENSION_NAME; | |
1846,1853d1636 | |
< #else // _WIN32 | |
< if (!strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, | |
< instance_extensions[i].extensionName)) { | |
< platformSurfaceExtFound = 1; | |
< demo->extension_names[demo->enabled_extension_count++] = | |
< VK_KHR_XCB_SURFACE_EXTENSION_NAME; | |
< } | |
< #endif // _WIN32 | |
1877d1659 | |
< #ifdef _WIN32 | |
1879c1661 | |
< "the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME | |
--- | |
> "the " PLATFORM_SURFACE_EXTENSION_NAME | |
1885,1893d1666 | |
< #else // _WIN32 | |
< ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " | |
< "the " VK_KHR_XCB_SURFACE_EXTENSION_NAME | |
< " 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"); | |
< #endif // _WIN32 | |
2153,2155c1926,1927 | |
< #ifdef _WIN32 | |
< VkWin32SurfaceCreateInfoKHR createInfo; | |
< createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; | |
--- | |
> PlatformSurfaceCreateInfo createInfo; | |
> createInfo.sType = PLATFORM_SURFACE_CREATE_INFO; | |
2158,2173c1930,1934 | |
< createInfo.hinstance = demo->connection; | |
< createInfo.hwnd = demo->window; | |
< | |
< err = | |
< vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface); | |
< | |
< #else // _WIN32 | |
< VkXcbSurfaceCreateInfoKHR createInfo; | |
< createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; | |
< createInfo.pNext = NULL; | |
< createInfo.flags = 0; | |
< createInfo.connection = demo->connection; | |
< createInfo.window = demo->window; | |
< | |
< err = vkCreateXcbSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface); | |
< #endif // _WIN32 | |
--- | |
> SDL_SysWMinfo info; | |
> SDL_VERSION(&info.version); | |
> SDL_GetWindowWMInfo(demo->window, &info); | |
> PLATFORM_ASSIGN_SURFACEINFO; | |
> err = PlatformCreateSurface(demo->inst, &createInfo, NULL, &demo->surface); | |
2264,2286d2024 | |
< 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 | |
< } | |
< | |
2319d2056 | |
< demo_init_connection(demo); | |
2379,2384d2115 | |
< | |
< #ifndef _WIN32 | |
< xcb_destroy_window(demo->connection, demo->window); | |
< xcb_disconnect(demo->connection); | |
< free(demo->atom_wm_delete_window); | |
< #endif // _WIN32 | |
2440a2172,2174 | |
> int main(const int argc, const char *argv[]) { | |
> struct demo demo; | |
> SDL_Event evt; | |
2442,2444c2176,2185 | |
< #ifdef _WIN32 | |
< // Include header required for parsing the command line options. | |
< #include <shellapi.h> | |
--- | |
> demo_init(&demo, argc, argv); | |
> demo.window = SDL_CreateWindow( | |
> APP_LONG_NAME, | |
> SDL_WINDOWPOS_CENTERED, | |
> SDL_WINDOWPOS_CENTERED, | |
> demo.width, | |
> demo.height, | |
> SDL_WINDOW_RESIZABLE | |
> ); | |
> demo_init_vk_swapchain(&demo); | |
2446,2476c2187,2198 | |
< int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | |
< LPSTR pCmdLine, int nCmdShow) { | |
< MSG msg; // message | |
< bool done; // flag saying when app is complete | |
< int argc; | |
< char **argv; | |
< | |
< // Use the CommandLine functions to get the command line arguments. | |
< // Unfortunately, Microsoft outputs | |
< // this information as wide characters for Unicode, and we simply want the | |
< // Ascii version to be compatible | |
< // with the non-Windows side. So, we have to convert the information to | |
< // Ascii character strings. | |
< LPWSTR *commandLineArgs = CommandLineToArgvW(GetCommandLineW(), &argc); | |
< if (NULL == commandLineArgs) { | |
< argc = 0; | |
< } | |
< | |
< if (argc > 0) { | |
< argv = (char **)malloc(sizeof(char *) * argc); | |
< if (argv == NULL) { | |
< argc = 0; | |
< } else { | |
< for (int iii = 0; iii < argc; iii++) { | |
< size_t wideCharLen = wcslen(commandLineArgs[iii]); | |
< size_t numConverted = 0; | |
< | |
< argv[iii] = (char *)malloc(sizeof(char) * (wideCharLen + 1)); | |
< if (argv[iii] != NULL) { | |
< wcstombs_s(&numConverted, argv[iii], wideCharLen + 1, | |
< commandLineArgs[iii], wideCharLen + 1); | |
--- | |
> demo_prepare(&demo); | |
> while (!demo.quit) { | |
> while (SDL_PollEvent(&evt) == 1) { | |
> if (evt.type == SDL_QUIT) | |
> demo.quit = true; | |
> if (evt.type == SDL_WINDOWEVENT | |
> && evt.window.event == SDL_WINDOWEVENT_RESIZED) { | |
> if ((demo.width != evt.window.data1) | |
> || (demo.height != evt.window.data2)) { | |
> demo.width = evt.window.data1; | |
> demo.height = evt.window.data2; | |
> demo_resize(&demo); | |
2480,2482d2201 | |
< } else { | |
< argv = NULL; | |
< } | |
2484c2203 | |
< demo_init(&demo, argc, argv); | |
--- | |
> demo_draw(&demo); | |
2486,2499c2205,2208 | |
< // Free up the items we had to allocate for the command line arguments. | |
< if (argc > 0 && argv != NULL) { | |
< for (int iii = 0; iii < argc; iii++) { | |
< if (argv[iii] != NULL) { | |
< free(argv[iii]); | |
< } | |
< } | |
< free(argv); | |
< } | |
< | |
< demo.connection = hInstance; | |
< strncpy(demo.name, "tri", APP_NAME_STR_LEN); | |
< demo_create_window(&demo); | |
< demo_init_vk_swapchain(&demo); | |
--- | |
> if (demo.depthStencil > 0.99f) | |
> demo.depthIncrement = -0.001f; | |
> if (demo.depthStencil < 0.8f) | |
> demo.depthIncrement = 0.001f; | |
2501c2210 | |
< demo_prepare(&demo); | |
--- | |
> demo.depthStencil += demo.depthIncrement; | |
2503,2515c2212,2216 | |
< 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); | |
< } | |
< RedrawWindow(demo.window, NULL, NULL, RDW_INTERNALPAINT); | |
--- | |
> // Wait for work to finish before updating MVP. | |
> vkDeviceWaitIdle(demo.device); | |
> demo.curFrame++; | |
> if (demo.frameCount != INT32_MAX && demo.curFrame == demo.frameCount) | |
> demo.quit = true; | |
2520,2533c2221,2222 | |
< 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_init_vk_swapchain(&demo); | |
< | |
< demo_prepare(&demo); | |
< demo_run(&demo); | |
< | |
< demo_cleanup(&demo); | |
--- | |
> SDL_DestroyWindow(demo.window); | |
> SDL_Quit(); | |
2537d2225 | |
< #endif // _WIN32 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment