Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016 The Chromium OS Authors. All rights reserved. |
| 3 | * Use of this source code is governed by a BSD-style license that can be |
| 4 | * found in the LICENSE file. |
| 5 | */ |
| 6 | |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 7 | #include <math.h> |
| 8 | #include <stdbool.h> |
| 9 | #include <stdio.h> |
| 10 | #include <stdlib.h> |
| 11 | |
| 12 | #include <vulkan/vulkan.h> |
| 13 | |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 14 | #include "bs_drm.h" |
| 15 | |
| 16 | // Used for double-buffering. |
| 17 | struct frame { |
| 18 | struct gbm_bo *bo; |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 19 | uint32_t drm_fb_id; |
| 20 | VkDeviceMemory vk_memory; |
| 21 | VkImage vk_image; |
| 22 | VkImageView vk_image_view; |
| 23 | VkFramebuffer vk_framebuffer; |
| 24 | VkCommandBuffer vk_cmd_buf; |
| 25 | }; |
| 26 | |
| 27 | #define check_vk_success(result, vk_func) \ |
| 28 | __check_vk_success(__FILE__, __LINE__, __func__, (result), (vk_func)) |
| 29 | |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 30 | static void __check_vk_success(const char *file, int line, const char *func, VkResult result, |
| 31 | const char *vk_func) |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 32 | { |
| 33 | if (result == VK_SUCCESS) |
| 34 | return; |
| 35 | |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 36 | bs_debug_print("ERROR", func, file, line, "%s failed with VkResult(%d)", vk_func, result); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 37 | exit(EXIT_FAILURE); |
| 38 | } |
| 39 | |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 40 | static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, |
| 41 | void *data) |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 42 | { |
| 43 | bool *waiting_for_flip = data; |
| 44 | *waiting_for_flip = false; |
| 45 | } |
| 46 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 47 | // Choose a physical device that supports Vulkan 1.1 or later. Exit on failure. |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 48 | VkPhysicalDevice choose_physical_device(VkInstance inst) |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 49 | { |
| 50 | uint32_t n_phys_devs; |
| 51 | VkResult res; |
| 52 | |
| 53 | res = vkEnumeratePhysicalDevices(inst, &n_phys_devs, NULL); |
| 54 | check_vk_success(res, "vkEnumeratePhysicalDevices"); |
| 55 | |
| 56 | if (n_phys_devs == 0) { |
| 57 | fprintf(stderr, "No available VkPhysicalDevices\n"); |
| 58 | exit(EXIT_FAILURE); |
| 59 | } |
| 60 | |
| 61 | VkPhysicalDevice phys_devs[n_phys_devs]; |
| 62 | res = vkEnumeratePhysicalDevices(inst, &n_phys_devs, phys_devs); |
| 63 | check_vk_success(res, "vkEnumeratePhysicalDevices"); |
| 64 | |
| 65 | // Print information about all available devices. This helps debugging |
| 66 | // when bringing up Vulkan on a new system. |
| 67 | printf("Available VkPhysicalDevices:\n"); |
| 68 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 69 | uint32_t physical_device_idx = 0; |
| 70 | VkPhysicalDevice physical_device = VK_NULL_HANDLE; |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 71 | for (uint32_t i = 0; i < n_phys_devs; ++i) { |
| 72 | VkPhysicalDeviceProperties props; |
| 73 | |
| 74 | vkGetPhysicalDeviceProperties(phys_devs[i], &props); |
| 75 | |
| 76 | printf(" VkPhysicalDevice %u:\n", i); |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 77 | printf(" apiVersion: %u.%u.%u\n", VK_VERSION_MAJOR(props.apiVersion), |
| 78 | VK_VERSION_MINOR(props.apiVersion), VK_VERSION_PATCH(props.apiVersion)); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 79 | printf(" driverVersion: %u\n", props.driverVersion); |
| 80 | printf(" vendorID: 0x%x\n", props.vendorID); |
| 81 | printf(" deviceID: 0x%x\n", props.deviceID); |
| 82 | printf(" deviceName: %s\n", props.deviceName); |
| 83 | printf(" pipelineCacheUUID: %x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x\n", |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 84 | props.pipelineCacheUUID[0], props.pipelineCacheUUID[1], |
| 85 | props.pipelineCacheUUID[2], props.pipelineCacheUUID[3], |
| 86 | props.pipelineCacheUUID[4], props.pipelineCacheUUID[5], |
| 87 | props.pipelineCacheUUID[6], props.pipelineCacheUUID[7], |
| 88 | props.pipelineCacheUUID[8], props.pipelineCacheUUID[9], |
| 89 | props.pipelineCacheUUID[10], props.pipelineCacheUUID[11], |
| 90 | props.pipelineCacheUUID[12], props.pipelineCacheUUID[13], |
| 91 | props.pipelineCacheUUID[14], props.pipelineCacheUUID[15]); |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 92 | if (physical_device == VK_NULL_HANDLE && |
| 93 | VK_VERSION_MAJOR(props.apiVersion) >= 1 && |
| 94 | VK_VERSION_MINOR(props.apiVersion) >= 1) { |
| 95 | physical_device_idx = i; |
| 96 | physical_device = phys_devs[i]; |
| 97 | } |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 98 | } |
| 99 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 100 | if (physical_device == VK_NULL_HANDLE) { |
| 101 | bs_debug_error("unable to find a suitable physical device"); |
| 102 | exit(EXIT_FAILURE); |
| 103 | } |
| 104 | printf("Chose VkPhysicalDevice %d\n", physical_device_idx); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 105 | fflush(stdout); |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 106 | return physical_device; |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | // Return the index of a graphics-enabled queue family. Return UINT32_MAX on |
| 110 | // failure. |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 111 | uint32_t choose_gfx_queue_family(VkPhysicalDevice phys_dev) |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 112 | { |
| 113 | uint32_t family_idx = UINT32_MAX; |
| 114 | VkQueueFamilyProperties *props = NULL; |
| 115 | uint32_t n_props = 0; |
| 116 | |
| 117 | vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &n_props, NULL); |
| 118 | |
| 119 | props = calloc(sizeof(props[0]), n_props); |
| 120 | if (!props) { |
| 121 | bs_debug_error("out of memory"); |
| 122 | exit(EXIT_FAILURE); |
| 123 | } |
| 124 | |
| 125 | vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &n_props, props); |
| 126 | |
| 127 | // Choose the first graphics queue. |
| 128 | for (uint32_t i = 0; i < n_props; ++i) { |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 129 | if ((props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && props[i].queueCount > 0) { |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 130 | family_idx = i; |
| 131 | break; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | free(props); |
| 136 | return family_idx; |
| 137 | } |
| 138 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 139 | bool is_format_valid(VkPhysicalDevice phys_dev, VkFormat format) |
| 140 | { |
| 141 | VkFormatProperties2 format_props = { |
| 142 | .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, |
| 143 | }; |
| 144 | vkGetPhysicalDeviceFormatProperties2(phys_dev, format, &format_props); |
| 145 | return format_props.formatProperties.linearTilingFeatures & |
| 146 | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; |
| 147 | } |
| 148 | |
| 149 | VkImage create_image(VkPhysicalDevice phys_dev, VkDevice dev, |
| 150 | struct gbm_bo *bo, VkFormat format) |
| 151 | { |
| 152 | VkImage image; |
| 153 | VkExternalImageFormatProperties external_image_format_props = { |
| 154 | .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES, |
| 155 | }; |
| 156 | VkImageFormatProperties2 image_format_props = { |
| 157 | .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, |
| 158 | .pNext = &external_image_format_props, |
| 159 | }; |
| 160 | VkResult res = vkGetPhysicalDeviceImageFormatProperties2(phys_dev, |
| 161 | &(VkPhysicalDeviceImageFormatInfo2){ |
| 162 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, |
| 163 | .pNext = &(VkPhysicalDeviceExternalImageFormatInfo){ |
| 164 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, |
| 165 | .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, |
| 166 | }, |
| 167 | .format = format, |
| 168 | .type = VK_IMAGE_TYPE_2D, |
| 169 | .tiling = VK_IMAGE_TILING_LINEAR, |
| 170 | .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| 171 | }, |
| 172 | &image_format_props); |
| 173 | check_vk_success(res, "vkGetPhysicalDeviceFormatProperties2"); |
| 174 | |
| 175 | VkExternalMemoryFeatureFlags feature_flags = external_image_format_props |
| 176 | .externalMemoryProperties |
| 177 | .externalMemoryFeatures; |
| 178 | if (!(feature_flags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) { |
| 179 | bs_debug_error("dma-buf does not support Vulkan import"); |
| 180 | exit(EXIT_FAILURE); |
| 181 | } |
| 182 | |
| 183 | // TODO(crbug.com/1002071): Pass the stride of the imported buffer to vkCreateImage |
| 184 | // once the extension is supported on Intel. |
| 185 | res = vkCreateImage(dev, |
| 186 | &(VkImageCreateInfo){ |
| 187 | .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| 188 | .pNext = &(VkExternalMemoryImageCreateInfo){ |
| 189 | .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, |
| 190 | .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, |
| 191 | }, |
| 192 | .imageType = VK_IMAGE_TYPE_2D, |
| 193 | .format = format, |
| 194 | .extent = (VkExtent3D){ |
| 195 | gbm_bo_get_width(bo), |
| 196 | gbm_bo_get_height(bo), 1 |
| 197 | }, |
| 198 | .mipLevels = 1, |
| 199 | .arrayLayers = 1, |
| 200 | .samples = 1, |
| 201 | .tiling = VK_IMAGE_TILING_LINEAR, |
| 202 | .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| 203 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| 204 | }, |
| 205 | /*pAllocator*/ NULL, &image); |
| 206 | check_vk_success(res, "vkCreateImage"); |
| 207 | |
| 208 | return image; |
| 209 | } |
| 210 | |
| 211 | bool bind_image_bo(VkDevice dev, |
| 212 | VkImage image, |
| 213 | struct gbm_bo *bo, |
| 214 | VkDeviceMemory *mems) |
| 215 | { |
| 216 | PFN_vkGetMemoryFdPropertiesKHR bs_vkGetMemoryFdPropertiesKHR = |
| 217 | (void *)vkGetDeviceProcAddr(dev, "vkGetMemoryFdPropertiesKHR"); |
| 218 | if (bs_vkGetMemoryFdPropertiesKHR == NULL) { |
| 219 | bs_debug_error("vkGetDeviceProcAddr(\"vkGetMemoryFdPropertiesKHR\") failed"); |
| 220 | return false; |
| 221 | } |
| 222 | |
| 223 | int prime_fd = gbm_bo_get_fd(bo); |
| 224 | if (prime_fd < 0) { |
| 225 | bs_debug_error("failed to get prime fd for gbm_bo"); |
| 226 | return false; |
| 227 | } |
| 228 | |
| 229 | VkMemoryFdPropertiesKHR fd_props = { |
| 230 | .sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR, |
| 231 | }; |
| 232 | bs_vkGetMemoryFdPropertiesKHR(dev, |
| 233 | VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, |
| 234 | prime_fd, &fd_props); |
| 235 | |
| 236 | /* get image memory requirements */ |
| 237 | const VkImageMemoryRequirementsInfo2 mem_reqs_info = { |
| 238 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, |
| 239 | .image = image, |
| 240 | }; |
| 241 | VkMemoryRequirements2 mem_reqs = { |
| 242 | .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, |
| 243 | }; |
| 244 | vkGetImageMemoryRequirements2(dev, &mem_reqs_info, &mem_reqs); |
| 245 | |
| 246 | const uint32_t memory_type_bits = fd_props.memoryTypeBits & |
| 247 | mem_reqs.memoryRequirements.memoryTypeBits; |
| 248 | if (!memory_type_bits) { |
| 249 | bs_debug_error("no valid memory type"); |
| 250 | close(prime_fd); |
| 251 | return false; |
| 252 | } |
| 253 | |
| 254 | const VkMemoryDedicatedAllocateInfo memory_dedicated_info = { |
| 255 | .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, |
| 256 | .image = image, |
| 257 | }; |
| 258 | const VkImportMemoryFdInfoKHR memory_fd_info = { |
| 259 | .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, |
| 260 | .pNext = &memory_dedicated_info, |
| 261 | .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, |
| 262 | .fd = prime_fd, |
| 263 | }; |
| 264 | VkResult res = vkAllocateMemory(dev, |
| 265 | &(VkMemoryAllocateInfo){ |
| 266 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
| 267 | .pNext = &memory_fd_info, |
| 268 | .allocationSize = mem_reqs.memoryRequirements.size, |
| 269 | |
| 270 | // Simply choose the first available memory type. We |
| 271 | // need neither performance nor mmap, so all memory |
| 272 | // types are equally good. |
| 273 | .memoryTypeIndex = ffs(memory_type_bits) - 1, |
| 274 | }, |
| 275 | /*pAllocator*/ NULL, mems); |
| 276 | check_vk_success(res, "vkAllocateMemory"); |
| 277 | |
| 278 | res = vkBindImageMemory(dev, image, mems[0], 0); |
| 279 | check_vk_success(res, "vkBindImageMemory"); |
| 280 | |
| 281 | return true; |
| 282 | } |
| 283 | |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 284 | int main(int argc, char **argv) |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 285 | { |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 286 | const uint32_t gbm_format = GBM_FORMAT_XBGR8888; |
| 287 | const VkFormat format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 288 | VkInstance inst; |
| 289 | VkPhysicalDevice phys_dev; |
| 290 | uint32_t gfx_queue_family_idx; |
| 291 | VkDevice dev; |
| 292 | VkQueue gfx_queue; |
| 293 | VkRenderPass pass; |
| 294 | VkCommandPool cmd_pool; |
| 295 | VkResult res; |
| 296 | struct frame frames[2]; |
| 297 | int err; |
| 298 | |
| 299 | int dev_fd = bs_drm_open_main_display(); |
| 300 | if (dev_fd < 0) { |
| 301 | bs_debug_error("failed to open display device"); |
| 302 | exit(EXIT_FAILURE); |
| 303 | } |
| 304 | |
| 305 | struct gbm_device *gbm = gbm_create_device(dev_fd); |
| 306 | if (!gbm) { |
| 307 | bs_debug_error("failed to create gbm_device"); |
| 308 | exit(EXIT_FAILURE); |
| 309 | } |
| 310 | |
| 311 | struct bs_drm_pipe pipe = { 0 }; |
| 312 | if (!bs_drm_pipe_make(dev_fd, &pipe)) { |
| 313 | bs_debug_error("failed to make drm pipe"); |
| 314 | exit(EXIT_FAILURE); |
| 315 | } |
| 316 | |
| 317 | drmModeConnector *connector = drmModeGetConnector(dev_fd, pipe.connector_id); |
| 318 | if (!connector) { |
| 319 | bs_debug_error("drmModeGetConnector failed"); |
| 320 | exit(EXIT_FAILURE); |
| 321 | } |
| 322 | |
| 323 | drmModeModeInfo *mode = &connector->modes[0]; |
| 324 | |
| 325 | res = vkCreateInstance( |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 326 | &(VkInstanceCreateInfo){ |
| 327 | .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, |
| 328 | .pApplicationInfo = |
| 329 | &(VkApplicationInfo){ |
| 330 | .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 331 | .apiVersion = VK_MAKE_VERSION(1, 1, 0), |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 332 | }, |
| 333 | }, |
| 334 | /*pAllocator*/ NULL, &inst); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 335 | check_vk_success(res, "vkCreateInstance"); |
| 336 | |
| 337 | phys_dev = choose_physical_device(inst); |
| 338 | |
| 339 | gfx_queue_family_idx = choose_gfx_queue_family(phys_dev); |
| 340 | if (gfx_queue_family_idx == UINT32_MAX) { |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 341 | bs_debug_error( |
| 342 | "VkPhysicalDevice exposes no VkQueueFamilyProperties " |
| 343 | "with graphics"); |
| 344 | exit(EXIT_FAILURE); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 345 | } |
| 346 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 347 | if (!is_format_valid(phys_dev, format)) { |
| 348 | bs_debug_error("VkPhysicalDevice does not support 0x%x", format); |
| 349 | exit(EXIT_FAILURE); |
| 350 | } |
| 351 | |
| 352 | const uint32_t num_required_extensions = 2; |
| 353 | const char* required_extensions[] = { |
| 354 | "VK_KHR_external_memory_fd", |
| 355 | "VK_EXT_external_memory_dma_buf", |
| 356 | }; |
| 357 | uint32_t extension_count; |
| 358 | vkEnumerateDeviceExtensionProperties(phys_dev, NULL, &extension_count, NULL); |
| 359 | VkExtensionProperties available_extensions[extension_count]; |
| 360 | vkEnumerateDeviceExtensionProperties(phys_dev, NULL, &extension_count, available_extensions); |
| 361 | for (uint32_t i = 0; i < num_required_extensions; i++) { |
| 362 | uint32_t j; |
| 363 | for (j = 0; j < extension_count; j++) { |
| 364 | if (strcmp(required_extensions[i], available_extensions[j].extensionName) == 0) { |
| 365 | break; |
| 366 | } |
| 367 | } |
| 368 | if (j == extension_count) { |
| 369 | bs_debug_error("unsupported device extension: %s", required_extensions[i]); |
| 370 | exit(EXIT_FAILURE); |
| 371 | } |
| 372 | } |
| 373 | |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 374 | res = vkCreateDevice(phys_dev, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 375 | &(VkDeviceCreateInfo){ |
| 376 | .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
| 377 | .queueCreateInfoCount = 1, |
| 378 | .pQueueCreateInfos = |
| 379 | (VkDeviceQueueCreateInfo[]){ |
| 380 | { |
| 381 | .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, |
| 382 | .queueFamilyIndex = gfx_queue_family_idx, |
| 383 | .queueCount = 1, |
| 384 | .pQueuePriorities = (float[]){ 1.0f }, |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 385 | |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 386 | }, |
| 387 | }, |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 388 | .enabledExtensionCount = num_required_extensions, |
| 389 | .ppEnabledExtensionNames = required_extensions, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 390 | }, |
| 391 | /*pAllocator*/ NULL, &dev); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 392 | check_vk_success(res, "vkCreateDevice"); |
| 393 | |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 394 | vkGetDeviceQueue(dev, gfx_queue_family_idx, /*queueIndex*/ 0, &gfx_queue); |
| 395 | |
| 396 | res = vkCreateCommandPool(dev, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 397 | &(VkCommandPoolCreateInfo){ |
| 398 | .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, |
| 399 | .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | |
| 400 | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, |
| 401 | .queueFamilyIndex = gfx_queue_family_idx, |
| 402 | }, |
| 403 | /*pAllocator*/ NULL, &cmd_pool); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 404 | check_vk_success(res, "vkCreateCommandPool"); |
| 405 | |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 406 | res = vkCreateRenderPass( |
| 407 | dev, |
| 408 | &(VkRenderPassCreateInfo){ |
| 409 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, |
| 410 | .attachmentCount = 1, |
| 411 | .pAttachments = |
| 412 | (VkAttachmentDescription[]){ |
| 413 | { |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 414 | .format = format, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 415 | .samples = 1, |
| 416 | .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, |
| 417 | .storeOp = VK_ATTACHMENT_STORE_OP_STORE, |
| 418 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| 419 | .finalLayout = VK_IMAGE_LAYOUT_GENERAL, |
| 420 | }, |
| 421 | }, |
| 422 | .subpassCount = 1, |
| 423 | .pSubpasses = |
| 424 | (VkSubpassDescription[]){ |
| 425 | { |
| 426 | .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, |
| 427 | .colorAttachmentCount = 1, |
| 428 | .pColorAttachments = |
| 429 | (VkAttachmentReference[]){ |
| 430 | { |
| 431 | .attachment = 0, |
| 432 | .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| 433 | }, |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 434 | }, |
| 435 | }, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 436 | }, |
| 437 | }, |
| 438 | /*pAllocator*/ NULL, &pass); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 439 | check_vk_success(res, "vkCreateRenderPass"); |
| 440 | |
| 441 | for (int i = 0; i < BS_ARRAY_LEN(frames); ++i) { |
| 442 | struct frame *fr = &frames[i]; |
| 443 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 444 | fr->bo = gbm_bo_create(gbm, mode->hdisplay, mode->vdisplay, gbm_format, |
| 445 | GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 446 | if (fr->bo == NULL) { |
| 447 | bs_debug_error("failed to create framebuffer's gbm_bo"); |
| 448 | return 1; |
| 449 | } |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 450 | if (gbm_bo_get_modifier(fr->bo)) { |
| 451 | bs_debug_error("gmb_bo was created with modifiers"); |
| 452 | return 1; |
| 453 | } |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 454 | |
| 455 | fr->drm_fb_id = bs_drm_fb_create_gbm(fr->bo); |
| 456 | if (fr->drm_fb_id == 0) { |
| 457 | bs_debug_error("failed to create drm framebuffer id"); |
| 458 | return 1; |
| 459 | } |
| 460 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 461 | fr->vk_image = create_image(phys_dev, dev, fr->bo, format); |
| 462 | if (fr->vk_image == VK_NULL_HANDLE) { |
| 463 | bs_debug_error("failed to create VkImage"); |
| 464 | exit(EXIT_FAILURE); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 465 | } |
| 466 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 467 | if (!bind_image_bo(dev, fr->vk_image, fr->bo, &fr->vk_memory)) { |
| 468 | bs_debug_error("failed to bind bo to image"); |
| 469 | exit(EXIT_FAILURE); |
| 470 | } |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 471 | |
| 472 | res = vkCreateImageView(dev, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 473 | &(VkImageViewCreateInfo){ |
| 474 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| 475 | .image = fr->vk_image, |
| 476 | .viewType = VK_IMAGE_VIEW_TYPE_2D, |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 477 | .format = format, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 478 | .components = |
| 479 | (VkComponentMapping){ |
| 480 | .r = VK_COMPONENT_SWIZZLE_IDENTITY, |
| 481 | .b = VK_COMPONENT_SWIZZLE_IDENTITY, |
| 482 | .g = VK_COMPONENT_SWIZZLE_IDENTITY, |
| 483 | .a = VK_COMPONENT_SWIZZLE_IDENTITY, |
| 484 | }, |
| 485 | .subresourceRange = |
| 486 | (VkImageSubresourceRange){ |
| 487 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| 488 | .baseMipLevel = 0, |
| 489 | .levelCount = 1, |
| 490 | .baseArrayLayer = 0, |
| 491 | .layerCount = 1, |
| 492 | }, |
| 493 | }, |
| 494 | /*pAllocator*/ NULL, &fr->vk_image_view); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 495 | check_vk_success(res, "vkCreateImageView"); |
| 496 | |
| 497 | res = vkCreateFramebuffer(dev, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 498 | &(VkFramebufferCreateInfo){ |
| 499 | .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, |
| 500 | .renderPass = pass, |
| 501 | .attachmentCount = 1, |
| 502 | .pAttachments = (VkImageView[]){ fr->vk_image_view }, |
| 503 | .width = mode->hdisplay, |
| 504 | .height = mode->vdisplay, |
| 505 | .layers = 1, |
| 506 | }, |
| 507 | /*pAllocator*/ NULL, &fr->vk_framebuffer); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 508 | check_vk_success(res, "vkCreateFramebuffer"); |
| 509 | |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 510 | res = vkAllocateCommandBuffers( |
| 511 | dev, |
| 512 | &(VkCommandBufferAllocateInfo){ |
| 513 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, |
| 514 | .commandPool = cmd_pool, |
| 515 | .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, |
| 516 | .commandBufferCount = 1, |
| 517 | }, |
| 518 | &fr->vk_cmd_buf); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 519 | check_vk_success(res, "vkAllocateCommandBuffers"); |
| 520 | } |
| 521 | |
| 522 | // We set the screen mode using framebuffer 0. Then the first page flip |
| 523 | // waits on framebuffer 1. |
| 524 | err = drmModeSetCrtc(dev_fd, pipe.crtc_id, frames[0].drm_fb_id, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 525 | /*x*/ 0, /*y*/ 0, &pipe.connector_id, /*connector_count*/ 1, mode); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 526 | if (err) { |
| 527 | bs_debug_error("drmModeSetCrtc failed: %d", err); |
| 528 | exit(EXIT_FAILURE); |
| 529 | } |
| 530 | |
| 531 | // We set an upper bound on the render loop so we can run this in |
| 532 | // from a testsuite. |
| 533 | for (int i = 1; i < 500; ++i) { |
| 534 | struct frame *fr = &frames[i % BS_ARRAY_LEN(frames)]; |
| 535 | |
| 536 | // vkBeginCommandBuffer implicity resets the command buffer due |
| 537 | // to VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT. |
| 538 | res = vkBeginCommandBuffer(fr->vk_cmd_buf, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 539 | &(VkCommandBufferBeginInfo){ |
| 540 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, |
| 541 | .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, |
| 542 | }); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 543 | check_vk_success(res, "vkBeginCommandBuffer"); |
| 544 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 545 | // Transfer ownership of the dma-buf from DRM to Vulkan. |
| 546 | vkCmdPipelineBarrier( |
| 547 | fr->vk_cmd_buf, |
| 548 | // srcStageMask is ignored when acquiring ownership, but various |
| 549 | // validation layers complain when you pass 0 here, so we just set |
| 550 | // it to the same as dstStageMask. |
| 551 | VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, |
| 552 | VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, |
| 553 | 0, 0, NULL, 0, NULL, |
| 554 | 1, |
| 555 | &(VkImageMemoryBarrier){ |
| 556 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
| 557 | .pNext = NULL, |
| 558 | .srcAccessMask = 0, /** ignored for transfers */ |
| 559 | .dstAccessMask = 0, /** ignored for transfers */ |
| 560 | .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| 561 | .newLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| 562 | // TODO(crbug.com/1000666): Replace this with the foreign queue |
| 563 | // family once the extension is available on CrOS. |
| 564 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL, |
| 565 | .dstQueueFamilyIndex = gfx_queue_family_idx, |
| 566 | .image = fr->vk_image, |
| 567 | .subresourceRange = (VkImageSubresourceRange){ |
| 568 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| 569 | .baseMipLevel = 0, |
| 570 | .levelCount = 1, |
| 571 | .baseArrayLayer = 0, |
| 572 | .layerCount = 1, |
| 573 | }, |
| 574 | }); |
| 575 | |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 576 | // Cycle along the circumference of the RGB color wheel. |
| 577 | VkClearValue clear_color = { |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 578 | .color = |
| 579 | { |
| 580 | .float32 = |
| 581 | { |
| 582 | 0.5f + 0.5f * sinf(2 * M_PI * i / 240.0f), |
| 583 | 0.5f + 0.5f * sinf(2 * M_PI * i / 240.0f + |
| 584 | (2.0f / 3.0f * M_PI)), |
| 585 | 0.5f + 0.5f * sinf(2 * M_PI * i / 240.0f + |
| 586 | (4.0f / 3.0f * M_PI)), |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 587 | 1.0f, |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 588 | }, |
| 589 | }, |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 590 | }; |
| 591 | |
Gurchetan Singh | 4268fca | 2017-11-17 18:10:48 -0800 | [diff] [blame] | 592 | vkCmdBeginRenderPass(fr->vk_cmd_buf, |
| 593 | &(VkRenderPassBeginInfo){ |
| 594 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, |
| 595 | .renderPass = pass, |
| 596 | .framebuffer = fr->vk_framebuffer, |
| 597 | .renderArea = |
| 598 | (VkRect2D){ |
| 599 | .offset = { 0, 0 }, |
| 600 | .extent = { mode->hdisplay, mode->vdisplay }, |
| 601 | }, |
| 602 | .clearValueCount = 1, |
| 603 | .pClearValues = (VkClearValue[]){ clear_color }, |
| 604 | }, |
| 605 | VK_SUBPASS_CONTENTS_INLINE); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 606 | vkCmdEndRenderPass(fr->vk_cmd_buf); |
| 607 | |
Brian Ho | f14d024 | 2019-08-21 16:43:17 -0400 | [diff] [blame] | 608 | // Transfer ownership of the dma-buf from Vulkan to DRM. |
| 609 | vkCmdPipelineBarrier( |
| 610 | fr->vk_cmd_buf, |
| 611 | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, |
| 612 | // dstStageMask is ignored when releasing ownership, but various |
| 613 | // validation layers complain when you pass 0 here, so we just set |
| 614 | // it to the same as srcStageMask. |
| 615 | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, |
| 616 | 0, 0, NULL, 0, NULL, |
| 617 | 1, |
| 618 | &(VkImageMemoryBarrier){ |
| 619 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
| 620 | .pNext = NULL, |
| 621 | .srcAccessMask = 0, /** ignored for transfers */ |
| 622 | .dstAccessMask = 0, /** ignored for transfers */ |
| 623 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, |
| 624 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, |
| 625 | .srcQueueFamilyIndex = gfx_queue_family_idx, |
| 626 | // TODO(crbug.com/1000666): Replace this with the foreign queue |
| 627 | // family once the extension is available on CrOS. |
| 628 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL, |
| 629 | .image = fr->vk_image, |
| 630 | .subresourceRange = (VkImageSubresourceRange){ |
| 631 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| 632 | .baseMipLevel = 0, |
| 633 | .levelCount = 1, |
| 634 | .baseArrayLayer = 0, |
| 635 | .layerCount = 1, |
| 636 | }, |
| 637 | }); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 638 | res = vkEndCommandBuffer(fr->vk_cmd_buf); |
| 639 | check_vk_success(res, "vkEndCommandBuffer"); |
| 640 | |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 641 | res = |
| 642 | vkQueueSubmit(gfx_queue, |
| 643 | /*submitCount*/ 1, |
| 644 | (VkSubmitInfo[]){ |
| 645 | { |
| 646 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| 647 | .commandBufferCount = 1, |
| 648 | .pCommandBuffers = (VkCommandBuffer[]){ fr->vk_cmd_buf }, |
| 649 | }, |
| 650 | }, |
| 651 | VK_NULL_HANDLE); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 652 | check_vk_success(res, "vkQueueSubmit"); |
| 653 | |
| 654 | res = vkQueueWaitIdle(gfx_queue); |
| 655 | check_vk_success(res, "vkQueueWaitIdle"); |
| 656 | |
| 657 | bool waiting_for_flip = true; |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 658 | err = drmModePageFlip(dev_fd, pipe.crtc_id, fr->drm_fb_id, DRM_MODE_PAGE_FLIP_EVENT, |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 659 | &waiting_for_flip); |
| 660 | if (err) { |
| 661 | bs_debug_error("failed page flip: error=%d", err); |
| 662 | exit(EXIT_FAILURE); |
| 663 | } |
| 664 | |
| 665 | while (waiting_for_flip) { |
| 666 | drmEventContext ev_ctx = { |
| 667 | .version = DRM_EVENT_CONTEXT_VERSION, |
| 668 | .page_flip_handler = page_flip_handler, |
| 669 | }; |
| 670 | |
| 671 | fd_set fds; |
| 672 | FD_ZERO(&fds); |
| 673 | FD_SET(dev_fd, &fds); |
| 674 | |
| 675 | int n_fds = select(dev_fd + 1, &fds, NULL, NULL, NULL); |
| 676 | if (n_fds < 0) { |
| 677 | bs_debug_error("select() failed on page flip: %s", strerror(errno)); |
| 678 | exit(EXIT_FAILURE); |
| 679 | } else if (n_fds == 0) { |
| 680 | bs_debug_error("select() timeout on page flip"); |
| 681 | exit(EXIT_FAILURE); |
| 682 | } |
| 683 | |
| 684 | err = drmHandleEvent(dev_fd, &ev_ctx); |
| 685 | if (err) { |
Gurchetan Singh | 3f82d6d | 2017-11-08 14:43:20 -0800 | [diff] [blame] | 686 | bs_debug_error( |
| 687 | "drmHandleEvent failed while " |
| 688 | "waiting for page flip: error=%d", |
| 689 | err); |
Chad Versace | 9458087 | 2016-09-21 06:44:08 -0700 | [diff] [blame] | 690 | exit(EXIT_FAILURE); |
| 691 | } |
| 692 | } |
| 693 | } |
| 694 | |
| 695 | return 0; |
| 696 | } |