// 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 <<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 <<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)
