Compare commits

...

7 Commits

Author SHA1 Message Date
Hans-Kristian Arntzen b817f972f4 tests: Add test coverage for two stages of AddToStateObject().
Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-05-04 15:15:18 +02:00
Hans-Kristian Arntzen b9c575d678 tests: Add test for AddToStateObject.
Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-05-04 15:15:18 +02:00
Hans-Kristian Arntzen ab85bdf94f tests: Add default NODE_MASK state object to RTPSO tests.
Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-05-04 15:15:18 +02:00
Hans-Kristian Arntzen 3d1b2d2042 vkd3d: Implement AddToStateObject().
This is barely implementable, and relies on implementations to do kinda
what we want.

To make this work in practice, we need to allow two pipelines per state
object. One that is created with LIBRARY and one that can be bound. When
incrementing the PSO, we use the LIBRARY one.

It seems to be allowed to create a new library from an old library.
It is more convenient for us if we're allowed to do this, so do this
until we're forced to do otherwise.

DXR 1.1 requires that shader identifiers remain invariant for child
pipelines if the parent pipeline also have them.
Vulkan has no such guarantee, but we can speculate that it works and
validate that identifiers remain invariant. This seems to work fine on
NVIDIA at least ... It probably makes sense that it works for
implementations where pipeline libraries are compiled at that time.

The basic implementation of AddToStateObject() is to consider
the parent pipeline as a COLLECTION pipeline. This composes well and
avoids a lot of extra implementation cruft.

Also adds validation to ensure that COLLECTION global state matches with
other COLLECTION objects and the parent. We will also inherit global
state like root signatures, pipeline config, shader configs etc when
using AddToStateObject().

The tests pass on NVIDIA at least.

Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-05-04 14:59:07 +02:00
Hans-Kristian Arntzen ccf927da7f vkd3d: Hold private ownership over global root signature.
Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-05-04 14:59:02 +02:00
Hans-Kristian Arntzen c940d548f4 vkd3d: Allow different but compatible global root signature objects.
Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-05-04 14:59:02 +02:00
Hans-Kristian Arntzen 53309d05a1 vkd3d: Ignore NODE_MASK subobjects.
Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
2022-05-04 14:57:24 +02:00
4 changed files with 461 additions and 49 deletions

View File

@ -4889,7 +4889,7 @@ static HRESULT STDMETHODCALLTYPE d3d12_device_CreateStateObject(d3d12_device_ifa
TRACE("iface %p, desc %p, iid %s, state_object %p!\n",
iface, desc, debugstr_guid(iid), state_object);
if (FAILED(hr = d3d12_state_object_create(device, desc, &state)))
if (FAILED(hr = d3d12_state_object_create(device, desc, NULL, &state)))
return hr;
return return_interface(&state->ID3D12StateObject_iface, &IID_ID3D12StateObject, iid, state_object);
@ -4957,13 +4957,23 @@ static HRESULT STDMETHODCALLTYPE d3d12_device_SetBackgroundProcessingMode(d3d12_
return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE d3d12_device_AddToStateObject(d3d12_device_iface *iface, const D3D12_STATE_OBJECT_DESC *addition,
ID3D12StateObject *state_object, REFIID riid, void **new_state_object)
static HRESULT STDMETHODCALLTYPE d3d12_device_AddToStateObject(d3d12_device_iface *iface,
const D3D12_STATE_OBJECT_DESC *addition,
ID3D12StateObject *parent_state, REFIID riid, void **new_state_object)
{
FIXME("iface %p, addition %p, state_object %p, riid %s, new_state_object %p stub!\n",
iface, addition, state_object, debugstr_guid(riid), new_state_object);
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_state_object *parent;
struct d3d12_state_object *state;
HRESULT hr;
return E_NOTIMPL;
TRACE("iface %p, addition %p, state_object %p, riid %s, new_state_object %p stub!\n",
iface, addition, parent_state, debugstr_guid(riid), new_state_object);
parent = impl_from_ID3D12StateObject(parent_state);
if (FAILED(hr = d3d12_state_object_add(device, addition, parent, &state)))
return hr;
return return_interface(&state->ID3D12StateObject_iface, &IID_ID3D12StateObject, riid, new_state_object);
}
static HRESULT STDMETHODCALLTYPE d3d12_device_CreateProtectedResourceSession1(d3d12_device_iface *iface,

View File

@ -122,7 +122,11 @@ static void d3d12_state_object_cleanup(struct d3d12_state_object *object)
d3d12_state_object_dec_ref(object->collections[i]);
vkd3d_free(object->collections);
if (object->global_root_signature)
d3d12_root_signature_dec_ref(object->global_root_signature);
VK_CALL(vkDestroyPipeline(object->device->vk_device, object->pipeline, NULL));
VK_CALL(vkDestroyPipeline(object->device->vk_device, object->pipeline_library, NULL));
VK_CALL(vkDestroyPipelineLayout(object->device->vk_device,
object->local_static_sampler.pipeline_layout, NULL));
@ -237,6 +241,7 @@ static void * STDMETHODCALLTYPE d3d12_state_object_properties_GetShaderIdentifie
LPCWSTR export_name)
{
struct d3d12_state_object *object = impl_from_ID3D12StateObjectProperties(iface);
struct d3d12_state_object_identifier *export;
const WCHAR *subtype = NULL;
uint32_t index;
@ -247,7 +252,14 @@ static void * STDMETHODCALLTYPE d3d12_state_object_properties_GetShaderIdentifie
/* Cannot query shader identifier for non-group names. */
if (!subtype && index != UINT32_MAX)
{
return object->exports[index].identifier;
export = &object->exports[index];
/* Need to return the parent SBT pointer if it exists */
while (export->inherited_collection_index >= 0)
{
object = object->collections[export->inherited_collection_index];
export = &object->exports[export->inherited_collection_export_index];
}
return export->identifier;
}
else
{
@ -350,9 +362,9 @@ struct d3d12_state_object_pipeline_data
bool has_pipeline_config;
const D3D12_RAYTRACING_SHADER_CONFIG *shader_config;
ID3D12RootSignature *global_root_signature;
ID3D12RootSignature *high_priority_local_root_signature;
ID3D12RootSignature *low_priority_local_root_signature;
struct d3d12_root_signature *global_root_signature;
struct d3d12_root_signature *high_priority_local_root_signature;
struct d3d12_root_signature *low_priority_local_root_signature;
/* Map 1:1 with VkShaderModule. */
struct vkd3d_shader_library_entry_point *entry_points;
@ -423,10 +435,78 @@ static void d3d12_state_object_pipeline_data_cleanup(struct d3d12_state_object_p
vkd3d_free(data->vk_libraries);
}
static HRESULT d3d12_state_object_add_collection(
struct d3d12_state_object *collection,
struct d3d12_state_object_pipeline_data *data,
const D3D12_EXPORT_DESC *exports, unsigned int num_exports)
{
if (!vkd3d_array_reserve((void **)&data->collections, &data->collections_size,
data->collections_count + 1, sizeof(*data->collections)))
return E_OUTOFMEMORY;
/* If a PSO only declares collections, but no pipelines, just inherit various state.
* Also, validates that we have a match across different PSOs. */
if (data->global_root_signature)
{
if (!collection->global_root_signature ||
data->global_root_signature->compatibility_hash != collection->global_root_signature->compatibility_hash)
{
FIXME("Mismatch in global root signature state for PSO and collection.\n");
return E_INVALIDARG;
}
}
else
data->global_root_signature = collection->global_root_signature;
if (data->has_pipeline_config)
{
if (memcmp(&data->pipeline_config, &collection->pipeline_config, sizeof(data->pipeline_config)) != 0)
{
FIXME("Mismatch in pipeline config state for collection and PSO.\n");
return E_INVALIDARG;
}
}
else
{
data->pipeline_config = collection->pipeline_config;
data->has_pipeline_config = true;
}
if (data->shader_config)
{
if (memcmp(data->shader_config, &collection->shader_config, sizeof(*data->shader_config)) != 0)
{
FIXME("Mismatch in shader config state for collection and PSO.\n");
return E_INVALIDARG;
}
}
else
data->shader_config = &collection->shader_config;
data->collections[data->collections_count].object = collection;
data->collections[data->collections_count].num_exports = num_exports;
data->collections[data->collections_count].exports = exports;
vkd3d_array_reserve((void **)&data->vk_libraries, &data->vk_libraries_size,
data->vk_libraries_count + 1, sizeof(*data->vk_libraries));
data->vk_libraries[data->vk_libraries_count] =
data->collections[data->collections_count].object->pipeline_library;
data->collections_count += 1;
data->vk_libraries_count += 1;
return S_OK;
}
static HRESULT d3d12_state_object_parse_subobjects(struct d3d12_state_object *object,
const D3D12_STATE_OBJECT_DESC *desc, struct d3d12_state_object_pipeline_data *data)
const D3D12_STATE_OBJECT_DESC *desc,
struct d3d12_state_object *parent,
struct d3d12_state_object_pipeline_data *data)
{
unsigned int i, j;
HRESULT hr;
if (parent && FAILED(hr = d3d12_state_object_add_collection(parent, data, NULL, 0)))
return hr;
for (i = 0; i < desc->NumSubobjects; i++)
{
@ -435,9 +515,12 @@ static HRESULT d3d12_state_object_parse_subobjects(struct d3d12_state_object *ob
{
case D3D12_STATE_SUBOBJECT_TYPE_STATE_OBJECT_CONFIG:
{
const uint32_t supported_flags =
D3D12_STATE_OBJECT_FLAG_ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL_DEFINITIONS |
D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS;
const D3D12_STATE_OBJECT_CONFIG *object_config = obj->pDesc;
object->flags = object_config->Flags;
if (object->flags & ~D3D12_STATE_OBJECT_FLAG_ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL_DEFINITIONS)
if (object->flags & ~supported_flags)
{
FIXME("Object config flag #%x is not supported.\n", object->flags);
return E_INVALIDARG;
@ -448,13 +531,21 @@ static HRESULT d3d12_state_object_parse_subobjects(struct d3d12_state_object *ob
case D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE:
{
const D3D12_GLOBAL_ROOT_SIGNATURE *rs = obj->pDesc;
if (data->global_root_signature)
struct d3d12_root_signature *new_rs;
struct d3d12_root_signature *old_rs;
new_rs = impl_from_ID3D12RootSignature(rs->pGlobalRootSignature);
old_rs = data->global_root_signature;
if (new_rs && old_rs && new_rs->compatibility_hash != old_rs->compatibility_hash)
{
/* Simplicity for now. */
FIXME("More than one global root signature is used.\n");
FIXME("More than one unique global root signature is used.\n");
return E_INVALIDARG;
}
data->global_root_signature = rs->pGlobalRootSignature;
if (!data->global_root_signature)
data->global_root_signature = new_rs;
break;
}
@ -465,7 +556,7 @@ static HRESULT d3d12_state_object_parse_subobjects(struct d3d12_state_object *ob
* Conflicting definitions seem to cause runtime to choose something
* arbitrary. Just override the low priority default.
* A high priority default association takes precedence if it exists. */
data->low_priority_local_root_signature = rs->pLocalRootSignature;
data->low_priority_local_root_signature = impl_from_ID3D12RootSignature(rs->pLocalRootSignature);
break;
}
@ -574,7 +665,8 @@ static HRESULT d3d12_state_object_parse_subobjects(struct d3d12_state_object *ob
else
{
/* Local root signatures being exported to NULL takes priority as the default local RS. */
data->high_priority_local_root_signature = local_rs->pLocalRootSignature;
data->high_priority_local_root_signature =
impl_from_ID3D12RootSignature(local_rs->pLocalRootSignature);
}
break;
}
@ -589,22 +681,20 @@ static HRESULT d3d12_state_object_parse_subobjects(struct d3d12_state_object *ob
case D3D12_STATE_SUBOBJECT_TYPE_EXISTING_COLLECTION:
{
const D3D12_EXISTING_COLLECTION_DESC *collection = obj->pDesc;
vkd3d_array_reserve((void **)&data->collections, &data->collections_size,
data->collections_count + 1, sizeof(*data->collections));
data->collections[data->collections_count].object = impl_from_ID3D12StateObject(collection->pExistingCollection);
data->collections[data->collections_count].num_exports = collection->NumExports;
data->collections[data->collections_count].exports = collection->pExports;
vkd3d_array_reserve((void **)&data->vk_libraries, &data->vk_libraries_size,
data->vk_libraries_count + 1, sizeof(*data->vk_libraries));
data->vk_libraries[data->vk_libraries_count] = data->collections[data->collections_count].object->pipeline;
data->collections_count += 1;
data->vk_libraries_count += 1;
struct d3d12_state_object *library_state;
library_state = impl_from_ID3D12StateObject(collection->pExistingCollection);
if (FAILED(hr = d3d12_state_object_add_collection(library_state, data,
collection->pExports, collection->NumExports)))
{
return hr;
}
break;
}
case D3D12_STATE_SUBOBJECT_TYPE_NODE_MASK:
/* Just ignore this. It's irrelevant for us. */
break;
default:
FIXME("Unrecognized subobject type: %u.\n", obj->Type);
return E_INVALIDARG;
@ -660,7 +750,8 @@ static uint32_t d3d12_state_object_pipeline_data_find_entry(
offset += data->stages_count;
/* Try to look in collections. */
/* Try to look in collections. We'll only find something in the ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL
* situation. Otherwise entry_points will be NULL. */
for (i = 0; i < data->collections_count; i++)
{
index = d3d12_state_object_pipeline_data_find_entry_inner(data->collections[i].object->entry_points,
@ -833,9 +924,9 @@ static struct d3d12_root_signature *d3d12_state_object_pipeline_data_get_local_r
if (!rs)
{
if (data->high_priority_local_root_signature)
rs = impl_from_ID3D12RootSignature(data->high_priority_local_root_signature);
rs = data->high_priority_local_root_signature;
else if (data->low_priority_local_root_signature)
rs = impl_from_ID3D12RootSignature(data->low_priority_local_root_signature);
rs = data->low_priority_local_root_signature;
else
rs = NULL;
}
@ -847,6 +938,8 @@ static HRESULT d3d12_state_object_get_group_handles(struct d3d12_state_object *o
const struct d3d12_state_object_pipeline_data *data)
{
const struct vkd3d_vk_device_procs *vk_procs = &object->device->vk_procs;
uint32_t collection_export;
int collection_index;
uint32_t group_index;
VkResult vr;
size_t i;
@ -862,6 +955,27 @@ static HRESULT d3d12_state_object_get_group_handles(struct d3d12_state_object *o
if (vr)
return hresult_from_vk_result(vr);
collection_export = data->exports[i].inherited_collection_export_index;
collection_index = data->exports[i].inherited_collection_index;
if (collection_index >= 0)
{
const uint8_t *parent_identifier;
const uint8_t *child_identifier;
parent_identifier = data->collections[collection_index].object->exports[collection_export].identifier;
child_identifier = data->exports[i].identifier;
/* Validate that we get an exact match for SBT handle.
* It appears to work just fine on NV. */
if (memcmp(parent_identifier, child_identifier, D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES) != 0)
{
FIXME("SBT identifiers do not match for parent and child pipelines. "
"Vulkan does not guarantee this, but DXR 1.1 requires this. Cannot use pipeline.\n");
return E_NOTIMPL;
}
}
data->exports[i].stack_size_general = UINT32_MAX;
data->exports[i].stack_size_any = UINT32_MAX;
data->exports[i].stack_size_closest = UINT32_MAX;
@ -998,7 +1112,7 @@ static HRESULT d3d12_state_object_compile_pipeline(struct d3d12_state_object *ob
shader_interface_info.stage = VK_SHADER_STAGE_ALL;
shader_interface_info.xfb_info = NULL;
global_signature = impl_from_ID3D12RootSignature(data->global_root_signature);
global_signature = data->global_root_signature;
if (global_signature)
{
@ -1083,6 +1197,8 @@ static HRESULT d3d12_state_object_compile_pipeline(struct d3d12_state_object *ob
export->closest_stage_index = VK_SHADER_UNUSED_KHR;
export->anyhit_stage_index = VK_SHADER_UNUSED_KHR;
export->intersection_stage_index = VK_SHADER_UNUSED_KHR;
export->inherited_collection_index = -1;
export->inherited_collection_export_index = 0;
export->general_stage = entry->stage;
entry->mangled_entry_point = NULL;
entry->plain_entry_point = NULL;
@ -1182,6 +1298,8 @@ static HRESULT d3d12_state_object_compile_pipeline(struct d3d12_state_object *ob
VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR,
export);
export->inherited_collection_index = -1;
export->inherited_collection_export_index = 0;
data->exports_count += 1;
data->groups_count += 1;
}
@ -1264,6 +1382,16 @@ static HRESULT d3d12_state_object_compile_pipeline(struct d3d12_state_object *ob
if (export->intersection_stage_index != VK_SHADER_UNUSED_KHR)
export->intersection_stage_index += pstage_offset;
/* If we inherited from a real pipeline, we must observe the rules of AddToStateObject().
* SBT pointer must be invariant as well as its contents.
* Vulkan does not guarantee this, but we can validate and accept the pipeline if
* implementation happens to satisfy this rule. */
if (collection->object->type == D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE)
export->inherited_collection_index = (int)i;
else
export->inherited_collection_index = -1;
export->inherited_collection_export_index = input_export->group_index;
data->exports_count += 1;
}
@ -1303,7 +1431,12 @@ static HRESULT d3d12_state_object_compile_pipeline(struct d3d12_state_object *ob
pipeline_create_info.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR;
pipeline_create_info.pNext = NULL;
pipeline_create_info.flags = object->type == D3D12_STATE_OBJECT_TYPE_COLLECTION ?
/* If we allow state object additions, we must first lower this pipeline to a library, and
* then link it to itself so we can use it a library in subsequent PSO creations, but we
* must also be able to trace rays from the library. */
pipeline_create_info.flags = (object->type == D3D12_STATE_OBJECT_TYPE_COLLECTION ||
(object->flags & D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS)) ?
VK_PIPELINE_CREATE_LIBRARY_BIT_KHR : 0;
/* FIXME: What if we have no global root signature? */
@ -1363,7 +1496,27 @@ static HRESULT d3d12_state_object_compile_pipeline(struct d3d12_state_object *ob
dynamic_state.pDynamicStates = dynamic_states;
vr = VK_CALL(vkCreateRayTracingPipelinesKHR(object->device->vk_device, VK_NULL_HANDLE,
VK_NULL_HANDLE, 1, &pipeline_create_info, NULL, &object->pipeline));
VK_NULL_HANDLE, 1, &pipeline_create_info, NULL,
(pipeline_create_info.flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) ?
&object->pipeline_library : &object->pipeline));
if (vr == VK_SUCCESS && (object->flags & D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS) &&
object->type == D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE)
{
/* TODO: Is it actually valid to inherit other pipeline libraries while creating a pipeline library? */
pipeline_create_info.flags &= ~VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
pipeline_create_info.pStages = NULL;
pipeline_create_info.pGroups = NULL;
pipeline_create_info.stageCount = 0;
pipeline_create_info.groupCount = 0;
library_info.libraryCount = 1;
library_info.pLibraries = &object->pipeline_library;
/* Self-link the pipeline library. */
vr = VK_CALL(vkCreateRayTracingPipelinesKHR(object->device->vk_device, VK_NULL_HANDLE,
VK_NULL_HANDLE, 1, &pipeline_create_info, NULL, &object->pipeline));
}
if (vr)
return hresult_from_vk_result(vr);
@ -1387,6 +1540,11 @@ static HRESULT d3d12_state_object_compile_pipeline(struct d3d12_state_object *ob
data->exports_size = 0;
data->exports_count = 0;
d3d12_root_signature_inc_ref(object->global_root_signature = global_signature);
object->shader_config = *data->shader_config;
object->pipeline_config = data->pipeline_config;
/* Spec says we need to hold a reference to the collection object, but it doesn't show up in API,
* so we must assume private reference. */
if (data->collections_count)
@ -1417,7 +1575,8 @@ static HRESULT d3d12_state_object_compile_pipeline(struct d3d12_state_object *ob
static HRESULT d3d12_state_object_init(struct d3d12_state_object *object,
struct d3d12_device *device,
const D3D12_STATE_OBJECT_DESC *desc)
const D3D12_STATE_OBJECT_DESC *desc,
struct d3d12_state_object *parent)
{
struct d3d12_state_object_pipeline_data data;
HRESULT hr = S_OK;
@ -1429,7 +1588,7 @@ static HRESULT d3d12_state_object_init(struct d3d12_state_object *object,
object->type = desc->Type;
memset(&data, 0, sizeof(data));
if (FAILED(hr = d3d12_state_object_parse_subobjects(object, desc, &data)))
if (FAILED(hr = d3d12_state_object_parse_subobjects(object, desc, parent, &data)))
goto fail;
if (FAILED(hr = d3d12_state_object_compile_pipeline(object, &data)))
@ -1449,6 +1608,7 @@ fail:
}
HRESULT d3d12_state_object_create(struct d3d12_device *device, const D3D12_STATE_OBJECT_DESC *desc,
struct d3d12_state_object *parent,
struct d3d12_state_object **state_object)
{
struct d3d12_state_object *object;
@ -1457,7 +1617,7 @@ HRESULT d3d12_state_object_create(struct d3d12_device *device, const D3D12_STATE
if (!(object = vkd3d_calloc(1, sizeof(*object))))
return E_OUTOFMEMORY;
hr = d3d12_state_object_init(object, device, desc);
hr = d3d12_state_object_init(object, device, desc, parent);
if (FAILED(hr))
{
vkd3d_free(object);
@ -1467,3 +1627,34 @@ HRESULT d3d12_state_object_create(struct d3d12_device *device, const D3D12_STATE
*state_object = object;
return S_OK;
}
HRESULT d3d12_state_object_add(struct d3d12_device *device, const D3D12_STATE_OBJECT_DESC *desc,
struct d3d12_state_object *parent, struct d3d12_state_object **state_object)
{
unsigned int i;
HRESULT hr;
if (!parent)
return E_INVALIDARG;
if (!(parent->flags & D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS))
return E_INVALIDARG;
if (desc->Type != D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE)
return E_INVALIDARG;
/* Addition must also allow this scenario. */
for (i = 0; i < desc->NumSubobjects; i++)
{
if (desc->pSubobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_STATE_OBJECT_CONFIG)
{
const D3D12_STATE_OBJECT_CONFIG *config = desc->pSubobjects[i].pDesc;
if (config->Flags & D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS)
break;
}
}
if (i == desc->NumSubobjects)
return E_INVALIDARG;
hr = d3d12_state_object_create(device, desc, parent, state_object);
return hr;
}

View File

@ -3374,6 +3374,14 @@ struct d3d12_state_object_identifier
/* The index into vkGetShaderStackSize and friends for pGroups[]. */
uint32_t group_index;
/* For AddToStateObject(). We need to return the identifier pointer
* for the parent, not the child. This makes it easy to validate that
* we observe the same SBT handles as specified by DXR 1.1. */
/* If -1, ignore, otherwise, redirect. */
int inherited_collection_index;
uint32_t inherited_collection_export_index;
};
struct d3d12_state_object_stack_info
@ -3407,7 +3415,14 @@ struct d3d12_state_object
/* Normally stages_count == entry_points_count, but entry_points is the entry points we
* export externally, and stages_count matches pStages[] size for purposes of index fixups. */
/* Can be bound. */
VkPipeline pipeline;
/* Can be used as a library. */
VkPipeline pipeline_library;
/* Can be inherited by AddToStateObject(). */
D3D12_RAYTRACING_PIPELINE_CONFIG1 pipeline_config;
D3D12_RAYTRACING_SHADER_CONFIG shader_config;
struct
{
@ -3424,10 +3439,16 @@ struct d3d12_state_object
struct d3d12_state_object **collections;
size_t collections_count;
struct d3d12_root_signature *global_root_signature;
struct vkd3d_private_store private_store;
};
HRESULT d3d12_state_object_create(struct d3d12_device *device, const D3D12_STATE_OBJECT_DESC *desc,
struct d3d12_state_object *parent,
struct d3d12_state_object **object);
HRESULT d3d12_state_object_add(struct d3d12_device *device, const D3D12_STATE_OBJECT_DESC *desc,
struct d3d12_state_object *parent,
struct d3d12_state_object **object);
static inline struct d3d12_state_object *impl_from_ID3D12StateObject(ID3D12StateObject *iface)

View File

@ -1395,6 +1395,21 @@ static unsigned int rt_pso_factory_add_existing_collection(struct rt_pso_factory
return rt_pso_factory_add_subobject(factory, &desc);
}
static unsigned int rt_pso_factory_add_default_node_mask(struct rt_pso_factory* factory)
{
D3D12_STATE_SUBOBJECT desc;
D3D12_NODE_MASK *mask;
mask = rt_pso_factory_calloc(factory, 1, sizeof(*mask));
/* This node mask is weird and some runtimes have bugs. We'll just ignore it anyways in vkd3d-proton.
* https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_state_subobject_type */
mask->NodeMask = 1;
desc.Type = D3D12_STATE_SUBOBJECT_TYPE_NODE_MASK;
desc.pDesc = mask;
return rt_pso_factory_add_subobject(factory, &desc);
}
static ID3D12StateObject *rt_pso_factory_compile(struct raytracing_test_context *context,
struct rt_pso_factory *factory,
D3D12_STATE_OBJECT_TYPE type)
@ -1433,6 +1448,62 @@ static ID3D12StateObject *rt_pso_factory_compile(struct raytracing_test_context
return rt_pso;
}
static ID3D12StateObject *rt_pso_add_to_state_object(ID3D12Device5 *device, ID3D12StateObject *parent, ID3D12StateObject *addition,
const D3D12_HIT_GROUP_DESC *hit_group)
{
D3D12_EXISTING_COLLECTION_DESC existing;
ID3D12StateObject *new_state_object;
D3D12_STATE_OBJECT_CONFIG config;
D3D12_STATE_SUBOBJECT subobj[3];
D3D12_STATE_OBJECT_DESC desc;
ID3D12Device7 *device7;
HRESULT hr;
desc.NumSubobjects = 1;
desc.Type = D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE;
desc.pSubobjects = subobj;
subobj[0].Type = D3D12_STATE_SUBOBJECT_TYPE_EXISTING_COLLECTION;
subobj[0].pDesc = &existing;
existing.NumExports = 0;
existing.pExports = NULL;
existing.pExistingCollection = addition;
if (hit_group)
{
subobj[desc.NumSubobjects].Type = D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP;
subobj[desc.NumSubobjects].pDesc = hit_group;
desc.NumSubobjects++;
}
if (FAILED(ID3D12Device5_QueryInterface(device, &IID_ID3D12Device7, (void**)&device7)))
{
skip("Failed to query ID3D12Device7.\n");
return NULL;
}
/* Have to add ALLOW_STATE_OBJECT_ADDITIONS for both parent and addition. */
hr = ID3D12Device7_AddToStateObject(device7, &desc, parent, &IID_ID3D12StateObject, (void**)&new_state_object);
ok(hr == E_INVALIDARG, "Unexpected hr #%x.\n", hr);
config.Flags = D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS;
subobj[desc.NumSubobjects].Type = D3D12_STATE_SUBOBJECT_TYPE_STATE_OBJECT_CONFIG;
subobj[desc.NumSubobjects].pDesc = &config;
desc.NumSubobjects++;
/* Type must be RAYTRACING_PIPELINE. */
desc.Type = D3D12_STATE_OBJECT_TYPE_COLLECTION;
hr = ID3D12Device7_AddToStateObject(device7, &desc, parent, &IID_ID3D12StateObject, (void**)&new_state_object);
ok(hr == E_INVALIDARG, "Unexpected hr #%x.\n", hr);
desc.Type = D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE;
hr = ID3D12Device7_AddToStateObject(device7, &desc, parent, &IID_ID3D12StateObject, (void**)&new_state_object);
ok(SUCCEEDED(hr), "Failed to AddToStateObject, hr #%x.\n", hr);
if (FAILED(hr))
new_state_object = NULL;
ID3D12Device7_Release(device7);
return new_state_object;
}
enum rt_test_mode
{
TEST_MODE_PLAIN,
@ -1442,6 +1513,7 @@ enum rt_test_mode
TEST_MODE_TRACE_RAY_SKIP_AABBS,
TEST_MODE_PSO_SKIP_TRIANGLES,
TEST_MODE_PSO_SKIP_AABBS,
TEST_MODE_PSO_ADD_TO_STATE_OBJECT,
};
static ID3D12StateObject *create_rt_collection(struct raytracing_test_context *context,
@ -1454,8 +1526,19 @@ static ID3D12StateObject *create_rt_collection(struct raytracing_test_context *c
rt_pso_factory_init(&factory);
rt_pso_factory_add_state_object_config(&factory,
rt_pso_factory_add_default_node_mask(&factory);
if (test_mode == TEST_MODE_PSO_ADD_TO_STATE_OBJECT)
{
rt_pso_factory_add_state_object_config(&factory,
D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS |
D3D12_STATE_OBJECT_FLAG_ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL_DEFINITIONS);
}
else
{
rt_pso_factory_add_state_object_config(&factory,
D3D12_STATE_OBJECT_FLAG_ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL_DEFINITIONS);
}
if (test_mode == TEST_MODE_PSO_SKIP_TRIANGLES)
rt_pso_factory_add_pipeline_config1(&factory, 1, D3D12_RAYTRACING_PIPELINE_FLAG_SKIP_TRIANGLES);
@ -1527,6 +1610,7 @@ static void test_raytracing_pipeline(enum rt_test_mode mode, D3D12_RAYTRACING_TI
ID3D12Resource *sbt_colors_buffer;
ID3D12Resource *postbuild_buffer;
unsigned int i, descriptor_size;
ID3D12StateObject *rt_pso_added;
ID3D12RootSignature *global_rs;
struct test_geometry test_geom;
ID3D12RootSignature *local_rs;
@ -1687,8 +1771,12 @@ static void test_raytracing_pipeline(enum rt_test_mode mode, D3D12_RAYTRACING_TI
unsigned int local_rs_index;
rt_pso_factory_init(&factory);
rt_pso_factory_add_default_node_mask(&factory);
rt_pso_factory_add_state_object_config(&factory, D3D12_STATE_OBJECT_FLAG_NONE);
if (mode == TEST_MODE_PSO_ADD_TO_STATE_OBJECT)
rt_pso_factory_add_state_object_config(&factory, D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS);
else
rt_pso_factory_add_state_object_config(&factory, D3D12_STATE_OBJECT_FLAG_NONE);
rt_pso_factory_add_global_root_signature(&factory, global_rs);
if (mode == TEST_MODE_PSO_SKIP_TRIANGLES)
@ -1710,27 +1798,128 @@ static void test_raytracing_pipeline(enum rt_test_mode mode, D3D12_RAYTRACING_TI
rt_pso_factory_add_subobject_to_exports_association(&factory,
local_rs_index, 0, NULL);
rt_pso_factory_add_existing_collection(&factory, rt_object_library_tri, 0, NULL);
rt_pso_factory_add_existing_collection(&factory, rt_object_library_aabb, 0, NULL);
/* Defer this. */
if (mode != TEST_MODE_PSO_ADD_TO_STATE_OBJECT)
{
rt_pso_factory_add_existing_collection(&factory, rt_object_library_tri, 0, NULL);
rt_pso_factory_add_existing_collection(&factory, rt_object_library_aabb, 0, NULL);
memset(&hit_group, 0, sizeof(hit_group));
hit_group.Type = D3D12_HIT_GROUP_TYPE_TRIANGLES;
hit_group.ClosestHitShaderImport = u"XRayClosest";
hit_group.HitGroupExport = u"XRayHit2";
rt_pso_factory_add_hit_group(&factory, &hit_group);
}
rt_pso = rt_pso_factory_compile(&context, &factory, D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE);
if (mode != TEST_MODE_PSO_ADD_TO_STATE_OBJECT)
{
/* Docs say there should be ref-count of the collection, but apparently, that refcount is private. */
ref_count = ID3D12StateObject_AddRef(rt_object_library_tri);
ok(ref_count == 2, "Collection ref count is %u.\n", ref_count);
ID3D12StateObject_Release(rt_object_library_tri);
ref_count = ID3D12StateObject_AddRef(rt_object_library_aabb);
ok(ref_count == 2, "Collection ref count is %u.\n", ref_count);
ID3D12StateObject_Release(rt_object_library_aabb);
}
}
else
rt_pso = NULL;
/* Add two iterations of AddToStateObject so we have test coverage of that scenario. */
if (mode == TEST_MODE_PSO_ADD_TO_STATE_OBJECT && rt_pso)
{
D3D12_HIT_GROUP_DESC hit_group;
memset(&hit_group, 0, sizeof(hit_group));
hit_group.Type = D3D12_HIT_GROUP_TYPE_TRIANGLES;
hit_group.ClosestHitShaderImport = u"XRayClosest";
hit_group.HitGroupExport = u"XRayHit2";
rt_pso_factory_add_hit_group(&factory, &hit_group);
rt_pso = rt_pso_factory_compile(&context, &factory, D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE);
/* Docs say there should be ref-count of the collection, but apparently, that refcount is private. */
rt_pso_added = rt_pso_add_to_state_object(context.device5, rt_pso, rt_object_library_tri, &hit_group);
ID3D12StateObject_Release(rt_pso);
rt_pso = rt_pso_added;
ref_count = ID3D12StateObject_AddRef(rt_object_library_tri);
ok(ref_count == 2, "Collection ref count is %u.\n", ref_count);
ID3D12StateObject_Release(rt_object_library_tri);
}
if (mode == TEST_MODE_PSO_ADD_TO_STATE_OBJECT && rt_pso)
{
uint8_t pre_ray_miss_data[D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES];
uint8_t pre_ray_gen_data[D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES];
uint8_t pre_hit_data[D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES];
ID3D12StateObjectProperties *combined_props;
static const WCHAR ray_miss[] = u"XRayMiss";
static const WCHAR ray_hit2[] = u"XRayHit2";
static const WCHAR ray_gen[] = u"XRayGen";
ID3D12StateObjectProperties *post_props;
ID3D12StateObjectProperties *pre_props;
uint8_t *combined_ray_miss;
uint8_t *combined_ray_gen;
uint8_t *combined_hit;
uint8_t *post_ray_miss;
uint8_t *post_ray_gen;
uint8_t *post_hit;
uint8_t *pre_ray_miss;
uint8_t *pre_ray_gen;
uint8_t *pre_hit;
ID3D12StateObject_QueryInterface(rt_pso, &IID_ID3D12StateObjectProperties, (void**)&pre_props);
pre_ray_gen = ID3D12StateObjectProperties_GetShaderIdentifier(pre_props, ray_gen);
pre_hit = ID3D12StateObjectProperties_GetShaderIdentifier(pre_props, ray_hit2);
pre_ray_miss = ID3D12StateObjectProperties_GetShaderIdentifier(pre_props, ray_miss);
memcpy(pre_ray_miss_data, pre_ray_miss, sizeof(pre_ray_miss_data));
memcpy(pre_ray_gen_data, pre_ray_gen, sizeof(pre_ray_gen_data));
memcpy(pre_hit_data, pre_hit, sizeof(pre_hit_data));
rt_pso_added = rt_pso_add_to_state_object(context.device5, rt_pso, rt_object_library_aabb, NULL);
ID3D12StateObject_QueryInterface(rt_pso, &IID_ID3D12StateObjectProperties, (void**)&post_props);
ID3D12StateObject_QueryInterface(rt_pso_added, &IID_ID3D12StateObjectProperties, (void**)&combined_props);
post_ray_gen = ID3D12StateObjectProperties_GetShaderIdentifier(post_props, ray_gen);
post_hit = ID3D12StateObjectProperties_GetShaderIdentifier(post_props, ray_hit2);
post_ray_miss = ID3D12StateObjectProperties_GetShaderIdentifier(post_props, ray_miss);
combined_ray_gen = ID3D12StateObjectProperties_GetShaderIdentifier(combined_props, ray_gen);
combined_hit = ID3D12StateObjectProperties_GetShaderIdentifier(combined_props, ray_hit2);
combined_ray_miss = ID3D12StateObjectProperties_GetShaderIdentifier(combined_props, ray_miss);
/* The docs do talk about taking some weird internal locks to deal with AddToStateObject(), so verify
* that we don't have to return the parent property pointer here. */
ok(pre_props == post_props, "Unexpected result in interface check.\n");
ok(combined_props != post_props, "Unexpected result in interface check.\n");
ok(pre_ray_gen == post_ray_gen, "Unexpected SBT pointers.\n");
ok(pre_hit == post_hit, "Unexpected SBT pointers.\n");
ok(pre_ray_miss == post_ray_miss, "Unexpected SBT pointers.\n");
/* Apparently, we have to inherit the pointer to the SBT directly. */
ok(combined_ray_gen == post_ray_gen, "Unexpected SBT pointers.\n");
ok(combined_hit == post_hit, "Unexpected SBT pointers.\n");
ok(combined_ray_miss == post_ray_miss, "Unexpected SBT pointers.\n");
/* Verify that we cannot modify the SBT data in place. */
ok(memcmp(combined_hit, pre_hit_data, sizeof(pre_hit_data)) == 0, "Detected variance for existing SBT entries.\n");
ok(memcmp(combined_ray_gen, pre_ray_gen_data, sizeof(pre_ray_gen_data)) == 0, "Detected variance for existing SBT entries.\n");
ok(memcmp(combined_ray_miss, pre_ray_miss_data, sizeof(pre_ray_miss_data)) == 0, "Detected variance for existing SBT entries.\n");
ID3D12StateObject_Release(rt_pso);
rt_pso = rt_pso_added;
ref_count = ID3D12StateObject_AddRef(rt_object_library_aabb);
ok(ref_count == 2, "Collection ref count is %u.\n", ref_count);
ID3D12StateObject_Release(rt_object_library_aabb);
ID3D12StateObjectProperties_Release(pre_props);
ID3D12StateObjectProperties_Release(post_props);
ID3D12StateObjectProperties_Release(combined_props);
}
else
rt_pso = NULL;
/* Docs say that refcount should be held by RTPSO, but apparently it doesn't on native drivers. */
ID3D12RootSignature_AddRef(global_rs);
@ -2197,6 +2386,7 @@ void test_raytracing(void)
{ TEST_MODE_TRACE_RAY_SKIP_AABBS, D3D12_RAYTRACING_TIER_1_1, "TraceRaySkipAABBs" },
{ TEST_MODE_PSO_SKIP_TRIANGLES, D3D12_RAYTRACING_TIER_1_1, "PSOSkipTriangles" },
{ TEST_MODE_PSO_SKIP_AABBS, D3D12_RAYTRACING_TIER_1_1, "PSOSkipAABBs" },
{ TEST_MODE_PSO_ADD_TO_STATE_OBJECT, D3D12_RAYTRACING_TIER_1_1, "AddToStateObject" },
};
unsigned int i;