| // Copyright 2014-2021 The Khronos Group, Inc. |
| // |
| // SPDX-License-Identifier: CC-BY-4.0 |
| |
| include::{generated}/meta/{refprefix}VK_KHR_deferred_host_operations.txt[] |
| |
| === Other Extension Metadata |
| |
| *Last Modified Date*:: |
| 2020-11-12 |
| *IP Status*:: |
| No known IP claims. |
| *Contributors*:: |
| - Joshua Barczak, Intel |
| - Jeff Bolz, NVIDIA |
| - Daniel Koch, NVIDIA |
| - Slawek Grajewski, Intel |
| - Tobias Hector, AMD |
| - Yuriy O'Donnell, Epic |
| - Eric Werness, NVIDIA |
| - Baldur Karlsson, Valve |
| - Jesse Barker, Unity |
| - Contributors to VK_KHR_acceleration_structure, |
| VK_KHR_ray_tracing_pipeline |
| |
| === Description |
| |
| The `apiext:VK_KHR_deferred_host_operations` extension defines the |
| infrastructure and usage patterns for deferrable commands, but does not |
| specify any commands as deferrable. |
| This is left to additional dependent extensions. |
| Commands must: not be deferred unless the deferral is specifically allowed |
| by another extension which depends on |
| `apiext:VK_KHR_deferred_host_operations`. |
| |
| include::{generated}/interfaces/VK_KHR_deferred_host_operations.txt[] |
| |
| === Code Examples |
| |
| The following examples will illustrate the concept of deferrable operations |
| using a hypothetical example. |
| The command ftext:vkDoSomethingExpensiveEXT denotes a deferrable command. |
| The structure stext:VkExpensiveOperationArgsEXT represents the arguments |
| which it would normally accept. |
| |
| The following example illustrates how a vulkan application might request |
| deferral of an expensive operation: |
| |
| [source,cpp] |
| ---- |
| |
| // create a deferred operation |
| VkDeferredOperationKHR hOp; |
| VkResult result = vkCreateDeferredOperationKHR(device, pCallbacks, &hOp); |
| assert(result == VK_SUCCESS); |
| |
| result = vkDoSomethingExpensive(device, hOp, ...); |
| assert( result == VK_OPERATION_DEFERRED_KHR ); |
| |
| // operation was deferred. Execute it asynchronously |
| std::async::launch( |
| [ hOp ] ( ) |
| { |
| vkDeferredOperationJoinKHR(device, hOp); |
| |
| result = vkGetDeferredOperationResultKHR(device, hOp); |
| |
| // deferred operation is now complete. 'result' indicates success or failure |
| |
| vkDestroyDeferredOperationKHR(device, hOp, pCallbacks); |
| } |
| ); |
| |
| ---- |
| |
| The following example illustrates extracting concurrency from a single |
| deferred operation: |
| |
| [source,cpp] |
| ---- |
| |
| // create a deferred operation |
| VkDeferredOperationKHR hOp; |
| VkResult result = vkCreateDeferredOperationKHR(device, pCallbacks, &hOp); |
| assert(result == VK_SUCCESS); |
| |
| result = vkDoSomethingExpensive(device, hOp, ...); |
| assert( result == VK_OPERATION_DEFERRED_KHR ); |
| |
| // Query the maximum amount of concurrency and clamp to the desired maximum |
| uint32_t numLaunches = std::min(vkGetDeferredOperationMaxConcurrencyKHR(device, hOp), maxThreads); |
| |
| std::vector<std::future<void> > joins; |
| |
| for (uint32_t i = 0; i < numLaunches; i++) { |
| joins.emplace_back(std::async::launch( |
| [ hOp ] ( ) |
| { |
| vkDeferredOperationJoinKHR(device, hOp); |
| // in a job system, a return of VK_THREAD_IDLE_KHR should queue another |
| // job, but it is not functionally required |
| } |
| ); |
| } |
| |
| for (auto &f : joins) { |
| f.get(); |
| } |
| |
| result = vkGetDeferredOperationResultKHR(device, hOp); |
| |
| // deferred operation is now complete. 'result' indicates success or failure |
| |
| vkDestroyDeferredOperationKHR(device, hOp, pCallbacks); |
| |
| ---- |
| |
| |
| The following example shows a subroutine which guarantees completion of a |
| deferred operation, in the presence of multiple worker threads, and returns |
| the result of the operation. |
| |
| [source,cpp] |
| ---- |
| |
| VkResult FinishDeferredOperation(VkDeferredOperationKHR hOp) |
| { |
| // Attempt to join the operation until the implementation indicates that we should stop |
| |
| VkResult result = vkDeferredOperationJoinKHR(device, hOp); |
| while( result == VK_THREAD_IDLE_KHR ) |
| { |
| std::this_thread::yield(); |
| result = vkDeferredOperationJoinKHR(device, hOp); |
| } |
| |
| switch( result ) |
| { |
| case VK_SUCCESS: |
| { |
| // deferred operation has finished. Query its result |
| result = vkGetDeferredOperationResultKHR(device, hOp); |
| } |
| break; |
| |
| case VK_THREAD_DONE_KHR: |
| { |
| // deferred operation is being wrapped up by another thread |
| // wait for that thread to finish |
| do |
| { |
| std::this_thread::yield(); |
| result = vkGetDeferredOperationResultKHR(device, hOp); |
| } while( result == VK_NOT_READY ); |
| } |
| break; |
| |
| default: |
| assert(false); // other conditions are illegal. |
| break; |
| } |
| |
| return result; |
| } |
| ---- |
| |
| === Issues |
| |
| . Should this extension have a VkPhysicalDevice*FeaturesKHR structure? |
| |
| *RESOLVED*: No. |
| This extension does not add any functionality on its own and requires a |
| dependent extension to actually enable functionality and thus there is no |
| value in adding a feature structure. |
| If necessary, any dependent extension could add a feature boolean if it |
| wanted to indicate that it is adding optional deferral support. |
| |
| === Version History |
| |
| * Revision 1, 2019-12-05 (Josh Barczak, Daniel Koch) |
| - Initial draft. |
| * Revision 2, 2020-03-06 (Daniel Koch, Tobias Hector) |
| - Add missing VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR enum |
| - fix sample code |
| - Clarified deferred operation parameter lifetimes (#2018,!3647) |
| * Revision 3, 2020-05-15 (Josh Barczak) |
| - Clarify behavior of vkGetDeferredOperationMaxConcurrencyKHR, allowing |
| it to return 0 if the operation is complete (#2036,!3850) |
| * Revision 4, 2020-11-12 (Tobias Hector, Daniel Koch) |
| - Remove VkDeferredOperationInfoKHR and change return value semantics |
| when deferred host operations are in use (#2067,3813) |
| - clarify return value of vkGetDeferredOperationResultKHR (#2339,!4110) |