blob: 468ce93da9ba501b9222df5700df7090f50d7ff8 [file] [edit]
/*
* Copyright 2024 Hans-Kristian Arntzen for Valve Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API
#include "vkd3d_private.h"
#include "vkd3d_string.h"
bool vkd3d_export_equal(LPCWSTR export, const struct vkd3d_shader_library_entry_point *entry)
{
return vkd3d_export_strequal(export, entry->mangled_entry_point) ||
vkd3d_export_strequal(export, entry->plain_entry_point);
}
bool d3d12_state_object_association_data_equal(
const struct d3d12_state_object_association *a,
const struct d3d12_state_object_association *b)
{
/* Normalize dummy root signatures. */
if (a && (a->kind == VKD3D_SHADER_SUBOBJECT_KIND_GLOBAL_ROOT_SIGNATURE ||
a->kind == VKD3D_SHADER_SUBOBJECT_KIND_LOCAL_ROOT_SIGNATURE))
{
if (!a->root_signature || a->root_signature->layout_compatibility_hash == 0)
a = NULL;
}
if (b && (b->kind == VKD3D_SHADER_SUBOBJECT_KIND_GLOBAL_ROOT_SIGNATURE ||
b->kind == VKD3D_SHADER_SUBOBJECT_KIND_LOCAL_ROOT_SIGNATURE))
{
if (!b->root_signature || b->root_signature->layout_compatibility_hash == 0)
b = NULL;
}
if (!a && !b)
return true;
if ((!!a) != (!!b))
return false;
if (a->kind != b->kind)
return false;
switch (a->kind)
{
case VKD3D_SHADER_SUBOBJECT_KIND_RAYTRACING_PIPELINE_CONFIG1:
return memcmp(&a->pipeline_config, &b->pipeline_config, sizeof(a->pipeline_config)) == 0;
case VKD3D_SHADER_SUBOBJECT_KIND_RAYTRACING_SHADER_CONFIG:
return memcmp(&a->shader_config, &b->shader_config, sizeof(a->shader_config)) == 0;
case VKD3D_SHADER_SUBOBJECT_KIND_LOCAL_ROOT_SIGNATURE:
case VKD3D_SHADER_SUBOBJECT_KIND_GLOBAL_ROOT_SIGNATURE:
/* Here we need exact checks so we can resolve conflicts. */
return d3d12_root_signature_is_pipeline_compatible(a->root_signature, b->root_signature);
default:
break;
}
return false;
}
static bool d3d12_state_object_find_explicit_assignment_override(
enum vkd3d_shader_subobject_kind kind,
const struct d3d12_state_object_association *associations,
size_t associations_count,
const struct d3d12_state_object_association *association)
{
size_t i;
for (i = 0; i < associations_count; i++)
{
/* Don't care about explicit DXIL subobject to entry point assignments since even default declared RTPSO objects
* will override them. */
if (associations[i].priority != VKD3D_ASSOCIATION_PRIORITY_EXPLICIT)
continue;
if (d3d12_state_object_association_data_equal(association, &associations[i]))
return true;
}
return false;
}
const struct d3d12_state_object_association *d3d12_state_object_find_association(
enum vkd3d_shader_subobject_kind kind,
const struct d3d12_state_object_association *associations,
size_t associations_count,
const struct D3D12_HIT_GROUP_DESC **hit_groups,
size_t hit_groups_count,
const struct vkd3d_shader_library_entry_point *entry,
LPCWSTR export)
{
const struct d3d12_state_object_association *hit_group_association = NULL;
const struct d3d12_state_object_association *association = NULL;
const D3D12_HIT_GROUP_DESC *hit_group;
bool conflict = false;
bool match;
size_t i;
for (i = 0; i < associations_count; i++)
{
if (associations[i].kind != kind)
continue;
if (association && associations[i].priority < association->priority)
continue;
if (associations[i].export)
{
if (entry)
match = vkd3d_export_equal(associations[i].export, entry);
else
match = vkd3d_export_strequal(associations[i].export, export);
}
else
match = true;
if (match)
{
if (!association || associations[i].priority > association->priority)
{
association = &associations[i];
conflict = false;
}
else if (!d3d12_state_object_association_data_equal(association, &associations[i]))
{
/* We might get a higher priority match later that makes this conflict irrelevant. */
conflict = true;
if (association->priority == VKD3D_ASSOCIATION_PRIORITY_DECLARED_STATE_OBJECT)
{
/* Attempt to tie-break. There is a special rule where if an object is assigned explicitly,
* it takes lower precedence when it comes to resolving default associations.
* Attempt to tie-break this conflict.
* Normally, explicit default assignment should be used, but it is not required :( */
bool has_explicit_association_candidate;
bool has_explicit_association_existing;
has_explicit_association_existing =
d3d12_state_object_find_explicit_assignment_override(
kind, associations, associations_count, association);
has_explicit_association_candidate =
d3d12_state_object_find_explicit_assignment_override(
kind, associations, associations_count, &associations[i]);
if (has_explicit_association_candidate != has_explicit_association_existing)
{
/* Somewhat inverse. If the existing association has an explicit one, discount it here
* and accept the new one. */
if (has_explicit_association_existing)
association = &associations[i];
conflict = false;
}
}
}
}
}
hit_group_association = NULL;
/* If we didn't find an association for this entry point, we might have an association
* in a hit group export. Alternatively, we might have a higher priority association which is only
* set for the hit group.
* FIXME: Is it possible to have multiple hit groups, all referring to same entry point, while using
* different root signatures for the different instances of the entry point? :| */
if (entry && (entry->stage == VK_SHADER_STAGE_ANY_HIT_BIT_KHR ||
entry->stage == VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR ||
entry->stage == VK_SHADER_STAGE_INTERSECTION_BIT_KHR))
{
for (i = 0; i < hit_groups_count; i++)
{
hit_group = hit_groups[i];
match = vkd3d_export_equal(hit_group->ClosestHitShaderImport, entry) ||
vkd3d_export_equal(hit_group->AnyHitShaderImport, entry) ||
vkd3d_export_equal(hit_group->IntersectionShaderImport, entry);
if (match)
{
hit_group_association = d3d12_state_object_find_association(
kind, associations, associations_count, hit_groups, hit_groups_count, NULL, hit_group->HitGroupExport);
/* Accept hit group association if it has a higher priority, otherwise tie-break to the export itself. */
if (hit_group_association && hit_group_association->priority > association->priority)
{
association = hit_group_association;
conflict = false;
break;
}
}
}
}
if (conflict)
{
/* Ending up with NULL might be intentional. It is only an error if a shader accesses resources that needs a
* particular root signature. State objects however are more fatal since they are required. */
if (kind == VKD3D_SHADER_SUBOBJECT_KIND_LOCAL_ROOT_SIGNATURE)
WARN("Conflicting local root signatures defined for same export, using NULL local root signature.\n");
else if (kind == VKD3D_SHADER_SUBOBJECT_KIND_GLOBAL_ROOT_SIGNATURE)
WARN("Conflicting global root signatures defined for same export, using NULL global root signature.\n");
else
ERR("Conflicting state object defined for same export.\n");
return NULL;
}
return association;
}