Created
September 2, 2016 07:00
-
-
Save jwollen/7ca379980a0613a439e2b77adf22c337 to your computer and use it in GitHub Desktop.
SubmissionTest
This file contains 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. | |
* | |
* 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 | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* 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 <olvaffe@gmail.com> | |
* Author: Cody Northrop <cody@lunarg.com> | |
* Author: Courtney Goeltzenleuchter <courtney@LunarG.com> | |
* Author: Ian Elliott <ian@LunarG.com> | |
* Author: Jon Ashburn <jon@lunarg.com> | |
* Author: Piers Daniell <pdaniell@nvidia.com> | |
* Author: Gwan-gyeong Mun <elongbug@gmail.com> | |
*/ | |
/* | |
* 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. | |
*/ | |
#define NO_SUBMISSIONS | |
#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 <time.h> | |
#ifdef _WIN32 | |
#pragma comment(linker, "/subsystem:windows") | |
#define APP_NAME_STR_LEN 80 | |
#endif // _WIN32 | |
#ifdef ANDROID | |
#include "vulkan_wrapper.h" | |
#else | |
#include <vulkan/vulkan.h> | |
#endif | |
#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 | |
#if defined(__GNUC__) | |
#define UNUSED __attribute__((unused)) | |
#else | |
#define UNUSED | |
#endif | |
#ifdef _WIN32 | |
#define ERR_EXIT(err_msg, err_class) \ | |
do { \ | |
if (!demo->suppress_popups) \ | |
MessageBox(NULL, err_msg, err_class, MB_OK); \ | |
exit(1); \ | |
} while (0) | |
#elif defined __ANDROID__ | |
#include <android/log.h> | |
#define ERR_EXIT(err_msg, err_class) \ | |
do { \ | |
((void)__android_log_print(ANDROID_LOG_INFO, "Tri", err_msg)); \ | |
exit(1); \ | |
} while (0) | |
#else | |
#define ERR_EXIT(err_msg, err_class) \ | |
do { \ | |
printf(err_msg); \ | |
fflush(stdout); \ | |
exit(1); \ | |
} while (0) | |
#endif | |
#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 | |
BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, | |
uint64_t srcObject, size_t location, int32_t msgCode, | |
const char *pLayerPrefix, const char *pMsg, | |
void *pUserData) { | |
#ifndef WIN32 | |
raise(SIGTRAP); | |
#else | |
DebugBreak(); | |
#endif | |
return false; | |
} | |
typedef struct { | |
VkImage image; | |
VkCommandBuffer cmd; | |
VkImageView view; | |
} SwapchainBuffers; | |
struct demo { | |
#if defined(VK_USE_PLATFORM_WIN32_KHR) | |
#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 | |
POINT minsize; // minimum window size | |
#elif defined(VK_USE_PLATFORM_XCB_KHR) | |
xcb_connection_t *connection; | |
xcb_screen_t *screen; | |
xcb_window_t window; | |
xcb_intern_atom_reply_t *atom_wm_delete_window; | |
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
struct wl_display *display; | |
struct wl_registry *registry; | |
struct wl_compositor *compositor; | |
struct wl_surface *window; | |
struct wl_shell *shell; | |
struct wl_shell_surface *shell_surface; | |
#elif defined(VK_USE_PLATFORM_ANDROID_KHR) | |
ANativeWindow* window; | |
#endif | |
VkSurfaceKHR surface; | |
bool prepared; | |
bool use_staging_buffer; | |
bool suppress_popups; | |
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; | |
char *extension_names[64]; | |
char *enabled_layers[64]; | |
int width, height; | |
VkFormat format; | |
VkColorSpaceKHR color_space; | |
PFN_vkGetPhysicalDeviceSurfaceSupportKHR | |
fpGetPhysicalDeviceSurfaceSupportKHR; | |
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR | |
fpGetPhysicalDeviceSurfaceCapabilitiesKHR; | |
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR | |
fpGetPhysicalDeviceSurfaceFormatsKHR; | |
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR | |
fpGetPhysicalDeviceSurfacePresentModesKHR; | |
PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; | |
PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; | |
PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; | |
PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; | |
PFN_vkQueuePresentKHR fpQueuePresentKHR; | |
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 validate; | |
bool use_break; | |
PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback; | |
PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback; | |
VkDebugReportCallbackEXT msg_callback; | |
PFN_vkDebugReportMessageEXT DebugReportMessage; | |
float depthStencil; | |
float depthIncrement; | |
bool quit; | |
uint32_t current_buffer; | |
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; | |
} | |
#ifdef _WIN32 | |
struct demo *demo = (struct demo*) pUserData; | |
if (!demo->suppress_popups) | |
MessageBox(NULL, message, "Alert", MB_OK); | |
#else | |
printf("%s\n", message); | |
fflush(stdout); | |
#endif | |
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); | |
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 < VK_MAX_MEMORY_TYPES; 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); | |
VkCommandBufferBeginInfo cmd_buf_info = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
.pNext = NULL, | |
.flags = 0, | |
.pInheritanceInfo = NULL, | |
}; | |
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 VkCommandBufferBeginInfo cmd_buf_info = { | |
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
.pNext = NULL, | |
.flags = 0, | |
.pInheritanceInfo = NULL, | |
}; | |
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); | |
/* | |
// 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); | |
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); | |
} | |
int test = 0; | |
int frameCount = 0; | |
static void demo_draw(struct demo *demo) { | |
auto start = clock(); | |
for (int i = 0; i < 10000000; i++) | |
{ | |
test++; | |
} | |
auto elapsed = clock() - start; | |
if (frameCount++ % 60 == 0) | |
{ | |
printf("%f\n", elapsed * 1000.0f / CLOCKS_PER_SEC); | |
} | |
#ifndef NO_SUBMISSIONS | |
if (frameCount > 1) | |
#endif | |
return; | |
VkResult U_ASSERT_ONLY err; | |
/* | |
VkSemaphore imageAcquiredSemaphore, drawCompleteSemaphore; | |
VkSemaphoreCreateInfo semaphoreCreateInfo = { | |
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, | |
.pNext = NULL, | |
.flags = 0, | |
}; | |
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, | |
imageAcquiredSemaphore, | |
(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, 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 | |
// presentation engine will still present the image correctly. | |
} else { | |
assert(!err); | |
} | |
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. | |
*/ | |
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 = 0, | |
.pWaitSemaphores = NULL, | |
.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, | |
.waitSemaphoreCount = 1, | |
.pWaitSemaphores = &drawCompleteSemaphore, | |
.swapchainCount = 1, | |
.pSwapchains = &demo->swapchain, | |
.pImageIndices = &demo->current_buffer, | |
}; | |
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 | |
// 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, imageAcquiredSemaphore, NULL); | |
vkDestroySemaphore(demo->device, drawCompleteSemaphore, 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 = demo->fpGetPhysicalDeviceSurfaceCapabilitiesKHR( | |
demo->gpu, demo->surface, &surfCapabilities); | |
assert(!err); | |
uint32_t presentModeCount; | |
err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR( | |
demo->gpu, demo->surface, &presentModeCount, NULL); | |
assert(!err); | |
VkPresentModeKHR *presentModes = | |
(VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); | |
assert(presentModes); | |
err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR( | |
demo->gpu, demo->surface, &presentModeCount, presentModes); | |
assert(!err); | |
VkExtent2D swapchainExtent; | |
// 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; | |
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. | |
// 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) && | |
(desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) { | |
// Application must settle for fewer images than desired: | |
desiredNumOfSwapchainImages = 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 = desiredNumOfSwapchainImages, | |
.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 = demo->fpCreateSwapchainKHR(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) { | |
demo->fpDestroySwapchainKHR(demo->device, oldSwapchain, NULL); | |
} | |
err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain, | |
&demo->swapchainImageCount, NULL); | |
assert(!err); | |
VkImage *swapchainImages = | |
(VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage)); | |
assert(swapchainImages); | |
err = demo->fpGetSwapchainImagesKHR(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]; | |
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 | | |
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 | | |
VK_MEMORY_PROPERTY_HOST_COHERENT_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 | | |
VK_MEMORY_PROPERTY_HOST_COHERENT_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); | |
} | |
//TODO: Merge shader reading | |
#ifndef __ANDROID__ | |
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) | |
return NULL; | |
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; | |
} | |
#endif | |
static VkShaderModule demo_prepare_vs(struct demo *demo) { | |
#ifdef __ANDROID__ | |
VkShaderModuleCreateInfo sh_info = {}; | |
sh_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; | |
#include "tri.vert.h" | |
sh_info.codeSize = sizeof(tri_vert); | |
sh_info.pCode = tri_vert; | |
VkResult U_ASSERT_ONLY err = vkCreateShaderModule(demo->device, &sh_info, NULL, &demo->vert_shader_module); | |
assert(!err); | |
#else | |
void *vertShaderCode; | |
size_t size = 0; | |
vertShaderCode = demo_read_spv("tri-vert.spv", &size); | |
demo->vert_shader_module = | |
demo_prepare_shader_module(demo, vertShaderCode, size); | |
free(vertShaderCode); | |
#endif | |
return demo->vert_shader_module; | |
} | |
static VkShaderModule demo_prepare_fs(struct demo *demo) { | |
#ifdef __ANDROID__ | |
VkShaderModuleCreateInfo sh_info = {}; | |
sh_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; | |
#include "tri.frag.h" | |
sh_info.codeSize = sizeof(tri_frag); | |
sh_info.pCode = tri_frag; | |
VkResult U_ASSERT_ONLY err = vkCreateShaderModule(demo->device, &sh_info, NULL, &demo->frag_shader_module); | |
assert(!err); | |
#else | |
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); | |
#endif | |
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; | |
rs.lineWidth = 1.0f; | |
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; | |
} | |
#if defined(VK_USE_PLATFORM_WIN32_KHR) | |
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_GETMINMAXINFO: // set window's minimum size | |
((MINMAXINFO*)lParam)->ptMinTrackSize = demo.minsize; | |
return 0; | |
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; | |
if (demo.height < 64) demo.height = 64; | |
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); | |
} | |
// Window client area size must be at least 1 pixel high, to prevent crash. | |
demo->minsize.x = GetSystemMetrics(SM_CXMINTRACK); | |
demo->minsize.y = GetSystemMetrics(SM_CYMINTRACK) + 1; | |
} | |
#elif defined(VK_USE_PLATFORM_XCB_KHR) | |
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); | |
} | |
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
static void demo_run(struct demo *demo) { | |
while (!demo->quit) { | |
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 handle_ping(void *data UNUSED, | |
struct wl_shell_surface *shell_surface, | |
uint32_t serial) { | |
wl_shell_surface_pong(shell_surface, serial); | |
} | |
static void handle_configure(void *data UNUSED, | |
struct wl_shell_surface *shell_surface UNUSED, | |
uint32_t edges UNUSED, int32_t width UNUSED, | |
int32_t height UNUSED) {} | |
static void handle_popup_done(void *data UNUSED, | |
struct wl_shell_surface *shell_surface UNUSED) {} | |
static const struct wl_shell_surface_listener shell_surface_listener = { | |
handle_ping, handle_configure, handle_popup_done}; | |
static void demo_create_window(struct demo *demo) { | |
demo->window = wl_compositor_create_surface(demo->compositor); | |
if (!demo->window) { | |
printf("Can not create wayland_surface from compositor!\n"); | |
fflush(stdout); | |
exit(1); | |
} | |
demo->shell_surface = wl_shell_get_shell_surface(demo->shell, demo->window); | |
if (!demo->shell_surface) { | |
printf("Can not get shell_surface from wayland_surface!\n"); | |
fflush(stdout); | |
exit(1); | |
} | |
wl_shell_surface_add_listener(demo->shell_surface, &shell_surface_listener, | |
demo); | |
wl_shell_surface_set_toplevel(demo->shell_surface); | |
wl_shell_surface_set_title(demo->shell_surface, APP_SHORT_NAME); | |
} | |
#elif defined(VK_USE_PLATFORM_ANDROID_KHR) | |
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++; | |
} | |
#endif | |
/* | |
* 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; | |
uint32_t instance_layer_count = 0; | |
uint32_t validation_layer_count = 0; | |
char **instance_validation_layers = NULL; | |
demo->enabled_extension_count = 0; | |
demo->enabled_layer_count = 0; | |
char *instance_validation_layers_alt1[] = { | |
"VK_LAYER_LUNARG_standard_validation" | |
}; | |
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; | |
if (demo->validate) { | |
err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); | |
assert(!err); | |
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_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 (uint32_t i = 0; i < validation_layer_count; i++) { | |
demo->enabled_layers[i] = instance_validation_layers[i]; | |
} | |
} | |
free(instance_layers); | |
} | |
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 */ | |
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 defined(VK_USE_PLATFORM_WIN32_KHR) | |
if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, | |
instance_extensions[i].extensionName)) { | |
platformSurfaceExtFound = 1; | |
demo->extension_names[demo->enabled_extension_count++] = | |
VK_KHR_WIN32_SURFACE_EXTENSION_NAME; | |
} | |
#elif defined(VK_USE_PLATFORM_XCB_KHR) | |
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; | |
} | |
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
if (!strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, | |
instance_extensions[i].extensionName)) { | |
platformSurfaceExtFound = 1; | |
demo->extension_names[demo->enabled_extension_count++] = | |
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; | |
} | |
#elif defined(VK_USE_PLATFORM_ANDROID_KHR) | |
if (!strcmp(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, | |
instance_extensions[i].extensionName)) { | |
platformSurfaceExtFound = 1; | |
demo->extension_names[demo->enabled_extension_count++] = | |
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; | |
} | |
#endif | |
if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, | |
instance_extensions[i].extensionName)) { | |
if (demo->validate) { | |
demo->extension_names[demo->enabled_extension_count++] = | |
VK_EXT_DEBUG_REPORT_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) { | |
#if defined(VK_USE_PLATFORM_WIN32_KHR) | |
ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " | |
"the " VK_KHR_WIN32_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"); | |
#elif defined(VK_USE_PLATFORM_XCB_KHR) | |
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"); | |
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " | |
"the " VK_KHR_WAYLAND_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"); | |
#elif defined(VK_USE_PLATFORM_ANDROID_KHR) | |
ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " | |
"the " VK_KHR_ANDROID_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 | |
} | |
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 = demo->enabled_layer_count, | |
.ppEnabledLayerNames = (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"); | |
} | |
if (demo->validate) { | |
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 = demo->use_break ? BreakCallback : dbgFunc; | |
dbgCreateInfo.pUserData = demo; | |
dbgCreateInfo.pNext = NULL; | |
err = demo->CreateDebugReportCallback(demo->inst, &dbgCreateInfo, NULL, | |
&demo->msg_callback); | |
switch (err) { | |
case VK_SUCCESS: | |
break; | |
case VK_ERROR_OUT_OF_HOST_MEMORY: | |
ERR_EXIT("CreateDebugReportCallback: out of host memory\n", | |
"CreateDebugReportCallback Failure"); | |
break; | |
default: | |
ERR_EXIT("CreateDebugReportCallback: unknown failure\n", | |
"CreateDebugReportCallback Failure"); | |
break; | |
} | |
} | |
// Having these GIPA queries of device extension entry points both | |
// BEFORE and AFTER vkCreateDevice is a good test for the loader | |
GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceCapabilitiesKHR); | |
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); | |
// 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); | |
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 | |
} | |
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; | |
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 = 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: | |
#if defined(VK_USE_PLATFORM_WIN32_KHR) | |
VkWin32SurfaceCreateInfoKHR createInfo; | |
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; | |
createInfo.pNext = NULL; | |
createInfo.flags = 0; | |
createInfo.hinstance = demo->connection; | |
createInfo.hwnd = demo->window; | |
err = | |
vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface); | |
#elif defined(VK_USE_PLATFORM_XCB_KHR) | |
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); | |
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
VkWaylandSurfaceCreateInfoKHR createInfo; | |
createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; | |
createInfo.pNext = NULL; | |
createInfo.flags = 0; | |
createInfo.display = demo->display; | |
createInfo.surface = demo->window; | |
err = vkCreateWaylandSurfaceKHR(demo->inst, &createInfo, NULL, | |
&demo->surface); | |
#elif defined(VK_USE_PLATFORM_ANDROID_KHR) | |
VkAndroidSurfaceCreateInfoKHR createInfo; | |
createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; | |
createInfo.pNext = NULL; | |
createInfo.flags = 0; | |
createInfo.window = (ANativeWindow*)(demo->window); | |
err = vkCreateAndroidSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface); | |
#endif | |
// 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++) { | |
demo->fpGetPhysicalDeviceSurfaceSupportKHR(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 = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, | |
&formatCount, NULL); | |
assert(!err); | |
VkSurfaceFormatKHR *surfFormats = | |
(VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); | |
err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(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); | |
} | |
#if defined(VK_USE_PLATFORM_WAYLAND_KHR) && !defined(VK_USE_PLATFORM_XCB_KHR) | |
static void registry_handle_global(void *data, struct wl_registry *registry, | |
uint32_t name, const char *interface, | |
uint32_t version UNUSED) { | |
struct demo *demo = data; | |
if (strcmp(interface, "wl_compositor") == 0) { | |
demo->compositor = | |
wl_registry_bind(registry, name, &wl_compositor_interface, 3); | |
/* Todo: When xdg_shell protocol has stablized, we should move wl_shell | |
* tp xdg_shell */ | |
} else if (strcmp(interface, "wl_shell") == 0) { | |
demo->shell = wl_registry_bind(registry, name, &wl_shell_interface, 1); | |
} | |
} | |
static void registry_handle_global_remove(void *data UNUSED, | |
struct wl_registry *registry UNUSED, | |
uint32_t name UNUSED) {} | |
static const struct wl_registry_listener registry_listener = { | |
registry_handle_global, registry_handle_global_remove}; | |
#endif | |
static void demo_init_connection(struct demo *demo) { | |
#if defined(VK_USE_PLATFORM_XCB_KHR) | |
const xcb_setup_t *setup; | |
xcb_screen_iterator_t iter; | |
int scr; | |
demo->connection = xcb_connect(NULL, &scr); | |
if (xcb_connection_has_error(demo->connection) > 0) { | |
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; | |
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
demo->display = wl_display_connect(NULL); | |
if (demo->display == NULL) { | |
printf("Cannot find a compatible Vulkan installable client driver " | |
"(ICD).\nExiting ...\n"); | |
fflush(stdout); | |
exit(1); | |
} | |
demo->registry = wl_display_get_registry(demo->display); | |
wl_registry_add_listener(demo->registry, ®istry_listener, demo); | |
wl_display_dispatch(demo->display); | |
#endif // _WIN32 | |
} | |
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], "--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; | |
} | |
if (strcmp(argv[i], "--suppress_popups") == 0) { | |
demo->suppress_popups = true; | |
continue; | |
} | |
fprintf(stderr, "Usage:\n %s [--use_staging] [--validate] [--break] " | |
"[--c <framecount>] [--suppress_popups]\n", | |
APP_SHORT_NAME); | |
fflush(stderr); | |
exit(1); | |
} | |
demo_init_connection(demo); | |
demo_init_vk(demo); | |
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); | |
demo->fpDestroySwapchainKHR(demo->device, demo->swapchain, NULL); | |
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, NULL); | |
free(demo->queue_props); | |
#if defined(VK_USE_PLATFORM_XCB_KHR) | |
xcb_destroy_window(demo->connection, demo->window); | |
xcb_disconnect(demo->connection); | |
free(demo->atom_wm_delete_window); | |
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
wl_shell_surface_destroy(demo->shell_surface); | |
wl_surface_destroy(demo->window); | |
wl_shell_destroy(demo->shell); | |
wl_compositor_destroy(demo->compositor); | |
wl_registry_destroy(demo->registry); | |
wl_display_disconnect(demo->display); | |
#endif | |
} | |
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); | |
} | |
#if defined(VK_USE_PLATFORM_WIN32_KHR) | |
// Include header required for parsing the command line options. | |
#include <shellapi.h> | |
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; | |
if (AllocConsole()) | |
freopen("CONOUT$", "w", stdout); | |
// 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); | |
} | |
} | |
} | |
} else { | |
argv = NULL; | |
} | |
demo_init(&demo, argc, argv); | |
// 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); | |
demo_prepare(&demo); | |
done = false; // initialize loop condition variable | |
/* main message loop*/ | |
while (!done) { | |
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); | |
if (msg.message == WM_QUIT) // check for a quit message | |
{ | |
done = true; // if found, quit app | |
} else { | |
/* Translate and dispatch to event queue*/ | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
RedrawWindow(demo.window, NULL, NULL, RDW_INTERNALPAINT); | |
} | |
demo_cleanup(&demo); | |
FreeConsole(); | |
return (int)msg.wParam; | |
} | |
#elif defined(VK_USE_PLATFORM_ANDROID_KHR) | |
#include <android/log.h> | |
#include <android_native_app_glue.h> | |
static bool initialized = false; | |
static bool active = false; | |
struct demo demo; | |
static int32_t processInput(struct android_app* app, AInputEvent* event) { | |
return 0; | |
} | |
static void processCommand(struct android_app* app, int32_t cmd) { | |
switch(cmd) { | |
case APP_CMD_INIT_WINDOW: { | |
if (app->window) { | |
demo_init(&demo, 0, NULL); | |
demo.window = (void*)app->window; | |
demo_init_vk_swapchain(&demo); | |
demo_prepare(&demo); | |
initialized = true; | |
} | |
break; | |
} | |
case APP_CMD_GAINED_FOCUS: { | |
active = true; | |
break; | |
} | |
case APP_CMD_LOST_FOCUS: { | |
active = false; | |
break; | |
} | |
} | |
} | |
void android_main(struct android_app *app) | |
{ | |
app_dummy(); | |
#ifdef ANDROID | |
int vulkanSupport = InitVulkan(); | |
if (vulkanSupport == 0) | |
return; | |
#endif | |
app->onAppCmd = processCommand; | |
app->onInputEvent = processInput; | |
while(1) { | |
int events; | |
struct android_poll_source* source; | |
while (ALooper_pollAll(active ? 0 : -1, NULL, &events, (void**)&source) >= 0) { | |
if (source) { | |
source->process(app, source); | |
} | |
if (app->destroyRequested != 0) { | |
demo_cleanup(&demo); | |
return; | |
} | |
} | |
if (initialized && active) { | |
demo_run(&demo); | |
} | |
} | |
} | |
#elif defined(VK_USE_PLATFORM_XCB_KHR) | defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
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); | |
return validation_error; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment