1// Copyright 2014-2023 The Khronos Group Inc. 2// 3// SPDX-License-Identifier: CC-BY-4.0 4 5[[deferred-host-operations]] 6= Deferred Host Operations 7 8Certain Vulkan commands are inherently expensive for the host CPU to 9execute. 10It is often desirable to offload such work onto background threads, and to 11parallelize the work across multiple CPUs. 12The concept of _deferred operations_ allows applications and drivers to 13coordinate the execution of expensive host commands using an 14application-managed thread pool. 15 16The `apiext:VK_KHR_deferred_host_operations` extension defines the 17infrastructure and usage patterns for _deferrable commands_, but does not 18specify any commands as deferrable. 19This is left to additional dependent extensions. 20Commands must: not be deferred unless the deferral is specifically allowed 21by another extension which depends on 22`apiext:VK_KHR_deferred_host_operations`. 23This specification will refer to such extensions as _deferral extensions_. 24 25 26[[deferred-host-operations-requesting]] 27== Requesting Deferral 28 29When an application requests an operation deferral, the implementation may: 30defer the operation. 31When deferral is requested and the implementation defers any operation, the 32implementation must: return ename:VK_OPERATION_DEFERRED_KHR as the success 33code if no errors occurred. 34When deferral is requested, the implementation should: defer the operation 35when the workload is significant, however if the implementation chooses not 36to defer any of the requested operations and instead executes all of them 37immediately, the implementation must: return 38ename:VK_OPERATION_NOT_DEFERRED_KHR as the success code if no errors 39occurred. 40 41A deferred operation is created _complete_ with an initial result value of 42ename:VK_SUCCESS. 43The deferred operation becomes _pending_ when an operation has been 44successfully deferred with that deferred operation object. 45 46A deferred operation is considered pending until the deferred operation 47completes. 48A pending deferred operation becomes _complete_ when it has been fully 49executed by one or more threads. 50Pending deferred operations will never complete until they are _joined_ by 51an application thread, using flink:vkDeferredOperationJoinKHR. 52Applications can: join multiple threads to the same deferred operation, 53enabling concurrent execution of subtasks within that operation. 54 55The application can: query the status of a slink:VkDeferredOperationKHR 56using the flink:vkGetDeferredOperationMaxConcurrencyKHR or 57flink:vkGetDeferredOperationResultKHR commands. 58 59Parameters to the command requesting a deferred operation may: be accessed 60by the implementation at any time until the deferred operation enters the 61complete state. 62The application must: obey the following rules while a deferred operation is 63pending: 64 65 * Externally synchronized parameters must: not be accessed. 66 * Pointer parameters must: not be modified (e.g. reallocated/freed). 67 * The contents of pointer parameters which may: be read by the command 68 must: not be modified. 69 * The contents of pointer parameters which may: be written by the command 70 must: not be read. 71 * Vulkan object parameters must: not be passed as externally synchronized 72 parameters to any other command. 73 74When the deferred operation is complete, the application should: call 75flink:vkGetDeferredOperationResultKHR to obtain the elink:VkResult 76indicating success or failure of the operation. 77The elink:VkResult value returned will be one of the values that the command 78requesting the deferred operation is able to return. 79Writes to output parameters of the requesting command will happen-before the 80deferred operation is complete. 81 82When a deferral is requested for a command, the implementation may: perform 83memory management operations on the allocator supplied to 84flink:vkCreateDeferredOperationKHR for the deferred operation object, as 85described in the <<memory-allocation,Memory Allocation>> chapter. 86Such allocations must: occur on the thread which requests deferral. 87 88If an allocator was supplied for the deferred command at the time of the 89deferral request, then the implementation may: perform memory management 90operations on this allocator during the execution of 91flink:vkDeferredOperationJoinKHR. 92These operations may: occur concurrently and may: be performed by any joined 93thread. 94The application must: ensure that the supplied allocator is able to operate 95correctly under these conditions. 96 97 98== Deferred Host Operations API 99 100[open,refpage='VkDeferredOperationKHR',desc='A deferred operation',type='handles'] 101-- 102The sname:VkDeferredOperationKHR handle is defined as: 103 104include::{generated}/api/handles/VkDeferredOperationKHR.adoc[] 105 106This handle refers to a tracking structure which manages the execution state 107for a deferred command. 108-- 109 110[open,refpage='vkCreateDeferredOperationKHR',desc='Create a deferred operation handle',type='protos'] 111-- 112:refpage: vkCreateDeferredOperationKHR 113 114To construct the tracking object for a deferred command, call: 115 116include::{generated}/api/protos/vkCreateDeferredOperationKHR.adoc[] 117 118 * pname:device is the device which owns pname:operation. 119 * pname:pAllocator controls host memory allocation as described in the 120 <<memory-allocation,Memory Allocation>> chapter. 121 * pname:pDeferredOperation is a pointer to a handle in which the created 122 slink:VkDeferredOperationKHR is returned. 123 124include::{generated}/validity/protos/vkCreateDeferredOperationKHR.adoc[] 125-- 126 127[open,refpage='vkDeferredOperationJoinKHR',desc='Assign a thread to a deferred operation',type='protos'] 128-- 129:refpage: vkDeferredOperationJoinKHR 130 131To assign a thread to a deferred operation, call: 132 133include::{generated}/api/protos/vkDeferredOperationJoinKHR.adoc[] 134 135 * pname:device is the device which owns pname:operation. 136 * pname:operation is the deferred operation that the calling thread should 137 work on. 138 139The fname:vkDeferredOperationJoinKHR command will execute a portion of the 140deferred operation on the calling thread. 141 142The return value will be one of the following: 143 144 * A return value of ename:VK_SUCCESS indicates that pname:operation is 145 complete. 146 The application should: use flink:vkGetDeferredOperationResultKHR to 147 retrieve the result of pname:operation. 148 * A return value of ename:VK_THREAD_DONE_KHR indicates that the deferred 149 operation is not complete, but there is no work remaining to assign to 150 threads. 151 Future calls to flink:vkDeferredOperationJoinKHR are not necessary and 152 will simply harm performance. 153 This situation may: occur when other threads executing 154 flink:vkDeferredOperationJoinKHR are about to complete pname:operation, 155 and the implementation is unable to partition the workload any further. 156 * A return value of ename:VK_THREAD_IDLE_KHR indicates that the deferred 157 operation is not complete, and there is no work for the thread to do at 158 the time of the call. 159 This situation may: occur if the operation encounters a temporary 160 reduction in parallelism. 161 By returning ename:VK_THREAD_IDLE_KHR, the implementation is signaling 162 that it expects that more opportunities for parallelism will emerge as 163 execution progresses, and that future calls to 164 flink:vkDeferredOperationJoinKHR can: be beneficial. 165 In the meantime, the application can: perform other work on the calling 166 thread. 167 168Implementations must: guarantee forward progress by enforcing the following 169invariants: 170 171 1. If only one thread has invoked flink:vkDeferredOperationJoinKHR on a 172 given operation, that thread must: execute the operation to completion 173 and return ename:VK_SUCCESS. 174 2. If multiple threads have concurrently invoked 175 flink:vkDeferredOperationJoinKHR on the same operation, then at least 176 one of them must: complete the operation and return ename:VK_SUCCESS. 177 178include::{generated}/validity/protos/vkDeferredOperationJoinKHR.adoc[] 179-- 180 181[open,refpage='vkDestroyDeferredOperationKHR',desc='Destroy a deferred operation handle',type='protos'] 182-- 183:refpage: vkDestroyDeferredOperationKHR 184 185When a deferred operation is completed, the application can: destroy the 186tracking object by calling: 187 188include::{generated}/api/protos/vkDestroyDeferredOperationKHR.adoc[] 189 190 * pname:device is the device which owns pname:operation. 191 * pname:operation is the completed operation to be destroyed. 192 * pname:pAllocator controls host memory allocation as described in the 193 <<memory-allocation,Memory Allocation>> chapter. 194 195.Valid Usage 196**** 197ifndef::VKSC_VERSION_1_0[] 198 * [[VUID-vkDestroyDeferredOperationKHR-operation-03434]] 199 If sname:VkAllocationCallbacks were provided when pname:operation was 200 created, a compatible set of callbacks must: be provided here 201 * [[VUID-vkDestroyDeferredOperationKHR-operation-03435]] 202 If no sname:VkAllocationCallbacks were provided when pname:operation was 203 created, pname:pAllocator must: be `NULL` 204endif::VKSC_VERSION_1_0[] 205 * [[VUID-vkDestroyDeferredOperationKHR-operation-03436]] 206 pname:operation must: be completed 207**** 208include::{generated}/validity/protos/vkDestroyDeferredOperationKHR.adoc[] 209-- 210 211[open,refpage='vkGetDeferredOperationMaxConcurrencyKHR',desc='Query the maximum concurrency on a deferred operation',type='protos'] 212-- 213:refpage: vkGetDeferredOperationMaxConcurrencyKHR 214 215To query the number of additional threads that can usefully be joined to a 216deferred operation, call: 217 218include::{generated}/api/protos/vkGetDeferredOperationMaxConcurrencyKHR.adoc[] 219 220 * pname:device is the device which owns pname:operation. 221 * pname:operation is the deferred operation to be queried. 222 223The returned value is the maximum number of threads that can usefully 224execute a deferred operation concurrently, reported for the state of the 225deferred operation at the point this command is called. 226This value is intended to be used to better schedule work onto available 227threads. 228Applications can: join any number of threads to the deferred operation and 229expect it to eventually complete, though excessive joins may: return 230ename:VK_THREAD_DONE_KHR immediately, performing no useful work. 231 232If pname:operation is complete, 233fname:vkGetDeferredOperationMaxConcurrencyKHR returns zero. 234 235If pname:operation is currently joined to any threads, the value returned by 236this command may: immediately be out of date. 237 238If pname:operation is pending, implementations must: not return zero unless 239at least one thread is currently executing flink:vkDeferredOperationJoinKHR 240on pname:operation. 241If there are such threads, the implementation should: return an estimate of 242the number of additional threads which it could profitably use. 243 244Implementations may: return [eq]#2^32^-1# to indicate that the maximum 245concurrency is unknown and cannot be easily derived. 246Implementations may: return values larger than the maximum concurrency 247available on the host CPU. 248In these situations, an application should: clamp the return value rather 249than oversubscribing the machine. 250 251[NOTE] 252.Note 253==== 254The recommended usage pattern for applications is to query this value once, 255after deferral, and schedule no more than the specified number of threads to 256join the operation. 257Each time a joined thread receives ename:VK_THREAD_IDLE_KHR, the application 258should schedule an additional join at some point in the future, but is not 259required to do so. 260==== 261 262include::{generated}/validity/protos/vkGetDeferredOperationMaxConcurrencyKHR.adoc[] 263-- 264 265[open,refpage='vkGetDeferredOperationResultKHR',desc='Query the result of a deferred operation',type='protos'] 266-- 267:refpage: vkGetDeferredOperationResultKHR 268 269The fname:vkGetDeferredOperationResultKHR function is defined as: 270 271include::{generated}/api/protos/vkGetDeferredOperationResultKHR.adoc[] 272 273 * pname:device is the device which owns pname:operation. 274 * pname:operation is the operation whose deferred result is being queried. 275 276If no command has been deferred on pname:operation, 277fname:vkGetDeferredOperationResultKHR returns ename:VK_SUCCESS. 278 279If the deferred operation is pending, fname:vkGetDeferredOperationResultKHR 280returns ename:VK_NOT_READY. 281 282If the deferred operation is complete, it returns the appropriate return 283value from the original command. 284This value must: be one of the elink:VkResult values which could have been 285returned by the original command if the operation had not been deferred. 286 287include::{generated}/validity/protos/vkGetDeferredOperationResultKHR.adoc[] 288-- 289 290