diff --git a/tests/vulkan.c b/tests/vulkan.c index 1b62d681..bbfcb17d 100644 --- a/tests/vulkan.c +++ b/tests/vulkan.c @@ -3,24 +3,17 @@ * 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: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The above copyright notice(s) and this permission notice shall be included in - * all copies or substantial portions of the Materials. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * Author: Chia-I Wu * Author: Cody Northrop @@ -28,6 +21,8 @@ * Author: Ian Elliott * Author: Jon Ashburn * Author: Piers Daniell + * Author: Gwan-gyeong Mun + * Porter: Camilla Berglund */ /* * Draw a textured triangle with depth testing. This is written against Intel @@ -35,22 +30,24 @@ * should. It also does no error checking. */ -#ifndef _MSC_VER -#define _ISOC11_SOURCE /* for aligned_alloc() */ -#endif - #include #include #include #include #include +#include -#include +#ifdef _WIN32 +#include +#endif + +#define GLFW_INCLUDE_NONE +#define GLFW_INCLUDE_VULKAN #include #define DEMO_TEXTURE_COUNT 1 #define VERTEX_BUFFER_BIND_ID 0 -#define APP_SHORT_NAME "vulkan" +#define APP_SHORT_NAME "tri" #define APP_LONG_NAME "The Vulkan Triangle Demo Program" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -245,39 +242,23 @@ struct texture_object { 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); +BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, size_t location, int32_t msgCode, + const char *pLayerPrefix, const char *pMsg, + void *pUserData) { +#ifdef _WIN32 + DebugBreak(); +#else + raise(SIGTRAP); +#endif - assert(message); - - 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; - } - - printf("%s\n", message); - fflush(stdout); - 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; } -typedef struct _SwapchainBuffers { +typedef struct { VkImage image; VkCommandBuffer cmd; VkImageView view; @@ -288,20 +269,19 @@ struct demo { VkSurfaceKHR surface; bool use_staging_buffer; - VkAllocationCallbacks allocator; - VkInstance inst; VkPhysicalDevice gpu; VkDevice device; VkQueue queue; VkPhysicalDeviceProperties gpu_props; + VkPhysicalDeviceFeatures gpu_features; VkQueueFamilyProperties *queue_props; uint32_t graphics_queue_node_index; uint32_t enabled_extension_count; uint32_t enabled_layer_count; const char *extension_names[64]; - char *device_validation_layers[64]; + const char *enabled_layers[64]; int width, height; VkFormat format; @@ -363,10 +343,14 @@ struct demo { VkPhysicalDeviceMemoryProperties memory_properties; + int32_t curFrame; + int32_t frameCount; bool validate; + bool use_break; PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback; PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback; VkDebugReportCallbackEXT msg_callback; + PFN_vkDebugReportMessageEXT DebugReportMessage; float depthStencil; float depthIncrement; @@ -375,6 +359,40 @@ struct demo { uint32_t queue_count; }; +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; + } + + printf("%s\n", message); + fflush(stdout); + 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; +} + // Forward declaration: static void demo_resize(struct demo *demo); @@ -382,9 +400,8 @@ static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex) { uint32_t i; - // Search memtypes to find first index with those properties - for (i = 0; i < 32; i++) { + for (i = 0; i < VK_MAX_MEMORY_TYPES; i++) { if ((typeBits & 1) == 1) { // Type is available, does it match user properties? if ((demo->memory_properties.memoryTypes[i].propertyFlags & @@ -433,7 +450,9 @@ static void demo_flush_init_cmd(struct demo *demo) { static void demo_set_image_layout(struct demo *demo, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, - VkImageLayout new_image_layout) { + VkImageLayout new_image_layout, + VkAccessFlagBits srcAccessMask) { + VkResult U_ASSERT_ONLY err; if (demo->setup_cmd == VK_NULL_HANDLE) { @@ -448,21 +467,11 @@ static void demo_set_image_layout(struct demo *demo, VkImage image, 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, + .pInheritanceInfo = NULL, }; err = vkBeginCommandBuffer(demo->setup_cmd, &cmd_buf_info); assert(!err); @@ -471,7 +480,7 @@ static void demo_set_image_layout(struct demo *demo, VkImage image, VkImageMemoryBarrier image_memory_barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = NULL, - .srcAccessMask = 0, + .srcAccessMask = srcAccessMask, .dstAccessMask = 0, .oldLayout = old_image_layout, .newLayout = new_image_layout, @@ -509,21 +518,11 @@ static void demo_set_image_layout(struct demo *demo, VkImage image, } 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, + .pInheritanceInfo = NULL, }; const VkClearValue clear_values[2] = { [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}}, @@ -546,6 +545,23 @@ static void demo_draw_build_cmd(struct demo *demo) { err = vkBeginCommandBuffer(demo->draw_cmd, &cmd_buf_info); assert(!err); + // We can use LAYOUT_UNDEFINED as a wildcard here because we don't care what + // happens to the previous contents of the image + VkImageMemoryBarrier image_memory_barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = NULL, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = demo->buffers[demo->current_buffer].image, + .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; + + vkCmdPipelineBarrier(demo->draw_cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, + NULL, 1, &image_memory_barrier); vkCmdBeginRenderPass(demo->draw_cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, demo->pipeline); @@ -599,20 +615,24 @@ static void demo_draw_build_cmd(struct demo *demo) { static void demo_draw(struct demo *demo) { VkResult U_ASSERT_ONLY err; - VkSemaphore presentCompleteSemaphore; - VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = { + VkSemaphore imageAcquiredSemaphore, drawCompleteSemaphore; + VkSemaphoreCreateInfo semaphoreCreateInfo = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = NULL, .flags = 0, }; - err = vkCreateSemaphore(demo->device, &presentCompleteSemaphoreCreateInfo, - NULL, &presentCompleteSemaphore); + err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, + NULL, &imageAcquiredSemaphore); + assert(!err); + + err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, + NULL, &drawCompleteSemaphore); assert(!err); // Get the index of the next available swapchain image: err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX, - presentCompleteSemaphore, + imageAcquiredSemaphore, (VkFence)0, // TODO: Show use of fence &demo->current_buffer); if (err == VK_ERROR_OUT_OF_DATE_KHR) { @@ -620,7 +640,8 @@ static void demo_draw(struct demo *demo) { // must be recreated: demo_resize(demo); demo_draw(demo); - vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); + vkDestroySemaphore(demo->device, imageAcquiredSemaphore, NULL); + vkDestroySemaphore(demo->device, drawCompleteSemaphore, NULL); return; } else if (err == VK_SUBOPTIMAL_KHR) { // demo->swapchain is not as optimal as it could be, but the platform's @@ -629,12 +650,6 @@ static void demo_draw(struct demo *demo) { 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); demo_flush_init_cmd(demo); // Wait for the present complete semaphore to be signaled to ensure @@ -642,7 +657,6 @@ static void demo_draw(struct demo *demo) { // 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 = @@ -650,12 +664,12 @@ static void demo_draw(struct demo *demo) { VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = NULL, .waitSemaphoreCount = 1, - .pWaitSemaphores = &presentCompleteSemaphore, + .pWaitSemaphores = &imageAcquiredSemaphore, .pWaitDstStageMask = &pipe_stage_flags, .commandBufferCount = 1, .pCommandBuffers = &demo->draw_cmd, - .signalSemaphoreCount = 0, - .pSignalSemaphores = NULL}; + .signalSemaphoreCount = 1, + .pSignalSemaphores = &drawCompleteSemaphore}; err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence); assert(!err); @@ -663,12 +677,13 @@ static void demo_draw(struct demo *demo) { VkPresentInfoKHR present = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = NULL, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &drawCompleteSemaphore, .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); if (err == VK_ERROR_OUT_OF_DATE_KHR) { // demo->swapchain is out of date (e.g. the window was resized) and @@ -684,7 +699,8 @@ static void demo_draw(struct demo *demo) { err = vkQueueWaitIdle(demo->queue); assert(err == VK_SUCCESS); - vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); + vkDestroySemaphore(demo->device, imageAcquiredSemaphore, NULL); + vkDestroySemaphore(demo->device, drawCompleteSemaphore, NULL); } static void demo_prepare_buffers(struct demo *demo) { @@ -709,12 +725,25 @@ static void demo_prepare_buffers(struct demo *demo) { 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. + // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. + if (surfCapabilities.currentExtent.width == 0xFFFFFFFF) { + // If the surface size is undefined, the size is set to the size + // of the images requested, which must fit within the minimum and + // maximum values. swapchainExtent.width = demo->width; swapchainExtent.height = demo->height; + + if (swapchainExtent.width < surfCapabilities.minImageExtent.width) { + swapchainExtent.width = surfCapabilities.minImageExtent.width; + } else if (swapchainExtent.width > surfCapabilities.maxImageExtent.width) { + swapchainExtent.width = surfCapabilities.maxImageExtent.width; + } + + if (swapchainExtent.height < surfCapabilities.minImageExtent.height) { + swapchainExtent.height = surfCapabilities.minImageExtent.height; + } else if (swapchainExtent.height > surfCapabilities.maxImageExtent.height) { + swapchainExtent.height = surfCapabilities.maxImageExtent.height; + } } else { // If the surface size is defined, the swap chain size must match swapchainExtent = surfCapabilities.currentExtent; @@ -724,15 +753,16 @@ static void demo_prepare_buffers(struct demo *demo) { 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; + // Determine the number of VkImage's to use in the swap chain. + // Application desires to only acquire 1 image at a time (which is + // "surfCapabilities.minImageCount"). + uint32_t desiredNumOfSwapchainImages = surfCapabilities.minImageCount; + // If maxImageCount is 0, we can ask for as many images as we want; + // otherwise we're limited to maxImageCount if ((surfCapabilities.maxImageCount > 0) && - (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) { + (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) { // Application must settle for fewer images than desired: - desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount; + desiredNumOfSwapchainImages = surfCapabilities.maxImageCount; } VkSurfaceTransformFlagsKHR preTransform; @@ -747,7 +777,7 @@ static void demo_prepare_buffers(struct demo *demo) { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .pNext = NULL, .surface = demo->surface, - .minImageCount = desiredNumberOfSwapchainImages, + .minImageCount = desiredNumOfSwapchainImages, .imageFormat = demo->format, .imageColorSpace = demo->color_space, .imageExtent = @@ -818,14 +848,6 @@ static void demo_prepare_buffers(struct demo *demo) { 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); - color_attachment_view.image = demo->buffers[i].image; err = vkCreateImageView(demo->device, &color_attachment_view, NULL, @@ -906,7 +928,8 @@ static void demo_prepare_depth(struct demo *demo) { demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + 0); /* create image view */ view.image = demo->depth.image; @@ -939,6 +962,7 @@ demo_prepare_texture_image(struct demo *demo, const uint32_t *tex_colors, .tiling = tiling, .usage = usage, .flags = 0, + .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED }; VkMemoryAllocateInfo mem_alloc = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, @@ -997,7 +1021,8 @@ demo_prepare_texture_image(struct demo *demo, const uint32_t *tex_colors, 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_UNDEFINED, tex_obj->imageLayout); + 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 */ } @@ -1025,20 +1050,22 @@ static void demo_prepare_textures(struct demo *demo) { 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); + 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 | + VK_MEMORY_PROPERTY_HOST_COHERENT_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], &staging_texture, VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); demo_prepare_texture_image( demo, tex_colors[i], &demo->textures[i], @@ -1049,12 +1076,14 @@ static void demo_prepare_textures(struct demo *demo) { demo_set_image_layout(demo, staging_texture.image, VK_IMAGE_ASPECT_COLOR_BIT, staging_texture.imageLayout, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + 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); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0); VkImageCopy copy_region = { .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, @@ -1072,7 +1101,8 @@ static void demo_prepare_textures(struct demo *demo) { demo_set_image_layout(demo, demo->textures[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - demo->textures[i].imageLayout); + demo->textures[i].imageLayout, + 0); demo_flush_init_cmd(demo); @@ -1165,7 +1195,8 @@ static void demo_prepare_vertices(struct demo *demo) { mem_alloc.allocationSize = mem_reqs.size; pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &mem_alloc.memoryTypeIndex); assert(pass); @@ -1376,6 +1407,7 @@ static void demo_prepare_pipeline(struct demo *demo) { rs.depthClampEnable = VK_FALSE; rs.rasterizerDiscardEnable = VK_FALSE; rs.depthBiasEnable = VK_FALSE; + rs.lineWidth = 1.0f; memset(&cb, 0, sizeof(cb)); cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; @@ -1608,6 +1640,9 @@ static void demo_run(struct demo *demo) { // Wait for work to finish before updating MVP. vkDeviceWaitIdle(demo->device); + demo->curFrame++; + if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) + glfwSetWindowShouldClose(demo->window, GLFW_TRUE); } } @@ -1636,7 +1671,7 @@ static void demo_create_window(struct demo *demo) { * 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, +static VkBool32 demo_check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers) { uint32_t i, j; @@ -1656,79 +1691,77 @@ static VkBool32 demo_check_layers(uint32_t check_count, char **check_names, return 1; } -VKAPI_ATTR void *VKAPI_CALL myrealloc(void *pUserData, void *pOriginal, - size_t size, size_t alignment, - VkSystemAllocationScope allocationScope) { - return realloc(pOriginal, size); -} - -VKAPI_ATTR void *VKAPI_CALL myalloc(void *pUserData, size_t size, - size_t alignment, - VkSystemAllocationScope allocationScope) { -#ifdef _MSC_VER - return _aligned_malloc(size, alignment); -#else - return aligned_alloc(alignment, size); -#endif -} - -VKAPI_ATTR void VKAPI_CALL myfree(void *pUserData, void *pMemory) { -#ifdef _MSC_VER - _aligned_free(pMemory); -#else - free(pMemory); -#endif -} - static void demo_init_vk(struct demo *demo) { VkResult err; - uint32_t required_extension_count; - const char** required_extensions; - uint32_t i; + uint32_t i = 0; + uint32_t required_extension_count = 0; uint32_t instance_extension_count = 0; uint32_t instance_layer_count = 0; - uint32_t device_validation_layer_count = 0; + uint32_t validation_layer_count = 0; + const char **required_extensions = NULL; + const char **instance_validation_layers = NULL; demo->enabled_extension_count = 0; demo->enabled_layer_count = 0; - char *instance_validation_layers[] = { - "VK_LAYER_LUNARG_mem_tracker", - "VK_LAYER_GOOGLE_unique_objects", + char *instance_validation_layers_alt1[] = { + "VK_LAYER_LUNARG_standard_validation" }; - demo->device_validation_layers[0] = "VK_LAYER_LUNARG_mem_tracker"; - demo->device_validation_layers[1] = "VK_LAYER_GOOGLE_unique_objects"; - device_validation_layer_count = 2; + char *instance_validation_layers_alt2[] = { + "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", + "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_image", + "VK_LAYER_LUNARG_core_validation", "VK_LAYER_LUNARG_swapchain", + "VK_LAYER_GOOGLE_unique_objects" + }; /* Look for validation layers */ VkBool32 validation_found = 0; - err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); - assert(!err); + if (demo->validate) { - if (instance_layer_count > 0) { - VkLayerProperties *instance_layers = - malloc(sizeof(VkLayerProperties) * instance_layer_count); - err = vkEnumerateInstanceLayerProperties(&instance_layer_count, - instance_layers); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); assert(!err); - if (demo->validate) { + instance_validation_layers = instance_validation_layers_alt1; + if (instance_layer_count > 0) { + VkLayerProperties *instance_layers = + malloc(sizeof (VkLayerProperties) * instance_layer_count); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, + instance_layers); + assert(!err); + + validation_found = demo_check_layers( - ARRAY_SIZE(instance_validation_layers), - instance_validation_layers, instance_layer_count, - instance_layers); - demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers); + ARRAY_SIZE(instance_validation_layers_alt1), + instance_validation_layers, instance_layer_count, + instance_layers); + if (validation_found) { + demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt1); + demo->enabled_layers[0] = "VK_LAYER_LUNARG_standard_validation"; + validation_layer_count = 1; + } else { + // use alternative set of validation layers + instance_validation_layers = instance_validation_layers_alt2; + demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt2); + validation_found = demo_check_layers( + ARRAY_SIZE(instance_validation_layers_alt2), + instance_validation_layers, instance_layer_count, + instance_layers); + validation_layer_count = + ARRAY_SIZE(instance_validation_layers_alt2); + for (i = 0; i < validation_layer_count; i++) { + demo->enabled_layers[i] = instance_validation_layers[i]; + } + } + free(instance_layers); } - free(instance_layers); - } - - if (demo->validate && !validation_found) { - ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find" - "required validation layer.\n\n" - "Please look at the Getting Started guide for additional " - "information.\n", - "vkCreateInstance Failure"); + if (!validation_found) { + ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find " + "required validation layer.\n\n" + "Please look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } } /* Look for instance extensions */ @@ -1792,11 +1825,7 @@ static void demo_init_vk(struct demo *demo) { uint32_t gpu_count; - demo->allocator.pfnAllocation = myalloc; - demo->allocator.pfnFree = myfree; - demo->allocator.pfnReallocation = myrealloc; - - err = vkCreateInstance(&inst_info, &demo->allocator, &demo->inst); + 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 " @@ -1834,40 +1863,6 @@ static void demo_init_vk(struct demo *demo) { "vkEnumeratePhysicalDevices Failure"); } - /* Look for validation layers */ - validation_found = 0; - demo->enabled_layer_count = 0; - uint32_t device_layer_count = 0; - err = - vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, NULL); - assert(!err); - - if (device_layer_count > 0) { - VkLayerProperties *device_layers = - malloc(sizeof(VkLayerProperties) * device_layer_count); - err = vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, - device_layers); - assert(!err); - - if (demo->validate) { - validation_found = demo_check_layers(device_validation_layer_count, - demo->device_validation_layers, - device_layer_count, - device_layers); - demo->enabled_layer_count = device_validation_layer_count; - } - - free(device_layers); - } - - if (demo->validate && !validation_found) { - ERR_EXIT("vkEnumerateDeviceLayerProperties failed to find " - "a required validation layer.\n\n" - "Please look at the Getting Started guide for additional " - "information.\n", - "vkCreateDevice Failure"); - } - /* Look for device extensions */ uint32_t device_extension_count = 0; VkBool32 swapchainExtFound = 0; @@ -1911,17 +1906,33 @@ static void demo_init_vk(struct demo *demo) { demo->CreateDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr( demo->inst, "vkCreateDebugReportCallbackEXT"); + demo->DestroyDebugReportCallback = + (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr( + demo->inst, "vkDestroyDebugReportCallbackEXT"); if (!demo->CreateDebugReportCallback) { ERR_EXIT( "GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n", "vkGetProcAddr Failure"); } + if (!demo->DestroyDebugReportCallback) { + ERR_EXIT( + "GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT\n", + "vkGetProcAddr Failure"); + } + demo->DebugReportMessage = + (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr( + demo->inst, "vkDebugReportMessageEXT"); + if (!demo->DebugReportMessage) { + ERR_EXIT("GetProcAddr: Unable to find vkDebugReportMessageEXT\n", + "vkGetProcAddr Failure"); + } + VkDebugReportCallbackCreateInfoEXT dbgCreateInfo; dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - dbgCreateInfo.pfnCallback = dbgFunc; - dbgCreateInfo.pUserData = NULL; + dbgCreateInfo.pfnCallback = demo->use_break ? BreakCallback : dbgFunc; + dbgCreateInfo.pUserData = demo; dbgCreateInfo.pNext = NULL; err = demo->CreateDebugReportCallback(demo->inst, &dbgCreateInfo, NULL, &demo->msg_callback); @@ -1945,11 +1956,6 @@ static void demo_init_vk(struct demo *demo) { GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceFormatsKHR); GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfacePresentModesKHR); GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceSupportKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, CreateSwapchainKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, DestroySwapchainKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, GetSwapchainImagesKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, AcquireNextImageKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, QueuePresentKHR); vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props); @@ -1963,6 +1969,8 @@ static void demo_init_vk(struct demo *demo) { demo->queue_props); assert(demo->queue_count >= 1); + vkGetPhysicalDeviceFeatures(demo->gpu, &demo->gpu_features); + // Graphics queue and MemMgr queue can be separate. // TODO: Add support for separate queues, including synchronization, // and appropriate tracking for QueueSubmit @@ -1979,18 +1987,23 @@ static void demo_init_device(struct demo *demo) { .queueCount = 1, .pQueuePriorities = queue_priorities}; + + VkPhysicalDeviceFeatures features; + memset(&features, 0, sizeof(features)); + if (demo->gpu_features.shaderClipDistance) { + features.shaderClipDistance = VK_TRUE; + } + VkDeviceCreateInfo device = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = NULL, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queue, - .enabledLayerCount = demo->enabled_layer_count, - .ppEnabledLayerNames = - (const char *const *)((demo->validate) - ? demo->device_validation_layers - : NULL), + .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); @@ -2092,6 +2105,8 @@ static void demo_init_vk_swapchain(struct demo *demo) { } demo->color_space = surfFormats[0].colorSpace; + demo->curFrame = 0; + // Get Memory information and properties vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties); } @@ -2115,12 +2130,34 @@ static void demo_init_connection(struct demo *demo) { static void demo_init(struct demo *demo, const int argc, const char *argv[]) { int i; - memset(demo, 0, sizeof(*demo)); + demo->frameCount = INT32_MAX; - for (i = 0; i < argc; i++) { - if (strncmp(argv[i], "--use_staging", strlen("--use_staging")) == 0) + for (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], "--validate") == 0) { + demo->validate = 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] [--validate] [--break] " + "[--c ]\n", + APP_SHORT_NAME); + fflush(stderr); + exit(1); } demo_init_connection(demo); @@ -2174,8 +2211,11 @@ static void demo_cleanup(struct demo *demo) { free(demo->buffers); vkDestroyDevice(demo->device, NULL); + if (demo->validate) { + demo->DestroyDebugReportCallback(demo->inst, demo->msg_callback, NULL); + } vkDestroySurfaceKHR(demo->inst, demo->surface, NULL); - vkDestroyInstance(demo->inst, &demo->allocator); + vkDestroyInstance(demo->inst, NULL); free(demo->queue_props); @@ -2186,6 +2226,11 @@ static void demo_cleanup(struct demo *demo) { static void demo_resize(struct demo *demo) { uint32_t i; + // 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: + for (i = 0; i < demo->swapchainImageCount; i++) { vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); } @@ -2240,6 +2285,6 @@ int main(const int argc, const char *argv[]) { demo_cleanup(&demo); - return 0; + return validation_error; }