VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance instance, const char *funcName) { PFN_vkVoidFunction addr; layer_data *my_data; addr = layer_intercept_instance_proc(funcName); if (!addr) addr = layer_intercept_proc(funcName); if (addr) { return addr; } assert(instance); my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map); addr = debug_report_get_instance_proc_addr(my_data->report_data, funcName); if (addr) { return addr; } VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table; if (pTable->GetInstanceProcAddr == NULL) { return NULL; } return pTable->GetInstanceProcAddr(instance, funcName); }
VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char* funcName) { PFN_vkVoidFunction addr; layer_data* my_data; addr = layer_intercept_instance_proc(funcName); if (addr) { return addr; } if (instance == VK_NULL_HANDLE) { return NULL; } my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map); addr = debug_report_get_instance_proc_addr(my_data->report_data, funcName); if (addr) { return addr; } VkLayerInstanceDispatchTable* pTable = my_data->instance_dispatch_table; if (pTable->GetInstanceProcAddr == NULL) { return NULL; } return pTable->GetInstanceProcAddr(instance, funcName); }
VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) { dispatch_key key = get_dispatch_key(instance); layer_data *my_data = get_my_data_ptr(key, layer_data_map); VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table; startWriteObject(my_data, instance); pTable->DestroyInstance(instance, pAllocator); finishWriteObject(my_data, instance); // Clean up logging callback, if any while (my_data->logging_callback.size() > 0) { VkDebugReportCallbackEXT callback = my_data->logging_callback.back(); layer_destroy_msg_callback(my_data->report_data, callback, pAllocator); my_data->logging_callback.pop_back(); } layer_debug_report_destroy_instance(my_data->report_data); delete my_data->instance_dispatch_table; layer_data_map.erase(key); if (layer_data_map.empty()) { // Release mutex when destroying last instance. loader_platform_thread_delete_mutex(&threadingLock); threadingLockInitialized = 0; } }
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance instance, const char* funcName) { // Return the functions that are intercepted by this layer. static const struct { const char *name; PFN_vkVoidFunction proc; } core_instance_commands[] = { { "vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr) }, { "vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(CreateInstance) }, { "vkDestroyInstance", reinterpret_cast<PFN_vkVoidFunction>(DestroyInstance) } }; for (size_t i = 0; i < ARRAY_SIZE(core_instance_commands); i++) { if (!strcmp(core_instance_commands[i].name, funcName)) { return core_instance_commands[i].proc; } } // Only call down the chain for Vulkan commands that this layer does not intercept. layer_data *instance_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map); VkLayerInstanceDispatchTable *pTable = instance_data->instance_dispatch_table; if (pTable->GetInstanceProcAddr == nullptr) { return nullptr; } return pTable->GetInstanceProcAddr(instance, funcName); }
VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) { dispatch_key key = get_dispatch_key(instance); layer_data *my_data = GetLayerDataPtr(key, layer_data_map); VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table; pTable->DestroyInstance(instance, pAllocator); delete pTable; layer_data_map.erase(key); }
VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *funcName) { PFN_vkVoidFunction fptr; layer_data *my_data; if (!strcmp(funcName, "vkGetInstanceProcAddr")) return (PFN_vkVoidFunction)vkGetInstanceProcAddr; if (!strcmp(funcName, "vkGetDeviceProcAddr")) return (PFN_vkVoidFunction)vkGetDeviceProcAddr; if (!strcmp(funcName, "vkCreateInstance")) return (PFN_vkVoidFunction)vkCreateInstance; if (!strcmp(funcName, "vkDestroyInstance")) return (PFN_vkVoidFunction)vkDestroyInstance; if (!strcmp(funcName, "vkCreateDevice")) return (PFN_vkVoidFunction)vkCreateDevice; if (!strcmp(funcName, "vkEnumeratePhysicalDevices")) return (PFN_vkVoidFunction)vkEnumeratePhysicalDevices; if (!strcmp(funcName, "vkGetPhysicalDeviceFeatures")) return (PFN_vkVoidFunction)vkGetPhysicalDeviceFeatures; if (!strcmp(funcName, "vkGetPhysicalDeviceFormatProperties")) return (PFN_vkVoidFunction)vkGetPhysicalDeviceFormatProperties; if (!strcmp(funcName, "vkGetPhysicalDeviceImageFormatProperties")) return (PFN_vkVoidFunction)vkGetPhysicalDeviceImageFormatProperties; if (!strcmp(funcName, "vkGetPhysicalDeviceProperties")) return (PFN_vkVoidFunction)vkGetPhysicalDeviceProperties; if (!strcmp(funcName, "vkGetPhysicalDeviceQueueFamilyProperties")) return (PFN_vkVoidFunction)vkGetPhysicalDeviceQueueFamilyProperties; if (!strcmp(funcName, "vkGetPhysicalDeviceMemoryProperties")) return (PFN_vkVoidFunction)vkGetPhysicalDeviceMemoryProperties; if (!strcmp(funcName, "vkGetPhysicalDeviceSparseImageFormatProperties")) return (PFN_vkVoidFunction)vkGetPhysicalDeviceSparseImageFormatProperties; if (!strcmp(funcName, "vkEnumerateInstanceLayerProperties")) return (PFN_vkVoidFunction)vkEnumerateInstanceLayerProperties; if (!strcmp(funcName, "vkEnumerateDeviceLayerProperties")) return (PFN_vkVoidFunction)vkEnumerateDeviceLayerProperties; if (!strcmp(funcName, "vkEnumerateInstanceExtensionProperties")) return (PFN_vkVoidFunction)vkEnumerateInstanceExtensionProperties; if (!strcmp(funcName, "vkEnumerateInstanceDeviceProperties")) return (PFN_vkVoidFunction)vkEnumerateDeviceExtensionProperties; if (!instance) return NULL; my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map); fptr = debug_report_get_instance_proc_addr(my_data->report_data, funcName); if (fptr) return fptr; { VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table; if (pTable->GetInstanceProcAddr == NULL) return NULL; return pTable->GetInstanceProcAddr(instance, funcName); } }
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetPhysicalDeviceProcAddr(VkInstance instance, const char *funcName) { assert(instance); layer_data *instance_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map); VkLayerInstanceDispatchTable *pTable = instance_data->instance_dispatch_table; if (pTable->GetPhysicalDeviceProcAddr == nullptr) { return nullptr; } return pTable->GetPhysicalDeviceProcAddr(instance, funcName); }
VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceExtensionProperties( VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pCount, VkExtensionProperties *pProperties) { if (pLayerName && !strcmp(pLayerName, global_layer.layerName)) return util_GetExtensionProperties(0, NULL, pCount, pProperties); assert(physicalDevice); VkLayerInstanceDispatchTable *pTable = instance_dispatch_table(physicalDevice); return pTable->EnumerateDeviceExtensionProperties( physicalDevice, pLayerName, pCount, pProperties); }
VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *funcName) { #define ADD_HOOK(fn) \ if (!strncmp(#fn, funcName, sizeof(#fn))) return (PFN_vkVoidFunction)fn ADD_HOOK(vkCreateInstance); ADD_HOOK(vkCreateDevice); ADD_HOOK(vkDestroyInstance); #undef ADD_HOOK if (instance == NULL) return NULL; layer_data *instance_data; instance_data = GetLayerDataPtr(get_dispatch_key(instance), layer_data_map); VkLayerInstanceDispatchTable *pTable = instance_data->instance_dispatch_table; if (pTable->GetInstanceProcAddr == NULL) return NULL; return pTable->GetInstanceProcAddr(instance, funcName); }
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance instance, const char *funcName) { PFN_vkVoidFunction proc = intercept_core_instance_command(funcName); if (proc) return proc; assert(instance); proc = intercept_core_device_command(funcName); if (!proc) proc = intercept_khr_swapchain_command(funcName, VK_NULL_HANDLE); if (proc) return proc; VkLayerInstanceDispatchTable *pTable = instance_dispatch_table(instance); if (pTable->GetInstanceProcAddr == NULL) return NULL; return pTable->GetInstanceProcAddr(instance, funcName); }
VKAPI_ATTR VkResult VKAPI_CALL EnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) { VkResult result; VkLayerInstanceDispatchTable *pTable = instance_dispatch_table(instance); result = pTable->EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices); if (result == VK_SUCCESS && *pPhysicalDeviceCount > 0 && pPhysicalDevices) { for (uint32_t i = 0; i < *pPhysicalDeviceCount; i++) { // Create a mapping from a physicalDevice to an instance if (physDeviceMap[pPhysicalDevices[i]] == NULL) { PhysDeviceMapStruct *physDeviceMapElem = new PhysDeviceMapStruct; physDeviceMap[pPhysicalDevices[i]] = physDeviceMapElem; } physDeviceMap[pPhysicalDevices[i]]->instance = instance; } } return result; }
VKAPI_ATTR void VKAPI_CALL DestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) { dispatch_key key = get_dispatch_key(instance); layer_data *my_data = get_my_data_ptr(key, layer_data_map); VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table; // Enable the temporary callback(s) here to catch cleanup issues: bool callback_setup = false; if (my_data->num_tmp_callbacks > 0) { if (!layer_enable_tmp_callbacks(my_data->report_data, my_data->num_tmp_callbacks, my_data->tmp_dbg_create_infos, my_data->tmp_callbacks)) { callback_setup = true; } } startWriteObject(my_data, instance); pTable->DestroyInstance(instance, pAllocator); finishWriteObject(my_data, instance); // Disable and cleanup the temporary callback(s): if (callback_setup) { layer_disable_tmp_callbacks(my_data->report_data, my_data->num_tmp_callbacks, my_data->tmp_callbacks); } if (my_data->num_tmp_callbacks > 0) { layer_free_tmp_callbacks(my_data->tmp_dbg_create_infos, my_data->tmp_callbacks); my_data->num_tmp_callbacks = 0; } // Clean up logging callback, if any while (my_data->logging_callback.size() > 0) { VkDebugReportCallbackEXT callback = my_data->logging_callback.back(); layer_destroy_msg_callback(my_data->report_data, callback, pAllocator); my_data->logging_callback.pop_back(); } layer_debug_report_destroy_instance(my_data->report_data); delete my_data->instance_dispatch_table; layer_data_map.erase(key); }
// Save an image to a PPM image file. // // This function issues commands to copy/convert the swapchain image // from whatever compatible format the swapchain image uses // to a single format (VK_FORMAT_R8G8B8A8_UNORM) so that the converted // result can be easily written to a PPM file. // // Error handling: If there is a problem, this function should silently // fail without affecting the Present operation going on in the caller. // The numerous debug asserts are to catch programming errors and are not // expected to assert. Recovery and clean up are implemented for image memory // allocation failures. // (TODO) It would be nice to pass any failure info to DebugReport or something. static void writePPM(const char *filename, VkImage image1) { VkResult err; bool pass; // Bail immediately if we can't find the image. if (imageMap.empty() || imageMap.find(image1) == imageMap.end()) return; // Collect object info from maps. This info is generally recorded // by the other functions hooked in this layer. VkDevice device = imageMap[image1]->device; VkPhysicalDevice physicalDevice = deviceMap[device]->physicalDevice; VkInstance instance = physDeviceMap[physicalDevice]->instance; VkQueue queue = deviceMap[device]->queue; DeviceMapStruct *devMap = get_dev_info(device); if (NULL == devMap) { assert(0); return; } VkLayerDispatchTable *pTableDevice = devMap->device_dispatch_table; VkLayerDispatchTable *pTableQueue = get_dev_info(static_cast<VkDevice>(static_cast<void *>(queue))) ->device_dispatch_table; VkLayerInstanceDispatchTable *pInstanceTable; pInstanceTable = instance_dispatch_table(instance); // Gather incoming image info and check image format for compatibility with // the target format. // This function supports both 24-bit and 32-bit swapchain images. VkFormat const target32bitFormat = VK_FORMAT_R8G8B8A8_UNORM; VkFormat const target24bitFormat = VK_FORMAT_R8G8B8_UNORM; uint32_t const width = imageMap[image1]->imageExtent.width; uint32_t const height = imageMap[image1]->imageExtent.height; VkFormat const format = imageMap[image1]->format; uint32_t const numChannels = vk_format_get_channel_count(format); if ((vk_format_get_compatibility_class(target24bitFormat) != vk_format_get_compatibility_class(format)) && (vk_format_get_compatibility_class(target32bitFormat) != vk_format_get_compatibility_class(format))) { assert(0); return; } if ((3 != numChannels) && (4 != numChannels)) { assert(0); return; } // General Approach // // The idea here is to copy/convert the swapchain image into another image // that can be mapped and read by the CPU to produce a PPM file. // The image must be untiled and converted to a specific format for easy // parsing. The memory for the final image must be host-visible. // Note that in Vulkan, a BLIT operation must be used to perform a format // conversion. // // Devices vary in their ability to blit to/from linear and optimal tiling. // So we must query the device properties to get this information. // // If the device cannot BLIT to a LINEAR image, then the operation must be // done in two steps: // 1) BLIT the swapchain image (image1) to a temp image (image2) that is // created with TILING_OPTIMAL. // 2) COPY image2 to another temp image (image3) that is created with // TILING_LINEAR. // 3) Map image 3 and write the PPM file. // // If the device can BLIT to a LINEAR image, then: // 1) BLIT the swapchain image (image1) to a temp image (image2) that is // created with TILING_LINEAR. // 2) Map image 2 and write the PPM file. // // There seems to be no way to tell if the swapchain image (image1) is tiled // or not. We therefore assume that the BLIT operation can always read from // both linear and optimal tiled (swapchain) images. // There is therefore no point in looking at the BLIT_SRC properties. // // There is also the optimization where the incoming and target formats are // the same. In this case, just do a COPY. VkFormatProperties targetFormatProps; pInstanceTable->GetPhysicalDeviceFormatProperties( physicalDevice, (3 == numChannels) ? target24bitFormat : target32bitFormat, &targetFormatProps); bool need2steps = false; bool copyOnly = false; if ((target24bitFormat == format) || (target32bitFormat == format)) { copyOnly = true; } else { bool const bltLinear = targetFormatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT ? true : false; bool const bltOptimal = targetFormatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT ? true : false; if (!bltLinear && !bltOptimal) { // Cannot blit to either target tiling type. It should be pretty // unlikely to have a device that cannot blit to either type. // But punt by just doing a copy and possibly have the wrong // colors. This should be quite rare. copyOnly = true; } else if (!bltLinear && bltOptimal) { // Cannot blit to a linear target but can blt to optimal, so copy // after blit is needed. need2steps = true; } // Else bltLinear is available and only 1 step is needed. } // Put resources that need to be cleaned up in a struct with a destructor // so that things get cleaned up when this function is exited. WritePPMCleanupData data = {}; data.device = device; data.pTableDevice = pTableDevice; // Set up the image creation info for both the blit and copy images, in case // both are needed. VkImageCreateInfo imgCreateInfo2 = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0, VK_IMAGE_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM, {width, height, 1}, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, NULL, VK_IMAGE_LAYOUT_UNDEFINED, }; VkImageCreateInfo imgCreateInfo3 = imgCreateInfo2; // If we need both images, set up image2 to be read/write and tiled. if (need2steps) { imgCreateInfo2.tiling = VK_IMAGE_TILING_OPTIMAL; imgCreateInfo2.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; } VkMemoryAllocateInfo memAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, 0, // allocationSize, queried later 0 // memoryTypeIndex, queried later }; VkMemoryRequirements memRequirements; VkPhysicalDeviceMemoryProperties memoryProperties; // Create image2 and allocate its memory. It could be the intermediate or // final image. err = pTableDevice->CreateImage(device, &imgCreateInfo2, NULL, &data.image2); assert(!err); if (VK_SUCCESS != err) return; pTableDevice->GetImageMemoryRequirements(device, data.image2, &memRequirements); memAllocInfo.allocationSize = memRequirements.size; pInstanceTable->GetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); pass = memory_type_from_properties( &memoryProperties, memRequirements.memoryTypeBits, need2steps ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); assert(pass); err = pTableDevice->AllocateMemory(device, &memAllocInfo, NULL, &data.mem2); assert(!err); if (VK_SUCCESS != err) return; err = pTableQueue->BindImageMemory(device, data.image2, data.mem2, 0); assert(!err); if (VK_SUCCESS != err) return; // Create image3 and allocate its memory, if needed. if (need2steps) { err = pTableDevice->CreateImage(device, &imgCreateInfo3, NULL, &data.image3); assert(!err); if (VK_SUCCESS != err) return; pTableDevice->GetImageMemoryRequirements(device, data.image3, &memRequirements); memAllocInfo.allocationSize = memRequirements.size; pInstanceTable->GetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); pass = memory_type_from_properties( &memoryProperties, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); assert(pass); err = pTableDevice->AllocateMemory(device, &memAllocInfo, NULL, &data.mem3); assert(!err); if (VK_SUCCESS != err) return; err = pTableQueue->BindImageMemory(device, data.image3, data.mem3, 0); assert(!err); if (VK_SUCCESS != err) return; } // Set up the command buffer. We get a command buffer from a pool we saved // in a hooked function, which would be the application's pool. const VkCommandBufferAllocateInfo allocCommandBufferInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, NULL, deviceMap[device]->commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1}; data.commandPool = deviceMap[device]->commandPool; err = pTableDevice->AllocateCommandBuffers(device, &allocCommandBufferInfo, &data.commandBuffer); assert(!err); if (VK_SUCCESS != err) return; VkDevice cmdBuf = static_cast<VkDevice>(static_cast<void *>(data.commandBuffer)); deviceMap.emplace(cmdBuf, devMap); VkLayerDispatchTable *pTableCommandBuffer; pTableCommandBuffer = get_dev_info(cmdBuf)->device_dispatch_table; // We have just created a dispatchable object, but the dispatch table has // not been placed in the object yet. When a "normal" application creates // a command buffer, the dispatch table is installed by the top-level api // binding (trampoline.c). But here, we have to do it ourselves. if (!devMap->pfn_dev_init) { *((const void **)data.commandBuffer) = *(void **)device; } else { err = devMap->pfn_dev_init(device, (void *)data.commandBuffer); assert(!err); } const VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; err = pTableCommandBuffer->BeginCommandBuffer(data.commandBuffer, &commandBufferBeginInfo); assert(!err); // This barrier is used to transition from/to present Layout VkImageMemoryBarrier presentMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, image1, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; // This barrier is used to transition from a newly-created layout to a blt // or copy destination layout. VkImageMemoryBarrier destMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, data.image2, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; // This barrier is used to transition a dest layout to general layout. VkImageMemoryBarrier generalMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, data.image2, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; VkPipelineStageFlags srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT; VkPipelineStageFlags dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT; // The source image needs to be transitioned from present to transfer // source. pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &presentMemoryBarrier); // image2 needs to be transitioned from its undefined state to transfer // destination. pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &destMemoryBarrier); const VkImageCopy imageCopyRegion = {{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {width, height, 1}}; if (copyOnly) { pTableCommandBuffer->CmdCopyImage( data.commandBuffer, image1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion); } else { VkImageBlit imageBlitRegion = {}; imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.srcSubresource.baseArrayLayer = 0; imageBlitRegion.srcSubresource.layerCount = 1; imageBlitRegion.srcSubresource.mipLevel = 0; imageBlitRegion.srcOffsets[1].x = width; imageBlitRegion.srcOffsets[1].y = height; imageBlitRegion.srcOffsets[1].z = 1; imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.dstSubresource.baseArrayLayer = 0; imageBlitRegion.dstSubresource.layerCount = 1; imageBlitRegion.dstSubresource.mipLevel = 0; imageBlitRegion.dstOffsets[1].x = width; imageBlitRegion.dstOffsets[1].y = height; imageBlitRegion.dstOffsets[1].z = 1; pTableCommandBuffer->CmdBlitImage( data.commandBuffer, image1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlitRegion, VK_FILTER_NEAREST); if (need2steps) { // image 3 needs to be transitioned from its undefined state to a // transfer destination. destMemoryBarrier.image = data.image3; pTableCommandBuffer->CmdPipelineBarrier( data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &destMemoryBarrier); // Transition image2 so that it can be read for the upcoming copy to // image 3. destMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; destMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; destMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; destMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; destMemoryBarrier.image = data.image2; pTableCommandBuffer->CmdPipelineBarrier( data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &destMemoryBarrier); // This step essentially untiles the image. pTableCommandBuffer->CmdCopyImage( data.commandBuffer, data.image2, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image3, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion); generalMemoryBarrier.image = data.image3; } } // The destination needs to be transitioned from the optimal copy format to // the format we can read with the CPU. pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &generalMemoryBarrier); // Restore the swap chain image layout to what it was before. // This may not be strictly needed, but it is generally good to restore // things to original state. presentMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; presentMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; presentMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; presentMemoryBarrier.dstAccessMask = 0; pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &presentMemoryBarrier); err = pTableCommandBuffer->EndCommandBuffer(data.commandBuffer); assert(!err); VkFence nullFence = {VK_NULL_HANDLE}; VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = NULL; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = NULL; submitInfo.pWaitDstStageMask = NULL; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &data.commandBuffer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = NULL; err = pTableQueue->QueueSubmit(queue, 1, &submitInfo, nullFence); assert(!err); err = pTableQueue->QueueWaitIdle(queue); assert(!err); err = pTableDevice->DeviceWaitIdle(device); assert(!err); // Map the final image so that the CPU can read it. const VkImageSubresource sr = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0}; VkSubresourceLayout srLayout; const char *ptr; if (!need2steps) { pTableDevice->GetImageSubresourceLayout(device, data.image2, &sr, &srLayout); err = pTableDevice->MapMemory(device, data.mem2, 0, VK_WHOLE_SIZE, 0, (void **)&ptr); assert(!err); if (VK_SUCCESS != err) return; data.mem2mapped = true; } else { pTableDevice->GetImageSubresourceLayout(device, data.image3, &sr, &srLayout); err = pTableDevice->MapMemory(device, data.mem3, 0, VK_WHOLE_SIZE, 0, (void **)&ptr); assert(!err); if (VK_SUCCESS != err) return; data.mem3mapped = true; } // Write the data to a PPM file. ofstream file(filename, ios::binary); file << "P6\n"; file << width << "\n"; file << height << "\n"; file << 255 << "\n"; ptr += srLayout.offset; if (3 == numChannels) { for (uint32_t y = 0; y < height; y++) { file.write(ptr, 3 * width); ptr += srLayout.rowPitch; } } else if (4 == numChannels) { for (uint32_t y = 0; y < height; y++) { const unsigned int *row = (const unsigned int *)ptr; for (uint32_t x = 0; x < width; x++) { file.write((char *)row, 3); row++; } ptr += srLayout.rowPitch; } } file.close(); // Clean up handled by ~WritePPMCleanupData() }