Compare commits

...

8 Commits

Author SHA1 Message Date
Hans-Kristian Arntzen b817f972f4 tests: Add test coverage for two stages of AddToStateObject(). 2 months ago
Hans-Kristian Arntzen b9c575d678 tests: Add test for AddToStateObject. 2 months ago
Hans-Kristian Arntzen ab85bdf94f tests: Add default NODE_MASK state object to RTPSO tests. 2 months ago
Hans-Kristian Arntzen 3d1b2d2042 vkd3d: Implement AddToStateObject(). 2 months ago
Hans-Kristian Arntzen ccf927da7f vkd3d: Hold private ownership over global root signature. 2 months ago
Hans-Kristian Arntzen c940d548f4 vkd3d: Allow different but compatible global root signature objects. 2 months ago
Hans-Kristian Arntzen 53309d05a1 vkd3d: Ignore NODE_MASK subobjects. 2 months ago
Hans-Kristian Arntzen 71940797d1 vkd3d: Check for redundant dynamic state in some cases. 2 months ago
  1. 56
      libs/vkd3d/command.c
  2. 22
      libs/vkd3d/device.c
  3. 257
      libs/vkd3d/raytracing_pipeline.c
  4. 21
      libs/vkd3d/vkd3d_private.h
  5. 210
      tests/d3d12_raytracing.c

@ -6810,14 +6810,14 @@ static void STDMETHODCALLTYPE d3d12_command_list_OMSetBlendFactor(d3d12_command_
{
struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList(iface);
struct vkd3d_dynamic_state *dyn_state = &list->dynamic_state;
unsigned int i;
TRACE("iface %p, blend_factor %p.\n", iface, blend_factor);
for (i = 0; i < 4; i++)
dyn_state->blend_constants[i] = blend_factor[i];
dyn_state->dirty_flags |= VKD3D_DYNAMIC_STATE_BLEND_CONSTANTS;
if (memcmp(dyn_state->blend_constants, blend_factor, sizeof(dyn_state->blend_constants)) != 0)
{
memcpy(dyn_state->blend_constants, blend_factor, sizeof(dyn_state->blend_constants));
dyn_state->dirty_flags |= VKD3D_DYNAMIC_STATE_BLEND_CONSTANTS;
}
}
static void STDMETHODCALLTYPE d3d12_command_list_OMSetStencilRef(d3d12_command_list_iface *iface,
@ -6828,8 +6828,11 @@ static void STDMETHODCALLTYPE d3d12_command_list_OMSetStencilRef(d3d12_command_l
TRACE("iface %p, stencil_ref %u.\n", iface, stencil_ref);
dyn_state->stencil_reference = stencil_ref;
dyn_state->dirty_flags |= VKD3D_DYNAMIC_STATE_STENCIL_REFERENCE;
if (dyn_state->stencil_reference != stencil_ref)
{
dyn_state->stencil_reference = stencil_ref;
dyn_state->dirty_flags |= VKD3D_DYNAMIC_STATE_STENCIL_REFERENCE;
}
}
static void STDMETHODCALLTYPE d3d12_command_list_SetPipelineState(d3d12_command_list_iface *iface,
@ -9596,10 +9599,12 @@ static void STDMETHODCALLTYPE d3d12_command_list_OMSetDepthBounds(d3d12_command_
TRACE("iface %p, min %.8e, max %.8e.\n", iface, min, max);
dyn_state->min_depth_bounds = min;
dyn_state->max_depth_bounds = max;
dyn_state->dirty_flags |= VKD3D_DYNAMIC_STATE_DEPTH_BOUNDS;
if (dyn_state->min_depth_bounds != min || dyn_state->max_depth_bounds != max)
{
dyn_state->min_depth_bounds = min;
dyn_state->max_depth_bounds = max;
dyn_state->dirty_flags |= VKD3D_DYNAMIC_STATE_DEPTH_BOUNDS;
}
}
static void STDMETHODCALLTYPE d3d12_command_list_SetSamplePositions(d3d12_command_list_iface *iface,
@ -10103,22 +10108,31 @@ static uint32_t vk_fragment_size_from_d3d12(D3D12_AXIS_SHADING_RATE axis_rate)
static void STDMETHODCALLTYPE d3d12_command_list_RSSetShadingRate(d3d12_command_list_iface *iface,
D3D12_SHADING_RATE base, const D3D12_SHADING_RATE_COMBINER *combiners)
{
VkFragmentShadingRateCombinerOpKHR combiner_ops[D3D12_RS_SET_SHADING_RATE_COMBINER_COUNT];
struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList(iface);
struct vkd3d_dynamic_state *dyn_state = &list->dynamic_state;
VkExtent2D fragment_size;
uint32_t i;
TRACE("iface %p, base %#x, combiners %p\n", iface, base, combiners);
dyn_state->fragment_shading_rate.fragment_size = (VkExtent2D) {
vk_fragment_size_from_d3d12(D3D12_GET_COARSE_SHADING_RATE_X_AXIS(base)),
vk_fragment_size_from_d3d12(D3D12_GET_COARSE_SHADING_RATE_Y_AXIS(base))
};
fragment_size.width = vk_fragment_size_from_d3d12(D3D12_GET_COARSE_SHADING_RATE_X_AXIS(base));
fragment_size.height = vk_fragment_size_from_d3d12(D3D12_GET_COARSE_SHADING_RATE_Y_AXIS(base));
for (uint32_t i = 0; i < D3D12_RS_SET_SHADING_RATE_COMBINER_COUNT; i++)
dyn_state->fragment_shading_rate.combiner_ops[i] = combiners
? vk_shading_rate_combiner_from_d3d12(combiners[i])
: VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
dyn_state->dirty_flags |= VKD3D_DYNAMIC_STATE_FRAGMENT_SHADING_RATE;
for (i = 0; i < D3D12_RS_SET_SHADING_RATE_COMBINER_COUNT; i++)
{
combiner_ops[i] = combiners ?
vk_shading_rate_combiner_from_d3d12(combiners[i]) :
VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
}
if (memcmp(&fragment_size, &dyn_state->fragment_shading_rate.fragment_size, sizeof(fragment_size)) != 0 ||
memcmp(combiner_ops, dyn_state->fragment_shading_rate.combiner_ops, sizeof(combiner_ops)) != 0)
{
dyn_state->fragment_shading_rate.fragment_size = fragment_size;
memcpy(dyn_state->fragment_shading_rate.combiner_ops, combiner_ops, sizeof(combiner_ops));
dyn_state->dirty_flags |= VKD3D_DYNAMIC_STATE_FRAGMENT_SHADING_RATE;
}
}
static void STDMETHODCALLTYPE d3d12_command_list_RSSetShadingRateImage(d3d12_command_list_iface *iface,

@ -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,

@ -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;
}

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

@ -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;

Loading…
Cancel
Save